|
| 1 | +# geospec — GeoJSON for Python (RFC 7946) |
| 2 | + |
| 3 | +**Replaces:** [jazzband/geojson](https://github.com/jazzband/geojson) (4.5M/month PyPI, 984 stars) |
| 4 | +**Package name:** `geospec` (verified available on PyPI) |
| 5 | +**Language:** Python (3.10+) |
| 6 | +**License:** BSD-3-Clause |
| 7 | + |
| 8 | +## Why |
| 9 | + |
| 10 | +The jazzband/geojson package is at risk: |
| 11 | +- Jazzband is sunsetting by end of 2026 (AI spam crisis, governance failure) |
| 12 | +- Last meaningful commit: Dec 2024 (release 3.2.0) |
| 13 | +- Python 3.14 compatibility PR sitting unmerged for 3 months |
| 14 | +- 28 open issues, 25 open PRs — no maintainer response |
| 15 | +- No standalone drop-in alternative exists (geojson-pydantic requires Pydantic, different API) |
| 16 | + |
| 17 | +## Scope |
| 18 | + |
| 19 | +Lightweight, zero-dependency Python library implementing RFC 7946 GeoJSON objects with: |
| 20 | +- All 7 geometry types + Feature + FeatureCollection |
| 21 | +- JSON serialization/deserialization with GeoJSON object hooks |
| 22 | +- Coordinate validation per RFC 7946 |
| 23 | +- `__geo_interface__` protocol support |
| 24 | +- Coordinate utility functions (extraction, transformation) |
| 25 | + |
| 26 | +## Architecture |
| 27 | + |
| 28 | +``` |
| 29 | +geospec/ |
| 30 | +├── __init__.py # Public API re-exports |
| 31 | +├── _types.py # Type aliases (Coord2D, Coord3D, BBox, etc.) |
| 32 | +├── base.py # GeoJSON base class (dict subclass) |
| 33 | +├── geometry.py # Point, LineString, Polygon, Multi*, GeometryCollection |
| 34 | +├── feature.py # Feature, FeatureCollection |
| 35 | +├── codec.py # dump, dumps, load, loads, GeoJSONEncoder |
| 36 | +├── validation.py # RFC 7946 validation logic (separated from geometry) |
| 37 | +├── utils.py # coords, map_coords, map_tuples, map_geometries |
| 38 | +└── py.typed # PEP 561 marker |
| 39 | +``` |
| 40 | + |
| 41 | +## Key Design Decisions |
| 42 | + |
| 43 | +1. **Dict subclass** — same as jazzband/geojson. GeoJSON objects behave as dicts with attribute access. This ensures JSON serialization compatibility and familiar API. |
| 44 | + |
| 45 | +2. **Separated validation** — validation logic in its own module for testability. Each geometry type delegates to validation functions. |
| 46 | + |
| 47 | +3. **Type hints throughout** — full type annotations with `py.typed` marker. TypeAlias for coordinate types (`Coord2D = tuple[float, float]`, `Coord3D = tuple[float, float, float]`). |
| 48 | + |
| 49 | +4. **RFC 7946 strictness** — optional strict mode validates: |
| 50 | + - Longitude range [-180, 180], Latitude range [-90, 90] |
| 51 | + - Right-hand rule for polygon winding order |
| 52 | + - Linear ring closure (first == last coordinate) |
| 53 | + - Minimum coordinate counts per geometry type |
| 54 | + |
| 55 | +5. **Zero dependencies** — stdlib `json` only. No simplejson fallback (unnecessary for modern Python). |
| 56 | + |
| 57 | +6. **API compatibility** — drop-in replacement for jazzband/geojson: |
| 58 | + - Same class names: `Point`, `LineString`, `Polygon`, `MultiPoint`, `MultiLineString`, `MultiPolygon`, `GeometryCollection`, `Feature`, `FeatureCollection` |
| 59 | + - Same functions: `dump`, `dumps`, `load`, `loads`, `coords`, `map_coords` |
| 60 | + - Same `__geo_interface__` protocol |
| 61 | + - Same `errors()` / `is_valid` API |
| 62 | + - Same dict-like behavior with attribute access |
| 63 | + |
| 64 | +## Public API |
| 65 | + |
| 66 | +### Classes |
| 67 | +- `GeoJSON` — base class |
| 68 | +- `Point(coordinates, validate=False, precision=6)` |
| 69 | +- `LineString(coordinates, validate=False, precision=6)` |
| 70 | +- `Polygon(coordinates, validate=False, precision=6)` |
| 71 | +- `MultiPoint(coordinates, validate=False, precision=6)` |
| 72 | +- `MultiLineString(coordinates, validate=False, precision=6)` |
| 73 | +- `MultiPolygon(coordinates, validate=False, precision=6)` |
| 74 | +- `GeometryCollection(geometries=None)` |
| 75 | +- `Feature(id=None, geometry=None, properties=None)` |
| 76 | +- `FeatureCollection(features)` |
| 77 | + |
| 78 | +### Functions |
| 79 | +- `dump(obj, fp)` / `dumps(obj)` — serialize to GeoJSON |
| 80 | +- `load(fp)` / `loads(s)` — deserialize from GeoJSON |
| 81 | +- `coords(obj)` — yield coordinate tuples from any GeoJSON object |
| 82 | +- `map_coords(func, obj)` — apply function to each coordinate dimension |
| 83 | +- `map_tuples(func, obj)` — apply function to each coordinate tuple |
| 84 | +- `map_geometries(func, obj)` — apply function to each geometry |
| 85 | + |
| 86 | +### Properties |
| 87 | +- `obj.is_valid` — bool, True if no validation errors |
| 88 | +- `obj.errors()` — list of validation error strings |
| 89 | +- `obj.__geo_interface__` — GeoJSON-compatible dict |
| 90 | + |
| 91 | +## Improvements Over jazzband/geojson |
| 92 | + |
| 93 | +1. **Type hints** — full annotations, `py.typed` marker for IDE support |
| 94 | +2. **RFC 7946 range validation** — optional lat/lon bounds checking |
| 95 | +3. **Winding order validation** — check right-hand rule for polygons |
| 96 | +4. **No simplejson dependency** — stdlib json only |
| 97 | +5. **Python 3.10+** — modern syntax, no legacy compatibility code |
| 98 | +6. **Separated validation module** — cleaner, more testable |
| 99 | + |
| 100 | +## Deliverables |
| 101 | + |
| 102 | +- [ ] Core implementation (all classes, codec, validation, utils) |
| 103 | +- [ ] Comprehensive test suite (pytest) |
| 104 | +- [ ] pyproject.toml with modern packaging |
| 105 | +- [ ] CI/CD (GitHub Actions) |
| 106 | +- [ ] Migration guide from jazzband/geojson |
0 commit comments