Expand description
Fast reverse geocoding library with enriched location data.
genom converts latitude/longitude coordinates into detailed place information including
city, region, country, timezone, currency, postal code, and more. It’s designed for
high-performance applications that need sub-millisecond geocoding with rich metadata.
§Features
- Simple API - Single function call:
lookup(lat, lon) - Rich Data - Returns 18 fields including timezone, currency, postal code, region, EU status
- Fast Lookups - Grid-based spatial indexing for sub-millisecond queries
- Zero Config - Database builds automatically on first install from GeoNames data
- Thread-Safe - Global singleton with lazy initialization, safe for concurrent access
- Compact - Efficient binary format with string interning (~20-30 MB for 100+ countries)
- Offline - No external API calls after initial build, works completely offline
§Quick Start
Add genom to your Cargo.toml:
[dependencies]
genom = "0.1"Basic usage:
// Lookup coordinates
if let Some(place) = genom::lookup(40.7128, -74.0060) {
println!("{}, {}", place.city, place.country_name);
// Output: New York, United States
}§Detailed Example
// Look up coordinates for Paris, France
if let Some(place) = genom::lookup(48.8566, 2.3522) {
// Location information
println!("City: {}", place.city); // Paris
println!("Region: {}", place.region); // Île-de-France
println!("Country: {}", place.country_name); // France
println!("Country Code: {}", place.country_code); // FR
// Geographic details
println!("Continent: {}", place.continent_name); // Europe
println!("Postal Code: {}", place.postal_code); // 75001
println!("Coordinates: {}, {}", place.latitude, place.longitude);
// Timezone information
println!("Timezone: {}", place.timezone); // Europe/Paris
println!("TZ Abbr: {}", place.timezone_abbr); // CET or CEST
println!("UTC Offset: {}", place.utc_offset_str); // UTC+1 or UTC+2
println!("DST Active: {}", place.dst_active); // true/false
// Economic/political data
println!("Currency: {}", place.currency); // EUR
println!("EU Member: {}", place.is_eu); // true
}§Architecture
§Database Structure
The library uses a pre-built binary database that’s embedded in your compiled binary:
- String Interning: Common strings (country codes, timezones) stored once
- Fixed-Point Coordinates: 32-bit integers instead of 64-bit floats
- Spatial Grid Index: World divided into 0.1° × 0.1° cells (~11km at equator)
§Lookup Algorithm
- Quantize input coordinates to grid key (0.1° resolution)
- Search target cell and 8 neighboring cells (3×3 grid)
- Calculate haversine distance to all candidates
- Return nearest place with enriched metadata
This provides O(1) average-case lookup with typically 10-50 candidates to check.
§Data Enrichment
Raw place data is enriched with:
- Country names from ISO codes
- Currency codes by country
- Continent information
- EU membership status
- Current timezone offset and abbreviation
- DST (Daylight Saving Time) status
§Performance
- First lookup: ~100ms (database initialization and decompression)
- Subsequent lookups: <1ms (typically 0.1-0.5ms)
- Memory usage: ~30-50 MB (depending on number of countries)
- Binary size increase: ~20-30 MB (embedded database)
The database is initialized lazily on first use and cached in a static OnceLock,
making it safe and efficient for concurrent access.
§Build Process
On first cargo build, the library:
- Downloads geographic data from GeoNames.org
- Processes and filters place data (cities, towns, villages)
- Merges postal code information
- Deduplicates nearby entries
- Builds string interning table and spatial index
- Serializes to compact binary format
This happens automatically and takes 2-5 minutes depending on network speed.
The database is cached in target/ and only rebuilt when necessary.
§Skipping the Build
To skip database generation (e.g., for docs.rs or CI):
[dependencies]
genom = { version = "0.1", features = ["no-build-database"] }§Thread Safety
All operations are thread-safe:
- Database initialization uses
OnceLockfor safe concurrent initialization - All lookups are read-only after initialization
- No locks needed for queries (lock-free reads)
- Safe to call from multiple threads simultaneously
use std::thread;
let handles: Vec<_> = (0..10)
.map(|i| {
thread::spawn(move || {
let lat = 40.0 + i as f64;
let lon = -74.0;
genom::lookup(lat, lon)
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}§Data Sources
All geographic data comes from GeoNames.org, which provides free geographic data under the Creative Commons Attribution 4.0 License.
The library includes data for 100+ countries with significant population and data quality.
§Limitations
- Ocean coordinates: Returns
Nonefor coordinates far from land - Precision: Nearest city/town, not street-level accuracy
- Coverage: Limited to countries included in the build (see
build/builder.rs) - Updates: Database is static; requires rebuild for updated data
- Size: Adds ~20-30 MB to your binary
§Use Cases
- Analytics: Enrich user location data with timezone and region
- Logging: Add geographic context to log entries
- APIs: Convert coordinates to human-readable locations
- IoT: Offline geocoding for edge devices
- Mobile: Embedded geocoding without network requests
- Privacy: No external API calls, all data stays local
§Modules
types- Core data structures (Place,Location, [Database])enrichment- Data enrichment functions and lookup tables
§See Also
Re-exports§
Modules§
- enrichment
- Data enrichment module that adds computed fields to raw geographic data.
- types
- Core data structures for geographic information.
Structs§
- Geocoder
- The core geocoding engine. Manages the spatial database and performs coordinate lookups.
Functions§
- lookup
- Performs reverse geocoding on the given coordinates, returning enriched place data if found.