BitChat Geohash Location Channel Precision Levels: From Region to Building
BitChat implements six discrete geohash precision levels—from 2-character regions to 8-character buildings—to create hierarchical geographic chat channels on Nostr.
BitChat, an open-source decentralized chat application developed by permissionlesstech, uses geohash-based location channels to scope conversations to specific geographic areas. The system defines distinct BitChat geohash location channel precision levels that map directly to geohash string lengths, enabling users to create chat rooms at granularities ranging from entire continents down to individual structures.
How BitChat Maps Precision Levels to Geographic Granularity
The hierarchy of location channels is defined in bitchat/Protocols/LocationChannel.swift. Here, the GeohashChannelLevel enum establishes the relationship between human-readable geographic concepts and their corresponding geohash character counts.
The GeohashChannelLevel Enum
This enumeration provides six cases, each specifying an exact geohash length. Shorter strings cover larger geographic areas, creating a nested hierarchy of chat channels:
// LocationChannel.swift
enum GeohashChannelLevel: CaseIterable, Codable, Equatable {
case building, block, neighborhood, city, province, region
var precision: Int { // ↔ geohash length
switch self { // 8-char = building, …, 2-char = region
case .building: return 8
case .block: return 7
case .neighborhood: return 6
case .city: return 5
case .province: return 4
case .region: return 2
}
}
}
The precision values are not arbitrary: each increment in geohash length subdivides the parent cell into 32 smaller cells, allowing building-level channels to target roughly 20-meter squares while region-level channels encompass thousands of kilometers.
Encoding Coordinates into Geohash Strings
Before a channel can be created, raw latitude and longitude coordinates must be converted into a truncated base-32 geohash string. This conversion is handled by the lightweight encoder in bitchat/Protocols/Geohash.swift.
The Base-32 Geohash Encoder
The static method Geohash.encode(latitude:longitude:precision:) generates a geohash string of the exact length required by the selected precision level:
// Geohash.swift
static func encode(latitude: Double, longitude: Double, precision: Int) -> String { … }
This function performs the coordinate-to-string conversion that enables the spatial indexing necessary for geographic message routing.
Constructing Channel IDs for Nostr Publication
Once encoded, the geohash string and its corresponding level are bundled into a concrete channel structure suitable for Nostr publication.
GeohashChannel and ChannelID Implementation
The GeohashChannel struct stores the precision level and the already-truncated geohash string. It conforms to Identifiable and Hashable for use in SwiftUI lists and as dictionary keys:
// LocationChannel.swift
struct GeohashChannel: Codable, Equatable, Hashable, Identifiable {
let level: GeohashChannelLevel
let geohash: String // already truncated to `level.precision`
var id: String { "\(level)-\(geohash)" }
var displayName: String { "\(level.displayName) • \(geohash)" }
}
enum ChannelID: Equatable, Codable {
case mesh
case location(GeohashChannel) // ↔ Nostr “#g” tag
var nostrGeohashTag: String? {
switch self { case .mesh: nil; case .location(let ch): ch.geohash }
}
}
When a user selects a location channel, the app instantiates ChannelID.location(GeohashChannel), which exposes the nostrGeohashTag property. This tag is then attached to Nostr events as a g tag, ensuring only peers subscribed to the same geohash cell receive the messages.
Practical Workflow for Location Channel Activation
The integration of these components occurs in bitchat/Services/LocationStateManager.swift and the UI layer in bitchat/Views/LocationChannelsSheet.swift. The following pattern demonstrates how to activate a location channel at a specific precision:
import BitChat
// 1️⃣ Choose a precision level (e.g., city)
let level = GeohashChannelLevel.city // precision = 5
// 2️⃣ Encode current coordinates to a geohash of that length
let latitude = 37.7749
let longitude = -122.4194
let geohash = Geohash.encode(latitude: latitude,
longitude: longitude,
precision: level.precision) // → "9q8yy"
// 3️⃣ Build the channel object
let cityChannel = GeohashChannel(level: level, geohash: geohash)
// 4️⃣ Activate it
let activeChannel = ChannelID.location(cityChannel)
// 5️⃣ Retrieve the Nostr tag for publishing
if let tag = activeChannel.nostrGeohashTag {
// publish event with `tags: [["g", tag]]`
}
The bitchat/App/LocationChannelsModel.swift file handles the reverse mapping, resolving a geohash length back to its GeohashChannelLevel and providing human-readable display names for each precision tier.
Summary
- Six precision levels are available, ranging from 2-character region to 8-character building granularity.
- The
GeohashChannelLevelenum inLocationChannel.swiftdefines the mapping between geographic concepts and geohash string lengths. Geohash.encode(latitude:longitude:precision:)converts GPS coordinates to the appropriate precision level.GeohashChannelbundles the level and truncated geohash, whileChannelID.locationgenerates the Nostrgtag for message scoping.- UI components use
LocationChannelsSheet.swiftandLocationChannelsModel.swiftto present and manage the different precision options.
Frequently Asked Questions
What is the most precise location channel available in BitChat?
The building level provides the highest precision using 8-character geohashes, typically corresponding to an area of approximately 20 meters by 20 meters. This allows users to create hyper-local chat rooms specific to a particular structure or address.
How does BitChat convert GPS coordinates into a channel identifier?
The app calls Geohash.encode(latitude:longitude:precision:) with the precision integer obtained from the selected GeohashChannelLevel. The resulting string is stored in a GeohashChannel struct, which is then wrapped in ChannelID.location to generate the Nostr "g" tag used for message routing.
Why does the region level use only 2 characters while building uses 8?
Geohash length inversely correlates with geographic coverage—each additional character subdivides the parent cell into 32 smaller cells. BitChat maps shorter strings (2 characters) to region-scale areas and longer strings (8 characters) to building-scale areas, creating an appropriate chat room granularity for each use case.
Which source files handle the geohash precision logic?
bitchat/Protocols/LocationChannel.swift defines the precision levels and channel structures, bitchat/Protocols/Geohash.swift implements the base-32 encoder, and bitchat/Services/LocationStateManager.swift manages the generation workflow for active channels. The UI presentation logic resides in bitchat/Views/LocationChannelsSheet.swift and bitchat/App/LocationChannelsModel.swift.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →