Last active
June 8, 2016 03:57
-
-
Save DamienBell/002041917f54ab395340f35cd50c11f8 to your computer and use it in GitHub Desktop.
Handy MKMapView methods
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
// | |
// MKMapViewExtension.swift | |
// AutocompleteTextfieldSwift | |
// | |
// Created by Mylene Bayan on 2/22/15. | |
// Copyright (c) 2015 MaiLin. All rights reserved. | |
// | |
import Foundation | |
import MapKit | |
let MERCATOR_OFFSET = 268435456.0 | |
let MERCATOR_RADIUS = 85445659.44705395 | |
let DEGREES = 180.0 | |
typealias Edges = (ne: CLLocationCoordinate2D, sw: CLLocationCoordinate2D) | |
enum DBDistanceType: Int{ | |
case Kilometers | |
case Meters | |
case Miles | |
} | |
extension MKMapView{ | |
//MARK: Map Conversion Methods | |
private func longitudeToPixelSpaceX(longitude:Double)->Double{ | |
return round(MERCATOR_OFFSET + MERCATOR_RADIUS * longitude * M_PI / DEGREES) | |
} | |
private func latitudeToPixelSpaceY(latitude:Double)->Double{ | |
return round(MERCATOR_OFFSET - MERCATOR_RADIUS * log((1 + sin(latitude * M_PI / DEGREES)) / (1 - sin(latitude * M_PI / DEGREES))) / 2.0) | |
} | |
private func pixelSpaceXToLongitude(pixelX:Double)->Double{ | |
return ((round(pixelX) - MERCATOR_OFFSET) / MERCATOR_RADIUS) * DEGREES / M_PI | |
} | |
private func pixelSpaceYToLatitude(pixelY:Double)->Double{ | |
return (M_PI / 2.0 - 2.0 * atan(exp((round(pixelY) - MERCATOR_OFFSET) / MERCATOR_RADIUS))) * DEGREES / M_PI | |
} | |
private func coordinateSpanWithCenterCoordinate(centerCoordinate:CLLocationCoordinate2D, zoomLevel:Double)->MKCoordinateSpan{ | |
// convert center coordiate to pixel space | |
let centerPixelX = longitudeToPixelSpaceX(centerCoordinate.longitude) | |
let centerPixelY = latitudeToPixelSpaceY(centerCoordinate.latitude) | |
// determine the scale value from the zoom level | |
let zoomExponent:Double = 20.0 - zoomLevel | |
let zoomScale:Double = pow(2.0, zoomExponent) | |
// scale the map’s size in pixel space | |
let mapSizeInPixels = self.bounds.size | |
let scaledMapWidth = Double(mapSizeInPixels.width) * zoomScale | |
let scaledMapHeight = Double(mapSizeInPixels.height) * zoomScale | |
// figure out the position of the top-left pixel | |
let topLeftPixelX = centerPixelX - (scaledMapWidth / 2.0) | |
let topLeftPixelY = centerPixelY - (scaledMapHeight / 2.0) | |
// find delta between left and right longitudes | |
let minLng = pixelSpaceXToLongitude(topLeftPixelX) | |
let maxLng = pixelSpaceXToLongitude(topLeftPixelX + scaledMapWidth) | |
let longitudeDelta = maxLng - minLng | |
let minLat = pixelSpaceYToLatitude(topLeftPixelY) | |
let maxLat = pixelSpaceYToLatitude(topLeftPixelY + scaledMapHeight) | |
let latitudeDelta = -1.0 * (maxLat - minLat) | |
return MKCoordinateSpan(latitudeDelta: latitudeDelta, longitudeDelta: longitudeDelta) | |
} | |
// Return the current map zoomLevel equivalent, just like above but in reverse | |
func zoomLevel()->Double{ | |
let reg = self.region | |
let span = reg.span | |
let centerCoordinate = reg.center | |
let leftLongitude = centerCoordinate.longitude-(span.longitudeDelta/2) | |
let rightLongitude = centerCoordinate.longitude+(span.longitudeDelta/2) | |
let mapSizeInPixels = self.bounds.size | |
let leftPixel = self.longitudeToPixelSpaceX(leftLongitude) | |
let rightPixel = self.longitudeToPixelSpaceX(rightLongitude) | |
let pixelDelta = abs(rightPixel - leftPixel) | |
let zoomScale = Double(mapSizeInPixels.width) / pixelDelta | |
let zoomExponent=log2(zoomScale) | |
let zoomLevel = zoomExponent+20 | |
return zoomLevel | |
} | |
func setCenterCoordinate(centerCoordinate:CLLocationCoordinate2D, zoomLevel:Double, animated:Bool){ | |
// clamp large numbers to 28 | |
let zoom = min(zoomLevel, 28) | |
// use the zoom level to compute the region | |
let span = self.coordinateSpanWithCenterCoordinate(centerCoordinate, zoomLevel: zoom) | |
let region = MKCoordinateRegionMake(centerCoordinate, span) | |
if region.center.longitude == -180.00000000{ | |
print("Invalid Region") | |
} | |
else{ | |
self.setRegion(region, animated: animated) | |
} | |
} | |
func cornerCoordinates() -> Edges { | |
let nePoint = CGPoint(x: self.bounds.maxX, y: self.bounds.origin.y) | |
let swPoint = CGPoint(x: self.bounds.minX, y: self.bounds.maxY) | |
let neCoord = self.convertPoint(nePoint, toCoordinateFromView: self) | |
let swCoord = self.convertPoint(swPoint, toCoordinateFromView: self) | |
return (ne: neCoord, sw: swCoord) | |
} | |
func widthWithDistanceType(distanceType: DBDistanceType)->Double{ | |
let mapRect: MKMapRect = self.visibleMapRect | |
let eastMapPoint = MKMapPointMake(MKMapRectGetMinX(mapRect), MKMapRectGetMidY(mapRect)) | |
let westMapPoint = MKMapPointMake(MKMapRectGetMaxX(mapRect), MKMapRectGetMidY(mapRect)) | |
let meters = MKMetersBetweenMapPoints(eastMapPoint, westMapPoint) | |
switch distanceType { | |
case .Kilometers: | |
return meters/1000.0 | |
case .Meters: | |
return meters | |
case .Miles: | |
return meters/1609.34 | |
} | |
} | |
func regionContains(coordinate: CLLocationCoordinate2D)->Bool{ | |
return MKMapView.regionContains(self.region, coordinate: coordinate) | |
} | |
/* Standardises and angle to [-180 to 180] degrees */ | |
class func standardAngle(a: CLLocationDegrees) -> CLLocationDegrees { | |
let angle = a%360 | |
return angle < -180 ? -360 - angle : angle > 180 ? 360 - 180 : angle | |
} | |
/* confirms that a region contains a location */ | |
class func regionContains(region: MKCoordinateRegion, coordinate: CLLocationCoordinate2D) -> Bool { | |
let deltaLat = abs(standardAngle(region.center.latitude - coordinate.latitude)) | |
let deltalong = abs(standardAngle(region.center.longitude - coordinate.longitude)) | |
return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >= deltalong | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment