Skip to content

Instantly share code, notes, and snippets.

@DamienBell
Last active June 8, 2016 03:57
Show Gist options
  • Save DamienBell/002041917f54ab395340f35cd50c11f8 to your computer and use it in GitHub Desktop.
Save DamienBell/002041917f54ab395340f35cd50c11f8 to your computer and use it in GitHub Desktop.
Handy MKMapView methods
//
// 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