Convert OpenStreetMap and Overpass API data (JSON or XML) to GeoJSON or Shapely geometries.
Output closely matches osmtogeojson (the JavaScript converter used by overpass-turbo), verified by a compatibility suite.
Highlights:
- Assembles full geometries from raw OSM elements: multipolygon and boundary relations, routes, ways and POI nodes
- Accepts Overpass JSON, Overpass XML and plain OSM XML, including
out centerandout bbresponses - Produces a GeoJSON
FeatureCollectionor a list of Shapely shapes with properties — ready for GIS pipelines or rendering - Ships a command-line tool (
osm2geojson) - Lightweight: the only dependencies are
shapelyandrequests
pip install osm2geojsonRequires Python 3.8+.
Try the 1.0 release candidate. Version 1.0 changes the produced GeoJSON (matching osmtogeojson) and cleans up the API. Regular installs are unaffected until the final release; to test it now:
pip install --pre --upgrade osm2geojsonSee the CHANGELOG and MIGRATION_NOTES.md for what changed - and please report any unexpected output differences.
import osm2geojson
# Fetch data from the Overpass API and convert it
xml = osm2geojson.overpass_call('rel(448930); out geom;')
geojson = osm2geojson.xml2geojson(xml)
# Or convert a local OSM/Overpass file
with open('data.osm', encoding='utf-8') as f:
geojson = osm2geojson.xml2geojson(f.read())osm2geojson map.osm map.geojson -i 2 # convert a file, pretty-printed
osm2geojson data.json - # Overpass JSON to stdoutRun osm2geojson --help for all options (input format autodetect/override,
indentation, custom area/polygon definitions, verbosity).
| Function | Input | Output |
|---|---|---|
json2geojson(data, **options) |
Overpass JSON (dict or str) | GeoJSON FeatureCollection |
xml2geojson(xml_str, **options) |
OSM/Overpass XML | GeoJSON FeatureCollection |
json2shapes(data, **options) |
Overpass JSON (dict or str) | list of Shape objects |
xml2shapes(xml_str, **options) |
OSM/Overpass XML | list of Shape objects |
All conversion functions accept these optional keyword-only parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
filter_used_refs |
bool | True |
Drop elements that are only used as parts of other features (False returns everything) |
log_level |
str | None |
Set the library logger level for this call ('DEBUG', 'INFO', ...); None leaves your logging configuration untouched |
area_keys |
dict | None |
Custom area key definitions (defaults from areaKeys.json) |
polygon_features |
list | None |
Custom polygon feature whitelist/blacklist (defaults from polygon-features.json) |
raise_on_failure |
bool | False |
Raise ConversionError on geometry conversion failure instead of skipping the element |
Conversion functions never modify the data passed to them.
json2shapes/xml2shapes return dictionaries pairing a Shapely geometry with
the OSM properties:
{
'shape': Point | LineString | Polygon | ..., # Shapely geometry
'properties': {
'type': 'node' | 'way' | 'relation',
'tags': { ... },
'id': 123,
...
}
}Use shape_to_feature(shape_obj, properties) to turn a Shape object back into
a GeoJSON Feature.
Execute an Overpass QL query and return the raw response text:
result = osm2geojson.overpass_call('[out:json];node(50.746,7.154,50.748,7.157);out;')Optional keyword-only parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
endpoint |
str | overpass-api.de | Overpass API endpoint URL |
timeout |
float | 180 |
Timeout in seconds for each HTTP request |
retries |
int | 5 |
Retries on rate limiting (429), transient server errors (5xx), timeouts and connection errors; other errors fail immediately (0 disables retrying) |
retry_delay |
float | 5 |
Seconds to sleep between attempts |
import osm2geojson
query = """
[out:json];
(
node["amenity"="restaurant"](50.746,7.154,50.748,7.157);
way["amenity"="restaurant"](50.746,7.154,50.748,7.157);
);
out body geom;
"""
result = osm2geojson.overpass_call(query)
geojson = osm2geojson.json2geojson(result)import json
import osm2geojson
with open('overpass.json', encoding='utf-8') as f:
data = json.load(f)
shapes = osm2geojson.json2shapes(data)
for shape_obj in shapes:
geometry = shape_obj['shape'] # Shapely object
osm_tags = shape_obj['properties']['tags']
print(f"Type: {geometry.geom_type}, Tags: {osm_tags}")Version 1.0 changed the produced GeoJSON (to match osmtogeojson) and made converter options keyword-only. See the CHANGELOG for what changed and MIGRATION_NOTES.md for upgrade help.
git clone https://github.com/rapkin/osm2geojson.git
cd osm2geojson
make setup # one-command setup (installs deps + pre-commit hooks)
make all # format, lint and test (do this before committing!)Submodules (osm-polygon-features, id-area-keys) are optional - they are
only needed to regenerate the bundled JSON data (update-osm-polygon-features.sh).
Fetch them with git submodule update --init when needed.
- CONTRIBUTING.md - development setup, workflow and guidelines
- AI_AGENT_GUIDE.md - codebase guide for AI coding assistants
- RELEASE_GUIDE.md - release process for maintainers
Developed by rapkin
Uses data from:
- osm-polygon-features - polygon feature definitions
- id-area-keys - area key definitions (extracted from the iD editor's tagging schema)