GeoJSON library for Python with types.
The world doesn't need another GeoJSON library for python, but it needs a better one. The goal of this library is to provide a simple and typed data model for GeoJSON such that y'all can get static code check and editor support with mypy. And of course, data should be data, so the data model is not polluted with inheritance, custom methods, utilities, extension or other "conveniences". The data is of course immutable.
We have built pygeojson
on top of Python's
dataclasses
to provide
type support and immutability. We do not use inheritance, but rather combine
the classes with Union
types
where applicable.
In addition to the data representation, we provide serializers and deserializers
via the dump
, dumps
, load
, loads
, load_feature_collection
and
loads_feature_collection
functions.
pygeojson
requires Python 3.7 and up. Install it via pipenv
:
pipenv install pygeojson
or pip
:
pip install pygeojson
The classes provided in pygeojson
matches the GeoJSON specification
(RFC 7946) with some minor adjustments:
- Coordinates are tuples, not lists, e.g
(1.0, 2.0)
, not[1.0, 2.0]
- Attributes that are part of the GeoJSON standard are accessed as class
members, e.g
point.coordinates
and not `point["coordinates"]. - Extra attributes not mentioned in the standard are set it the
extra_attributes
dictionary and accessed like a dictionary entry, e.gpoint["custom_attribute"]
.
We provide the following classes that is a part of the GeometryObject
union
type:
Point
MultiPoint
LineString
MultiLineString
Polygon
MultiPolygon
GeometryCollection
In addition, the GeoJSON
union type, in addition to the elements in
GeometryObject
includes:
Feature
FeatureCollection
To read a file, use the load
method:
from pygeojson import load
with open("myfile.geojson") as f:
my_object = load(f) # my_object is type GeoJSON
print(my_object.type) # prints the type of the object
if isinstance(my_object, Feature):
print(my_object.geometry) # Prints a representation of the Feature's geometry
if isinstance(my_object, FeatureCollection):
print(len(my_object.features)) # Prints the number of features in a feature collection
print(my_object["name"]) # Prints an attribute from the original file that is not part of the geojson standard
Because most files are stored as FeatureCollection
s, we provide a utility
function for returning that data type directly, and raising a TypeError
if
it's not really a FeatureCollection
:
from pygeojson import load_feature_collection
with open("myfile.geojson") as f:
my_feature_collection = load_feature_collection(f) # my_feature_collection is type FeatureCollection
Both load
and load_feature_collection
comes with a companion loads
and
loads_feature_collection
for reading strings instead of files.
To write data, use the dump
method:
from pygeojson import FeatureCollection, Feature, Point
feature_collection = FeatureCollection(
[
Feature(
Point((1.0, 2.0))
)
],
extra_attributes={"name": "foo"}
)
with open("feature_collection.geojson", "w") as f:
dump(feature_collection, f)
You may also use dumps
to serialize into a string instead of a file.
Since the data is immutable, one must always store modifications into a new object. For instance, the following won't work:
from pygeojson import Point
p = Point((1.0, 2.0), bbox=(0.0, 1.1, 2.2, 3.3))
p.coordinates = (2.0, 1.0)
"""
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 4, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'coordinates'
"""
Instead, we use the replace
method in dataclasses
:
from pygeojson import Point
from dataclasses import replace
p = Point((1.0, 2.0), bbox=(0.0, 1.1, 2.2, 3.3))
p2 = replace(p, coordinates=(1.0, 2.0))
print(p2) # Prints "Point(coordinates=(1.0, 2.0), bbox=(0.0, 1.1, 2.2, 3.3), type='Point')"
We know it can take a little time getting used to, but we believe immutable data structures is paramount in developing functions without side effects, which in turn creates better and easier-to-maintain software.
Please see CONTRIBUTING.md.