Skip to main content

Given a geopoint, find the nearest city using PostGIS (reverse geocode).

Project description

Simple PostGIS Reverse Geocoder

HOT

Given a geopoint, find the nearest city using PostGIS (reverse geocode).

Publish Docs Publish Test Package version Downloads License


📖 Documentation: https://hotosm.github.io/pg-nearest-city/

🖥️ Source Code: https://github.com/hotosm/pg-nearest-city


Why do we need this?

This package was developed primarily as a basic reverse geocoder for use within web frameworks (APIs) that have an existing PostGIS connection to utilise.

  • The reverse geocoding package in Python here is probably the original and canonincal implementation using K-D tree.
    • However, it's a bit outdated now, with numerous unattended pull requests and uses an unfavourable multiprocessing-based approach.
  • The package here is an excellent revamp of the package above, an likely the best choice in many scenarios.

The K-D tree implementation in Python is performant (see benchmarks) and an excellent choice for scripts.

However, it does leave a large memory footprint of approximately 160Mb to load the K-D tree in memory (see benchmarks).

Once computed, the K-D tree remains in memory! This is an unacceptable compromise for a web server, for such a small amount of functionality, particularly if the web server is run via a container orchestrator as replicas with minimal memory.

As we already have a Postgres database running alongside our webserver, an approach to simply query via pre-loaded data via PostGIS is much more memory efficient (~2Mb) and has an acceptable performance penalty (see benchmarks).

[!NOTE] We don't discuss web based geocoding services here, such as Nominatim, as simple offline reverse-geocoding has two purposes:

  • Reduced latency, when very precise locations are not required.
  • Reduced load on free services such as Nominatim (particularly when running in automated tests frequently).

Priorities

  • Lightweight package size.
  • Minimal memory footprint.
  • Reasonably good performance.

How This Package Works

  • Ingest geonames.org data for cities over 1000 population.
  • Create voronoi polygons based on city geopoints.
  • Bundle the voronoi data with this package and load into Postgis.
  • Query the loaded voronoi data with a given geopoint, returning the city.

The diagram below should give a good indication for how this works:

voronoi_italy

Usage

Install

Distributed as a pip package on PyPi:

pip install pg-nearest-city
# or use your dependency manager of choice

Run The Code

Async

from pg_nearest_city import AsyncNearestCity

# Existing code to get db connection, say from API endpoint
db = await get_db_connection()

async with AsyncNearestCity(db) as geocoder:
    location = await geocoder.query(40.7128, -74.0060)

print(location.city)
# "New York City"
print(location.country)
# "USA"

Sync

from pg_nearest_city import NearestCity

# Existing code to get db connection, say from API endpoint
db = get_db_connection()

with NearestCity(db) as geocoder:
    location = geocoder.query(40.7128, -74.0060)

print(location.city)
# "New York City"
print(location.country)
# "USA"

Create A New DB Connection

  • If your app upstream already has a psycopg connection, this can be passed through.
  • If you require a new database connection, the connection parameters can be defined as DbConfig object variables:
from pg_nearest_city import DbConfig, AsyncNearestCity

db_config = DbConfig(
    dbname="db1",
    user="user1",
    password="pass1",
    host="localhost",
    port="5432",
)

async with AsyncNearestCity(db_config) as geocoder:
    location = await geocoder.query(40.7128, -74.0060)
  • Or alternatively as variables from your system environment:
PGNEAREST_DB_NAME=cities
PGNEAREST_DB_USER=cities
PGNEAREST_DB_PASSWORD=somepassword
PGNEAREST_DB_HOST=localhost
PGNEAREST_DB_PORT=5432

then

from pg_nearest_city import AsyncNearestCity

async with AsyncNearestCity() as geocoder:
    location = await geocoder.query(40.7128, -74.0060)

Benchmarks

  • todo

Testing

Run the tests with:

docker compose run --rm code pytest

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pg_nearest_city-0.2.0.tar.gz (13.3 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pg_nearest_city-0.2.0-py3-none-any.whl (13.1 MB view details)

Uploaded Python 3

File details

Details for the file pg_nearest_city-0.2.0.tar.gz.

File metadata

  • Download URL: pg_nearest_city-0.2.0.tar.gz
  • Upload date:
  • Size: 13.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: pdm/2.9.3 CPython/3.12.3

File hashes

Hashes for pg_nearest_city-0.2.0.tar.gz
Algorithm Hash digest
SHA256 c4d8894caca49f676e0e9a9562b65b192b477a1771e38fba9ed1ba6f851cec7f
MD5 c75d476d5342102fdb41f67f81c00390
BLAKE2b-256 7b4970102b50c39e765009a7d3774968e15956f812fd8c83b12203e9d05ac26a

See more details on using hashes here.

File details

Details for the file pg_nearest_city-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pg_nearest_city-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6baec18eb3c3943ce3cc7c69e43cd99503e235b1cd78075348dd0751f2f69d95
MD5 d332ba7cf8907d3cf6ef24cf799f977f
BLAKE2b-256 efd198273136ea3341efaf6d8f852df4dc943fdb48806784bf7cd9cd473a840b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page