-
-
Save quinncnl/5ab6c7ff94b8746e4560 to your computer and use it in GitHub Desktop.
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
// | |
// 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)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment