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.
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.gzversions of either are read transparently.name_keypicks 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=Truekeeps only features taggedproperties.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 wholepropertiesdict rather than just the name, so you keepadmin_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
- Overpass Turbo — draw a bounding box, run a query, export GeoJSON. The Overpass API guide and its cookbook cover the query language.
- polygons.openstreetmap.fr — GeoJSON for a single OSM relation id.
- simboli.eu/geojson-file — ready-made files.
Fetching straight from Overpass is planned for 0.3 as an optional command, so the library itself stays offline.
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.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| geoclassy-0.2.0.tar.gz | 17.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| geoclassy-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.5 kB
Release files / geoclassy-0.2.0.tar.gz
| Download URL | geoclassy-0.2.0.tar.gz |
|---|---|
| Size | 17.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3a7c4eb3b6ea201e413506e71ab07d31875b447c4f6ab7b9a267f4de74ac0524
|
|
BLAKE2b-256 checksum How to use checksums |
2825cf2beae6484c56c807d60d0bfff1fe9e6f51022afd674ea7b8a3ff350d4e
|
| 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 9, 2026.
Transparency logRelease files / geoclassy-0.2.0-py3-none-any.whl
| Download URL | geoclassy-0.2.0-py3-none-any.whl |
|---|---|
| Size | 11.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
295e9638065ad3b079472467dc9694dfd9d501ce3c6564167b28f2082e3cf28c
|
|
BLAKE2b-256 checksum How to use checksums |
fe3bf66019329e4f98e8972eacc0cff9eb923c108c22cc311895f7b4faa2bd35
|
| 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 9, 2026.
Transparency log