This is a simple and very lightweight reverse geocoder for offline use. It's implemented in Rust and uses a k-d-tree to find the closest cities for a given coordinate. The recommended data set from GeoNames contains ~200,000 cities with a population of at least 500, but smaller datasets work fine if you require less accuracy.
Development is sponsored by Treestack GmbH.
The image only contains a demo data set of a few cities. We chose not to include the full data file in the docker image, so both can be updated independently.
Download a cities500.zip
from GeoNames and unpack it. You should now have a cities500.txt
.
If you require less accuracy and/or have limited resources, you can download any other citiesN.txt
instead.
$ docker run \
-p 5353:5353 \
-v $(pwd)/cities500.txt:/cities.txt \
-e GEOCODER_BIND_ADDRESS=0.0.0.0:5353 \
ghcr.io/treestack/geocoder:master
docker-compose.yml
:
version: "3.9"
services:
geocoder:
image: ghcr.io/treestack/geocoder:master
ports:
- "5353:5353"
environment:
GEOCODER_BIND_ADDRESS: 0.0.0.0:5353
volumes:
- "./cities500.txt:/cities.txt"
There's a helm chart in the deploy
directory, but it's haphazardly stitched together.
Please feel free to create a PR if you have any suggestions.
helm install <name> deploy
You can configure the application with the following environment variables:
Parameter | Description | Default |
---|---|---|
GEOCODER_BIND_ADDRESS | Bind address | 127.0.0.1:5353 |
GEOCODER_LOGLEVEL | Log level | INFO |
GEOCODER_DATA_FILE | Data file name | ./cities.txt |
GEOCODER_WATCH_FOR_CHANGES | Reload geocoder when data file changes* | true |
GEOCODER_ALLOW_ORIGIN | CORS Access-Control-Allow-Origin header | * |
* Incredibly unreliable when the datafile is mounted as a docker volume.
curl "http://localhost:5353?lat=-48.875486&lng=-123.392519&results=1&details=true"
The microservice listens to all GET requests and supports the following query parameters:
Parameter | Description | Required | Example |
---|---|---|---|
lat | Latitude (WGS84, decimal) | Yes | -48.875 |
lng | Longitude (WGS84, decimal) | Yes | -123.392 |
results | Number of results, integer, defaults to 1 |
No | 10 |
details | Include details in response, boolean, defaults to false |
No | true |
The response is a valid GeoJSON FeatureCollection
. The feature's id
is added as foreign members.
The additional properties always includes the 'title' and distance to the given coordinates.
Optionally you can add most columns from the geonames dataset by setting the details
parameter to true
:
Property | Description |
---|---|
title | The city's name |
distanceToQuery | Approx. distance to given coordinates in kilometres (assuming earth radius of exactly 6371 km). |
admin1Code | |
admin2Code | |
admin3Code | |
admin4Code | |
countryCode | ISO-3166 2-letter country code |
cc2 | alternative country codes |
dem | digital elevation model, srtm3 or gtopo30 |
elevation | elevation in metres |
featureCode | Feature code. For a complete list, check here. |
modificationDate | Last modification date |
population | Population |
timezone | IANA timezone id |
{
"type": "FeatureCollection",
"features": [
{
"geometry": {
"coordinates": [
78.25628662109375,
28.95911979675293
],
"type": "Point"
},
"id": 1272983,
"properties": {
"distanceToQuery": 6,
"title": "Dhanaura"
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
78.23455810546875,
28.92694091796875
],
"type": "Point"
},
"id": 1278036,
"properties": {
"distanceToQuery": 8,
"title": "Bachhraon"
},
"type": "Feature"
}
]
}
The final docker image has a size of only 8 MB, memory usage depends on the used data set:
Dataset | Cities | Approx. mem. usage |
---|---|---|
cities500.txt | 199,606 | ~160 MiB |
cities5000.txt | 53,268 | ~45 MiB |
cities15000.txt | 26,457 | ~25 MiB |
With cities500.txt
, response time is consistently < 10 ms, measured on a M1 mac:
$ hyperfine --warmup 3 'curl "http://localhost:5353?lat=-48.875486&lng=-123.392519&results=1"'
Benchmark 1: curl "http://localhost:5353?lat=-48.875486&lng=-123.392519&results=1&details=true"
Time (mean ± σ): 5.8 ms ± 0.9 ms [User: 1.5 ms, System: 1.9 ms]
Range (min … max): 4.1 ms … 9.3 ms 374 runs
$ cargo run web
docker build -t treestack/geocoder:0 .