Skip to content

Commit

Permalink
Merge pull request #8 from fummicc1/fix-flipping-coord-geohash
Browse files Browse the repository at this point in the history
Fix the bound generation bug in crossing boundaries
  • Loading branch information
fummicc1 authored Dec 8, 2024
2 parents 5750591 + 4af8301 commit 382682b
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 58 deletions.
140 changes: 90 additions & 50 deletions Sources/GeoHashFramework/GeoHash.swift
Original file line number Diff line number Diff line change
Expand Up @@ -210,42 +210,74 @@ extension GeoHash {
}

public func getNeighbors() -> [GeoHash] {
let latitudeBits = self.latitudeBits
let longitudeBits = self.longitudeBits
// Calculate the step size based on precision
let latStep = 180.0 / pow(2.0, Double(latitudeBits.count))
let lngStep = 360.0 / pow(2.0, Double(longitudeBits.count))

let north = add(bits: latitudeBits, by: 1)
let south = add(bits: latitudeBits, by: -1)
let east = add(bits: longitudeBits, by: 1)
let west = add(bits: longitudeBits, by: -1)
// Decode current position
let (minLatitude, maxLatitude, minLongitude, maxLongitude) = getBound(binary: binary)

let northEast = combineBits(latitude: north, longitude: east)
let northWest = combineBits(latitude: north, longitude: west)
let southEast = combineBits(latitude: south, longitude: east)
let southWest = combineBits(latitude: south, longitude: west)
// Calculate center coordinates
let centerLat = (minLatitude + maxLatitude) / 2
let centerLng = (minLongitude + maxLongitude) / 2

return [
GeoHash(
binary: combineBits(latitude: north, longitude: longitudeBits),
precision: precision
),
GeoHash(binary: northEast, precision: precision),
GeoHash(
binary: combineBits(latitude: latitudeBits, longitude: east),
precision: precision
),
GeoHash(binary: southEast, precision: precision),
GeoHash(
binary: combineBits(latitude: south, longitude: longitudeBits),
precision: precision
),
GeoHash(binary: southWest, precision: precision),
GeoHash(
binary: combineBits(latitude: latitudeBits, longitude: west),
func makeNeighbor(latOffset: Double, lngOffset: Double) -> GeoHash {
var newLat = centerLat + (latOffset * latStep)
var newLng = centerLng + (lngOffset * lngStep)

newLat = clampLatitude(newLat)
newLng = normalizeLongitude(newLng)

return GeoHash(
latitude: newLat,
longitude: newLng,
precision: precision
),
GeoHash(binary: northWest, precision: precision),
)
}

return [
makeNeighbor(latOffset: 1, lngOffset: 0), // north
makeNeighbor(latOffset: 1, lngOffset: 1), // northeast
makeNeighbor(latOffset: 0, lngOffset: 1), // east
makeNeighbor(latOffset: -1, lngOffset: 1), // southeast
makeNeighbor(latOffset: -1, lngOffset: 0), // south
makeNeighbor(latOffset: -1, lngOffset: -1), // southwest
makeNeighbor(latOffset: 0, lngOffset: -1), // west
makeNeighbor(latOffset: 1, lngOffset: -1), // northwest
]
}

private func getBound(binary: String) -> (
minLatitude: Double,
maxLatitude: Double,
minLongitude: Double,
maxLongitude: Double
) {
var maxLatitude = 90.0
var minLatitude = -90.0
var maxLongitude = 180.0
var minLongitude = -180.0
for (index, bit) in binary.enumerated() {
if index % 2 == 0 {
// longitude bits
let mid = (minLongitude + maxLongitude) / 2
if bit == "1" {
minLongitude = mid
} else {
maxLongitude = mid
}
} else {
// latitude bits
let mid = (minLatitude + maxLatitude) / 2
if bit == "1" {
minLatitude = mid
} else {
maxLatitude = mid
}
}
}
return (minLatitude, maxLatitude, minLongitude, maxLongitude)
}

/// Add `delta` to `bits`
private func add(bits: String, by delta: Int) -> String {
Expand Down Expand Up @@ -277,37 +309,45 @@ extension GeoHash {
return result
}

public func getBound() -> [GeoHashCoordinate2D] {
let baseGeoCoordinate = coordinate
let precision = precision

let latitudeBits = precision.rawValue / 2
let longitudeBits = (precision.rawValue + 1) / 2
private func normalizeLongitude(_ lng: Double) -> Double {
var normalized = lng
while normalized > 180.0 {
normalized -= 360.0
}
while normalized < -180.0 {
normalized += 360.0
}
return normalized
}

let latitudeRange = 180.0 // 90 - (-90)
let latitudeDelta = latitudeRange / pow(2.0, Double(latitudeBits))
private func clampLatitude(_ lat: Double) -> Double {
return min(90.0, max(-90.0, lat))
}

let longitudeRange = 360.0 // 180 - (-180)
let longitudeDelta = longitudeRange / pow(2.0, Double(longitudeBits))
public func getBound() -> [GeoHashCoordinate2D] {

var (minLatitude, maxLatitude, minLongitude, maxLongitude) = getBound(binary: binary)

let longitude = baseGeoCoordinate.longitude
let latitude = baseGeoCoordinate.latitude
maxLatitude = clampLatitude(maxLatitude)
minLatitude = clampLatitude(minLatitude)
maxLongitude = normalizeLongitude(maxLongitude)
minLongitude = normalizeLongitude(minLongitude)

let topLeft = GeoHashCoordinate2D(
latitude: latitude + latitudeDelta,
longitude: longitude
latitude: maxLatitude,
longitude: minLongitude
)
let topRight = GeoHashCoordinate2D(
latitude: latitude + latitudeDelta,
longitude: longitude + longitudeDelta
latitude: maxLatitude,
longitude: maxLongitude
)
let bottomRight = GeoHashCoordinate2D(
latitude: latitude,
longitude: longitude + longitudeDelta
latitude: minLatitude,
longitude: maxLongitude
)
let bottomLeft = GeoHashCoordinate2D(
latitude: latitude,
longitude: longitude
latitude: minLatitude,
longitude: minLongitude
)

return [topLeft, topRight, bottomRight, bottomLeft]
Expand Down
16 changes: 8 additions & 8 deletions Tests/GeoHashFrameworkTests/GeoHashTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,20 @@ struct GeoHashTests {
let actual = geoHash.getBound()
let expected = [
GeoHashCoordinate2D(
latitude: 35.68634033203124,
longitude: 139.76257324218756
latitude: 35.68359375,
longitude: 139.757080078125
),
GeoHashCoordinate2D(
latitude: 35.68634033203124,
longitude: 139.77355957031256
latitude: 35.68359375,
longitude: 139.76806640625
),
GeoHashCoordinate2D(
latitude: 35.68084716796874,
longitude: 139.77355957031256
latitude: 35.6781005859375,
longitude: 139.76806640625
),
GeoHashCoordinate2D(
latitude: 35.68084716796874,
longitude: 139.76257324218756
latitude: 35.6781005859375,
longitude: 139.757080078125
),
]
#expect(actual == expected)
Expand Down

0 comments on commit 382682b

Please sign in to comment.