Metadata-Version: 2.4
Name: plateau-triage
Version: 0.1.4
Summary: Post-disaster building damage record layer: per-building damage state (danger / caution / inspected / unsurveyed) + confidence + provenance, bound to persistent PLATEAU 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>After an earthquake, which buildings do you send rescuers to first —<br>
  and which ones has nobody even checked yet?</b>
</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%E2%80%933.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">
</p>

## What it is

After a disaster a city has tens of thousands of buildings and far too few
inspectors. **plateau-triage turns disaster imagery into a building-by-building
map of damage** — and its one rule is that **a building nobody has checked is
never allowed to look "fine".**

**The trap it fixes:** most damage maps leave un-inspected buildings *blank* —
which looks exactly like "safe", so responders skip them. plateau-triage instead
gives **every** building one of four explicit labels:

> 🔴 **danger**  ·  🟡 **caution**  ·  🟢 **inspected — safe**  ·  ⬜ **not yet surveyed**

The grey **"not yet surveyed"** is the whole point: a building no one has looked
at is shown as **unknown — never assumed safe**. A **coverage meter** tells you
how much of the city is still grey (*"8,000 / 30,000 checked — 73% unknown"*),
so the question becomes *"what haven't we checked?"* instead of *"no red marks,
must be fine."*

<p align="center">
  <img src="assets/demo-annotated.png" alt="Annotated demo: red/yellow/green buildings someone checked, grey slabs for not-yet-surveyed (never assumed safe), and a coverage meter showing 33% unknown" width="880">
</p>

### Click any building to see *where its status came from*

Every label is traceable: which image, when, how confident, and the official
building ID it's bound to. A grey building says, in plain words, *"nobody has
looked here — not assumed safe."*

<p align="center">
  <img src="assets/demo-walkthrough.gif" alt="Clicking a grey building opens a provenance panel: not yet surveyed, nobody has looked, not assumed safe" width="760">
</p>

## Who uses it, and how

1. **A response HQ** opens the map and reads the coverage meter — *danger first,
   and "not yet surveyed" ranked above "confirmed safe"*, because an unknown
   building may be the most dangerous one on the block.
2. **An inspector** clicks a building to see where its status came from, decides
   whether to trust it or go look, and the record updates after the site visit.
3. **A relief desk** exports the records straight into existing systems — a
   screening feed for the victim-support system (被災者支援システム) and a
   pre-fill for the disaster-victim certificate (罹災証明). Unsurveyed buildings
   carry no claim, so the system can never issue a certificate for a building
   nobody checked.

Data comes either from **public satellite datasets** (bulk import) or from
**drone / street capture** in the field — and when the drone can't see a building
clearly, that building is honestly marked *not yet surveyed*, not guessed.

## Quickstart

```bash
pip install plateau-triage          # from PyPI
plateau-triage demo --out out/      # runs on a bundled earthquake sample
```

```
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:
    triage.geojson  ·  coverage.json  ·  records.duckdb
    support_system.csv  ·  risai_shomei.csv
```

(`危険 / 要注意 / 調査済 / 未調査` are the values actually stored in each record —
the Japanese emergency risk-assessment labels for danger / caution / inspected /
not-surveyed.)

**See the map** (the red/yellow/green/grey 3D view above, bilingual JP/EN):

```bash
make demo && make viz-data      # build the demo data
make viz-install && make viz-dev   # http://localhost:5173
```

**Field capture** (drone / street) and **exports** for existing systems:

```bash
plateau-triage capture --out out/                                   # drone capture → records
plateau-triage export --records out/records.duckdb --out feed.csv --format both
```

---

## How it works

```
   satellite / public datasets ──┐                  ┌── drone / street capture
        (bulk import)            │                  │   (weak / unseen ⇒ not surveyed)
                                 ▼                  ▼
                        honest per-building records   (missing data ⇒ not surveyed, never "safe")
                                 │ bind to a persistent building ID
                                 ▼
        ┌──────────── record layer (GeoParquet / DuckDB) ────────────┐
        • coverage meter — "how much have we NOT checked?"
        • map feed (red/yellow/green/grey) → the plateau-r3f viewer
        • query — look a building up; list what's still unchecked
        • export — victim-support-system feed + disaster-certificate pre-fill
        • eval — proves, on public data, that it says "unknown" when it should
```

| part | what it does |
|---|---|
| `plateau_triage.record` | the one record shape + the four states; the honesty rule lives here |
| `plateau_triage.accrue` | bring data in — `ingest_batch` (datasets) and `ingest_capture` (drone/street) |
| `plateau_triage.bind` | pin each record to a persistent building ID (`plateau-id-resolve`) |
| `plateau_triage.coverage` | the coverage meter + triage ordering (unknown above confirmed-safe) |
| `plateau_triage.store` | the record layer on disk (GeoParquet / DuckDB) |
| `plateau_triage.query` | look up a building / list what's still unchecked (mounted by `caveat-mcp`) |
| `plateau_triage.export` | feeds for the victim-support system + disaster-certificate pre-fill |
| `plateau_triage.eval` | self-proving honesty score (calibration + symmetric penalty) |
| `viz/` | the plateau-r3f web viewer (Vite + React Three Fiber, bilingual) |

### Honest by construction

The core rule — **a not-surveyed building never carries a value or gets called
safe** — is enforced independently at every layer (write, storage, field
extraction, export, query), so a regression is a *test failure*, not a silent
slip. The rule, and the seven places it's enforced, are written up in
[docs/honesty-spec.md](docs/honesty-spec.md). For the data model it is the
literal invariant `covered=false ⇒ value=null`.

### For developers

- The record is a frozen, shared contract (`contracts/record.schema.json`),
  also projectable onto **SOSA + PROV-O** and checkable in **SHACL** — standards
  alignment, not a new standard. See [docs/standards.md](docs/standards.md).
- `pip install -e ".[dev,shacl]"` then `python -m pytest` (118 tests) and
  `cd viz && npm run build`.
- Docs: [honesty-spec](docs/honesty-spec.md) · [standards](docs/standards.md) ·
  [data-sources](docs/data-sources.md) · [integration](docs/integration.md).
- Contributing: [CONTRIBUTING.md](CONTRIBUTING.md).

## License

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

This is 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>
