Metadata-Version: 2.4
Name: duckosm
Version: 0.4.0
Summary: High-performance OSM-to-routing-network converter built on DuckDB
Author: Kaveh Khoshkhah
License-Expression: MIT
Project-URL: Homepage, https://github.com/khoshkhah/duckOSM
Project-URL: Repository, https://github.com/khoshkhah/duckOSM
Project-URL: Issues, https://github.com/khoshkhah/duckOSM/issues
Keywords: osm,openstreetmap,routing,duckdb,h3,network
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: GIS
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: duckdb<2,>=1.0.0
Requires-Dist: shapely>=2.0.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: click>=8.0
Requires-Dist: h3>=4.0.0
Requires-Dist: rich>=13.0.0
Requires-Dist: pandas
Requires-Dist: timezonefinder>=6.0
Provides-Extra: levels
Requires-Dist: roadstyle>=0.18.1; extra == "levels"
Provides-Extra: viz
Requires-Dist: mapstyle>=0.2.0; extra == "viz"
Requires-Dist: lanestyle>=0.3.0; extra == "viz"
Provides-Extra: routing
Requires-Dist: networkx; extra == "routing"
Provides-Extra: sumo
Requires-Dist: eclipse-sumo; extra == "sumo"
Requires-Dist: sumolib; extra == "sumo"
Provides-Extra: elevation
Requires-Dist: rasterio>=1.3; extra == "elevation"
Requires-Dist: pyproj>=3.0; extra == "elevation"
Provides-Extra: dev
Requires-Dist: roadstyle>=0.18.1; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Requires-Dist: black; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: networkx; extra == "dev"
Requires-Dist: lxml; extra == "dev"
Requires-Dist: frictionless; extra == "dev"
Requires-Dist: path4gmns; extra == "dev"
Dynamic: license-file

<p align="center">
  <img src="https://raw.githubusercontent.com/Khoshkhah/duckOSM/main/images/duckosm-banner.svg" alt="duckOSM" width="720">
</p>

<p align="center">
  <b>OpenStreetMap → a routable road network in one DuckDB file,<br>
  with edge IDs that survive every rebuild, clip and export.</b>
</p>

<p align="center">
  <a href="https://pypi.org/project/duckosm/"><img alt="PyPI" src="https://img.shields.io/pypi/v/duckosm"></a>
  <a href="https://github.com/Khoshkhah/duckOSM/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/Khoshkhah/duckOSM/actions/workflows/ci.yml/badge.svg"></a>
  <a href="https://khoshkhah.github.io/duckOSM/"><img alt="Docs" src="https://img.shields.io/badge/docs-khoshkhah.github.io%2FduckOSM-17191f"></a>
  <img alt="Python 3.10+" src="https://img.shields.io/badge/python-3.10%2B-3776AB">
  <img alt="License MIT" src="https://img.shields.io/badge/license-MIT-blue">
</p>

<p align="center">
  <a href="https://khoshkhah.github.io/duckOSM/"><b>Documentation</b></a> ·
  <a href="https://khoshkhah.github.io/duckOSM/first_network/">Your first network</a> ·
  <a href="https://khoshkhah.github.io/duckOSM/guides/build/">Guides</a> ·
  <a href="https://khoshkhah.github.io/duckOSM/reference/database/">Database schema</a> ·
  <a href="https://khoshkhah.github.io/duckOSM/guides/query/">Query the database</a>
</p>

---

## Why duckOSM

Most OSM-to-network tools number their edges `0, 1, 2, …`, so the numbers change whenever you
rebuild, cut out an area or export, and every table keyed on them has to be matched again.
duckOSM's `edge_id` is a **content hash** of three ids that OpenStreetMap already gives the road
segment: the OSM **way** it comes from (`osm_id`) and the OSM **nodes** where it starts and ends
(`source`, `target`):

```text
edge_id = hash(osm_id, source, target)
```

The order of `source` and `target` is the direction, so the two directions of a two-way street get
different ids. None of the three is a number duckOSM makes up, so the id stays the same
**across rebuilds, clips and exports** ([details](https://khoshkhah.github.io/duckOSM/concepts/edge-ids/)).

## Quick start

```bash
pip install "duckosm[routing]"

curl -LO https://download.geofabrik.de/europe/monaco-latest.osm.pbf
duckosm build --pbf monaco-latest.osm.pbf --output monaco.duckdb --modes driving
```

```python
import duckdb
from duckosm import route

con = duckdb.connect("monaco.duckdb", read_only=True)
q = "SELECT min(edge_id) FROM driving.edges WHERE name = ?"
a = con.execute(q, ["Boulevard du Larvotto"]).fetchone()[0]
b = con.execute(q, ["Avenue Princesse Grace"]).fetchone()[0]

r = route(con, a, b)                     # fastest path; weight="length" for distance
r["time_s"], r["length_m"], r["edges"]   # ~114 s, ~1.5 km, the ordered edge_ids
```

## What you get

- **A driving, a walking and a cycling network** in one file, one schema each, split at the same
  junctions so a road has the same `edge_id` in every mode. OSM access tags are respected: private
  roads are kept for the map but never routed ([details](https://khoshkhah.github.io/duckOSM/concepts/networks/#access-private-and-forbidden-roads)).
- **The graph of legal turns** (`edge_graph`), with OSM turn restrictions, for routing in SQL,
  Python or networkx.
- **Base-map layers** (`features.*`: water, land use, buildings, POIs) in the same file.
- **Maps in one offline HTML page:** `duckosm viz` draws the network and the base map from the
  database; `duckosm route-map` is a route planner (drive, walk, cycle, walk + drive) where you drag a
  start and an end and get turn-by-turn directions ([try it](https://khoshkhah.github.io/duckOSM/guides/route/)).
  The pages are drawn by [mapstyle](https://github.com/Khoshkhah/mapstyle).
- **The drawing order of the roads:** `duckosm levels` stores which road is painted over which (casing and fill
  numbers) in the file ([how](https://khoshkhah.github.io/duckOSM/guides/drawing-order/)).
- **Any area:** a GeoJSON boundary, a place name, a box or an H3 cell; `duckosm boundary` finds an
  area's border by name, and `duckosm extract` cuts a city out of a country build in seconds.
- **Your own fixes for OSM errors**, applied on every build ([how](https://khoshkhah.github.io/duckOSM/guides/fix-osm-errors/)).

## Exports

| Format | Command | How it's verified |
|---|---|---|
| **SUMO** | `duckosm sumo` | SUMO's own `netconvert` assembles the network |
| **MATSim** | `duckosm matsim`, `matsim-lanes` | validated against MATSim's official DTD and XSD schemas |
| **GMNS** | `duckosm gmns` | follows the GMNS table spec |
| **GeoPackage / shapefile** | `duckosm export-gis` | `duckosm gis-debug` reads it back through GDAL and diffs every `edge_id` |
| **networkx** | `duckosm export-graph` | unit tests |

**Experimental:** OpenDRIVE, Lanelet2, railML, lane-level routing, GMNS meso/micro, intermodal
routing, elevation, admin boundaries, base-map layers. See the
[documentation](https://khoshkhah.github.io/duckOSM/).

## For AI agents

- To use duckOSM: the agent skill [`skills/duckosm/SKILL.md`](skills/duckosm/SKILL.md) (also a
  Claude Code plugin: `/plugin marketplace add Khoshkhah/duckOSM`), and the whole documentation as
  one text file: [`llms-full.txt`](https://khoshkhah.github.io/duckOSM/llms-full.txt)
  ([`llms.txt`](https://khoshkhah.github.io/duckOSM/llms.txt) is its index).
- `duckosm info DB --json` says what a database holds; `way`, `route-lanes` and `gis-debug` also
  take `--json`.
- To change duckOSM: the rules are in [`AGENTS.md`](AGENTS.md).

## License

MIT. duckOSM is built on [DuckDB](https://duckdb.org) and its spatial extension; it is an
independent project, not affiliated with or endorsed by the DuckDB Foundation.
