Switch location notes and UI helpers to building-level geohash (precision 8): add GeohashChannelLevel.building, map length 8, use building for notes selection and counter, add building name mapping

This commit is contained in:
jack
2025-09-13 11:08:38 +02:00
parent ead1d1e9a2
commit 46ca37ceaa
5 changed files with 28 additions and 13 deletions
+6
View File
@@ -2,6 +2,7 @@ import Foundation
/// Levels of location channels mapped to geohash precisions.
enum GeohashChannelLevel: CaseIterable, Codable, Equatable {
case building
case block
case neighborhood
case city
@@ -11,6 +12,7 @@ enum GeohashChannelLevel: CaseIterable, Codable, Equatable {
/// Geohash length used for this level.
var precision: Int {
switch self {
case .building: return 8
case .block: return 7
case .neighborhood: return 6
case .city: return 5
@@ -21,6 +23,7 @@ enum GeohashChannelLevel: CaseIterable, Codable, Equatable {
var displayName: String {
switch self {
case .building: return "Building"
case .block: return "Block"
case .neighborhood: return "Neighborhood"
case .city: return "City"
@@ -35,6 +38,7 @@ extension GeohashChannelLevel {
let container = try decoder.singleValueContainer()
if let raw = try? container.decode(String.self) {
switch raw {
case "building": self = .building
case "block": self = .block
case "neighborhood": self = .neighborhood
case "city": self = .city
@@ -46,6 +50,7 @@ extension GeohashChannelLevel {
}
} else if let precision = try? container.decode(Int.self) {
switch precision {
case 8: self = .building
case 7: self = .block
case 6: self = .neighborhood
case 5: self = .city
@@ -61,6 +66,7 @@ extension GeohashChannelLevel {
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .building: try container.encode("building")
case .block: try container.encode("block")
case .neighborhood: try container.encode("neighborhood")
case .city: try container.encode("city")
@@ -285,12 +285,18 @@ final class LocationChannelManager: NSObject, CLLocationManagerDelegate, Observa
} else if let locality = pm.locality, !locality.isEmpty {
dict[.neighborhood] = locality
}
// Block: reuse neighborhood/locality granularity without exposing street level
// Block: reuse neighborhood/locality granularity
if let subLocality = pm.subLocality, !subLocality.isEmpty {
dict[.block] = subLocality
} else if let locality = pm.locality, !locality.isEmpty {
dict[.block] = locality
}
// Building: prefer place name/street address without numbers where possible
if let name = pm.name, !name.isEmpty {
dict[.building] = name
} else if let thoroughfare = pm.thoroughfare, !thoroughfare.isEmpty {
dict[.building] = thoroughfare
}
return dict
}
}
+8 -7
View File
@@ -474,7 +474,8 @@ struct ContentView: View {
case 5: return .city
case 6: return .neighborhood
case 7: return .block
default: return .block
case 8: return .building
default: return .building
}
}
let level = levelForLength(gh.count)
@@ -1180,7 +1181,7 @@ struct ContentView: View {
LocationChannelManager.shared.enableLocationChannels()
LocationChannelManager.shared.refreshChannels()
// If we already have a block geohash, pass it; otherwise wait in the sheet.
notesGeohash = LocationChannelManager.shared.availableChannels.first(where: { $0.level == .block })?.geohash
notesGeohash = LocationChannelManager.shared.availableChannels.first(where: { $0.level == .building })?.geohash
showLocationNotes = true
}) {
HStack(spacing: 4) {
@@ -1231,7 +1232,7 @@ struct ContentView: View {
}
.sheet(isPresented: $showLocationNotes) {
Group {
if let gh = notesGeohash ?? LocationChannelManager.shared.availableChannels.first(where: { $0.level == .block })?.geohash {
if let gh = notesGeohash ?? LocationChannelManager.shared.availableChannels.first(where: { $0.level == .building })?.geohash {
LocationNotesView(geohash: gh)
.environmentObject(viewModel)
} else {
@@ -1265,8 +1266,8 @@ struct ContentView: View {
.background(backgroundColor)
.foregroundColor(textColor)
.onChange(of: locationManager.availableChannels) { channels in
if notesGeohash == nil, let block = channels.first(where: { $0.level == .block }) {
notesGeohash = block.geohash
if notesGeohash == nil, let building = channels.first(where: { $0.level == .building }) {
notesGeohash = building.geohash
}
}
}
@@ -1478,8 +1479,8 @@ extension ContentView {
private func updateNotesCounterSubscription() {
switch locationManager.selectedChannel {
case .mesh:
if let block = LocationChannelManager.shared.availableChannels.first(where: { $0.level == .block })?.geohash {
LocationNotesCounter.shared.subscribe(geohash: block)
if let building = LocationChannelManager.shared.availableChannels.first(where: { $0.level == .building })?.geohash {
LocationNotesCounter.shared.subscribe(geohash: building)
} else {
LocationNotesCounter.shared.cancel()
}
+4 -1
View File
@@ -381,7 +381,8 @@ struct LocationChannelsSheet: View {
case 5: return .city
case 6: return .neighborhood
case 7: return .block
default: return .block
case 8: return .building
default: return .building
}
}
}
@@ -462,6 +463,8 @@ extension LocationChannelsSheet {
switch level {
case .region:
return ""
case .building:
return ""
default:
return "~"
}
+3 -4
View File
@@ -13,7 +13,7 @@ struct LocationNotesSheet: View {
var body: some View {
Group {
if let gh = notesGeohash ?? locationManager.availableChannels.first(where: { $0.level == .block })?.geohash {
if let gh = notesGeohash ?? locationManager.availableChannels.first(where: { $0.level == .building })?.geohash {
// Found block geohash: show notes view
LocationNotesView(geohash: gh)
.environmentObject(viewModel)
@@ -55,12 +55,11 @@ struct LocationNotesSheet: View {
LocationChannelManager.shared.refreshChannels()
}
.onChange(of: locationManager.availableChannels) { channels in
if notesGeohash == nil, let block = channels.first(where: { $0.level == .block }) {
notesGeohash = block.geohash
if notesGeohash == nil, let building = channels.first(where: { $0.level == .building }) {
notesGeohash = building.geohash
}
}
}
}
}
}