geo
Multi-provider geocoding abstraction with automatic fallback chain, DB-backed caching, per-provider rate limiting, and Haversine distance calculation.
Key namespaces
| Namespace | Layer | Responsibility |
|---|---|---|
|
shared |
Malli schemas: |
|
shared |
|
|
core |
|
|
core |
|
|
shell |
Nominatim adapter (no API key required) |
|
shell |
Google Maps adapter (requires |
|
shell |
Mapbox adapter (requires |
|
shell |
|
|
shell |
Public API: |
Configuration
;; OpenStreetMap only (no API key)
:boundary/geo-service
{:provider :openstreetmap
:user-agent "MyApp/1.0 (contact@example.com)"
:cache-ttl 86400 ; 24 hours
:db #ig/ref :boundary/db}
;; Fallback chain: OSM first, then Google
:boundary/geo-service
{:provider :openstreetmap
:fallback {:provider :google :api-key #env BND_GEO_API_KEY}
:user-agent "MyApp/1.0"
:cache-ttl 86400
:db #ig/ref :boundary/db}
Usage
(require '[boundary.geo.shell.service :as geo])
;; Geocode an address
(geo/geocode! service {:address "Prinsengracht 263, Amsterdam"})
;=> {:lat 52.3747 :lon 4.8844 :formatted-address "Prinsengracht 263, Amsterdam"}
;; Reverse geocode
(geo/reverse-geocode! service {:lat 52.3747 :lon 4.8844})
;=> {:formatted-address "Prinsengracht 263, 1016 GV Amsterdam"}
;; Haversine distance (pure, no HTTP)
(geo/distance {:lat 52.3747 :lon 4.8844} {:lat 52.3702 :lon 4.8952})
;=> 0.84 ; kilometres
Rate limiting
-
OpenStreetMap (Nominatim): 1 request/second enforced
-
Google / Mapbox: 100 ms between requests
-
Do not share an adapter instance across threads
DB migration
Apply the geo_cache table migration before first use:
clojure -M:migrate up
Migration file: resources/boundary/geo/migrations/001-geo-cache.sql
Testing
clojure -M:test:db/h2 :geo