# GeoJSON AOI Parser
<!-- markdownlint-disable -->
<p align="center">
<img src="https://raw.githubusercontent.com/hotosm/geojson-aoi-parser/refs/heads/main/docs/images/hot_logo.png" style="width: 200px;" alt="HOT"></a>
</p>
<p align="center">
<em>Parse and normalize a GeoJSON area of interest, using using PostGIS.</em>
</p>
<p align="center">
<a href="https://github.com/hotosm/geojson-aoi-parser/actions/workflows/docs.yml" target="_blank">
<img src="https://github.com/hotosm/geojson-aoi-parser/actions/workflows/docs.yml/badge.svg" alt="Publish Docs">
</a>
<a href="https://github.com/hotosm/geojson-aoi-parser/actions/workflows/publish.yml" target="_blank">
<img src="https://github.com/hotosm/geojson-aoi-parser/actions/workflows/publish.yml/badge.svg" alt="Publish">
</a>
<a href="https://github.com/hotosm/geojson-aoi-parser/actions/workflows/pytest.yml" target="_blank">
<img src="https://github.com/hotosm/geojson-aoi-parser/actions/workflows/pytest.yml/badge.svg?branch=main" alt="Test">
</a>
<a href="https://pypi.org/project/geojson-aoi-parser" target="_blank">
<img src="https://img.shields.io/pypi/v/geojson-aoi-parser?color=%2334D058&label=pypi%20package" alt="Package version">
</a>
<a href="https://pypistats.org/packages/geojson-aoi-parser" target="_blank">
<img src="https://img.shields.io/pypi/dm/geojson-aoi-parser.svg" alt="Downloads">
</a>
<a href="https://github.com/hotosm/geojson-aoi-parser/blob/main/LICENSE.md" target="_blank">
<img src="https://img.shields.io/github/license/hotosm/geojson-aoi-parser.svg" alt="License">
</a>
</p>
---
📖 **Documentation**: <a href="https://hotosm.github.io/geojson-aoi-parser/" target="_blank">https://hotosm.github.io/geojson-aoi-parser/</a>
🖥️ **Source Code**: <a href="https://github.com/hotosm/geojson-aoi-parser" target="_blank">https://github.com/hotosm/geojson-aoi-parser</a>
---
<!-- markdownlint-enable -->
## Why do we need this?
- We generally need an Area of Interest (AOI) specified for software to run
on a geospatial area.
- GeoJSON is a simple exchange format to communicate this AOI.
- We only care about Polygon data types, but GeoJSON data can be quite variable,
with many options for presenting data.
- The goal of this package is to receive GeoJSON data in various forms, then
produce a normalised output that can be used for further processing.
## Priorities
- **Flexible data input**: file bytes, dict, string JSON.
- **Flexible geometry input**:
- Polygon
- MultiPolygons
- GeometryCollection
- Feature
- FeatureCollection
- Splits multipolygons into featcol of individual polygons.
- Handle geometries nested inside GeometryCollection.
- Remove any z-dimension coordinates.
- Warn user if CRS is provided, in a coordinate system other than EPSG:4326.
- **Normalised output**: FeatureCollection containing Polygon geoms.
## Capturing The Warnings
If the GeoJSON has an invalid CRS, or coordinates seem off, a warning
will be raised.
To install:
```bash
pip install geojson-aoi-parser
```
Basic example:
```python
from geojson_aoi import parse_aoi
polygon_geojson = {
"type": "Polygon",
"coordinates": [[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]],
}
# Where 'db' is some upstream database connection.
feat_col = parse_aoi(db, polygon_geojson)
print(feat_col)
# {
# 'type': 'FeatureCollection',
# 'features': [
# {
# 'type': 'Feature',
# 'geometry':
# {
# 'type': 'Polygon',
# 'coordinates': [[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]]
# }
# }
# ]
# }
```
Basic async example:
```python
from geojson_aoi import parse_aoi_async
polygon_geojson = {
"type": "Polygon",
"coordinates": [[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]],
}
# Where 'db' is some upstream database connection.
feat_col = parse_aoi_async(db, polygon_geojson)
print(feat_col)
# {
# 'type': 'FeatureCollection',
# 'features': [
# {
# 'type': 'Feature',
# 'geometry':
# {
# 'type': 'Polygon',
# 'coordinates': [[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]]
# }
# }
# ]
# }
```
To halt execution when a warning is raised and act on it:
```python
try:
featcol = parse_aoi(db, raw_geojson)
except UserWarning as warning:
log.error(warning.message)
msg = "Using a valid CRS is mandatory!"
log.error(msg)
raise HTTPException(HTTPStatus.BAD_REQUEST, detail=msg)
```
To record warnings, but allow execution to continue:
```python
import warnings
with warnings.catch_warnings(record=True) as recorded_warnings:
featcol = parse_aoi(db, raw_geojson)
if recorded_warnings:
for warning in recorded_warnings:
if isinstance(warning.message, UserWarning)
# do stuff with warning
logger.warning(f"A warning was encountered: {warning.message}")
```
Create a New DB Connection
- If your app upstream already has a psycopg connection, this can be passed through.
- If you require a new database connection, the connection parameters can be
defined as DbConfig object variables:
```python
from geojson_aoi import parse_aoi, DbConfig
db = DbConfig(
dbname="db1",
user="user1",
password="pass1",
host="localhost",
port="5432",
)
featcol = parse_aoi(db, raw_geojson)
```
- Or alternatively as variables from your system environment:
```bash
GEOJSON_AOI_DB_NAME=aoi
GEOJSON_AOI_DB_USER=aoi
GEOJSON_AOI_DB_PASSWORD=pass
GEOJSON_AOI_DB_HOST=localhost
GEOJSON_AOI_DB_PORT=5432
```
then
```python
from geojson_aoi import parse_aoi
featcol = parse_aoi(db, raw_geojson)
```
((All credit for DbConfig goes to <https://github.com/hotosm/pg-nearest-city/>))
## History
- Initially I tried to write a pure-Python implementation of this, no dependencies.
- I underestimated the amount of work that is! It could be possible to reverse
engineer C++ Geos or georust/geos, but it's more hassle than it's worth.
- As all of the target install candidates for this package use a db driver
anyway, I thought it wisest (and most time efficient) to use the PostGIS
Geos implementation (specifically for the unary_union and convex_hull
algorithms).
- An additional advantage is the potential to port this to PGLite when the
PostGIS extension is available, meaning AOI processing easily in the browser.
Raw data
{
"_id": null,
"home_page": null,
"name": "geojson-aoi-parser",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "aoi, geojson, hotosm, normalize, parser",
"author": null,
"author_email": "Sam Woodcock <sam.woodcock@hotosm.org>, Luke Everhart <luke.f.everhart@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/e3/49/87ba90676d4b78b8c1613a45c0c1d3b3487ecbe2b8244e67f22eb868bc5d/geojson_aoi_parser-0.3.0.tar.gz",
"platform": null,
"description": "# GeoJSON AOI Parser\n\n<!-- markdownlint-disable -->\n<p align=\"center\">\n <img src=\"https://raw.githubusercontent.com/hotosm/geojson-aoi-parser/refs/heads/main/docs/images/hot_logo.png\" style=\"width: 200px;\" alt=\"HOT\"></a>\n</p>\n<p align=\"center\">\n <em>Parse and normalize a GeoJSON area of interest, using using PostGIS.</em>\n</p>\n<p align=\"center\">\n <a href=\"https://github.com/hotosm/geojson-aoi-parser/actions/workflows/docs.yml\" target=\"_blank\">\n <img src=\"https://github.com/hotosm/geojson-aoi-parser/actions/workflows/docs.yml/badge.svg\" alt=\"Publish Docs\">\n </a>\n <a href=\"https://github.com/hotosm/geojson-aoi-parser/actions/workflows/publish.yml\" target=\"_blank\">\n <img src=\"https://github.com/hotosm/geojson-aoi-parser/actions/workflows/publish.yml/badge.svg\" alt=\"Publish\">\n </a>\n <a href=\"https://github.com/hotosm/geojson-aoi-parser/actions/workflows/pytest.yml\" target=\"_blank\">\n <img src=\"https://github.com/hotosm/geojson-aoi-parser/actions/workflows/pytest.yml/badge.svg?branch=main\" alt=\"Test\">\n </a>\n <a href=\"https://pypi.org/project/geojson-aoi-parser\" target=\"_blank\">\n <img src=\"https://img.shields.io/pypi/v/geojson-aoi-parser?color=%2334D058&label=pypi%20package\" alt=\"Package version\">\n </a>\n <a href=\"https://pypistats.org/packages/geojson-aoi-parser\" target=\"_blank\">\n <img src=\"https://img.shields.io/pypi/dm/geojson-aoi-parser.svg\" alt=\"Downloads\">\n </a>\n <a href=\"https://github.com/hotosm/geojson-aoi-parser/blob/main/LICENSE.md\" target=\"_blank\">\n <img src=\"https://img.shields.io/github/license/hotosm/geojson-aoi-parser.svg\" alt=\"License\">\n </a>\n</p>\n\n---\n\n\ud83d\udcd6 **Documentation**: <a href=\"https://hotosm.github.io/geojson-aoi-parser/\" target=\"_blank\">https://hotosm.github.io/geojson-aoi-parser/</a>\n\n\ud83d\udda5\ufe0f **Source Code**: <a href=\"https://github.com/hotosm/geojson-aoi-parser\" target=\"_blank\">https://github.com/hotosm/geojson-aoi-parser</a>\n\n---\n\n<!-- markdownlint-enable -->\n\n## Why do we need this?\n\n- We generally need an Area of Interest (AOI) specified for software to run\n on a geospatial area.\n- GeoJSON is a simple exchange format to communicate this AOI.\n- We only care about Polygon data types, but GeoJSON data can be quite variable,\n with many options for presenting data.\n- The goal of this package is to receive GeoJSON data in various forms, then\n produce a normalised output that can be used for further processing.\n\n## Priorities\n\n- **Flexible data input**: file bytes, dict, string JSON.\n- **Flexible geometry input**:\n - Polygon\n - MultiPolygons\n - GeometryCollection\n - Feature\n - FeatureCollection\n- Splits multipolygons into featcol of individual polygons.\n- Handle geometries nested inside GeometryCollection.\n- Remove any z-dimension coordinates.\n- Warn user if CRS is provided, in a coordinate system other than EPSG:4326.\n- **Normalised output**: FeatureCollection containing Polygon geoms.\n\n## Capturing The Warnings\n\nIf the GeoJSON has an invalid CRS, or coordinates seem off, a warning\nwill be raised.\n\nTo install:\n\n```bash\npip install geojson-aoi-parser\n```\n\nBasic example:\n\n```python\nfrom geojson_aoi import parse_aoi\n\npolygon_geojson = {\n \"type\": \"Polygon\",\n \"coordinates\": [[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]],\n }\n\n# Where 'db' is some upstream database connection.\nfeat_col = parse_aoi(db, polygon_geojson)\n\nprint(feat_col)\n# {\n# 'type': 'FeatureCollection',\n# 'features': [\n# {\n# 'type': 'Feature', \n# 'geometry': \n# {\n# 'type': 'Polygon', \n# 'coordinates': [[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]]\n# }\n# }\n# ]\n# }\n```\n\nBasic async example:\n\n```python\nfrom geojson_aoi import parse_aoi_async\n\npolygon_geojson = {\n \"type\": \"Polygon\",\n \"coordinates\": [[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]],\n }\n\n# Where 'db' is some upstream database connection.\nfeat_col = parse_aoi_async(db, polygon_geojson)\n\nprint(feat_col)\n# {\n# 'type': 'FeatureCollection',\n# 'features': [\n# {\n# 'type': 'Feature', \n# 'geometry': \n# {\n# 'type': 'Polygon', \n# 'coordinates': [[[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]]\n# }\n# }\n# ]\n# }\n```\n\nTo halt execution when a warning is raised and act on it:\n\n```python\ntry:\n featcol = parse_aoi(db, raw_geojson)\nexcept UserWarning as warning:\n log.error(warning.message)\n msg = \"Using a valid CRS is mandatory!\"\n log.error(msg)\n raise HTTPException(HTTPStatus.BAD_REQUEST, detail=msg)\n```\n\nTo record warnings, but allow execution to continue:\n\n```python\nimport warnings\n\nwith warnings.catch_warnings(record=True) as recorded_warnings:\n featcol = parse_aoi(db, raw_geojson)\n\nif recorded_warnings:\n for warning in recorded_warnings:\n if isinstance(warning.message, UserWarning)\n # do stuff with warning\n logger.warning(f\"A warning was encountered: {warning.message}\")\n```\n\nCreate a New DB Connection\n\n- If your app upstream already has a psycopg connection, this can be passed through.\n- If you require a new database connection, the connection parameters can be\n defined as DbConfig object variables:\n\n```python\nfrom geojson_aoi import parse_aoi, DbConfig\n\ndb = DbConfig(\n dbname=\"db1\",\n user=\"user1\",\n password=\"pass1\",\n host=\"localhost\",\n port=\"5432\",\n)\n\nfeatcol = parse_aoi(db, raw_geojson)\n```\n\n- Or alternatively as variables from your system environment:\n\n```bash\nGEOJSON_AOI_DB_NAME=aoi\nGEOJSON_AOI_DB_USER=aoi\nGEOJSON_AOI_DB_PASSWORD=pass\nGEOJSON_AOI_DB_HOST=localhost\nGEOJSON_AOI_DB_PORT=5432\n```\n\nthen\n\n```python\nfrom geojson_aoi import parse_aoi\n\nfeatcol = parse_aoi(db, raw_geojson)\n```\n\n((All credit for DbConfig goes to <https://github.com/hotosm/pg-nearest-city/>))\n\n## History\n\n- Initially I tried to write a pure-Python implementation of this, no dependencies.\n- I underestimated the amount of work that is! It could be possible to reverse\n engineer C++ Geos or georust/geos, but it's more hassle than it's worth.\n- As all of the target install candidates for this package use a db driver\n anyway, I thought it wisest (and most time efficient) to use the PostGIS\n Geos implementation (specifically for the unary_union and convex_hull\n algorithms).\n- An additional advantage is the potential to port this to PGLite when the\n PostGIS extension is available, meaning AOI processing easily in the browser.\n",
"bugtrack_url": null,
"license": "GPL-3.0-only",
"summary": "Parse and normalize a GeoJSON area of interest, using PostGIS.",
"version": "0.3.0",
"project_urls": null,
"split_keywords": [
"aoi",
" geojson",
" hotosm",
" normalize",
" parser"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "91d5a877827ecae92faaf6dee70d3962eebafaa2640d959d0315b5b4dbb8a8db",
"md5": "3e82d908a49c1ce337e689c18dbeb109",
"sha256": "34b3818516f7dae08bb538f85042977ec82614257f8435dda065b04d02a1f39c"
},
"downloads": -1,
"filename": "geojson_aoi_parser-0.3.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "3e82d908a49c1ce337e689c18dbeb109",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 30470,
"upload_time": "2025-07-15T01:41:22",
"upload_time_iso_8601": "2025-07-15T01:41:22.807195Z",
"url": "https://files.pythonhosted.org/packages/91/d5/a877827ecae92faaf6dee70d3962eebafaa2640d959d0315b5b4dbb8a8db/geojson_aoi_parser-0.3.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "e34987ba90676d4b78b8c1613a45c0c1d3b3487ecbe2b8244e67f22eb868bc5d",
"md5": "07a019e9e356e163e51c7909896b705a",
"sha256": "624176441f484ea067831727a11a6a0126a29e1c4e00d13d3c9f45834a726984"
},
"downloads": -1,
"filename": "geojson_aoi_parser-0.3.0.tar.gz",
"has_sig": false,
"md5_digest": "07a019e9e356e163e51c7909896b705a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 72524,
"upload_time": "2025-07-15T01:41:23",
"upload_time_iso_8601": "2025-07-15T01:41:23.883835Z",
"url": "https://files.pythonhosted.org/packages/e3/49/87ba90676d4b78b8c1613a45c0c1d3b3487ecbe2b8244e67f22eb868bc5d/geojson_aoi_parser-0.3.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-15 01:41:23",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "geojson-aoi-parser"
}