Skip to main content

geoClassy

Label GPS points with the area that contains them — a neighbourhood, a municipality, a district — using boundary polygons you already have as GeoJSON, typically exported from OpenStreetMap.

import geoClassy

areas = geoClassy.load("nyc-neighborhoods.geojson")

areas.locate(40.748417, -73.985833)  # 'Midtown South'
areas.locate(38.897699, -77.036553)  # None — outside every area

df["zone"] = areas.locate_many(df.latitude, df.longitude)

That is the whole library. len(areas) counts them, areas.names lists them. An executed, run-it-yourself walkthrough of everything below is in examples/getting-started.ipynb.

pip install geoClassy

Why not geopandas?

Because putting a point in an area shouldn't cost you GDAL and PROJ. geoClassy does this one job with Shapely and NumPy — no geospatial system libraries, no network calls, no API keys, and answers that don't change between runs.

geoClassy geopandas reverse-geocoding API
Install 2 wheels GDAL + PROJ —
Works offline yes yes no
Your own boundaries yes yes no
Rate limits none none yes

If you already run geopandas, use sjoin — it is the right tool and geoClassy adds nothing. This is for everyone else.

Speed

locate_many runs one spatial-index query for the whole batch instead of one per point. Use it instead of df.apply(...) row by row.

  • 5,000 points against 500 areas: 3 ms (the same work took 24 s in 0.1.x).
  • 1,000,000 points against a 5,000-area partition where every point lands in one: 1.1 s, after an 83 ms load.

Rows with missing coordinates come back as None rather than raising, so a dataframe with gaps still classifies in one call.

Overlapping areas

A point can legitimately fall inside several areas at once — a district inside a city inside a region, which is what you get when an Overpass query returns more than one admin_level. By default geoClassy returns the smallest matching area, that is, the most specific one:

areas.locate(45.472, 9.188)  # 'Brera', not 'Milano' or 'Lombardia'

Change it with on_overlap, either for the whole dataset or per call:

on_overlap result
"smallest" the smallest matching area (default)
"first" / "last" first or last match in file order
"all" every match, smallest first
"error" raise OverlapError

To find out whether this affects your data at all, ask:

areas.overlapping_pairs()  # [] means no policy can change anything

Loading

geoClassy.load(path, *, name_key="name", only_boundaries=False, on_overlap="smallest")
  • .geojson, .json, and .gz versions of either are read transparently.
  • name_key picks the property to label with — "ISO3166-2", "ref", whatever your export carries. If it is missing, the error tells you which keys exist.
  • only_boundaries=True keeps only features tagged properties.type == "boundary". Off by default: most export tools don't set it.
  • Areas.from_geojson(data) takes an already-parsed dict.
  • locate(..., full=True) returns the feature's whole properties dict rather than just the name, so you keep admin_level, wikidata, ISO codes.

Coordinates are WGS84 degrees, the system GeoJSON mandates. Arguments are (lat, lon); GeoJSON stores [lon, lat], and out-of-range values are rejected with a message pointing at that swap. Points exactly on a boundary count as inside. Invalid polygons — endemic in OSM exports — are repaired on load.

Where to get the boundaries

geoClassy never touches the network: you bring the file. Two ways to get one, in one request each:

  • one boundary by name — Nominatim returns it as GeoJSON, polygon included:

    curl -s -A "my-project/1.0 (me@example.com)" -G \
      --data-urlencode "q=Bologna, Italia" --data-urlencode "format=geojson" \
      --data-urlencode "polygon_geojson=1" --data-urlencode "limit=1" \
      "https://nominatim.openstreetmap.org/search" > bologna.geojson
    
  • all the subdivisions of an area — paste a five-line query into Overpass Turbo and export GeoJSON.

The full guide, Getting boundary data, has the Overpass queries ready to paste, explains why admin_level means different things in different countries, why most "neighbourhoods" in OSM are points rather than polygons, and how to check a file before trusting it.

Upgrading from 0.1.x

The old function API still works, with a DeprecationWarning, and will be removed in 1.0. It keeps 0.1.x semantics exactly, so upgrading changes no results before you migrate:

0.1.x 0.2
loadFile(path) areas = geoClassy.load(path)
getNames(lat, lon) areas.locate(lat, lon) — returns None, not 'unknown'
numPoly() len(areas)
polyList() areas.names
checkPoly() gone: load() validates and repairs, and raises if it can't
requisites() gone: just import geoClassy

Three of those crashed with TypeError on Shapely 2.x, and getNames returned whichever overlapping area happened to come last in the file. See CHANGELOG.md.

Contact

Written by Nicola Simboli — support@simboli.eu, simboli.eu. MIT licensed.

Release files for geoClassy 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for geoClassy 0.2.1
File Size Uploaded
geoclassy-0.2.1.tar.gz 19.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for geoClassy 0.2.1
File Interpreter ABI Platform
geoclassy-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 31.2 kB

Release files / geoclassy-0.2.1.tar.gz

Download URL geoclassy-0.2.1.tar.gz
Size 19.3 kB
Tags Source
SHA-256 checksum
How to use checksums
bf9665e20635f3d5dcbbb0fec423e0d294b32976ece8f3e19b0373b32c554546
BLAKE2b-256 checksum
How to use checksums
ccd8dc07e61840949a9974ed6078e8be488f6259c2afc30d5640b6f65a12fc65
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release files / geoclassy-0.2.1-py3-none-any.whl

Download URL geoclassy-0.2.1-py3-none-any.whl
Size 12.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5f9b24f4d208540d37f7613c93859c1f82765fa854acdcbad7603435acc4c8fa
BLAKE2b-256 checksum
How to use checksums
3101dcf49fcde2d0c75a6c45319a6824af3d18c1bed0bd273a3b0851a79a1332
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 10, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

0.0.7

2 release files

0.0.4

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page