Metadata-Version: 2.4
Name: plateau-triage
Version: 0.1.2
Summary: Post-disaster building damage record layer: 🔴危険 / 🟡要注意 / 🟢調査済 / ⬜未調査 + confidence + provenance, bound to persistent building IDs. Honest by construction: covered=false ⇒ value=null.
Author-email: "Yodo Labs (PixelX Inc. / ピクセルエックス株式会社)" <pan@yodolabs.jp>
License: Apache-2.0
Project-URL: Homepage, https://yodolabs.jp
Project-URL: Repository, https://github.com/pixelx-jp/plateau_triage
Project-URL: Issues, https://github.com/pixelx-jp/plateau_triage/issues
Keywords: plateau,disaster,physical-ai,record-layer,honesty,damage-assessment,geospatial
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: GIS
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: LICENSE-DATA
Requires-Dist: pydantic<3,>=2
Requires-Dist: jsonschema>=4
Requires-Dist: shapely>=2
Requires-Dist: duckdb>=1
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Provides-Extra: shacl
Requires-Dist: pyshacl>=0.26; extra == "shacl"
Requires-Dist: rdflib>=7; extra == "shacl"
Dynamic: license-file

<p align="center">
  <a href="https://yodolabs.jp"><img src="assets/yodo-logo.svg" alt="Yodo Labs" height="64"></a>
</p>

<h1 align="center">plateau-triage 🚩</h1>

<p align="center"><b>English</b> · <a href="README.ja.md">日本語</a></p>

<p align="center">
  <b>Post-disaster building damage record layer</b>
</p>

<p align="center">
  Turn disaster imagery into a per-building, ID-bound record of damage state —
  with confidence, provenance, and, above all, an explicit <b>"not yet surveyed"</b>.
</p>

<p align="center">
  <code>pip install plateau-triage</code> ·
  <a href="docs/">docs</a> ·
  <a href="DESIGN.md">DESIGN</a> ·
  Code Apache-2.0 · Data CC BY 4.0
</p>

<p align="center">
  <a href="https://pypi.org/project/plateau-triage/"><img src="https://img.shields.io/pypi/v/plateau-triage" alt="PyPI"></a>
  <a href="https://github.com/pixelx-jp/plateau_triage/actions/workflows/ci.yml"><img src="https://github.com/pixelx-jp/plateau_triage/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <img src="https://img.shields.io/badge/python-3.9%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue" alt="Python 3.9–3.13">
  <img src="https://img.shields.io/badge/code-Apache--2.0-green" alt="Code: Apache-2.0">
  <img src="https://img.shields.io/badge/data-CC%20BY%204.0-green" alt="Data: CC BY 4.0">
  <img src="https://img.shields.io/badge/honesty-covered%3Dfalse%20%E2%87%92%20value%3Dnull-9b9b9b" alt="Honesty invariant">
</p>

> After the quake, the most important thing to know is not which buildings are
> broken — it's which ones you haven't checked yet.

<p align="center">
  <img src="assets/demo.png" alt="plateau-triage Noto demo — red/yellow/green extruded buildings with grey unsurveyed slabs, a coverage meter, and a legend" width="820">
  <br>
  <sub>Noto demo: 🔴危険 / 🟡要注意 / 🟢調査済 extruded by severity, ⬜未調査 as the visible grey gaps, with the coverage meter. <code>make demo &amp;&amp; make viz-dev</code></sub>
</p>

Every building in a stricken city is one **honest record**, in one of four states:

| | state | meaning | record |
|---|---|---|---|
| 🔴 | **危険** | dangerous | `covered=true, value="危険"` |
| 🟡 | **要注意** | needs caution | `covered=true, value="要注意"` |
| 🟢 | **調査済** | inspected, safe | `covered=true, value="調査済"` |
| ⬜ | **未調査** | **not yet surveyed = unknown** | `covered=false, value=null` |

The first three mirror Japan's 応急危険度判定 (emergency risk-assessment) red/yellow/green
tags. The fourth, **未調査**, is the literal form of the stack-wide honesty invariant
`covered=false ⇒ value=null`: a building nobody has looked at is **never** defaulted
to "safe". That honesty contract — plus binding to a persistent building ID — is the
moat, **not** the damage-classification model.

---

## Quickstart

```bash
pip install plateau-triage          # from PyPI (pydantic, jsonschema, shapely, duckdb)
plateau-triage demo --out out/      # runs on the bundled Copernicus Noto sample
# point at your own data:  plateau-triage demo --input your_footprints.geojson --out out/
# from a clone, for dev:    pip install -e ".[dev,shacl]"   # + ruff, pyshacl/rdflib
```

```
plateau-triage — Noto demo
  ingested 12 footprints from the bundled Copernicus Noto sample
  coverage: 8 / 12 assessed; 33% unknown
  state breakdown:
    🔴 危険    danger          3
    🟡 要注意   caution         3
    🟢 調査済   inspected       2
    ⬜ 未調査   unsurveyed      4
  outputs:
    geojson      out/triage.geojson      # red/yellow/green/grey choropleth (plateau-r3f / any viewer)
    coverage     out/coverage.json       # the coverage meter
    duckdb       out/records.duckdb       # the record layer (DuckDB; also writes .parquet on request)
    support_csv  out/support_system.csv  # 被災者支援システム (victim-support system) screening feed
    risai_csv    out/risai_shomei.csv    # 罹災証明 (disaster certificate) pre-fill candidate — 認定調査 still required
```

This is the **Noto go/no-go demo**: zero fieldwork, zero PLATEAU/network dependency.
It uses public Copernicus-shaped damage footprints near Wajima (輪島), which is *not*
in PLATEAU — so records bind `dataset_footprint` IDs and render in 2.5D. Binding to
PLATEAU 建物ID + 3D is the production path, on covered cities.

> The bundled fixture is a 12-footprint synthetic sample mirroring the Copernicus
> EMS Noto 2024 schema. Point `--input` at the real FeatureCollection to run it for real.

### Visualisation (plateau-r3f)

The red/yellow/green/grey 2.5D choropleth + coverage meter + click-to-trace, with a
bilingual JP/EN UI.

```bash
make demo && make viz-data      # writes the feed into viz/public/demo.geojson
make viz-install                # cd viz && npm install
make viz-dev                    # http://localhost:5173   (or: make viz-build)
```

<p align="center">
  <img src="assets/demo-provenance.png" alt="Click-to-trace provenance panel showing a building's state, confidence, dataset, observed-at and ID-binding confidence" width="820">
  <br>
  <sub>Click-to-trace: state, confidence, dataset, observed-at, ID-binding confidence. An unsurveyed building is shown as "not assumed safe".</sub>
</p>

### Field capture (drone · street)

```bash
plateau-triage capture --out out/   # runs on the bundled drone capture sample
```

A weak or unseen building **abstains to 未調査**, never defaulted to safe (§6.2
graceful degradation); VSRD/SfM/SAM are pluggable backends.

### Feed existing systems

```bash
plateau-triage export --records out/records.duckdb --out out/feed.csv --format both
```

Emits both the 被災者支援システム feed and the 罹災証明 *pre-fill candidate* (never the
certificate itself — the 認定調査 always governs). See
[docs/integration.md](docs/integration.md).

---

## Pipeline

```
                         GeoJSON footprints + damage_grade        drone / street capture session
                                  │  accrue.ingest_batch                  │  accrue.ingest_capture
                                  │  (missing grade ⇒ 未調査)              │  (weak / unseen ⇒ 未調査)
                                  ▼                                       ▼
                                       honest Records  ── accrue.state_extract (honesty policy)
                                            │  bind   (resolve() → persistent id + geom_binding_conf)
                                            ▼
   ┌──────────────────────────── record layer (store: GeoParquet / DuckDB) ────────────────────────────┐
   │  query (RecordIndex: by id / bbox / triage queue / explicit unknown) ── caveat-mcp mounts this      │
   │  coverage ("8 / 12 assessed; 33% unknown")                                                          │
   │  viz (red/yellow/green/grey GeoJSON → plateau-r3f)                                                  │
   │  export (被災者支援システム · 罹災証明 pre-fill)                                                       │
   │  eval (self-proving: calibration + symmetric honesty penalty)                                       │
   └─────────────────────────────────────────────────────────────────────────────────────────────────┘
```

| module | role |
|---|---|
| `plateau_triage.record` | the one honest record contract (states + model + frozen-schema validator) |
| `plateau_triage.accrue.ingest_batch` | batch import of public damage datasets → records |
| `plateau_triage.accrue.ingest_capture` | drone/street capture session → records (unseen ⇒ 未調査) |
| `plateau_triage.accrue.state_extract` | per-building damage extraction + the honesty/abstention policy |
| `plateau_triage.bind` | pin records to a persistent building id (`Resolver` plug; `StubResolver` for the demo) |
| `plateau_triage.coverage` | the coverage meter + triage ordering |
| `plateau_triage.store` | record-layer persistence (flat GeoParquet / DuckDB) |
| `plateau_triage.query` | honest query surface (by id / bbox / triage queue / explicit unknown) — caveat-mcp mounts it |
| `plateau_triage.viz` | red/yellow/green/grey GeoJSON feed for plateau-r3f |
| `plateau_triage.export` | 被災者支援システム + 罹災証明 screening feeds |
| `plateau_triage.eval` | self-proving honesty eval (ECE + symmetric penalty) |
| `viz/` | the plateau-r3f front-end (Vite + React Three Fiber, bilingual) |

## Honesty, enforced everywhere

The invariant is guarded independently so drift is a test failure, not a silent
regression — see [docs/honesty-spec.md](docs/honesty-spec.md):

1. **Write side** — `Record` (pydantic) makes it structurally impossible to build
   a `covered=false` record with a value or a confidence.
2. **Wire side (JSON)** — `validate_record()` checks the serialised record against
   the **frozen** `contracts/record.schema.json` shared by all seven repos.
   **Wire side (RDF)** — the optional `[shacl]` extra validates the same invariant
   natively in **SHACL** over a SOSA/PROV-O projection ([docs/standards.md](docs/standards.md)).
3. **Extraction** — field perception abstains to 未調査 below its confidence threshold.
4. **Ingest** — an unseen / poorly-localised AOI building is explicitly 未調査; a
   later "safe" reading can never bury an earlier danger.
5. **Export** — refuses to emit a known/safe claim for an unsurveyed building (`FalseSafeError`).
6. **Query** — answers "unknown, not safe" for a building it has no record of.

## Contract

`record.schema.json` is the frozen, shared "universal honest record" plug (state,
rules and hazard are the same shape — only `attribute` differs). It is vendored at
`plateau_triage/contracts/record.schema.json`; binding to a persistent id is delegated
to `plateau-id-resolve` via the `Resolver` interface. The same record also projects
onto **SOSA + PROV-O** (`record.context.jsonld`) and is checkable in **SHACL**
(`record.shacl.ttl`) — standards alignment, not a new standard
([docs/standards.md](docs/standards.md)).

## Test

```bash
python -m pytest             # 118 tests (incl. SHACL when the extra is installed)
pip install -e ".[shacl]"    # optional: RDF/SHACL honesty check (rdflib + pyshacl)
cd viz && npm run build      # the plateau-r3f front-end builds clean
```

## Docs

- [docs/honesty-spec.md](docs/honesty-spec.md) — the invariant and where it's enforced
- [docs/standards.md](docs/standards.md) — SOSA / PROV-O / nilReason alignment + SHACL
- [docs/data-sources.md](docs/data-sources.md) — data sources, PLATEAU coverage, licensing
- [docs/integration.md](docs/integration.md) — feeding existing systems, agents and viewers

## License

Code **Apache-2.0** ([`LICENSE`](LICENSE)); data **CC BY 4.0**
([`LICENSE-DATA`](LICENSE-DATA), incl. PLATEAU / Copernicus attribution).

A *screening* layer that feeds existing systems — **not** a replacement for the legal
被害認定調査 (official damage assessment) a human signs.

---

<p align="center">
  <a href="https://yodolabs.jp"><img src="assets/yodo-logo.svg" alt="Yodo Labs" height="40"></a><br>
  <b>Yodo Labs</b> · PixelX Inc. / ピクセルエックス株式会社<br>
  <a href="https://yodolabs.jp">yodolabs.jp</a> · <a href="mailto:pan@yodolabs.jp">pan@yodolabs.jp</a>
</p>
