{"id":50360568,"url":"https://github.com/paulrobello/par-osm-rust","last_synced_at":"2026-05-30T01:06:16.188Z","repository":{"id":356949963,"uuid":"1231414440","full_name":"paulrobello/par-osm-rust","owner":"paulrobello","description":"Shared OpenStreetMap and SRTM fetch, parse, and cache utilities for Rust","archived":false,"fork":false,"pushed_at":"2026-05-10T15:29:15.000Z","size":84,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-10T17:07:12.526Z","etag":null,"topics":["openstreetmap","overpass","overture","rust","srtm"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/paulrobello.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-07T00:06:24.000Z","updated_at":"2026-05-10T15:29:19.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/paulrobello/par-osm-rust","commit_stats":null,"previous_names":["paulrobello/par-osm-rust"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/paulrobello/par-osm-rust","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paulrobello%2Fpar-osm-rust","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paulrobello%2Fpar-osm-rust/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paulrobello%2Fpar-osm-rust/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paulrobello%2Fpar-osm-rust/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/paulrobello","download_url":"https://codeload.github.com/paulrobello/par-osm-rust/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paulrobello%2Fpar-osm-rust/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33676215,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-29T02:00:06.066Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["openstreetmap","overpass","overture","rust","srtm"],"created_at":"2026-05-30T01:06:15.572Z","updated_at":"2026-05-30T01:06:16.182Z","avatar_url":"https://github.com/paulrobello.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# par-osm-rust\n\n[![CI](https://github.com/paulrobello/par-osm-rust/actions/workflows/ci.yml/badge.svg)](https://github.com/paulrobello/par-osm-rust/actions/workflows/ci.yml)\n[![Crates.io](https://img.shields.io/crates/v/par-osm-rust.svg)](https://crates.io/crates/par-osm-rust)\n[![Docs.rs](https://docs.rs/par-osm-rust/badge.svg)](https://docs.rs/par-osm-rust)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n[![Rust MSRV](https://img.shields.io/badge/rust-1.87%2B-orange.svg)](https://www.rust-lang.org)\n\nShared Rust utilities for fetching, caching, parsing, and normalizing OpenStreetMap-compatible map data.\n\n`par-osm-rust` is the data-source crate used by `osm-to-bedrock` and `osm-world`. It owns network and cache concerns only: Overpass/OSM fetching, optional Overture Maps fetching, source merge policy, OSM XML/PBF parsing, SRTM tile downloads, and HGT elevation lookup. It intentionally does **not** depend on Minecraft, WGPU, UI, renderer, or application-specific types.\n\n```toml\npar-osm-rust = \"0.1.0\"\n```\n\nFor local workspace development, use a path dependency instead:\n\n```toml\npar-osm-rust = { path = \"../par-osm-rust\" }\n```\n\n## What it provides\n\n- **OSM / Overpass**\n  - Safe Overpass endpoint validation with an approved HTTPS host allowlist.\n  - Overpass QL query generation from a bounding box and [`FeatureFilter`](src/filter.rs).\n  - URL-aware raw Overpass XML cache keys, so different endpoints do not share stale raw XML.\n- **Overture Maps**\n  - Optional runtime integration with the `overturemaps` Python CLI.\n  - Theme selection for buildings, transportation, places, base land/water, and addresses.\n  - GeoJSON-to-`OsmData` normalization with synthetic negative IDs for non-OSM geometry.\n- **Source orchestration**\n  - One high-level fetch path, `sources::fetch_map_data`, for OSM-only, Overture-only, merged, and Overture-preferred POI modes.\n  - Configurable Overture failure behavior: graceful OSM fallback by default, or strict failure.\n  - POI dedupe for near-duplicate OSM/Overture points, preferring Overture representatives.\n- **Parsing / serialization**\n  - OSM PBF parsing.\n  - OSM XML parsing for nodes, ways, relations, POIs, addresses, trees, and bounds.\n  - Normalized OSM XML writing via `osm::write_osm_xml_string`.\n- **Elevation**\n  - SRTM tile naming, download, cache lookup, and HGT elevation sampling.\n- **Cache migration**\n  - Shared cache locations under `par-osm-rust`.\n  - Legacy cache migration from older `osm-to-bedrock` cache directories.\n\n## Quick start: fetch normalized map data\n\nUse `sources::fetch_map_data` when an application wants the shared OSM/Overture source policy instead of manually fetching each source.\n\n```rust,no_run\nuse par_osm_rust::filter::FeatureFilter;\nuse par_osm_rust::overture::{OvertureParams, OvertureTheme};\nuse par_osm_rust::sources::{\n    fetch_map_data, OvertureFailureMode, PoiSourceMode, SourceOptions,\n};\n\nfn main() -\u003e anyhow::Result\u003c()\u003e {\n    let bbox = (38.0, -121.0, 38.01, -120.99); // south, west, north, east\n\n    let options = SourceOptions {\n        filter: FeatureFilter::default(),\n        overpass_url: None,       // None uses overpass::default_overpass_url()\n        use_overpass_cache: true,\n        overture: OvertureParams {\n            enabled: true,        // Overture is never fetched unless enabled is true\n            themes: vec![OvertureTheme::Place],\n            ..OvertureParams::default()\n        },\n        poi_source_mode: PoiSourceMode::OverturePreferred,\n        overture_failure_mode: OvertureFailureMode::FallbackToOsm,\n    };\n    let mut progress = |_: f32, _: \u0026str| {};\n    let result = fetch_map_data(bbox, \u0026options, \u0026mut progress)?;\n\n    println!(\"status: {:?}\", result.status);\n    println!(\"pois: {}\", result.data.poi_nodes.len());\n    for warning in result.warnings {\n        eprintln!(\"warning: {warning}\");\n    }\n\n    Ok(())\n}\n```\n\nIf you only need OSM/Overpass data, use `overpass::fetch_osm_data` directly:\n\n```rust,no_run\nuse par_osm_rust::{filter::FeatureFilter, overpass};\n\nfn main() -\u003e anyhow::Result\u003c()\u003e {\n    let bbox = (38.0, -121.0, 38.01, -120.99);\n    let url = overpass::default_overpass_url();\n    let data = overpass::fetch_osm_data(bbox, \u0026FeatureFilter::default(), true, url)?;\n    println!(\"ways: {}\", data.ways.len());\n    Ok(())\n}\n```\n\n## Source options and POI modes\n\n`SourceOptions::default()` uses:\n\n- `FeatureFilter::default()`\n- default Overpass URL resolution\n- `use_overpass_cache = true`\n- `OvertureParams::default()` (`enabled = false`)\n- `PoiSourceMode::OverturePreferred`\n- `OvertureFailureMode::FallbackToOsm`\n\nImportant nuance: `PoiSourceMode::OverturePreferred` is the default policy, but Overture is fetched only when `SourceOptions.overture.enabled == true`. With default options, `fetch_map_data` performs an OSM/Overpass fetch only.\n\nPOI source modes:\n\n| Mode | Behavior |\n| --- | --- |\n| `OsmOnly` | Keep OSM POIs. Overture non-POI data may still merge when explicitly fetched, but OSM POIs win. |\n| `OvertureOnly` | Use Overture POIs only; clear OSM POIs if Overture is unavailable. |\n| `Both` | Merge OSM and Overture POIs, dedupe near duplicates, prefer Overture representatives. |\n| `OverturePreferred` | Use Overture POIs when present; fall back to OSM POIs when Overture is unavailable or returns zero POIs. |\n\nOverture failure modes:\n\n| Mode | Behavior |\n| --- | --- |\n| `FallbackToOsm` | Return OSM data with warnings when Overture fetch fails. This is the default. |\n| `Fail` | Return an error if Overture fetch fails. |\n\n`SourceFetchResult.status` reports the effective outcome (`osm_only`, `overture_only`, `both`, `overture_preferred`, or `overture_fallback_to_osm`) and `warnings` carries human-readable fallback details.\n\n## Overture Maps runtime dependency\n\nOverture integration shells out to the optional `overturemaps` CLI. Install it separately when Overture data is desired:\n\n```bash\npython -m pip install overturemaps\n```\n\nUse `overture::is_cli_available()` to check availability before presenting Overture options in an application UI. If callers use `sources::fetch_map_data` with `OvertureFailureMode::FallbackToOsm`, a missing or failing CLI becomes a warning and OSM data is returned.\n\n## Cache locations\n\nDefault shared cache directories:\n\n- Overpass XML: `~/.cache/par-osm-rust/overpass`\n- Overture GeoJSON: `~/.cache/par-osm-rust/overture`\n- SRTM HGT: `~/.cache/par-osm-rust/srtm`\n\nEnvironment override priority:\n\n| Data | Priority |\n| --- | --- |\n| Overpass XML cache | `PAR_OSM_OVERPASS_CACHE_DIR`, then `OVERPASS_CACHE_DIR`, then shared default |\n| Overture GeoJSON cache | `PAR_OSM_OVERTURE_CACHE_DIR`, then `OVERTURE_CACHE_DIR`, then shared default |\n| SRTM HGT cache | `PAR_OSM_SRTM_CACHE_DIR`, then `SRTM_CACHE_DIR`, then shared default |\n| Overpass endpoint | `OVERPASS_URL`, then `https://overpass-api.de/api/interpreter` |\n\nOn first use, the crate can migrate legacy caches from:\n\n- `~/.cache/osm-to-bedrock/overpass`\n- `~/.cache/osm-to-bedrock/overture`\n- `~/.cache/osm-to-bedrock/srtm`\n\nConsumers can explicitly migrate legacy caches before starting work:\n\n```rust,no_run\nfn main() -\u003e anyhow::Result\u003c()\u003e {\n    let report = par_osm_rust::cache::migrate_legacy_caches()?;\n    println!(\n        \"migrated overpass files: {}\",\n        report.overpass.moved_files + report.overpass.copied_files\n    );\n    println!(\n        \"migrated overture files: {}\",\n        report.overture.moved_files + report.overture.copied_files\n    );\n    println!(\n        \"migrated srtm files: {}\",\n        report.srtm.moved_files + report.srtm.copied_files\n    );\n    Ok(())\n}\n```\n\nThe regular `osm_cache::cache_dir()`, `overture::overture_cache_dir()`, and `srtm::cache_dir()` helpers also attempt default-location legacy migration on first use.\n\n### Overpass cache isolation\n\nRaw Overpass cache entries are keyed by:\n\n- snapped bounding box (`south, west, north, east` rounded to 4 decimals),\n- `FeatureFilter`, and\n- canonical Overpass endpoint URL.\n\nUse `osm_cache::cache_key_for_url`, `read_for_url`, `write_for_url`, and `find_containing_for_url` for endpoint-aware raw XML cache operations. The legacy `cache_key`, `read`, `write`, and `find_containing` APIs remain available for backwards compatibility, but new endpoint-aware fetch paths should use the URL-aware helpers.\n\n## Normalized OSM data model\n\nThe central type is `osm::OsmData`:\n\n- `nodes`: OSM or synthetic node coordinates keyed by ID.\n- `ways`: ordered node-reference geometry with tags.\n- `ways_by_id`: relation lookup index into `ways`.\n- `relations`: multipolygon relations and roles.\n- `bounds`: optional dataset bounding box.\n- `poi_nodes`: renderable/tagged POI nodes.\n- `addr_nodes`: standalone address nodes.\n- `tree_nodes`: individual tree points.\n\n`osm::FeatureSource` tracks whether normalized features came from OSM, Overture, or synthetic generation. This is especially useful for POI merge/dedupe behavior.\n\nWhen serializing prepared data for another consumer, use:\n\n```rust,no_run\nfn write_prepared(data: \u0026par_osm_rust::osm::OsmData) -\u003e std::io::Result\u003c()\u003e {\n    let xml = par_osm_rust::osm::write_osm_xml_string(data);\n    std::fs::write(\"prepared.osm\", xml)\n}\n```\n\nThe XML parser handles nodes, ways, relation members, bounds, tags, POIs, addresses, and trees. It is designed for Overpass/OSM-style XML, including data where ways and relations may require lookup by previously parsed node/way IDs.\n\n## SRTM elevation\n\n```rust,no_run\nuse par_osm_rust::srtm;\n\nfn main() -\u003e anyhow::Result\u003c()\u003e {\n    let bbox = (38.0, -121.0, 38.01, -120.99);\n    let tiles = srtm::tiles_for_bbox(bbox.0, bbox.1, bbox.2, bbox.3);\n    srtm::download_tiles_for_bbox(\n        bbox.0,\n        bbox.1,\n        bbox.2,\n        bbox.3,\n        \u0026srtm::cache_dir(),\n        \u0026|_, _, _| {},\n    )?;\n    println!(\"needed tiles: {tiles:?}\");\n    Ok(())\n}\n```\n\n## Documentation\n\n- [Architecture](docs/ARCHITECTURE.md) - System design, module boundaries, source flow, and cache architecture.\n- [Documentation Style Guide](docs/DOCUMENTATION_STYLE_GUIDE.md) - Standards for project documentation and diagrams.\n\n## Release and publishing\n\nPublishing is manual through GitHub Actions. The workflow checks whether the current `Cargo.toml` version already exists on crates.io, runs tests unless explicitly skipped, performs `cargo publish --dry-run`, and then publishes with the `CARGO_REGISTRY_TOKEN` repository secret.\n\nUse the workflow from GitHub Actions:\n\n```text\nActions → Publish to crates.io → Run workflow\n```\n\nBefore triggering a release, verify locally:\n\n```bash\ncargo fmt -- --check\ncargo check --all-targets\ncargo clippy --all-targets --all-features -- -D warnings\ncargo test --all-features\ncargo publish --dry-run\n```\n\n## Verification\n\n```bash\ncargo fmt -- --check\ncargo check --all-targets\ncargo clippy --all-targets -- -D warnings\ncargo test\ncargo doc --no-deps\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaulrobello%2Fpar-osm-rust","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpaulrobello%2Fpar-osm-rust","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaulrobello%2Fpar-osm-rust/lists"}