Pydantic validation for GeoJson
Project description
pydantic-geojson 🌍
A type-safe, Pydantic-based library for validating and working with GeoJSON data according to RFC 7946 specification.
Supported GeoJSON Objects
| GeoJSON Objects | Status | Description |
|---|---|---|
| Point | ✅ | A single geographic coordinate |
| MultiPoint | ✅ | Multiple points |
| LineString | ✅ | A sequence of connected points forming a line |
| MultiLineString | ✅ | Multiple line strings |
| Polygon | ✅ | A closed area, optionally with holes |
| MultiPolygon | ✅ | Multiple polygons |
| GeometryCollection | ✅ | Collection of different geometry types |
| Feature | ✅ | Geometry with properties |
| FeatureCollection | ✅ | Collection of features |
Installation
pydantic-geojson is compatible with Python 3.9 and up.
The recommended way to install is via poetry:
poetry add pydantic_geojson
Using pip to install is also possible:
pip install pydantic_geojson
Quick Start
from pydantic_geojson import PointModel
# Create a point
point = PointModel(
type="Point",
coordinates=[-105.01621, 39.57422]
)
print(point)
# type='Point' coordinates=Coordinates(lon=-105.01621, lat=39.57422)
GeoJSON Types with Visualizations
| Type | Visualization | Usage |
|---|---|---|
| Point A single geographic coordinate |
from pydantic_geojson import PointModel
data = {
"type": "Point",
"coordinates": [-105.01621, 39.57422]
}
point = PointModel(**data)
|
|
| MultiPoint Multiple points |
from pydantic_geojson import MultiPointModel
data = {
"type": "MultiPoint",
"coordinates": [
[-105.01621, 39.57422],
[-80.666513, 35.053994]
]
}
multi_point = MultiPointModel(**data)
|
|
| LineString A sequence of connected points forming a line |
from pydantic_geojson import LineStringModel
data = {
"type": "LineString",
"coordinates": [
[-99.113159, 38.869651],
[-99.0802, 38.85682],
[-98.822021, 38.85682],
[-98.448486, 38.848264]
]
}
line_string = LineStringModel(**data)
|
|
| MultiLineString Multiple line strings |
from pydantic_geojson import MultiLineStringModel
data = {
"type": "MultiLineString",
"coordinates": [
[[-105.019898, 39.574997],
[-105.019598, 39.574898],
[-105.019061, 39.574782]],
[[-105.017173, 39.574402],
[-105.01698, 39.574385],
[-105.016636, 39.574385]]
]
}
multi_line_string = MultiLineStringModel(**data)
|
|
| Polygon A closed area, optionally with holes |
from pydantic_geojson import PolygonModel
data = {
"type": "Polygon",
"coordinates": [
[[100, 0],
[101, 0],
[101, 1],
[100, 1],
[100, 0]]
]
}
polygon = PolygonModel(**data)
|
|
| MultiPolygon Multiple polygons |
from pydantic_geojson import MultiPolygonModel
data = {
"type": "MultiPolygon",
"coordinates": [
[[[107, 7], [108, 7],
[108, 8], [107, 8],
[107, 7]]],
[[[100, 0], [101, 0],
[101, 1], [100, 1],
[100, 0]]]
]
}
multi_polygon = MultiPolygonModel(**data)
|
|
| GeometryCollection Collection of different geometry types |
from pydantic_geojson import GeometryCollectionModel
data = {
"type": "GeometryCollection",
"geometries": [
{
"type": "Point",
"coordinates": [-80.660805, 35.049392]
},
{
"type": "Polygon",
"coordinates": [[[-80.664582, 35.044965],
[-80.663874, 35.04428],
[-80.662586, 35.04558],
[-80.663444, 35.046036],
[-80.664582, 35.044965]]]
},
{
"type": "LineString",
"coordinates": [[-80.662372, 35.059509],
[-80.662693, 35.059263],
[-80.662844, 35.05893]]
}
]
}
geometry_collection = GeometryCollectionModel(**data)
|
Features and FeatureCollections
Feature
A geometry with properties.
from pydantic_geojson import FeatureModel
data = {
"type": "Feature",
"properties": {
"name": "Dinagat Islands",
"population": 10000
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-80.724878, 35.265454],
[-80.722646, 35.260338],
[-80.720329, 35.260618],
[-80.71681, 35.255361],
[-80.704793, 35.268397],
[-80.724878, 35.265454]
]
]
}
}
feature = FeatureModel(**data)
FeatureCollection
A collection of features.
from pydantic_geojson import FeatureCollectionModel
data = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-80.870885, 35.215151]
},
"properties": {
"name": "Location 1"
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-80.724878, 35.265454],
[-80.722646, 35.260338],
[-80.720329, 35.260618],
[-80.704793, 35.268397],
[-80.724878, 35.265454]
]
]
},
"properties": {
"name": "Location 2"
}
}
]
}
feature_collection = FeatureCollectionModel(**data)
Custom Properties Models
You can define typed properties models for type-safe feature properties:
from pydantic import BaseModel
from pydantic_geojson import FeatureModel
class CityProperties(BaseModel):
name: str
population: int
country: str
class CityFeature(FeatureModel):
properties: CityProperties
data = {
"type": "Feature",
"properties": {
"name": "New York",
"population": 8336817,
"country": "USA"
},
"geometry": {
"type": "Point",
"coordinates": [-74.006, 40.7128]
}
}
city = CityFeature(**data)
print(city.properties.name) # "New York"
print(city.properties.population) # 8336817
Validation
pydantic-geojson automatically validates:
- Coordinate ranges: Longitude must be between -180 and 180, latitude between -90 and 90
- Geometry types: Ensures correct type strings according to RFC 7946
- Structure: Validates GeoJSON object structure and required fields
- Type safety: Full type checking with Pydantic
Example: Invalid Coordinates
from pydantic_geojson import PointModel
from pydantic import ValidationError
try:
point = PointModel(
type="Point",
coordinates=[200, 50] # Invalid longitude (> 180)
)
except ValidationError as e:
print(e)
# 1 validation error for PointModel
# coordinates.0
# Input should be less than or equal to 180 [type=less_than_equal, input_value=200, input_type=int]
FastAPI Integration
pydantic-geojson works seamlessly with FastAPI for automatic API documentation and OpenAPI schema generation. FastAPI automatically generates interactive API documentation (Swagger UI) with proper GeoJSON schemas.
Basic Example
from fastapi import FastAPI
from pydantic_geojson import FeatureCollectionModel, FeatureModel
app = FastAPI()
@app.post("/features", response_model=FeatureModel)
async def create_feature(feature: FeatureModel):
"""Create a new GeoJSON feature."""
# Your business logic here
return feature
@app.get("/features", response_model=FeatureCollectionModel)
async def get_features():
"""Get all features as a FeatureCollection."""
return FeatureCollectionModel(
type="FeatureCollection",
features=[
FeatureModel(
type="Feature",
geometry={
"type": "Point",
"coordinates": [-105.01621, 39.57422]
}
)
]
)
Working with Different Geometry Types
You can use any GeoJSON geometry type in your FastAPI endpoints:
from fastapi import FastAPI
from pydantic_geojson import (
PointModel,
PolygonModel,
LineStringModel,
FeatureModel,
FeatureCollectionModel
)
app = FastAPI()
@app.post("/points", response_model=PointModel)
async def create_point(point: PointModel):
"""Create a Point geometry."""
return point
@app.post("/polygons", response_model=PolygonModel)
async def create_polygon(polygon: PolygonModel):
"""Create a Polygon geometry."""
return polygon
@app.post("/linestrings", response_model=LineStringModel)
async def create_linestring(linestring: LineStringModel):
"""Create a LineString geometry."""
return linestring
Custom Properties with FastAPI
Define typed properties models for type-safe feature properties in your API:
from fastapi import FastAPI
from pydantic import BaseModel
from pydantic_geojson import FeatureModel, FeatureCollectionModel
app = FastAPI()
class CityProperties(BaseModel):
name: str
population: int
country: str
area_km2: float
class CityFeature(FeatureModel):
properties: CityProperties
@app.post("/cities", response_model=CityFeature)
async def create_city(city: CityFeature):
"""Create a city feature with typed properties."""
# Access typed properties
print(f"City: {city.properties.name}")
print(f"Population: {city.properties.population}")
return city
@app.get("/cities", response_model=FeatureCollectionModel)
async def get_cities():
"""Get all cities as a FeatureCollection."""
return FeatureCollectionModel(
type="FeatureCollection",
features=[
CityFeature(
type="Feature",
properties=CityProperties(
name="New York",
population=8336817,
country="USA",
area_km2=783.8
),
geometry={
"type": "Point",
"coordinates": [-74.006, 40.7128]
}
)
]
)
OpenAPI Schema Generation
FastAPI automatically generates OpenAPI schemas for all pydantic-geojson models. The generated schemas include:
- Proper GeoJSON structure - All geometry types, Features, and FeatureCollections
- Coordinate validation - Longitude (-180 to 180) and latitude (-90 to 90) constraints
- Type definitions - Complete type information for all fields
- Example values - Sample data in the documentation
Access the interactive API documentation at:
- Swagger UI:
http://localhost:8000/docs - ReDoc:
http://localhost:8000/redoc - OpenAPI JSON:
http://localhost:8000/openapi.json
Example API with Full CRUD Operations
from fastapi import FastAPI, HTTPException
from pydantic_geojson import FeatureModel, FeatureCollectionModel
from typing import List
app = FastAPI()
# In-memory storage (use a database in production)
features_db: List[FeatureModel] = []
@app.post("/features", response_model=FeatureModel, status_code=201)
async def create_feature(feature: FeatureModel):
"""Create a new GeoJSON feature."""
features_db.append(feature)
return feature
@app.get("/features", response_model=FeatureCollectionModel)
async def list_features():
"""List all features as a FeatureCollection."""
return FeatureCollectionModel(
type="FeatureCollection",
features=features_db
)
@app.get("/features/{feature_id}", response_model=FeatureModel)
async def get_feature(feature_id: int):
"""Get a specific feature by ID."""
if feature_id >= len(features_db):
raise HTTPException(status_code=404, detail="Feature not found")
return features_db[feature_id]
@app.put("/features/{feature_id}", response_model=FeatureModel)
async def update_feature(feature_id: int, feature: FeatureModel):
"""Update a specific feature."""
if feature_id >= len(features_db):
raise HTTPException(status_code=404, detail="Feature not found")
features_db[feature_id] = feature
return feature
@app.delete("/features/{feature_id}", status_code=204)
async def delete_feature(feature_id: int):
"""Delete a specific feature."""
if feature_id >= len(features_db):
raise HTTPException(status_code=404, detail="Feature not found")
features_db.pop(feature_id)
Compatibility with Other Libraries
pydantic-geojson is designed to work well with popular Python geospatial libraries:
Pydantic
Built on Pydantic, so you get all the benefits:
- Type validation
- JSON serialization/deserialization
- Model configuration
- Field validation
FastAPI
Perfect integration for building GeoJSON APIs with automatic OpenAPI documentation.
GeoPandas
You can convert between pydantic-geojson and GeoPandas:
import geopandas as gpd
from pydantic_geojson import FeatureCollectionModel
# Convert FeatureCollection to GeoDataFrame
feature_collection = FeatureCollectionModel(...)
geojson_dict = feature_collection.model_dump()
gdf = gpd.GeoDataFrame.from_features(geojson_dict["features"])
# Convert GeoDataFrame to FeatureCollection
features = [
FeatureModel(**feature)
for feature in gdf.to_dict("records")
]
Shapely
Convert to/from Shapely geometries:
from shapely.geometry import Point as ShapelyPoint
from pydantic_geojson import PointModel
# Pydantic GeoJSON to Shapely
pydantic_point = PointModel(type="Point", coordinates=[-105.01621, 39.57422])
shapely_point = ShapelyPoint(
pydantic_point.coordinates.lon,
pydantic_point.coordinates.lat
)
# Shapely to Pydantic GeoJSON
shapely_geom = ShapelyPoint(-105.01621, 39.57422)
pydantic_point = PointModel(
type="Point",
coordinates=[shapely_geom.x, shapely_geom.y]
)
Testing
Run the test suite:
poetry run pytest
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Links
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pydantic_geojson-0.3.2.tar.gz.
File metadata
- Download URL: pydantic_geojson-0.3.2.tar.gz
- Upload date:
- Size: 17.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b6079978430199b6883539c0ccbc45fcbf38c6b885bd0e60781da599efbec202
|
|
| MD5 |
5517653b3016bb43c9877fd87cd4c680
|
|
| BLAKE2b-256 |
2cc0f445b00c0f664a6dd5a60cbe91077801f1cc70946fe38e289c349dd904e2
|
Provenance
The following attestation bundles were made for pydantic_geojson-0.3.2.tar.gz:
Publisher:
publish.yml on gb-libs/pydantic-geojson
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pydantic_geojson-0.3.2.tar.gz -
Subject digest:
b6079978430199b6883539c0ccbc45fcbf38c6b885bd0e60781da599efbec202 - Sigstore transparency entry: 1006494245
- Sigstore integration time:
-
Permalink:
gb-libs/pydantic-geojson@80be2206cfe2ee46b17cc45ec735bf398d8e86ad -
Branch / Tag:
refs/tags/0.3.2 - Owner: https://github.com/gb-libs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@80be2206cfe2ee46b17cc45ec735bf398d8e86ad -
Trigger Event:
release
-
Statement type:
File details
Details for the file pydantic_geojson-0.3.2-py3-none-any.whl.
File metadata
- Download URL: pydantic_geojson-0.3.2-py3-none-any.whl
- Upload date:
- Size: 19.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
714b74eafe4737809e979b7eb8806926ece3675c9f783ffdd4d93f2499ae0270
|
|
| MD5 |
e5f8960cfff720192ecbeb51da2611f4
|
|
| BLAKE2b-256 |
7cfe184cf565b3c6b63c4647660f51fca562d89691220651c920c0402ba8ed65
|
Provenance
The following attestation bundles were made for pydantic_geojson-0.3.2-py3-none-any.whl:
Publisher:
publish.yml on gb-libs/pydantic-geojson
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pydantic_geojson-0.3.2-py3-none-any.whl -
Subject digest:
714b74eafe4737809e979b7eb8806926ece3675c9f783ffdd4d93f2499ae0270 - Sigstore transparency entry: 1006494248
- Sigstore integration time:
-
Permalink:
gb-libs/pydantic-geojson@80be2206cfe2ee46b17cc45ec735bf398d8e86ad -
Branch / Tag:
refs/tags/0.3.2 - Owner: https://github.com/gb-libs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@80be2206cfe2ee46b17cc45ec735bf398d8e86ad -
Trigger Event:
release
-
Statement type: