Skip to main content

Persistent, cross-version building IDs for PLATEAU. gml:id changes every release; we recognise the same physical building across versions and mint a surrogate ID that never changes — honestly, with a match confidence.

Project description

Yodo Labs

plateau-id-resolve

Persistent, cross-version building IDs for Project PLATEAU.

Recognises the same physical building across PLATEAU releases and mints a surrogate_id that never changes — so records pinned to it survive the gml:id churn.

🇯🇵 日本語版 README

An open-source project by Yodo Labs · Contact pan@yodolabs.jp

PyPI CI License: MIT Python Yodo Labs

gml:id changes every PLATEAU release; the surrogate_id never does. The same physical building is recognised across versions by geometry, with a match confidence, and cross-version new/demolished/split/merged/modified changes are tracked as honest events.

"Your records are only as durable as the IDs they're pinned to."


The problem

PLATEAU's documentation states plainly that gml:id is not guaranteed to persist across releases: the same physical building can carry a completely different gml:id from one year's CityGML to the next. The 建物ID (市区町村コード-bldg-連番, e.g. 01100-bldg-344507) is more stable in format but is also not guaranteed persistent, and is assigned per-municipality in sequence rather than as a global GUID.

So every system that pins records — damage state, inspection history, hazards — to a building id silently breaks the moment the map updates: the ids drift and the history no longer lines up with the building it describes. This is the #1 shared technical blocker of the whole Physical AI Record Layer stack, and nobody (MLIT included) has solved it cleanly.

What this does

plateau-id-resolve recognises the same physical building across releases by geometry (footprint overlap, centroid, area, topology) and mints a permanent surrogate_id that never changes. It keeps the full history surrogate ↔ {year: gml_id, 建物ID}, detects cross-version 新築 / 滅失 / 分割 / 合併 / 増改築, and bridges 建物ID ↔ 不動産ID — always with a match confidence, and never pretending an ambiguous split/merge/rebuild is a clean 1:1.

This is a foundation layer (role in the stack). Everyone who uses PLATEAU across years hits this pit — so it is independently useful on its own.

Install

pip install plateau-id-resolve
# optional extras
pip install 'plateau-id-resolve[geoparquet]'   # read plateau-bridge GeoParquet
pip install 'plateau-id-resolve[http]'         # the HTTP API server

Core depends only on shapely. The registry is SQLite (stdlib) — one file, no server.

Quickstart (library)

from plateau_id_resolve import Resolver, read_footprints

r = Resolver("city.db")                       # a SQLite file (or ":memory:")
r.ingest_version("2020", read_footprints("city_2020.geojson"))
r.ingest_version("2023", read_footprints("city_2023.geojson"))   # gml:ids all changed

# A record pinned to the surrogate survives the gml:id churn:
res = r.resolve({"gml_id": "bldg_2023_x99"})
print(res.surrogate_id)        # sg_01jh...  (stable, permanent)
print(res.match_confidence)    # 1.0  (exact id lookup)
for h in res.history:
    print(h.dataset_year, h.gml_id, h.building_id, h.match_confidence)

# What changed between releases — honestly:
for e in r.changes(from_year="2020", to_year="2023"):
    print(e.kind, e.surrogate_id, e.confidence, e.related_surrogate_ids)

A complete, no-download runnable demo: examples/quickstart.py.

Resolve by anything

r.resolve({"gml_id": "..."})              # a release's gml:id
r.resolve({"building_id": "01100-bldg-1"})# 建物ID
r.resolve({"surrogate_id": "sg_..."})     # the persistent id itself
r.resolve({"lat": 35.68, "lon": 139.76})  # a coordinate (point-in-polygon)
r.resolve({"footprint": "POLYGON((...))"})# a footprint (WKT / WKB-hex / GeoJSON)
# Unknown? You get None — never a fabricated id.

CLI

plateau-id-resolve ingest  --db city.db --year 2020 city_2020.geojson
plateau-id-resolve ingest  --db city.db --year 2023 city_2023.parquet
plateau-id-resolve resolve --db city.db --gml-id bldg_2023_x99
plateau-id-resolve resolve --db city.db --lat 35.68 --lon 139.76
plateau-id-resolve changes --db city.db --from-year 2020 --to-year 2023
plateau-id-resolve bridge  --db city.db --csv crosswalk.csv     # 建物ID -> 不動産ID
plateau-id-resolve stats   --db city.db

All output is JSON on stdout, so it composes with jq.

HTTP API (optional)

pip install 'plateau-id-resolve[http]'
plateau-id-resolve serve --db city.db        # http://127.0.0.1:8000

GET /resolve?gml_id=… · GET /changes?from_year=…&to_year=…&bbox=… · GET /stats. An unresolved reference returns 404 — not a fabricated id.

建物ID ↔ 不動産ID bridge

PLATEAU's official 不動産ID matching is probabilistic and partial (≈7–69 % by city). We don't re-implement it and we don't fabricate a 1:1 mapping — if a backend has no answer, the 不動産ID is null / covered=false = unknown.

from plateau_id_resolve import TableBridge, bridge_all
backend = TableBridge.from_csv("crosswalk.csv")    # building_id,real_estate_id,confidence
coverage = bridge_all(r.registry, backend)
print(coverage.to_dict())   # honest: total / bridgeable / covered / coverage_ratio

Plug the official WebAPI (or a DB) via CallableBridge(fn) without this package taking a network dependency. For the two official MLIT systems specifically — RealEstateID-matching-system (batch/file) and Building-matching-WebAPI (online) — see the copy-me, runnable reference examples/official_bridge_adapter.py.

The honesty invariant

This package is part of a stack with one shared rule: unsurveyed = unknown, never pretend it's safe (covered=false ⇒ value=null). Here that means:

  • a match_confidence is always present — split/merge/rebuild are genuinely ambiguous, so we expose the uncertainty instead of forcing a 1:1;
  • a surrogate id is permanent and never reused;
  • an unknown 不動産ID is null, never guessed;
  • resolve() returns None rather than inventing a building.

See docs/matching.md and docs/confidence.md for the full method.

Data inputs

Footprints come as GeoJSON or GeoParquet (the latter is what plateau-bridge publishes). Extracting footprints from raw CityGML is plateau-bridge's job — this package consumes footprints, not CityGML. You need ≥2 release years to do anything cross-version.

Where this fits

Part of the Physical AI Record Layer open-source ecosystem. This repo implements the resolve() contract (§③) that the other repos bind to:

🧱 plateau-bridge (records + honesty/query core)   🧱 plateau-id-resolve (this repo — persistent IDs)
🚩 plateau-triage (damage state)   robo-permit (deployment rules)
🔌 caveat-mcp     🤖 sixth-sense     🏛 terra-incognita (benchmark)

plateau-triage / robo-permit / terra-incognita / caveat-mcp all bind their records to the surrogate_id this package mints, so history survives map updates.

Development

pip install -e '.[dev]'
pytest                       # full test suite
ruff check . && mypy plateau_id_resolve
python examples/quickstart.py

Contributing & contact

Issues and PRs welcome. The contracts (record.schema.json, resolve()) follow add-only, minor-bump versioning — backward compatibility is a discipline, not an aspiration.

License

MIT © 2026 PixelX Inc. (ピクセルエックス株式会社) / Yodo Labs

Built by Yodo Labs · PixelX Inc. · ピクセルエックス株式会社

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

plateau_id_resolve-0.1.0.tar.gz (155.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

plateau_id_resolve-0.1.0-py3-none-any.whl (35.0 kB view details)

Uploaded Python 3

File details

Details for the file plateau_id_resolve-0.1.0.tar.gz.

File metadata

  • Download URL: plateau_id_resolve-0.1.0.tar.gz
  • Upload date:
  • Size: 155.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for plateau_id_resolve-0.1.0.tar.gz
Algorithm Hash digest
SHA256 6dfeb6b687a3924e272e18c15e4bbc8fd79c41e006defeacf9677b18536e1bde
MD5 a7953ba4fc919f793d413b999cdf441b
BLAKE2b-256 5c2e9d9cd956d0886b6a4fbbaff426e29de45330ee23c28066af1c0e4eab5122

See more details on using hashes here.

Provenance

The following attestation bundles were made for plateau_id_resolve-0.1.0.tar.gz:

Publisher: release.yml on pixelx-jp/plateau_id_resolve

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file plateau_id_resolve-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for plateau_id_resolve-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5fdf9b064f82b85e6953dee6c7e86e887f5e59331d6a81bbe0a206e3b8bb9324
MD5 3123b790ada51723070e9c0dfd9f5c0e
BLAKE2b-256 5c98c31f6721fc5dda88ea2190a7ec66b6a5b26254a2bffa5ea0d5485e2ffa61

See more details on using hashes here.

Provenance

The following attestation bundles were made for plateau_id_resolve-0.1.0-py3-none-any.whl:

Publisher: release.yml on pixelx-jp/plateau_id_resolve

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page