// // Geohash.swift // mn_ios // // Created by Alex Bosworth on 11/26/14. // Copyright (c) 2014 adylitica. All rights reserved. // import Foundation import MapKit typealias GeoInterval = (CLLocationDegrees, CLLocationDegrees) class Geohash { private class func _joinInterval(interval: GeoInterval) -> Double { return (interval.0 + interval.1) / 2 } private class func _refineInterval(interval: GeoInterval, cd: Int, mask: Int) -> GeoInterval { let joined = _joinInterval(interval) return cd & mask != 0 ? (joined, interval.1) : (interval.0, joined) } private struct Constants { static let interval = (lat: GeoInterval(-90, 90), long: GeoInterval(-180, 180)) static let base32 = "0123456789bcdefghjkmnpqrstuvwxyz" static let bits = [16, 8, 4, 2, 1] } /** Convert a CLLocation into a geohash */ class func encode(coordinate: CLLocationCoordinate2D) -> String { var isEven = true var bit = 0 var ch = 0 var precision = 12 var geohash = [String]() var mid: Double = 0 var lat = Constants.interval.lat var lon = Constants.interval.long while geohash.count < precision { if isEven { mid = _joinInterval(lon) if coordinate.longitude > mid { ch |= Constants.bits[bit] lon.0 = mid } else { lon.1 = mid } } else { mid = _joinInterval(lat) if coordinate.latitude > mid { ch |= Constants.bits[bit] lat.0 = mid } else { lat.1 = mid } } isEven = !isEven if bit < 4 { bit++ } else { geohash += [String(Constants.base32[advance(Constants.base32.startIndex, ch)])] bit = 0 ch = 0 } } return join("", geohash.map { String($0) }) } /** Convert a geohash into a CLLocation */ class func decode(geohash: String) -> CLLocationCoordinate2D? { var isEven = true var interval = Constants.interval let base32 = Constants.base32 let bits = Constants.bits for char in geohash { let index = find(base32, char) // Exit early when the geohash is invalid if index == nil { return nil } let value = distance(base32.startIndex, index!) for mask in bits { if isEven { interval.long = _refineInterval(interval.long, cd: value, mask: mask) } else { interval.lat = _refineInterval(interval.lat, cd: value, mask: mask) } isEven = !isEven } } return CLLocationCoordinate2D(latitude: _joinInterval(interval.lat), longitude: _joinInterval(interval.long)) } }