Skip to main content

Python helper functions for manipulating GeoJSON

Project description

geojson-python-utils

Python utilities for working with GeoJSON dictionaries. The package covers common geometry checks, distance calculations, polygon helpers, coordinate conversion, FeatureCollection utilities, and point-line simplification.

The project began as a Python port inspired by geojson-js-utils. It now targets Python 3.8+ and ships inline type annotations.

Why This Package

  • Works with plain Python dictionaries that follow the GeoJSON shape.
  • Keeps dependencies small; requests is only used by the optional geocoding helper.
  • Includes typed public functions and a py.typed marker for downstream type checkers.
  • Provides tested helpers for common GIS tasks without requiring a full geospatial stack.

Features

  • LineString intersection detection.
  • Point-in-Polygon and Point-in-MultiPolygon checks.
  • Circle polygon generation from a center point and radius.
  • Polygon area, centroid, and rectangle centroid helpers.
  • Spherical and ellipsoidal distance calculations.
  • Radius checks for Point, LineString, and Polygon geometries.
  • Destination point calculation from bearing and distance.
  • FeatureCollection merging and endpoint extraction.
  • Point array simplification with a meter-based tolerance.
  • Coordinate conversion between WGS84, GCJ-02, and BD-09.
  • Validation and normalization helpers for GeoJSON geometries, Features, and FeatureCollections.

Requirements

  • Python 3.8 or newer.
  • requests>=2.9.1.

Python 2 is not supported.

Installation

pip install geojson_utils

You can also copy the geojson_utils/ package into a project and import from it directly.

Quick Start

from geojson_utils import point_distance, point_in_polygon

point = {"type": "Point", "coordinates": [5, 5]}
polygon = {
    "type": "Polygon",
    "coordinates": [[[0, 0], [10, 0], [10, 10], [0, 10]]],
}

print(point_in_polygon(point, polygon))

oakland = {"type": "Point", "coordinates": [-122.260000705719, 37.80919060818706]}
naval_base = {"type": "Point", "coordinates": [-122.32083320617676, 37.78774223089045]}

print(point_distance(oakland, naval_base))

Most functions accept and return plain GeoJSON dictionaries. The package does not require custom geometry classes.

Validation and Normalization

from geojson_utils import normalize_geojson, validate_geojson

polygon = {
    "type": "Polygon",
    "coordinates": [[[0, 0], [10, 0], [10, 10], [0, 10]]],
}

normalized = normalize_geojson(polygon, close_rings=True)
validate_geojson(normalized)

validate_geojson() supports every standard GeoJSON geometry type plus Feature and FeatureCollection objects. Invalid objects raise GeoJSONValidationError with a path to the failing field.

normalize_geojson() returns a copy. It can close polygon rings, orient exterior rings and holes, and optionally remove bbox or id fields.

Geometry Helpers

LineString Intersection

from geojson_utils import linestrings_intersect

diagonal_up = {"type": "LineString", "coordinates": [[0, 0], [10, 10]]}
diagonal_down = {"type": "LineString", "coordinates": [[10, 0], [0, 10]]}
far_away = {"type": "LineString", "coordinates": [[100, 100], [110, 110]]}

print(linestrings_intersect(diagonal_up, diagonal_down))
print(linestrings_intersect(diagonal_up, far_away))

Point in Polygon

from geojson_utils import point_in_multipolygon, point_in_polygon

point = {"type": "Point", "coordinates": [5, 5]}
polygon = {
    "type": "Polygon",
    "coordinates": [[[0, 0], [10, 0], [10, 10], [0, 10]]],
}

print(point_in_polygon(point, polygon))

multi_polygon = {
    "type": "MultiPolygon",
    "coordinates": [
        [[[0, 0], [0, 10], [10, 10], [10, 0], [0, 0]]],
        [[[10, 10], [10, 20], [20, 20], [20, 10], [10, 10]]],
    ],
}

print(point_in_multipolygon(point, multi_polygon))

Polygon holes are handled for point-in-polygon checks, area, and centroid calculations. Boundary points count as inside.

Draw a Circle Polygon

from geojson_utils import draw_circle

center = {"type": "Point", "coordinates": [0, 0]}
circle = draw_circle(10, center, steps=50)

print(circle["type"])
print(len(circle["coordinates"][0]))

Distance and Radius Checks

from geojson_utils import geometry_within_radius, point_distance

center = {"type": "Point", "coordinates": [-122.260000705719, 37.80919060818706]}
candidate = {"type": "Point", "coordinates": [-122.32083320617676, 37.78774223089045]}

print(point_distance(center, candidate))
print(geometry_within_radius(candidate, center, 5853))

Area and Centroid

from geojson_utils import area, centroid, rectangle_centroid

polygon = {
    "type": "Polygon",
    "coordinates": [[[0, 0], [10, 0], [10, 10], [0, 10]]],
}

print(area(polygon))
print(centroid(polygon))
print(rectangle_centroid(polygon))

Use close_ring(), ring_is_clockwise(), and orient_ring() when normalizing polygon topology before export.

Destination Point

from geojson_utils import destination_point

start = {"type": "Point", "coordinates": [-122.260000705719, 37.80919060818706]}

print(destination_point(start, 180, 2000))

FeatureCollection Helpers

from geojson_utils import merge_featurecollection, simplify_other

merged = merge_featurecollection(first_feature_collection, second_feature_collection)
deduped = simplify_other(major_points, minor_points, dist=50)

simplify_other() works on Point FeatureCollections. It appends points from the minor collection only when they are farther than dist meters from every point in the major collection.

Simplify Point Arrays

simplify() reduces a list of GeoJSON Point objects with the Ramer-Douglas-Peucker algorithm. The kink value is measured in meters.

from geojson_utils import simplify

points = [
    {"type": "Point", "coordinates": [0, 0]},
    {"type": "Point", "coordinates": [0.001, 0.00001]},
    {"type": "Point", "coordinates": [0.002, 0]},
]

print(simplify(points, kink=20))

The function preserves the first and last point and keeps intermediate points whose perpendicular distance is greater than the kink tolerance.

Coordinate Conversion

convertor() supports Geometry, Feature, FeatureCollection, and GeometryCollection inputs. It mutates the input by default for backwards compatibility; pass inplace=False to return a converted copy.

Method Conversion
wgs2gcj WGS84 to GCJ-02
gcj2wgs GCJ-02 to WGS84
wgs2bd WGS84 to BD-09
bd2wgs BD-09 to WGS84
gcj2bd GCJ-02 to BD-09
bd2gcj BD-09 to GCJ-02
import json

from geojson_utils import convertor

with open("tests/province_wgs.geojson", encoding="utf-8") as fp:
    geojson = json.load(fp)

for feature in geojson["features"]:
converted = convertor(feature["geometry"], method="wgs2gcj")
print(converted["type"])
converted = convertor(geojson, method="gcj2bd", inplace=False)

The coordinate-transform layer keeps the base install lightweight. EPSG/projection based transforms can be added later behind an optional extra such as geojson_utils[crs].

Format Conversion API

The conversion layer provides a small registry so the package can grow new adapters without a large monolithic conversion function.

from geojson_utils import convert, read_geojson, write_geojson

point = read_geojson("point.geojson")
text = convert(point, from_format="geojson", to_format="json")
round_tripped = convert(text, from_format="json", to_format="geojson")
write_geojson(round_tripped, "round-trip.geojson")

You can add adapters with register_converter(from_format, to_format, callable). Built-in adapters currently cover GeoJSON file IO and GeoJSON dictionary <-> JSON text conversion.

WKT / WKB

from geojson_utils import geojson_to_wkt, wkt_to_geojson

wkt = geojson_to_wkt({"type": "Point", "coordinates": [1, 2]})
geometry = wkt_to_geojson("POINT (1 2)")

WKT support is implemented for standard GeoJSON geometry types. WKB helpers are available through optional Shapely support and raise a clear error when Shapely is not installed.

CSV Points

from geojson_utils import csv_to_feature_collection, feature_collection_to_csv

collection = csv_to_feature_collection(
    "id,longitude,latitude,name\n1,120.1,30.2,Hangzhou\n",
    id_column="id",
)
text = feature_collection_to_csv(collection, id_column="id")

CSV conversion targets Point FeatureCollections. Coordinate columns default to longitude and latitude, and all other columns are preserved as Feature properties.

Shapefile / GeoPackage

Heavier desktop GIS formats are exposed through optional adapters so the base package stays small.

pip install "geojson_utils[files]"
from geojson_utils import read_geopackage, read_shapefile, write_geopackage, write_shapefile

collection = read_shapefile("roads.shp")
write_geopackage(collection, "roads.gpkg", layer="roads")

These adapters use GeoPandas when installed. Without the optional dependency, they raise OptionalAdapterError with installation guidance.

Streaming and NDJSON

For large datasets, use feature iterators and newline-delimited GeoJSON helpers instead of loading everything into memory.

from geojson_utils import read_ndjson_features, write_ndjson_features

with open("features.ndjson", encoding="utf-8") as source:
    features = read_ndjson_features(source)
    for feature in features:
        print(feature["geometry"]["type"])

iter_features() yields Feature objects from a Feature, FeatureCollection, or bare Geometry. write_ndjson_features() writes one Feature per line for pipeline-friendly processing.

Bounding Boxes and Spatial Filtering

from geojson_utils import BBoxIndex, bbox, filter_features_by_bbox

bounds = bbox(collection)
nearby = filter_features_by_bbox(collection, [120, 30, 121, 31])
indexed = BBoxIndex(collection).search([120, 30, 121, 31])

bbox() returns [min_lon, min_lat, max_lon, max_lat] for Geometry, Feature, and FeatureCollection objects. The lightweight BBoxIndex keeps precomputed feature bounds for repeated bbox searches without requiring an optional R-tree dependency.

Command Line

Installing the package exposes geojson-utils for common pipeline tasks.

geojson-utils validate input.geojson
geojson-utils convert input.geojson --to json --output output.json
geojson-utils transform input.geojson --method wgs2gcj --output gcj.geojson
geojson-utils simplify line.geojson --tolerance 20 --output simplified.geojson
geojson-utils bbox input.geojson

Use - as the input path to read GeoJSON from stdin. Commands write to stdout unless --output is provided.

Type Checking

The package includes inline annotations and a py.typed marker. Type checkers can read the installed package signatures without separate stub files.

Development

The active development branch is develop.

Run the test suite:

python3 -m unittest discover -v

Run a syntax check:

python3 -m py_compile geojson_utils/*.py test.py setup.py

Documentation

License

MIT

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

geojson_utils-0.0.3.tar.gz (31.7 kB view details)

Uploaded Source

Built Distribution

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

geojson_utils-0.0.3-py3-none-any.whl (26.2 kB view details)

Uploaded Python 3

File details

Details for the file geojson_utils-0.0.3.tar.gz.

File metadata

  • Download URL: geojson_utils-0.0.3.tar.gz
  • Upload date:
  • Size: 31.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for geojson_utils-0.0.3.tar.gz
Algorithm Hash digest
SHA256 b45105fcdb185b3883b16d5439ca090b43452c2b138825642ed7da7b632bfbb5
MD5 a812125a44df5ce100804728b44beb16
BLAKE2b-256 fc3d9a766562ff3fce2e1579575f47ef526b263dbfa294b199f61e61b25e6410

See more details on using hashes here.

File details

Details for the file geojson_utils-0.0.3-py3-none-any.whl.

File metadata

  • Download URL: geojson_utils-0.0.3-py3-none-any.whl
  • Upload date:
  • Size: 26.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for geojson_utils-0.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 1e259576e39b6eb4af3011340eb045314207b53e15c287bb4e1e97749e40c6c1
MD5 36486c73ad4207046057fb12197a1d06
BLAKE2b-256 5493329567c8ef7f14e8c903f064b0f67c8c0d0a4f0ea9b5fbe733f6f422cf41

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