Last active
February 10, 2020 02:49
-
-
Save varyP/93da6fa0d63a7249332e2b1c1eb42b8e to your computer and use it in GitHub Desktop.
Swift5 implementation to get radius from MKMapView's current zoomed in region & zoom-pan to any coordinate with animation.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Created by Varun Parakh on 09/04/19 via Daniel.Burke (https://gist.github.com/d2burke/ad29811b07ae31b378ff) | |
/* | |
Have these defined in your global constants. | |
let mercatorRadius = 85445659.44705395 | |
let maxGoogleZoomLevels = 20 | |
let mercatorOffset = 268435456.0 | |
*/ | |
// | |
// MKMapView+Ext.swift | |
// | |
import Foundation | |
import MapKit | |
extension MKMapView { | |
private func longitudeToPixelSpaceX(longitude: Double) -> Double { | |
return round(mercatorOffset + mercatorRadius * longitude * Double.pi / 180.0) | |
} | |
private func latitudeToPixelSpaceY(latitude: Double) -> Double { | |
return round(mercatorOffset - mercatorRadius * log((1 + sin(latitude * Double.pi | |
/ 180.0)) / (1 - sin(latitude * Double.pi / 180.0))) / 2.0) | |
} | |
private func pixelSpaceXToLongitude(pixelX: Double) -> Double { | |
return ((round(pixelX) - mercatorOffset) / mercatorRadius) * 180.0 / Double.pi | |
} | |
private func pixelSpaceYToLatitude(pixelY: Double) -> Double { | |
return (Double.pi / 2.0 - 2.0 * atan(exp((round(pixelY) - mercatorOffset / mercatorRadius) ))) * 180.0 / Double.pi | |
} | |
private func coordinateSpan(withMapView mapView: MKMapView, centerCoordinate: CLLocationCoordinate2D, zoomLevel: Double) -> MKCoordinateSpan { | |
let centerPixelX = longitudeToPixelSpaceX(longitude: centerCoordinate.longitude) | |
let centerPixelY = latitudeToPixelSpaceY(latitude: centerCoordinate.latitude) | |
let zoomExponent = Double(20 - zoomLevel) | |
let zoomScale = pow(2.0, zoomExponent) | |
let mapSizeInPixels = mapView.bounds.size | |
let scaledMapWidth = Double(mapSizeInPixels.width) * zoomScale | |
let scaledMapHeight = Double(mapSizeInPixels.height) * zoomScale | |
let topLeftPixelX = centerPixelX - (scaledMapWidth / 2) | |
let topLeftPixelY = centerPixelY - (scaledMapHeight / 2) | |
// // find delta between left and right longitudes | |
let minLng = pixelSpaceXToLongitude(pixelX: topLeftPixelX) | |
let maxLng = pixelSpaceXToLongitude(pixelX: topLeftPixelX + scaledMapWidth) | |
let longitudeDelta = maxLng - minLng | |
let minLat = pixelSpaceYToLatitude(pixelY: topLeftPixelY) | |
let maxLat = pixelSpaceYToLatitude(pixelY: topLeftPixelY + scaledMapHeight) | |
let latitudeDelta = -1 * (maxLat - minLat) | |
let span = MKCoordinateSpan(latitudeDelta: latitudeDelta, longitudeDelta: longitudeDelta) | |
return span | |
} | |
func zoom(toCenterCoordinate centerCoordinate: CLLocationCoordinate2D, zoomLevel: Double, animated: Bool) { | |
let zoomLevel = min(zoomLevel, Double(maxGoogleZoomLevels)) | |
let span = self.coordinateSpan(withMapView: self, centerCoordinate: centerCoordinate, zoomLevel: zoomLevel) | |
let region = MKCoordinateRegion(center: centerCoordinate, span: span) | |
self.setRegion(region, animated: animated) | |
} | |
func zoomLevel() -> Double { | |
let longitudeDelta = self.region.span.longitudeDelta | |
let mapWidthInPixels = self.bounds.size.width | |
let zoomScale = CGFloat(longitudeDelta * mercatorRadius * Double.pi) / (180.0 * mapWidthInPixels) | |
var zoomer = CGFloat(maxGoogleZoomLevels) - log2( zoomScale ) | |
//Zoom can not be less than 0 | |
zoomer = max(zoomer, 0) | |
return Double(zoomer) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment