Skip to content

Instantly share code, notes, and snippets.

@incanus
Last active June 23, 2017 23:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save incanus/7e19ca99f76c0b3d32ae147872325722 to your computer and use it in GitHub Desktop.
Save incanus/7e19ca99f76c0b3d32ae147872325722 to your computer and use it in GitHub Desktop.
class Overlay {
var image: UIImage?
var initialMetersPerPoint: Double?
var initialScale: Double?
var annotation: MGLPointAnnotation?
}
class ViewController: UIViewController, MGLMapViewDelegate {
var map: MGLMapView?
override func viewDidLoad() {
super.viewDidLoad()
map?.isPitchEnabled = false
map?.delegate = self
let image = UIImage(named: "newark.jpg")!
let nw = CLLocation(latitude: 40.773941, longitude: -74.22655)
let ne = CLLocation(latitude: 40.773941, longitude: -74.12544)
let sw = CLLocation(latitude: 40.712216, longitude: -74.22655)
let se = CLLocation(latitude: 40.712216, longitude: -74.12544)
let topDistance = ne.distance(from: nw)
let currentMeters = map?.metersPerPoint(atLatitude: (nw.coordinate.latitude + sw.coordinate.latitude) / 2)
let topPoints = topDistance / currentMeters!
let initialScale = Double(topPoints) / Double(image.size.width)
let annotation = MGLPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: (nw.coordinate.latitude + sw.coordinate.latitude) / 2,
longitude: (ne.coordinate.longitude + nw.coordinate.longitude) / 2)
overlay = Overlay()
overlay?.image = image
overlay?.initialMetersPerPoint = currentMeters
overlay?.initialScale = initialScale
overlay?.annotation = annotation
map?.addAnnotation(annotation)
}
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
guard annotation is MGLPointAnnotation else {
return nil
}
let reuseIdentifier = "overlay"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)
if annotationView == nil,
let overlay = overlay,
let image = overlay.image,
let initialScale = overlay.initialScale {
let imageView = UIImageView(image: image)
imageView.frame = CGRect(x: 0, y: 0, width: imageView.bounds.size.width * CGFloat(initialScale), height: imageView.bounds.size.height * CGFloat(initialScale))
annotationView = MGLAnnotationView(reuseIdentifier: reuseIdentifier)
annotationView?.addSubview(imageView)
imageView.center = annotationView!.center
annotationView?.alpha = 0.5
}
return annotationView
}
func mapViewRegionIsChanging(_ mapView: MGLMapView) {
if let overlay = overlay,
let initialMetersPerPoint = overlay.initialMetersPerPoint,
let annotation = overlay.annotation,
let annotationView = mapView.view(for: annotation) {
let factor = CGFloat(initialMetersPerPoint / mapView.metersPerPoint(atLatitude: annotation.coordinate.latitude))
annotationView.transform = CGAffineTransform(scaleX: factor, y: factor).rotated(by: MGLRadiansFromDegrees(-mapView.direction))
}
}
}
@incanus
Copy link
Author

incanus commented Nov 17, 2016

Here is a workaround for mapbox/mapbox-gl-native#1350 in the style of Google's web library GroundOverlay.

@trick14
Copy link

trick14 commented Jun 21, 2017

Actually this seems not correct way because of map projection. It is hard to recognize if map layer is small but if layer is big enough such as continent level, you will see what I'm saying. To fix this, need to convert point <-> lon/lat instead of using meter. (because metersPerPoints can be different based on lat)

class OverlayAnnotation: MGLPointAnnotation {
    let image: UIImage
    let nw: CLLocationCoordinate2D
    let se: CLLocationCoordinate2D
    weak var mapView: MGLMapView?
    
    let initialMetersPerPoints: Double
    let initialHorizontalScale: Double
    let initialVerticalScale: Double
    
    required init(image: UIImage, nw: CLLocationCoordinate2D, se: CLLocationCoordinate2D, mapView: MGLMapView) {
        self.image = image
        self.nw = nw
        self.se = se
        self.mapView = mapView
        
        initialMetersPerPoints = mapView.metersPerPoint(atLatitude: (nw.latitude + se.latitude) / 2) // meters in a point at the specific latitude
        
        let nwpt = mapView.convert(nw, toPointTo: nil)
        let sept = mapView.convert(se, toPointTo: nil)
        let hpt = abs(sept.x - nwpt.x)
        let vpt = abs(nwpt.y - sept.y)
        initialHorizontalScale = Double(hpt / image.size.width)
        initialVerticalScale = Double(vpt / image.size.height)
        
        super.init()
        
        self.coordinate = mapView.convert(CGPoint(x: (nwpt.x + sept.x) / 2, y: (nwpt.y + sept.y) / 2), toCoordinateFrom: nil)
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

class OverlayAnnotationView: MGLAnnotationView {
    let imageView = UIImageView()
}
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
        if let annotation = annotation as? OverlayAnnotation {
            let annotationView: OverlayAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "overlay") as? OverlayAnnotationView ?? OverlayAnnotationView(reuseIdentifier: "overlay")
            annotationView.annotation = annotation
            annotationView.imageView.image = annotation.image
            annotationView.imageView.frame = CGRect(x: 0,
                                                    y: 0,
                                                    width: annotation.image.size.width * CGFloat(annotation.initialHorizontalScale),
                                                    height: annotation.image.size.height * CGFloat(annotation.initialVerticalScale))
            annotationView.addSubview(annotationView.imageView)
            annotationView.imageView.center =  CGPoint(x: annotationView.center.x, y: annotationView.center.y)
            annotationView.alpha = 0.9
            
            return annotationView
            
        }
    }
    return nil
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment