From 22b15d8aa9d465657a0fa489ccc287bb4ef48cfa Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 02:57:33 +0200 Subject: [PATCH 1/7] 16c design: a land-cover class per triangle, natural colours Co-Authored-By: Claude Opus 5.5 --- ROADMAP.md | 2 +- docs/increments/16c-landcover-labels.md | 651 ++++++++++++++++++++++++ 2 files changed, 652 insertions(+), 1 deletion(-) create mode 100644 docs/increments/16c-landcover-labels.md diff --git a/ROADMAP.md b/ROADMAP.md index 12348301..dab0b043 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -46,7 +46,7 @@ increment that most needs a picture to check against | — | Release hardening: measure libc++'s `_LIBCPP_HARDENING_MODE_FAST` (and libstdc++'s assertions on the GCC leg), which bounds-check `std::vector`, `span` and the like in Release, on the 1 m benchmark; switch it on if the cost is small. Today an out-of-range index in shipped code the tests miss is undefined behaviour and crashes Python (Ola, 2026-09-27: "I'm surprised we don't have proper memory control"; CI's ASan/UBSan/TSan cover what the tests reach) | to measure (Ola, 2026-09-27); 21d, which it was placed after, is deferred behind the basin work | none yet | | 15 | A DEM in several tiles, and the domain in its own CRS: 15a Norway, many tiles in one projected CRS (Ola's 254 DTM10 UTM33 tiles; only the selected tiles must share a lattice, since 8 of them sit half a cell off); 15b the domain polygon reprojected into the DEM's CRS; 15c and 15d the São Francisco basin (geographic DEMs meshed in the DEM's own lattice frame, not resampled; window decoding and memory) | **15a shipped with branch `increment15-dem-mosaic`'s PR**: `--dem DIR` or several files, `--bbox`, tiles selected and stitched on one lattice, Q2-Q5 refusals; overlaps that disagree (real DTM10 tiles exported on different dates, up to 52 m) are split down the middle and reported per seam (Ola's Q1 revised, 2026-09-28). The design's acceptance box, 9 tiles and 10,051² nodes, meshes: 11.05 M triangles in 31 s on battery. **15b built with branch `increment15b-domain-crs`'s PR**: `--domain` in its own CRS (e.g. EPSG:4326) over a UTM33 mosaic, reprojected vertex by vertex. Then 16b (Ola, 2026-09-27: Norway first) | `docs/increments/15-dem-mosaic.md` | | 16b | Interior polygons and polylines as constraints ("terrain polygons": lakes, land cover, roads, rivers): `--features PATH`, a GeoJSON `FeatureCollection`, each feature naming a vocabulary property; closed or open `Breakline`s, crossings noded, off-node vertices with bilinear z. **Its working example is real data** (Ola, 2026-09-27): CORINE Land Cover 2018 over the benchmark tile `7908_3_10m_z33.tif`. The source is Ola's local copy, `rasputin_data/corine_sql/.../U2018_CLC2018_V2020_20u1.gpkg` (8.2 GB, EPSG:3035, a sibling of this repository, which also holds the 254-tile DTM10 archive for gap 6). It reads without GDAL: sqlite3 over its R-tree, the GeoPackage blob header stripped, `shapely.wkb`, then `pyproj` to 25833, so CRS stops in Python as before. Probed 2026-09-27 in 0.2 s: 60 polygons in 8 classes (heath, bare rock, sparse vegetation, bogs, intertidal flats, water, sea, urban), 11 068 vertices clipped to the tile, median segment 54 m against 10 m cells. The EEA's public ArcGIS service (`image.discomap.eea.europa.eu`, `Corine/CLC2018_WM`) returns the same 11 068 clipped vertices and is the route for anyone without the file. What it forces on 16b's design: neighbouring polygons share their boundaries, so each shared edge arrives twice; the polygons run past the domain and must be clipped; the extract is committed as a fixture with the Copernicus attribution. Placed before 20c because 20c may split constraint segments and should be designed and measured on inputs that have interior ones | **designed** (`@architect`, 2026-09-28): two PRs, 16b-0 (the noder's verifier indexed: it is quadratic, 25 s of a 30 s CORINE run) and 16b-1/2 (GeoJSON and GeoPackage features, class maps, clip as linework, the noder merges shared edges); Q1-Q6 ruled by Ola 2026-09-28 (Q6: the legacy CORINE GML stays and is read). **16b-0 shipped (#107, merged 2026-09-28)**: the verifier's pair search by sort and sweep, always on; `node` on a 48 km CORINE square 24.8 s → 0.067 s. **16b-1/2 built on branch `increment16b12-features`** (2026-09-28): GeoPackage, GeoJSON and the legacy GML (strict reader, fixture repaired) as `--features`; CORINE class maps; the pre-clip keeps whole edges and widens its margin per edge for long ones; the noder merges shared edges; net +642 production lines. Before 20c | `docs/increments/16b-terrain-polygons.md`, `docs/increments/16-domain-polygon.md` (R6) | -| 16c | A land-cover label per triangle: which input polygon (CORINE class) each triangle lies in, by a flood fill over the unconstrained edges. The bits on an edge say what kind of line it is; which class lies on each side is 16c's | planned: ruled by Ola 2026-09-28 (16b's Q2) as a separate increment, next after 16b | `docs/increments/16b-terrain-polygons.md` (Q2, R6) | +| 16c | A land-cover label per triangle: which input polygon (CORINE class) each triangle lies in, by a flood fill over the unconstrained edges. The bits on an edge say what kind of line it is; which class lies on each side is 16c's | **designed** (`@architect`, 2026-09-29): components across unconstrained edges, one point-in-polygon test per component, cell array `land_cover_code`, a natural-colour ParaView preset from `rasputin palette corine`; defaults D1-D7 for Ola to confirm; ~230 production lines. Ruled by Ola 2026-09-28 (16b's Q2) as a separate increment | `docs/increments/16c-landcover-labels.md`, `docs/increments/16b-terrain-polygons.md` (Q2, R6) | | 16d | A record of the input polylines in the mesh. Today a constraint edge keeps only its type bits; the source feature, its attributes (a river's name or order, a road's class) and which edges belong to one polyline are lost, and where edges from two features merge only the union of their bits survives. Proposed: a feature table (one row per input feature: source ID and attributes) and, per constraint edge, the list of features it came from (a list, since a merged edge belongs to several). The noder already tracks each piece's input chain; the open part is the output: a per-edge list in `.vtk`/`.ply`, or a side file | proposed by Ola 2026-09-28; to be designed by `@architect` once Ola has said what it is for; after 16b | - | | 20c | Soft quality criterion: a penalty that each Steiner node or constraint split must pay for in angle gained, instead of 20's hard 25°; applied at the start and during DEM refinement; may split constraint segments when that improves the mesh. Ola's rulings on 20's C1-C3 | to design after 16b (`@architect` measures cost against 20 first) | `docs/increments/20-start-quality.md` (Ola's rulings) | | — | Auto-catchment: the watershed upstream of a coordinate, computed from the DEM and handed to `--domain`, so a catchment no longer has to be supplied as a file (Ola, 2026-09-27: "not far into the future"). The textbook route is depression handling (Priority-Flood, Barnes, Lehman and Mulla 2014), D8 flow directions (O'Callaghan and Mark 1984) and accumulation, the pour point snapped to the strongest flow nearby, the upstream cells traced and their outline turned into a polygon; the literature check is `@architect`'s. Open for its design: whether it runs in the C++ core (a 10 m tile is 25 M cells); how a stair-stepped cell outline becomes a domain polygon, which meets input coarsening; and that a real catchment crosses tile edges, so it needs gap 6 (a DEM in several tiles) first. Legacy has nothing on it (`grep -rliE "watershed|flow.?acc|flow.?dir|pour.?point|catchment" legacy` returns no files) | to design; after gap 6, which it needs; placed after 20c, can move ahead of it on Ola's word | none yet | diff --git a/docs/increments/16c-landcover-labels.md b/docs/increments/16c-landcover-labels.md new file mode 100644 index 00000000..81640440 --- /dev/null +++ b/docs/increments/16c-landcover-labels.md @@ -0,0 +1,651 @@ +# Increment 16c — a land-cover class per triangle, in natural colours + +Status: **designed** (`@architect`, 2026-09-29, night). Written before +`@tester`, per `docs/increments/README.md` step 1, on branch +`increment16c-landcover-labels` off master `b4847d7` (16b-1/2 merged, 22 not). +Ola was asleep while this was written; every question a design would +otherwise put to Ola is decided below as a **Default (2026-09-29), for Ola to +confirm** (collected under "Defaults for Ola to confirm"). + +**Closes.** `ROADMAP.md`'s 16c row, 16b's Q2 (a) as ruled on 2026-09-28 +("a label per triangle by flood fill across unconstrained edges ..., written +as a cell field `land_cover` with the CLC code"), and Ola's request of +2026-09-29: *"For the example, I would like the land cover polygons colored in +a natural way for the vtk-vizualisation."* The example is increment 22's +Bygdin catchment meshed with `--features +--features-map corine`. After this increment, + +```sh +rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 \ + --domain bygdin_reduced_t20.geojson --tolerance 10 \ + --features ../rasputin_data/corine2018_dtm10_utm33.gpkg \ + --features-layer corine2018 --features-map corine --out bygdin.vtk +rasputin palette corine --out corine_natural.json +``` + +writes a `.vtk` whose triangles carry their CORINE code in the cell array +`land_cover_code`, and a ParaView colour preset that paints those codes in +natural colours (forest green, rock grey, heath light green, bog brown, water +blue, glacier white-blue). + +**Not closed.** "Water on exactly one side" as an edge property (16b's +Q2 note): the labels make it computable, but nothing here writes it. +Hydro-flattening lakes. A raster land-cover source (MapBiomas). Labels from a +`--features-map` without class codes (`property`; see R2). A colour table for +any code system other than CORINE. + +## Ola's direction this design is built on + +1. **16b's Q2 (a)**, ruled 2026-09-28: a separate increment, next after 16b; + a label per triangle by flood fill across unconstrained edges, the method + of Shewchuk's Triangle (`-A`), a cell field with the CLC code, about 80 + lines of pure Python over the trimmed mesh and the features. +2. **Natural colours, not the official CLC legend** (2026-09-29, via the + main session's brief): the official legend colours peat bogs blue + (`077-077-255`) and the sea almost white (`230-242-255`) + (`Legend/CLC_legend.csv` in Ola's download, read for this design). +3. **The I/O boundary** (`CLAUDE.md` §2): file decoding and CRS stay in + Python; nothing here goes near `_core`. +4. **Increment 13, ruling 5**: every cell array covers every cell; per-triangle + data gives lines a fill value. +5. **Lean** (2026-09-29): red, green and review in one night; no throwaway + implementations. + +## Prior art: legacy and literature + +### Literature + +- **Regional attributes on a constrained triangulation.** Shewchuk, + "Triangle: Engineering a 2D quality mesh generator and Delaunay + triangulator", *Applied Computational Geometry*, LNCS 1148, 203-222, 1996. + Triangle's `-A` takes a list of (seed point, attribute), finds the triangle + containing each seed, and spreads the attribute across every edge that is + not a segment; a triangle no seed reaches gets 0. Recalled (16b cited it the + same way), not reread tonight. + **What differs, and why.** R1 spreads first and looks up second: it finds + the connected components of triangles across non-constraint edges with no + seeds at all, then asks which input polygon each *component* lies in, using + one point per component. Triangle's order needs one seed per polygon piece, + chosen from the input, and that fails in three cases this project meets: + a polygon cut into two pieces by the domain's boundary (the quarter circle + does that), a polygon cut in two by a polyline running across it (a road), + and a seed that lands in the wrong triangle because the polygon is thinner + there than the noder's snap. Components-first has no seeds to place, so it + has none of the three. The spread itself (constant across non-constraint + edges, blocked by constraints, 0 where nothing applies) is Triangle's. +- **Connected components, vectorised.** Union-find with path compression: + Tarjan, "Efficiency of a good but not linear set union algorithm", *J. ACM* + 22(2):215-225, 1975. The array form R1 uses, hooking the larger root onto + the smaller and then pointer-jumping until every label is a root, is the + hook-and-shortcut scheme of Shiloach and Vishkin, "An O(log n) parallel + connectivity algorithm", *J. Algorithms* 3(1):57-67, 1982. Both recalled. + **What differs:** Shiloach-Vishkin hooks conditionally and bounds its + rounds by O(log n); R1 hooks every root to the smallest root it is joined + to and loops to a fixed point, which is correct (labels only decrease, and + hooking larger onto smaller cannot make a cycle) but claims no round bound. + The acceptance records the rounds. +- **Point in polygon** is GEOS's (shapely 2 `STRtree.query(..., + predicate="intersects")` over prepared polygons). Nothing is claimed about it + beyond what shapely documents. +- **Where the labels came from before.** The legacy labelled cell centres, + one Python point test per cell (see Legacy). The per-centre test is kept, + as the **test oracle**, not as production (R1). +- **The CORINE nomenclature.** Kosztra, Büttner, Hazeu and Arnold, *Updated + CLC illustrated nomenclature guidelines*, European Environment Agency, 2019: + the 44 level-3 classes and their codes. Recalled. The class names used in + R4 are the `LABEL3` column of `Legend/CLC_legend.csv` in Ola's download + (`../rasputin_data/corine_sql/u2018_clc2018_v2020_20u1_geoPackage/Legend/`), + read for this design. +- **Natural colour for land cover.** Patterson and Kelso, "Hal Shelton + revisited: designing and producing natural-color maps with satellite land + cover data", *Cartographic Perspectives* 47:28-55, 2004: land cover drawn in + the colours the ground has when seen from above (dark greens for conifers, + tans and greys for bare ground, blues for water, white for ice), instead of + a categorical legend. Recalled. R4's table follows that convention by hand; + it is taste, and is not claimed to reproduce their palette. +- **The ParaView preset file.** ParaView's colour-map presets are a JSON list + of objects; a categorical preset carries `"Name"`, `"Annotations"` (value, + label, value, label, ...) and `"IndexedColors"` (r, g, b in 0-1, one triple + per annotated value, in order), plus an optional `"NanColor"`. Recalled + from ParaView's own presets file, **not checked against a running ParaView + tonight** (none is installed). It is the acceptance's manual item, and the + fallback if it fails is in R4. + +**Novelty: none claimed.** Labelling a constrained triangulation's faces by +region is standard (Triangle, 1996). No search was needed because nothing new +is claimed; if a later increment claims a guarantee for region labels on +snapped input, it searches first (16b listed the queries). + +### Legacy + +```sh +$ grep -rlE 'cover_type|cover_color|def color\(' legacy/ | grep -v pycache | sort +legacy/rasputin/application.py +legacy/rasputin/globcov_repository.py +legacy/rasputin/gml_repository.py +legacy/rasputin/land_cover_repository.py +legacy/rasputin/tin_repository.py +legacy/rasputin/wfs_repository.py +legacy/tests/test_gml_repository.py +``` + +What matters, read directly: + +- `legacy/rasputin/gml_repository.py:184-226`, `land_cover`: every cell + centre tested against every polygon, one `shapely.Point` at a time (`# + TODO: Move to C++ for speed!`), raising on a centre in no polygon. + **Carried as the test oracle only** (R1, "the oracle"), with two changes: + the test is vectorised, and a centre in no polygon is 0, not an error, + because a general input need not cover the domain. +- `legacy/rasputin/gml_repository.py:69-117`, `LandCoverMetaInfo.color`: the + official CLC RGB triples. **Not carried**: Ola asked for natural colours + (direction 2). +- `legacy/rasputin/application.py:126-148`: writes the face fields + `cover_type` (the code) and `cover_color` (RGB). **Carried:** a per-face + code. **Not carried:** a per-face colour array (R4 says why) and the names + (`cover_type` collides with nothing, but R3's `land_cover_code` says what it + holds and sits next to 16b's `land_cover` bit). + +`@migration-expert` is not needed: nothing numeric is ported. + +## The blueprint: data flow and boundaries + +``` +cli.mesh (composition root) + │ + ├─ feature_input.open_features(...) 16b, extended [R5] + │ a feature from a map with class codes also keeps + │ code: int (its attribute value, e.g. Code_18 "322" -> 322) + │ polygon: Polygon | MultiPolygon | None, in the DEM's CRS + │ a coded polygon with no boundary inside the domain but covering it + │ is kept, with no lines + │ + ├─ _dem_mesh(...) -> Trimmed unchanged + │ vertices (N, 3), triangles (T, 3), edges (E, 2), edge_masks + │ + ├─ landcover.label_triangles( NEW, pure [R1, R2] + │ vertices, triangles, edges, + │ polygons=[(f.polygon, f.code) ...], margin=2 * snap spacing) + │ -> CoverLabels(codes (T,) int32, regions, outside, overlapped, thin) + │ regions(triangles, edges) -> (T,) component ids numpy only + │ one point per component -> STRtree query -> smallest-area rule + │ timed as the phase "land cover"; one stderr line + │ + ├─ io.vtk_legacy.write_vtk(..., triangle_codes=codes) [R3] + │ cell array land_cover_code: 0 on every LINES cell, the code on each + │ triangle; FieldData string land_cover_codes names the code system + ├─ io.ply.write_ply(..., face_codes=codes) [R3] + │ + └─ rasputin palette corine [--out FILE] NEW command [R4] + palettes.paraview_preset(CORINE_NATURAL) -> JSON +``` + +Boundaries: + +- `landcover.py` imports numpy and shapely, nothing first-party, never + `_core`, never a path. It takes arrays and `(geometry, code)` pairs and + returns arrays and counts. It is unit-testable on a hand-made mesh of three + triangles. +- `palettes.py` is data plus one function; it imports nothing first-party and + knows nothing about meshes. The CLI command and the acceptance's render + script both read it, so there is one colour table. +- The writers stay pure (`bytes` out) and gain one optional array each. +- No C++ changes, no bindings, no new dependency (numpy, shapely and, for + the picture only, the existing `viewer` extra's `vtk`). + +## Rulings + +### R1. The label: components first, one point per component + +**Production.** For a trimmed mesh with triangles `T`, constraint edges `E` +and coded polygons `P`: + +1. **Components** (`regions`). Each triangle's three edges as undirected keys + `min(a, b) * N + max(a, b)` (int64; N is the vertex count). A key in `E` + is blocked (`np.isin` on the same keys). The unblocked keys are sorted; + two equal neighbours in the sorted order are one interior edge and join + its two triangles. (A key appears at most twice: the trimmed mesh is a + manifold.) Components by the array union-find of the literature section: + `parent = arange(T)`; repeat { `ru, rv = parent[u], parent[v]`; + stop if `ru == rv` everywhere; `np.minimum.at(parent, max(ru, rv), + min(ru, rv))`; pointer-jump `parent = parent[parent]` until it is fixed }. + The component id of a triangle is its component's smallest triangle + index, so ids do not depend on the order edges are visited. +2. **One point per component.** Each triangle's incentre and inradius `r` + (incentre `(a·A + b·B + c·C)/(a + b + c)` with `a = |BC|` and so on, `r = + 2·area/(a + b + c)`, in x and y). Per component, the triangle with the + largest `r`, ties to the lowest triangle index; its incentre is the + component's point. +3. **Which polygon.** `STRtree([p for p, _ in P]).query(points, + predicate="intersects")`. A point in no polygon gives its component 0. A + point in several gives the code of the one with the **smallest area**, ties + to the smaller code (Default D2). +4. **Spread.** Every triangle gets its component's code. + +**Why one point is enough, and when it is not.** A component is bounded by +constraint edges, and the input polygon boundaries lie within `δ` of the +constraint edges made from them. `05-noder.md` (fix 8, the one-way Hausdorff +form) bounds the other direction: every output edge derived from an input +segment lies within `h/√2` of it per noding round, `k·h/√2` after `k` rounds, +for snap spacing `h`. The output chain runs, inside that tube, from near one +end of the input segment to near the other, so it crosses the perpendicular +through any point of the segment within the same distance: `δ <= k·h/√2` +(0.7 mm per round at the default `h = 1 mm`). `margin = 2h` covers two +rounds. Refinement's points on constraint segments (20b's feet) are rounded +to doubles, which adds nanometres. This bound says when the lookup is exact; +the tests rest on the oracle, not on it. +The incircle of a triangle lies inside the triangle, and no constraint edge +enters a triangle, so the incentre is at least `r` from every constraint edge +and at least `r - δ` from every input boundary. With `margin = 2h > δ`, a +component whose largest `r` exceeds `margin` has a point that is strictly on +one side of every input boundary, so the lookup is exact up to GEOS's own +arithmetic. A component whose largest `r` is at most `margin` is counted as +`thin`: it is still labelled by its point, but the stderr line says how many +there were. (If the noder took more than two rounds, `δ` can exceed +`margin`; the count is then optimistic, and the oracle is the check.) On CORINE such a component is a sliver the snap made between two +boundaries that nearly coincide. + +**Is the polygon the same object the constraints came from?** Yes, by R5: the +polygon tested is the feature's geometry moved into the DEM's CRS vertex by +vertex with the same transform that moved its chains, so its edges are the +straight lines the engine draws. The domain clip is linework on the chains +and is not repeated on the polygon; the triangles only exist inside the +domain anyway. + +**The oracle (tests only).** Each triangle's **centroid** is tested against +`P` with the same predicate and the same overlap rule, for every triangle with +`r > 1.5·margin`. The centroid is at least `2r/3` from the triangle's sides +(its distance to side `a` is `2·area/(3a)`, and `a <= perimeter/2`), so for +those triangles it is further than `margin > δ` from any input boundary and +the oracle's answer is exact. Production and oracle must agree on every such +triangle. This is the legacy's per-centre test; it borrows the producer's +*predicate* (a point against the input polygons) and none of its records (no +components, no chosen points), per the `computational-geometry` skill's rule. +It is O(T) point queries, which is why it is the oracle and not production. + +**Degeneracy and edge cases.** + +| case | outcome | +|---|---| +| overlapping polygons (not CORINE; any GeoJSON) | smallest area wins, ties to the smaller code; the component is counted in `overlapped` (D2) | +| two polygons sharing a boundary that the noder merged (every CORINE neighbour pair) | one constraint edge between them; its two sides are different components, each labelled by its own point | +| boundaries closer than the snap, merged or not | at most a sliver component, labelled by its point, counted `thin` | +| a polygon with holes | shapely's test excludes the hole; the hole's components get whatever polygon fills it, else 0 | +| a lake inside a forest, the forest with a hole (CORINE) | lake components 512, forest components 31x | +| a lake inside a forest, the forest without a hole (hand-made input) | lake components the lake's code, by the smallest-area rule | +| a polygon clipped by the domain into several pieces | each piece is its own component and is labelled | +| a polyline across a polygon (a road through a forest) | two components, both the forest's code | +| a polygon covering the whole domain, no boundary inside | kept with no lines (R5); the single component gets its code | +| a triangle outside the domain | none exist: the engine's mesh is the domain's | +| a triangle dropped by `trim` (no DEM data) | not in the file, not labelled; components are computed after trimming | +| a component in no polygon | 0, counted in `outside` | +| no coded polygons at all (`property` map, or no `--features`) | no labelling, no array, no field (R2) | +| a constraint edge with mask 0 (the domain boundary, an unclassified edge) | blocks the spread like any constraint (it is in `E`) | + +**Determinism.** The codes are a function of the triangle array, the +constraint edges and the polygons as a set: component ids are +smallest-index, the chosen point is largest-`r` then smallest-index, and the +overlap rule is order-free. 16b's I3 (rows in reverse order give a +bit-identical file) therefore extends to the new array with no new code; its +existing test covers it. + +### R2. Where it runs: Python, over the trimmed mesh's arrays + +In `src_python/tin_engine/landcover.py`, on numpy arrays, after `trim` and +before the writers. Not in the C++ core, because: + +- the class codes and polygons are attribute data read in Python, in a CRS + handled in Python; the core would need neither the polygons nor the codes, + only the component pass, and that is about 25 lines of numpy; +- a C++ pass would need bindings, a rebuild, a C++ suite and, touching + `include/terrain/mesh/`, `@perf`'s bench acceptance, which is not what a + night is for. + +**Cost at scale.** One sort of `3T` int64 keys, one `np.isin`, the union-find +rounds (each O(T)), and one point query per component, not per triangle. At +1 m on Bygdin (1.13 M triangles without features) that is a few million keys; +**not measured** (no prototype, per the retrospective agenda of 2026-09-29). +The acceptance records the phase time and the round count at 10 m and at 1 m. +If the phase costs more than the refine it follows at 1 m, moving `regions` +into the core is the next step, and the Python version stays as its oracle. + +**Which maps label.** Only a class map that declares a code system does +(`ClassMap.codes`, R5): `corine` and `clc18_kode` (and `corine-water`, whose +kept features are the water polygons only, so land is 0). The `property` map's +values are vocabulary names, not codes; it writes no labels (Default D3). + +### R3. What the files carry + +**`.vtk`** (`io/vtk_legacy.py`, `write_vtk(..., triangle_codes=None)`): + +- A cell array **`land_cover_code`**, `int`, one value per cell in the file's + cell order: **0 on every `LINES` cell**, then each triangle's code (13's + rulings 4 and 5: lines first, every array covers every cell, lines get the + fill value). It is written in the `FIELD` block beside the per-feature + arrays, not as the `SCALARS` block, which stays `feature_mask` (13, ruling + 5 is not reopened). The `FIELD` count includes it; with no feature arrays + the block is written for it alone. +- **Not named `land_cover`**: 16b's vocabulary bit 7 is `land_cover`, and + ruling 6 already writes a 0/1 cell array under that name. The writer refuses + `triangle_codes` with a vocabulary that names a property `land_cover_code`. +- A dataset string **`land_cover_codes`** in `FieldData`: the code system, + the attribute, the map, and what 0 means, e.g. + `CORINE Land Cover level-3 code, attribute Code_18, map corine; 0 = in no + polygon, and every constraint line`. `land_cover_codes` joins `RESERVED`. +- `triangle_codes` must have one entry per triangle and fit `int32`; else + `ValueError`. + +**`.ply`** (`io/ply.py`, `write_ply(..., face_codes=None)`): on the face file +only, a face property `int land_cover_code` after `vertex_indices`, and the +same text as a header comment `land_cover_codes ...`. Refused with `edges` +(the edge file has no faces). MDAL is expected to offer it as a face dataset +in QGIS; recalled, not measured, and not an acceptance item. + +**stderr**, one line after the `trim` line: +`land cover: regions, outside every polygon, in more than one, + thinner than the snap`. The phase `land cover` appears in `--stats`'s +phase table through the existing clock; no other report row (16b's ruling +that a table in a run report is output, not data). + +### R4. Natural colours: a ParaView preset, generated from one table + +**Where the table lives.** `src_python/tin_engine/palettes.py`: +`CORINE_NATURAL: dict[int, tuple[str, str]]`, code → (CLC `LABEL3`, `#rrggbb`), +all 44 CLC level-3 classes plus 0, and `paraview_preset(table, name) -> +list[dict[str, object]]`. It ships in the package, is type-checked, and the +tests pin it. Not a JSON data file in the package: `wheel.packages` would +carry it, but a Python table is one object that the CLI and the render +script both import, with no file-location lookup. + +**How a user gets it.** A new command, `rasputin palette corine [--out +FILE]`: the ParaView preset as JSON, to `FILE` or stdout. An unknown name is a +usage error listing the known ones. `mesh` does not write it beside the mesh +(Default D4): one more file per run, and a second name to keep from +colliding with `--out-edges` and `--stats`, for a table that never changes +between runs. + +**How it is applied in ParaView** (the manual acceptance item): + +1. Open `bygdin.vtk`; *Color By* `land_cover_code` (cell data). +2. Colour Map Editor → *Choose Preset* → *Import*, pick + `corine_natural.json`, select "rasputin CORINE natural", *Apply*. Imported + presets persist in ParaView's settings, so this is once per machine. +3. If the categorical mode is not switched on by the preset, tick + *Interpret Values As Categories*. +4. Optionally *Save current colour map as default for arrays named + `land_cover_code`* so every later file opens coloured. + +Constraint lines carry 0 and draw in the colour for 0, a dark grey, which +reads as a class boundary. A code the table lacks draws in `NanColor`, +magenta, so it is seen rather than blended in. + +**Rejected: a per-cell RGB array in the file** (the legacy's `cover_color`). +It needs no import, but it bakes one taste into every mesh file, adds three +bytes per cell, and a second viewer would want its own. It stays the fallback +if the preset fails the manual check. + +**The table.** Natural colours, chosen by hand after Patterson and Kelso; the +official CLC colour is not used for any class. Hex is the value `@tester` pins +by family (Tests), not by exact triple. + +| code | class (`LABEL3`) | colour | in the Norway extract | +|---|---|---|---| +| 0 | no polygon; constraint lines | `#3c3c3c` | (lines) | +| 111 | Continuous urban fabric | `#8c5f5a` | yes | +| 112 | Discontinuous urban fabric | `#a88d86` | yes | +| 121 | Industrial or commercial units | `#8e8a96` | yes | +| 122 | Road and rail networks and associated land | `#6e6e6e` | yes | +| 123 | Port areas | `#7d8796` | yes | +| 124 | Airports | `#a8a8a8` | yes | +| 131 | Mineral extraction sites | `#b49b78` | yes | +| 132 | Dump sites | `#8a7d64` | yes | +| 133 | Construction sites | `#bcae98` | yes | +| 141 | Green urban areas | `#86b86e` | yes | +| 142 | Sport and leisure facilities | `#a3cf7e` | yes | +| 211 | Non-irrigated arable land | `#e6d58c` | yes | +| 212 | Permanently irrigated land | `#d9cc6e` | | +| 213 | Rice fields | `#cdd89a` | | +| 221 | Vineyards | `#9c7a44` | | +| 222 | Fruit trees and berry plantations | `#a9ad5e` | yes | +| 223 | Olive groves | `#8f9a52` | | +| 231 | Pastures | `#b4d47a` | yes | +| 241 | Annual crops associated with permanent crops | `#dccf94` | | +| 242 | Complex cultivation patterns | `#d2c47c` | yes | +| 243 | Land principally occupied by agriculture, with significant areas of natural vegetation | `#bcc47e` | yes | +| 244 | Agro-forestry areas | `#a9b574` | | +| 311 | Broad-leaved forest | `#4f8f3f` | yes | +| 312 | Coniferous forest | `#1f5a2e` | yes | +| 313 | Mixed forest | `#357438` | yes | +| 321 | Natural grasslands | `#c2d68a` | yes | +| 322 | Moors and heathland | `#a7c47f` | yes | +| 323 | Sclerophyllous vegetation | `#8a9658` | | +| 324 | Transitional woodland-shrub | `#7ea65a` | yes | +| 331 | Beaches, dunes, sands | `#e9ddb2` | yes | +| 332 | Bare rocks | `#8f8f8f` | yes | +| 333 | Sparsely vegetated areas | `#b9b8a0` | yes | +| 334 | Burnt areas | `#4b3f3a` | yes | +| 335 | Glaciers and perpetual snow | `#eef6fb` | yes | +| 411 | Inland marshes | `#6e9470` | yes | +| 412 | Peat bogs | `#8a6642` | yes | +| 421 | Salt marshes | `#7f9f8c` | | +| 422 | Salines | `#d8d6cc` | | +| 423 | Intertidal flats | `#b3bdb3` | yes | +| 511 | Water courses | `#4c8ec4` | yes | +| 512 | Water bodies | `#3e7bb6` | yes | +| 521 | Coastal lagoons | `#5b93b3` | | +| 522 | Estuaries | `#5188b4` | yes | +| 523 | Sea and ocean | `#2b5d8e` | yes | + +The "in the Norway extract" column is from +`sqlite3 -readonly ../rasputin_data/corine2018_dtm10_utm33.gpkg "select +code_18, count(*) from corine2018 group by code_18"`, run for this design: 34 +codes, all present in the table. The unclassified codes 990, 995 and 999 do +not occur there and are left out; they draw in `NanColor`. + +### R5. What `feature_input` keeps for labelling + +- `ClassMap` gains `codes: str = ""`, the name of the code system. Non-empty + means the map's attribute values are integer class codes and labels are + written. `corine`, `corine-water` and `clc18_kode` set it to `CORINE Land + Cover level-3 code`; `property` leaves it empty. +- `TerrainFeature` gains `code: int | None = None` and `polygon: Polygon | + MultiPolygon | None = None`, both set only for a coded map: `code` is + `int(str(value))` of the feature's attribute, refused (`FeatureError`, + naming the feature and value) unless it is an integer in 1 .. 2³¹-1 — a + list value is refused too; `polygon` is the feature's polygonal parts, + moved into the DEM's CRS with the **same** transform that moved its chains + (on the moved-first path it is `moved` itself; on 16b's geographic + pre-clip path the whole polygon is moved once more). Lines have no polygon. +- **A coded polygon with no boundary in the domain** is today counted + `outside` and dropped. For a coded map it is kept with `lines=()` if it + covers the domain, tested by one point, `domain.polygon.point_on_surface()`: + no boundary crosses the domain's interior, so the interior is wholly inside + the polygon or wholly outside it. Without this, a catchment lying entirely + inside one large CORINE polygon would be labelled 0 everywhere. It counts as + kept, not clipped. +- Uncoded maps behave exactly as in 16b: no polygon is moved or kept, so no + cost is added to them. + +### R6. The CLI + +- `mesh`: labelling runs whenever `--features` is given with a coded map and + the output is `.vtk` or `.ply`. No new flag (Default D1). +- `palette`: R4. +- The `mesh` help text gains one sentence naming `land_cover_code` and + pointing at `rasputin palette`. + +## Invariants + +- **I1 (spread).** For every interior edge that is not a constraint edge, its + two triangles have the same code. Checked on the arrays alone, no geometry. +- **I2 (oracle).** For every triangle with `r > 1.5·margin`, the code equals + the centroid oracle's (R1). +- **I3 (file shape).** `land_cover_code` has one value per cell, 0 on every + `LINES` cell, and the triangle part equals `CoverLabels.codes`. +- **I4 (determinism).** 16b's reversed-rows run gives a bit-identical file. +- **I5 (area).** On a fixture whose polygons are known, the triangle area + (x, y) per code equals the area of `polygon ∩ domain` per code within + `margin × (boundary length inside the domain)` plus rounding. + +## Defaults for Ola to confirm + +Each marked **Default (2026-09-29), for Ola to confirm**. + +- **D1.** Labels are always written for a coded map; no `--no-labels` flag. +- **D2.** Overlapping polygons: the smallest area wins, ties to the smaller + code. The alternative, the last feature in source order wins (painter's + rule), makes the result depend on row order, which 16b's I3 forbids. +- **D3.** The `property` map writes no labels. Giving its names integer ids + would need an id table in the file; nobody has asked for it. +- **D4.** `rasputin palette corine` writes the preset on request; `mesh` + does not write it beside the mesh. +- **D5.** Names: the cell array `land_cover_code`, the field + `land_cover_codes`, the command `palette`, the preset "rasputin CORINE + natural". 16b's Q2 said "a cell field `land_cover`"; that name is taken by + bit 7's 0/1 array. +- **D6.** The colours in R4's table. Taste, and Ola's to change; the tests + pin only the families (green forest, grey rock, light-green heath, brown + bog, blue water, white-blue glacier). +- **D7.** No suite is named invariant-critical, so no mutation round: every + labelling fixture is checked against the independent oracle (I2) and I1, + which is what a mutation round would test (Ola's lean-brief rule). + +## Tests for `@tester` (the red suite) + +Lean: no throwaway implementation, no mutation round (D7). Two new files and +additions to three. + +**`tests/python/test_landcover.py`** — pure, no `_core`, hand-made meshes: + +- `regions`: two triangles sharing an unconstrained edge are one component; + the same with the edge in `E` are two; a strip of 1 000 triangles with no + constraints is one component (the loop reaches a fixed point); ids are the + smallest triangle index; permuting `E`'s rows changes nothing. +- `label_triangles`: a square cut by a constrained diagonal with a polygon + on each side gives each side its code; no polygons gives all 0 and + `outside` = regions; two nested polygons give the inner one's code + (smallest area) and count `overlapped`; a sliver component (largest `r` <= + margin) is counted `thin`; `codes` is int32 of length T. +- The oracle as a test helper (`landcover_oracle(vertices, triangles, + polygons, margin)`, per R1), used by the CLI tests below. + +**`tests/python/test_cli_mesh_landcover.py`** — through `rasputin mesh` on the +synthetic `bumpy` DEM of `test_cli_mesh_features.py`, GeoJSON features with a +`Code_18` attribute and `--features-map corine`. For each fixture: I1, I2, +I3, and the expected codes named below. + +1. **Two squares side by side** (311 and 512) sharing an edge: the triangles + on either side of the shared edge carry 311 and 512; nothing is 0 inside + the squares, the rest of the domain is 0. +2. **A hole**: a forest (312) with a square hole and nothing in it: the hole + is 0. +3. **A lake inside a forest**: (a) the forest holed and the lake filling the + hole: 512 and 312; (b) the forest not holed: still 512 in the lake + (D2), and the stderr line says `in more than one` > 0. +4. **A polygon clipped by the domain into two pieces** (a band crossing a + concave notch of the domain): both pieces carry its code. +5. **A road across a polygon** (a `LineString` feature with a code, under + `corine`): both sides carry the polygon's code. +6. **A polygon covering the domain** with no boundary inside: every triangle + carries its code; the feature is counted kept. +7. **Boundaries 0.1 mm apart** (two squares whose shared side is offset + by less than the snap): both squares labelled correctly away from the + seam (I2), and the run succeeds. +8. **Refusals**: a `Code_18` of `"forest"` under `corine` is a usage error + naming the feature and value; `--features-map property` writes no + `land_cover_code` and no `land_cover_codes`. +9. **`.ply`**: the face file has `property int land_cover_code` and the + comment; the edge file has neither. +10. **VTK readback** (`importorskip("vtk")`, the `viewer` extra's CI step): + `vtkPolyDataReader` sees `land_cover_code` with `GetNumberOfCells()` + values, 0 on the line cells. + +**Additions to `test_cli_mesh_features.py::TestCommittedExtract`** (the +quarter circle on the committed tile and extract, at 10 m): I1, I2, I3 on the +real mesh; I5 per code against shapely's `intersection` of each extract +polygon (moved to EPSG:25833) with the quarter circle, within 0.01 % of the +domain's area; the reversed-rows test (I4) passes unchanged. + +**Additions to `test_io_vtk_legacy.py` and `test_io_ply.py`**: the array's +place and fill in ASCII and binary, the length refusal, the refusal of a +vocabulary naming `land_cover_code`, and the reserved field name. + +**`tests/python/test_palettes.py`**: every one of the 34 Norway codes (R4's +list) and 0 is in `CORINE_NATURAL`; the 44 CLC codes are all there; families: +311-313 have g > r and g > b; 322 has g > r and g > b and is lighter than +312; 332 is grey (r = g = b); 412 has r > g > b (brown); every 5xx has b > r +and b > g; 335 has every channel >= 0xe0 and b >= r; `paraview_preset` +gives one object with `Name`, `Annotations` of length 2 × (entries), +`IndexedColors` of length 3 × (entries) in [0, 1], in the same order; +`rasputin palette corine` writes that JSON to stdout and to `--out`; an +unknown name is a usage error. + +## Acceptance (`@perf`, AC power recorded) + +On this branch, with the Bygdin reduced catchment taken from 22's branch +(no merge needed; 16c does not depend on 22's code): + +```sh +git show increment22-autocatchment:docs/benchmarks/2026-09-29/bygdin/bygdin_reduced_t20.geojson \ + > $SCRATCH/bygdin_reduced_t20.geojson +rasputin mesh --dem ../rasputin_data/DTM10_UTM33_20260925 \ + --domain $SCRATCH/bygdin_reduced_t20.geojson --tolerance 10 \ + --features ../rasputin_data/corine2018_dtm10_utm33.gpkg \ + --features-layer corine2018 --features-map corine \ + --out $SCRATCH/bygdin_lc.vtk --stats bygdin_lc.stats.md +rasputin palette corine --out corine_natural.json +``` + +Evidence under `docs/benchmarks/2026-09-29/16c-bygdin/`: + +1. **It opens in VTK with the array**: `vtkPolyDataReader` reads the file; + `land_cover_code` is present with one value per cell and 0 on every line. +2. **Class shares against CORINE clipped to the catchment.** Triangle area + (x, y) per code, as shares of the mesh's area, against 22's table + (`bygdin/README.md` on 22's branch: 333 42.34 %, 332 21.32 %, 322 + 17.00 %, 512 16.46 %, 335 2.44 %, 412 0.33 %, 142 0.10 %). **Pass:** the + same seven codes, no 0 triangles, and every share within 0.01 percentage + points. The expected gap is `δ` times the boundary length, well under a + hectare; anything larger is a leak between regions. +3. **I1 and I2** on the Bygdin mesh (the oracle over all ~83 000 triangles). +4. **Time**: the `land cover` phase and the union-find rounds at 10 m, and + one run at `--tolerance 1` with the same features for the scale. Recorded, + not gated (R2). +5. **A picture**, `bygdin_landcover.png`: `render.py` in the evidence + directory, using the `viewer` extra's `vtk` offscreen (`vtkRenderWindow` + with `SetOffScreenRendering(1)`, `vtkWindowToImageFilter`, `vtkPNGWriter`; + this design checked that the chain writes a PNG with vtk 9.7.0 in the + repository's venv on this Mac), a map view from above with a + `vtkLookupTable` in indexed mode built from `palettes.CORINE_NATURAL`, and + a legend of the codes present. No new dependency. +6. **Manual, for Ola**: import the preset in ParaView and record the version + and whether it switched to categorical by itself (R4, steps 2-3). + +`@perf`'s bench and thread sweep are not required: nothing under +`include/terrain/refinement/` or `include/terrain/mesh/` or what drives them +changes; labelling runs after the mesh is finished. + +## LOC + +Production lines as `CLAUDE.md` §2 counts them (tests excluded): + +| file | lines | +|---|---| +| `landcover.py` (`regions`, incentres, `label_triangles`, `CoverLabels`) | 75 | +| `palettes.py` (45-entry table, `paraview_preset`) | 60 | +| `feature_input.py` (`codes`, `code`, `polygon`, coded refusals, covering polygon) | 30 | +| `io/vtk_legacy.py` (array, fill, field, refusals) | 15 | +| `io/ply.py` (face property, comment, refusal) | 12 | +| `cli.py` (label call and phase, stderr line, fields and comments, `palette` command, help) | 40 | +| **total** | **~230** | + +Worst case about 320, if the writers need more reshaping than estimated. Well +under the 700-line ceiling; one PR. + +## Not in scope + +- An edge property "water on exactly one side" (computable from the labels). +- Labels for the `property` map, or for any map without integer codes (D3). +- Colour tables other than CORINE's; a colour array in the mesh file (R4). +- Moving `regions` into the C++ core (R2 says when it would be). +- Changing which array is the `.vtk` file's active `SCALARS` (13, ruling 5). From 196147ebc76e0ed1d087bc6d11c1012853d6fcb1 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 03:13:26 +0200 Subject: [PATCH 2/7] 16c red: a land-cover class per triangle, natural colours The red suite of docs/increments/16c-landcover-labels.md, tests only: - test_landcover.py: regions and label_triangles on hand-made grid meshes (no _core call): the design's fixtures as pure meshes, D2's overlap rule, determinism under permutation, thin slivers; I1 and the centroid oracle (I2) on every fixture. - test_cli_mesh_landcover.py: the same fixtures through rasputin mesh on the bumpy DEM with I1-I3, the land_cover_codes field, the stderr line and stats phase, refused codes, no labels without a coded map, the PLY face property, a VTK readback, palette --out, and R5's feature_input fields. - test_palettes.py: every CLC class, the 34 Norway codes and 0 coloured; families by hue ranges; the ParaView preset; the palette command. - test_io_vtk_legacy.py / test_io_ply.py: triangle_codes and face_codes. - test_cli_mesh_features.py::TestCommittedExtract: I1-I3 and I5 on the quarter circle against shapely clips of the extract. - landcover_fixtures.py: the oracle, the spread check, vtk_labels. Every new test fails on a missing module, parameter, array or behaviour; the rest of the suite, ruff and mypy are green. Co-Authored-By: Claude Opus 5.5 --- tests/python/landcover_fixtures.py | 172 ++++++++ tests/python/test_cli_mesh_features.py | 45 +- tests/python/test_cli_mesh_landcover.py | 541 ++++++++++++++++++++++++ tests/python/test_io_ply.py | 52 +++ tests/python/test_io_vtk_legacy.py | 114 +++++ tests/python/test_landcover.py | 458 ++++++++++++++++++++ tests/python/test_palettes.py | 226 ++++++++++ 7 files changed, 1607 insertions(+), 1 deletion(-) create mode 100644 tests/python/landcover_fixtures.py create mode 100644 tests/python/test_cli_mesh_landcover.py create mode 100644 tests/python/test_landcover.py create mode 100644 tests/python/test_palettes.py diff --git a/tests/python/landcover_fixtures.py b/tests/python/landcover_fixtures.py new file mode 100644 index 00000000..460fd6d3 --- /dev/null +++ b/tests/python/landcover_fixtures.py @@ -0,0 +1,172 @@ +"""Test support for increment 16c: the land-cover oracle and the spread check. + +`docs/increments/16c-landcover-labels.md`, R1 ("The oracle (tests only)") and +the invariants I1 and I2. Everything here is built from the triangles and the +input polygons, never from the producer's records: no component ids, no chosen +points. It borrows only the producer's *predicate* (a point against the input +polygons, `intersects`) and its overlap rule (smallest area, ties to the +smaller code; Default D2). + +- `landcover_oracle`: each triangle's centroid tested against the polygons, + for every triangle whose inradius exceeds `1.5 * margin` (the centroid is + then further than `margin` from every input boundary, so the answer is + exact). The legacy's per-centre test (`legacy/rasputin/gml_repository.py`, + `land_cover`), vectorised, with 0 for a centre in no polygon. +- `spread_violations`: I1. Every interior edge that is not a constraint edge + has the same code on both sides. Arrays only, no geometry. +- `vtk_labels`: I3, I1 and I2 on one `.vtk` file, for the CLI suites. +""" + +from __future__ import annotations + +from collections.abc import Sequence +from dataclasses import dataclass + +import numpy as np +import numpy.typing as npt +import shapely +from shapely.geometry.base import BaseGeometry + +from vtkread import VtkFile, lines_as_array, polygons_as_array + +#: The noder's snap spacing at the CLI's default (`cli.DEFAULT_SNAP_SPACING`). +SNAP = 1e-3 +#: R1: `margin = 2h`. +MARGIN = 2 * SNAP + +Coded = Sequence[tuple[BaseGeometry, int]] + + +def xy_of(vertices: npt.ArrayLike) -> np.ndarray: + return np.asarray(vertices, dtype=np.float64)[:, :2] + + +def inradius(vertices: npt.ArrayLike, triangles: npt.ArrayLike) -> np.ndarray: + """`2 * area / perimeter` per triangle, in x and y.""" + xy = xy_of(vertices) + tri = np.asarray(triangles, dtype=np.int64).reshape(-1, 3) + a, b, c = xy[tri[:, 0]], xy[tri[:, 1]], xy[tri[:, 2]] + area = 0.5 * np.abs( + (b[:, 0] - a[:, 0]) * (c[:, 1] - a[:, 1]) - (c[:, 0] - a[:, 0]) * (b[:, 1] - a[:, 1]) + ) + perimeter = np.hypot(*(b - c).T) + np.hypot(*(c - a).T) + np.hypot(*(a - b).T) + return np.divide(2 * area, perimeter, out=np.zeros_like(area), where=perimeter > 0) + + +def centroids(vertices: npt.ArrayLike, triangles: npt.ArrayLike) -> np.ndarray: + xy = xy_of(vertices) + tri = np.asarray(triangles, dtype=np.int64).reshape(-1, 3) + return xy[tri].mean(axis=1) + + +def pick(hits: Sequence[int], polygons: Coded) -> int: + """D2: the smallest area wins, ties to the smaller code; none is 0.""" + if not hits: + return 0 + return min((polygons[i][0].area, polygons[i][1]) for i in hits)[1] + + +def codes_at(points: np.ndarray, polygons: Coded) -> tuple[np.ndarray, np.ndarray]: + """Each point's code by D2, and how many polygons it is in.""" + codes = np.zeros(len(points), dtype=np.int64) + counts = np.zeros(len(points), dtype=np.int64) + if not len(polygons) or not len(points): + return codes, counts + tree = shapely.STRtree([p for p, _ in polygons]) + found, hit = tree.query(shapely.points(points), predicate="intersects") + per_point: dict[int, list[int]] = {} + for i, j in zip(found.tolist(), hit.tolist(), strict=True): + per_point.setdefault(i, []).append(j) + for i, hits in per_point.items(): + codes[i] = pick(hits, polygons) + counts[i] = len(hits) + return codes, counts + + +@dataclass(frozen=True) +class Oracle: + """The expected code of each triangle, and which triangles it is exact for.""" + + codes: np.ndarray + checked: np.ndarray + + def mismatches(self, produced: npt.ArrayLike) -> list[tuple[int, int, int]]: + got = np.asarray(produced).reshape(-1) + bad = np.flatnonzero(self.checked & (got != self.codes)) + return [(int(t), int(got[t]), int(self.codes[t])) for t in bad[:20]] + + +def landcover_oracle( + vertices: npt.ArrayLike, triangles: npt.ArrayLike, polygons: Coded, margin: float = MARGIN +) -> Oracle: + """R1's oracle: centroids against the polygons, where `r > 1.5 * margin`.""" + codes, _ = codes_at(centroids(vertices, triangles), polygons) + checked = inradius(vertices, triangles) > 1.5 * margin + return Oracle(codes=codes, checked=checked) + + +def edge_keys(pairs: npt.ArrayLike, n: int) -> np.ndarray: + e = np.asarray(pairs, dtype=np.int64).reshape(-1, 2) + return np.minimum(e[:, 0], e[:, 1]) * n + np.maximum(e[:, 0], e[:, 1]) + + +def spread_violations( + triangles: npt.ArrayLike, constraints: npt.ArrayLike, codes: npt.ArrayLike +) -> list[tuple[int, int, int, int]]: + """I1: `(t, u, code_t, code_u)` for each unconstrained interior edge whose + two triangles differ in code.""" + tri = np.asarray(triangles, dtype=np.int64).reshape(-1, 3) + got = np.asarray(codes).reshape(-1) + n = int(tri.max()) + 1 if len(tri) else 0 + sides = np.concatenate([tri[:, [0, 1]], tri[:, [1, 2]], tri[:, [2, 0]]]) + keys = edge_keys(sides, n) + owner = np.tile(np.arange(len(tri)), 3) + free = ~np.isin(keys, edge_keys(constraints, n)) + keys, owner = keys[free], owner[free] + order = np.argsort(keys, kind="stable") + keys, owner = keys[order], owner[order] + pair = np.flatnonzero(keys[1:] == keys[:-1]) + t, u = owner[pair], owner[pair + 1] + bad = np.flatnonzero(got[t] != got[u]) + return [(int(t[k]), int(u[k]), int(got[t[k]]), int(got[u[k]])) for k in bad[:20]] + + +def interior_edge_count(triangles: npt.ArrayLike) -> int: + """How many edges have two triangles: the probe I1 needs to mean anything.""" + tri = np.asarray(triangles, dtype=np.int64).reshape(-1, 3) + n = int(tri.max()) + 1 + keys = edge_keys(np.concatenate([tri[:, [0, 1]], tri[:, [1, 2]], tri[:, [2, 0]]]), n) + _, counts = np.unique(keys, return_counts=True) + return int((counts == 2).sum()) + + +def triangle_areas(vertices: npt.ArrayLike, triangles: npt.ArrayLike) -> np.ndarray: + xy = xy_of(vertices) + tri = np.asarray(triangles, dtype=np.int64).reshape(-1, 3) + a, b, c = xy[tri[:, 0]], xy[tri[:, 1]], xy[tri[:, 2]] + return 0.5 * np.abs( + (b[:, 0] - a[:, 0]) * (c[:, 1] - a[:, 1]) - (c[:, 0] - a[:, 0]) * (b[:, 1] - a[:, 1]) + ) + + +def vtk_labels( + vtk: VtkFile, polygons: Coded, margin: float = MARGIN +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + """I3, I1 and I2 on one `.vtk`; returns its lines, triangles and the + triangles' codes.""" + lines, triangles = lines_as_array(vtk), polygons_as_array(vtk) + array = vtk.cell_array("land_cover_code") + values = np.asarray(array.values) + # I3: an int scalar, one value per cell, 0 on every line. + assert (array.type_name, array.components) == ("int", 1) + assert len(values) == vtk.cells == len(lines) + len(triangles) + assert not values[: len(lines)].any() + codes = values[len(lines) :] + # I1: the spread, on the arrays alone. + assert interior_edge_count(triangles) > 0 + assert spread_violations(triangles, lines, codes) == [] + # I2: the oracle, from the input polygons; it must reach most triangles. + oracle = landcover_oracle(vtk.points, triangles, polygons, margin) + assert oracle.checked.sum() > 0.9 * len(triangles) + assert oracle.mismatches(codes) == [] + return lines, triangles, codes diff --git a/tests/python/test_cli_mesh_features.py b/tests/python/test_cli_mesh_features.py index 57fda6ea..dcaf843b 100644 --- a/tests/python/test_cli_mesh_features.py +++ b/tests/python/test_cli_mesh_features.py @@ -73,6 +73,7 @@ from gpkg_fixtures import ( DTM10, EXTRACT, + EXTRACT_TABLE, LEGACY_GML, OLA_EUROPE, OLA_NORWAY, @@ -82,6 +83,7 @@ needs_rtree, write_gpkg, ) +from landcover_fixtures import triangle_areas, vtk_labels from plyread import read_ply from test_cli_mesh_dem import USAGE, invoke, write_tiff from test_cli_mesh_domain import COLS, ROWS, SQUARE, geojson, quarter_circle @@ -589,7 +591,11 @@ def _max_error(tile: Any, points: np.ndarray, triangles: np.ndarray, domain: Pol class TestCommittedExtract: """Acceptance 16b-1/2 in CI: the quarter circle on the committed tile with the committed extract, `--features-map corine`, at 10 m (M5's reference: - about 54 840 triangles). I7: the tolerance holds, every node covered.""" + about 54 840 triangles). I7: the tolerance holds, every node covered. + + Increment 16c adds I1, I2, I3 and I5 on the same run (the `test_16c_` + tests, committed red: the file had no `land_cover_code`); its I4 is + `test_i3_rows_in_reverse_order_give_the_same_file`, unchanged.""" @pytest.fixture(scope="class") def run(self, tmp_path_factory: pytest.TempPathFactory) -> tuple[VtkFile, str, Path]: @@ -659,6 +665,35 @@ def test_i3_rows_in_reverse_order_give_the_same_file(self, run: Any, tmp_path: P assert code == 0, output assert target.read_bytes() == first.read_bytes() + @needs_codecs + def test_16c_i1_i2_i3_land_cover_codes(self, run: Any) -> None: + """Increment 16c on the real mesh: every cell carries a code, 0 on the + lines (I3); the spread (I1); the centroid oracle against the extract's + polygons moved to EPSG:25833 (I2). The extract covers the quarter + circle, so no triangle is 0.""" + vtk, _, _ = run + _, _, codes = vtk_labels(vtk, _extract_polygons()) + assert 0 not in set(codes.tolist()) + + @needs_codecs + def test_16c_i5_class_areas_match_the_clipped_extract(self, run: Any) -> None: + """I5: per code, the triangles' xy area equals the area of each + extract polygon (moved to EPSG:25833) intersected with the quarter + circle, within 0.01 % of the domain's area.""" + vtk, _, _ = run + codes = np.asarray(vtk.cell_array("land_cover_code").values)[len(lines_as_array(vtk)) :] + triangles = polygons_as_array(vtk) + areas = triangle_areas(vtk.points, triangles) + domain = Polygon(quarter_circle()) + expected: dict[int, float] = {} + for polygon, code in _extract_polygons(): + expected[code] = expected.get(code, 0.0) + polygon.intersection(domain).area + got = {int(c): float(areas[codes == c].sum()) for c in np.unique(codes)} + assert len(expected) >= 5 + for code in set(expected) | set(got): + gap = abs(got.get(code, 0.0) - expected.get(code, 0.0)) + assert gap <= 1e-4 * domain.area, (code, got.get(code), expected.get(code)) + @needs_codecs def test_the_legacy_gml(self, tmp_path: Path) -> None: """Q6 (b): the legacy GML (EPSG:4326) with `clc18_kode`, same domain.""" @@ -680,6 +715,14 @@ def test_the_legacy_gml(self, tmp_path: Path) -> None: assert V.mask("land_cover", "water") in set(edges(vtk)[1].tolist()) +def _extract_polygons() -> list[tuple[Any, int]]: + """The committed extract's polygons in the DEM's CRS, with their codes (16c).""" + return [ + (ff.moved(geometry, "EPSG:3035"), int(code)) + for _, geometry, code in ff.extract_features(EXTRACT, EXTRACT_TABLE) + ] + + # ------------------------------------------------------------ Ola's local data diff --git a/tests/python/test_cli_mesh_landcover.py b/tests/python/test_cli_mesh_landcover.py new file mode 100644 index 00000000..8690c70e --- /dev/null +++ b/tests/python/test_cli_mesh_landcover.py @@ -0,0 +1,541 @@ +"""`rasputin mesh ... --features-map corine`: a land-cover code per cell (16c). + +`docs/increments/16c-landcover-labels.md`: R3 (what the files carry), R5 (what +`feature_input` keeps), R6 (the CLI) and "Tests for @tester", through the +command on the synthetic `bumpy` DEM of `test_cli_mesh_features.py`, with +GeoJSON features carrying `Code_18`. For every fixture: I1 (the spread, from +the file's `LINES` and triangles), I2 (the centroid oracle of +`landcover_fixtures`, from the input polygons), I3 (one value per cell, 0 on +every line), and the codes the design names for it. + +Pinned beyond the design's text (the design leaves them open): + +- The stderr line is matched as `land cover: regions, outside every + polygon, in more than one, thinner than the snap` (R3's words). +- The `land_cover_codes` string contains `CORINE Land Cover level-3 code`, + `Code_18`, `corine` and `0 =`; the PLY comment is `land_cover_codes + `. +- A refused code is a usage error (exit 2) naming the feature and the value, + and `--features`, as every other `FeatureError` is (16b). +- A `LineString` under a coded map keeps its code and has no polygon. +- `rasputin mesh --help` names `land_cover_code` and `rasputin palette`. + +Committed red: no code writes `land_cover_code`, `land_cover_codes` or the +stderr line; `rasputin palette` is no command; `ClassMap` has no `codes` and +`TerrainFeature` no `code` or `polygon`; a covering polygon is dropped as +outside; and `corine` accepts any value. Every test fails on one of those. +""" + +from __future__ import annotations + +import json +import re +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +import numpy as np +import pytest +import shapely +from numpy.testing import assert_array_equal +from shapely.geometry import LineString, Polygon, box +from shapely.geometry.base import BaseGeometry +from typer.testing import CliRunner + +import feature_fixtures as ff +from feature_fixtures import Feat, domain_of, write_geojson +from geotiff_fixtures import TIE_X, TIE_Y, micro_tiff +from landcover_fixtures import MARGIN, landcover_oracle, spread_violations, vtk_labels +from plyread import read_ply +from test_cli_mesh import plain +from test_cli_mesh_dem import USAGE, invoke, write_tiff +from test_cli_mesh_domain import COLS, ROWS, SQUARE, geojson +from tin_engine.cli import app +from vtkread import VtkFile, read_vtk + +runner = CliRunner(env={"NO_COLOR": "1", "TERM": "dumb"}) + +LAND_COVER_LINE = re.compile( + r"land cover: (?P\d+) regions, (?P\d+) outside every polygon, " + r"(?P\d+) in more than one, (?P\d+) thinner than the snap" +) +FEATURES_LINE = re.compile( + r"\b(?P\d+) features kept, (?P\d+) dropped outside, (?P\d+) clipped, " + r"(?P\d+) empty skipped\b" +) +CODES_SYSTEM = "CORINE Land Cover level-3 code" +PRESET_NAME = "rasputin CORINE natural" + + +def rel(x: float, y: float) -> tuple[float, float]: + return (TIE_X + x, TIE_Y + y) + + +def rect(x0: float, y0: float, x1: float, y1: float) -> Polygon: + (ax, ay), (bx, by) = rel(x0, y0), rel(x1, y1) + return box(ax, ay, bx, by) + + +def coded(fid: Any, geometry: BaseGeometry, code: Any) -> Feat: + return Feat(fid, geometry, {"Code_18": code}) + + +# Inside `SQUARE` (x 12.3 .. 187.7, y -73.3 .. -6.7 from the tie point), off +# every DEM node. +WEST = rect(40.3, -60.2, 100.7, -20.4) +EAST = rect(100.7, -60.2, 160.9, -20.4) +EAST_OFFSET = rect(100.7001, -60.2, 160.9, -20.4) # 0.1 mm from WEST's east side +LAKE = rect(80.1, -50.2, 120.4, -30.3) +FOREST = rect(30.3, -65.1, 170.2, -15.3) +HOLED_FOREST = Polygon(FOREST.exterior, [LAKE.exterior]) +ROAD = LineString([rel(30.1, -40.3), rel(170.2, -40.3)]) +COVER = rect(-50.3, -150.1, 300.2, 50.4) +#: The domain with a notch cut down from its north side, x 80 .. 120. +NOTCHED: list[tuple[float, float]] = [ + rel(12.3, -73.3), + rel(187.7, -72.9), + rel(186.1, -6.7), + rel(120.3, -6.9), + rel(119.9, -40.1), + rel(80.1, -40.3), + rel(79.7, -7.0), + rel(13.9, -7.1), +] +BAND = rect(50.2, -30.1, 150.3, -15.2) + + +@pytest.fixture +def bumpy(tmp_path: Path) -> Path: + array = np.random.default_rng(16).uniform(0.0, 50.0, (ROWS, COLS)).astype(np.float32) + return write_tiff(tmp_path / "bumpy.tif", micro_tiff(array)) + + +@pytest.fixture +def plain_square(tmp_path: Path) -> Path: + return geojson(tmp_path / "square.geojson", SQUARE) + + +def run( + tmp_path: Path, tif: Path, domain: Path, *extra: str, out: str = "x.vtk" +) -> tuple[int, str, Path]: + target = tmp_path / out + code, output = invoke( + "--dem", str(tif), "--domain", str(domain), "--tolerance", "1", "--out", str(target), *extra + ) + return code, output, target + + +@dataclass(frozen=True) +class Labelled: + """A `.vtk` written with land-cover codes, and what I1-I3 need of it.""" + + vtk: VtkFile + output: str + points: np.ndarray + lines: np.ndarray + triangles: np.ndarray + codes: np.ndarray + + def inside(self, geometry: BaseGeometry) -> np.ndarray: + centroids = self.points[self.triangles][:, :, :2].mean(axis=1) + return np.asarray(shapely.contains_xy(geometry, *centroids.T)) + + def stderr_counts(self) -> dict[str, int]: + found = [m.groupdict() for m in LAND_COVER_LINE.finditer(self.output)] + assert len(found) == 1, self.output + return {k: int(v) for k, v in found[0].items()} + + +def labelled_vtk(vtk: VtkFile, output: str, polygons: list[tuple[BaseGeometry, int]]) -> Labelled: + """I1, I2 and I3 on one file (`landcover_fixtures.vtk_labels`).""" + lines, triangles, codes = vtk_labels(vtk, polygons, MARGIN) + return Labelled(vtk, output, vtk.points, lines, triangles, codes) + + +def polygons_of(features: list[Feat]) -> list[tuple[BaseGeometry, int]]: + return [ + (f.geometry, int(f.properties["Code_18"])) + for f in features + if isinstance(f.geometry, Polygon) + ] + + +def mesh_corine( + tmp_path: Path, tif: Path, features: list[Feat], domain: Path | None = None +) -> Labelled: + path = write_geojson(tmp_path / "corine.geojson", features) + domain = domain or geojson(tmp_path / "square.geojson", SQUARE) + code, output, target = run( + tmp_path, tif, domain, "--features", str(path), "--features-map", "corine" + ) + assert code == 0, output + return labelled_vtk(read_vtk(target.read_bytes()), output, polygons_of(features)) + + +def text_field(vtk: VtkFile, name: str) -> str: + (value,) = vtk.field_data[name].values + return str(value) + + +# ------------------------------------------------------------ the fixtures + + +class TestFixtures: + """The design's seven labelling fixtures, end to end.""" + + def test_two_squares_side_by_side(self, tmp_path: Path, bumpy: Path) -> None: + got = mesh_corine(tmp_path, bumpy, [coded(1, WEST, "311"), coded(2, EAST, "512")]) + assert set(got.codes[got.inside(WEST)].tolist()) == {311} + assert set(got.codes[got.inside(EAST)].tolist()) == {512} + assert set(got.codes[~got.inside(WEST.union(EAST))].tolist()) == {0} + # The triangles along the shared side carry both codes. + seam = LineString([rel(100.7, -55.1), rel(100.7, -25.3)]) + cells = shapely.polygons(got.points[got.triangles][:, :, :2]) + touching = np.asarray(shapely.distance(seam, cells)) <= 1e-9 + assert set(got.codes[touching].tolist()) == {311, 512} + counts = got.stderr_counts() + assert counts["o"] >= 1 and counts["v"] == 0 + + def test_a_forest_with_an_empty_hole(self, tmp_path: Path, bumpy: Path) -> None: + got = mesh_corine(tmp_path, bumpy, [coded("f", HOLED_FOREST, "312")]) + assert got.inside(LAKE).any() + assert set(got.codes[got.inside(LAKE)].tolist()) == {0} + assert set(got.codes[got.inside(HOLED_FOREST)].tolist()) == {312} + + def test_a_lake_in_the_hole_of_a_holed_forest(self, tmp_path: Path, bumpy: Path) -> None: + got = mesh_corine( + tmp_path, bumpy, [coded("f", HOLED_FOREST, "312"), coded("l", LAKE, "512")] + ) + assert set(got.codes[got.inside(LAKE)].tolist()) == {512} + assert set(got.codes[got.inside(HOLED_FOREST)].tolist()) == {312} + assert got.stderr_counts()["v"] == 0 + + def test_a_lake_in_an_unholed_forest(self, tmp_path: Path, bumpy: Path) -> None: + """D2: the smaller polygon wins, and stderr counts the overlap.""" + got = mesh_corine(tmp_path, bumpy, [coded("f", FOREST, "312"), coded("l", LAKE, "512")]) + assert set(got.codes[got.inside(LAKE)].tolist()) == {512} + assert set(got.codes[got.inside(FOREST.difference(LAKE))].tolist()) == {312} + assert got.stderr_counts()["v"] > 0 + + def test_a_polygon_clipped_by_the_domain_into_two_pieces( + self, tmp_path: Path, bumpy: Path + ) -> None: + domain = geojson(tmp_path / "notched.geojson", NOTCHED) + got = mesh_corine(tmp_path, bumpy, [coded("b", BAND, "324")], domain=domain) + west = got.inside(BAND.intersection(rect(12, -80, 80.1, 0))) + east = got.inside(BAND.intersection(rect(119.9, -80, 190, 0))) + assert west.any() and east.any() + assert set(got.codes[west | east].tolist()) == {324} + + def test_a_road_across_a_polygon(self, tmp_path: Path, bumpy: Path) -> None: + got = mesh_corine(tmp_path, bumpy, [coded("f", WEST, "311"), coded("r", ROAD, "122")]) + north = got.inside(WEST.intersection(rect(0, -40.3, 200, 0))) + south = got.inside(WEST.intersection(rect(0, -80, 200, -40.3))) + assert north.any() and south.any() + assert set(got.codes[north | south].tolist()) == {311} + + def test_a_polygon_covering_the_domain(self, tmp_path: Path, bumpy: Path) -> None: + """R5: kept with no lines, counted kept and not clipped.""" + got = mesh_corine(tmp_path, bumpy, [coded("c", COVER, "333")]) + assert set(got.codes.tolist()) == {333} + found = [m.groupdict() for m in FEATURES_LINE.finditer(got.output)] + assert found == [{"n": "1", "o": "0", "c": "0", "e": "0"}], got.output + assert got.stderr_counts()["o"] == 0 + + def test_boundaries_a_tenth_of_a_millimetre_apart(self, tmp_path: Path, bumpy: Path) -> None: + """Less than the snap apart: the run succeeds and, away from the seam, + each square carries its own code (I2).""" + got = mesh_corine(tmp_path, bumpy, [coded(1, WEST, "311"), coded(2, EAST_OFFSET, "512")]) + assert set(got.codes[got.inside(rect(40.3, -60.2, 100.6, -20.4))].tolist()) == {311} + assert set(got.codes[got.inside(rect(100.8, -60.2, 160.9, -20.4))].tolist()) == {512} + + def test_a_code_given_as_a_json_number(self, tmp_path: Path, bumpy: Path) -> None: + got = mesh_corine(tmp_path, bumpy, [coded(1, WEST, 311)]) + assert set(got.codes[got.inside(WEST)].tolist()) == {311} + + +# ------------------------------------------------------------ the record + + +class TestRecord: + @pytest.fixture + def two_squares(self, tmp_path: Path) -> Path: + return write_geojson( + tmp_path / "two.geojson", [coded(1, WEST, "311"), coded(2, EAST, "512")] + ) + + def test_the_codes_field( + self, tmp_path: Path, bumpy: Path, plain_square: Path, two_squares: Path + ) -> None: + code, output, target = run( + tmp_path, + bumpy, + plain_square, + "--features", + str(two_squares), + "--features-map", + "corine", + ) + assert code == 0, output + text = text_field(read_vtk(target.read_bytes()), "land_cover_codes") + for words in (CODES_SYSTEM, "Code_18", "corine", "0 ="): + assert words in text, text + + @pytest.mark.parametrize("binary", [False, True], ids=["ascii", "binary"]) + def test_every_cell_carries_a_code_in_both_encodings( + self, tmp_path: Path, bumpy: Path, plain_square: Path, two_squares: Path, binary: bool + ) -> None: + extra = ["--binary"] if binary else [] + code, output, target = run( + tmp_path, + bumpy, + plain_square, + "--features", + str(two_squares), + "--features-map", + "corine", + *extra, + ) + assert code == 0, output + got = labelled_vtk(read_vtk(target.read_bytes()), output, [(WEST, 311), (EAST, 512)]) + assert {311, 512} <= set(got.codes.tolist()) + assert list(got.vtk.scalars) == ["feature_mask"] + + def test_no_coded_map_no_codes( + self, tmp_path: Path, bumpy: Path, plain_square: Path, two_squares: Path + ) -> None: + """D3: `property` writes no labels, nor does a run without features; + `corine` over the same polygons does (the control).""" + by_property = write_geojson( + tmp_path / "p.geojson", + [Feat(1, WEST, {"property": "land_cover"}), Feat(2, EAST, {"property": "water"})], + ) + runs = { + "none": (), + "property": ("--features", str(by_property)), + "corine": ("--features", str(two_squares), "--features-map", "corine"), + } + files = {} + for name, extra in runs.items(): + (tmp_path / name).mkdir() + code, output, target = run(tmp_path / name, bumpy, plain_square, *extra) + assert code == 0, output + files[name] = (read_vtk(target.read_bytes()), output) + for name in ("none", "property"): + vtk, output = files[name] + with pytest.raises(KeyError): + vtk.cell_array("land_cover_code") + assert "land_cover_codes" not in vtk.field_data + assert not LAND_COVER_LINE.search(output), output + vtk, output = files["corine"] + assert "land_cover_codes" in vtk.field_data + assert LAND_COVER_LINE.search(output), output + + def test_the_stats_phase( + self, tmp_path: Path, bumpy: Path, plain_square: Path, two_squares: Path + ) -> None: + code, output, _ = run( + tmp_path, + bumpy, + plain_square, + "--features", + str(two_squares), + "--features-map", + "corine", + "--stats", + "-", + ) + assert code == 0, output + assert re.search(r"\|\s*land cover\s*\|", output), output + + def test_the_help_names_the_array_and_the_palette(self) -> None: + result = runner.invoke(app, ["mesh", "--help"]) + text = plain(result.output) + assert "land_cover_code" in text and "rasputin palette" in text, text + + +# ------------------------------------------------------------ refusals + + +class TestRefusals: + @pytest.mark.parametrize( + "value", + ["forest", "0", "-311", "2147483648", "3.5", ["311", "312"]], + ids=["word", "zero", "negative", "over-int32", "decimal", "list"], + ) + def test_a_code_that_is_not_a_class_code_is_refused( + self, tmp_path: Path, bumpy: Path, plain_square: Path, value: Any + ) -> None: + """R5: `int(str(value))` in 1 .. 2**31 - 1, else refused naming the + feature and the value; a list is refused too.""" + path = write_geojson(tmp_path / "bad.geojson", [coded("f-77", WEST, value)]) + code, output, target = run( + tmp_path, bumpy, plain_square, "--features", str(path), "--features-map", "corine" + ) + assert code == USAGE, output + assert "f-77" in output and "--features" in output, output + shown = value[0] if isinstance(value, list) else value + assert str(shown) in output, output + assert not target.exists() + + +# ------------------------------------------------------------ .ply + + +class TestPly: + def test_the_face_file_carries_the_codes_and_the_edge_file_not( + self, tmp_path: Path, bumpy: Path, plain_square: Path + ) -> None: + path = write_geojson( + tmp_path / "two.geojson", [coded(1, WEST, "311"), coded(2, EAST, "512")] + ) + edges_path = tmp_path / "e.ply" + code, output, target = run( + tmp_path, + bumpy, + plain_square, + "--features", + str(path), + "--features-map", + "corine", + "--out-edges", + str(edges_path), + out="x.ply", + ) + assert code == 0, output + header, data = read_ply(target.read_bytes()) + face = header.element("face") + assert [p.name for p in face.properties] == ["vertex_indices", "land_cover_code"] + assert face.properties[1].type_name == "int" + texts = [c for c in header.comments if c.startswith("land_cover_codes ")] + assert len(texts) == 1 and CODES_SYSTEM in texts[0], header.comments + + edge_header, edge_data = read_ply(edges_path.read_bytes()) + assert all(p.name != "land_cover_code" for e in edge_header.elements for p in e.properties) + assert not any(c.startswith("land_cover_codes") for c in edge_header.comments) + + # I1 and I2 on the face file, with the edge file's constraints. + vertex = data["vertex"] + points = np.column_stack([vertex["x"], vertex["y"], vertex["z"]]) + triangles = np.stack(list(data["face"]["vertex_indices"])).astype(np.int64) + codes = np.asarray(data["face"]["land_cover_code"]) + lines = np.column_stack([edge_data["edge"]["vertex1"], edge_data["edge"]["vertex2"]]) + assert spread_violations(triangles, lines, codes) == [] + oracle = landcover_oracle(points, triangles, [(WEST, 311), (EAST, 512)], MARGIN) + assert oracle.mismatches(codes) == [] + assert {311, 512} <= set(codes.tolist()) + + +# ------------------------------------------------------------ VTK readback + + +def test_vtk_reads_the_array_back(tmp_path: Path, bumpy: Path, plain_square: Path) -> None: + """`vtkPolyDataReader` sees `land_cover_code` with one value per cell, + 0 on the lines (which VTK numbers first). Skipped without `vtk`, as + `test_io_vtk_readback.py` is; CI runs it in the `viewer` extra's step.""" + vtk = pytest.importorskip("vtk") + path = write_geojson(tmp_path / "two.geojson", [coded(1, WEST, "311"), coded(2, EAST, "512")]) + code, output, target = run( + tmp_path, bumpy, plain_square, "--features", str(path), "--features-map", "corine" + ) + assert code == 0, output + reader = vtk.vtkPolyDataReader() + errors: list[str] = [] + reader.AddObserver("ErrorEvent", lambda _obj, _event: errors.append("error")) + reader.SetFileName(str(target)) + reader.ReadAllFieldsOn() + reader.Update() + assert not errors + polydata = reader.GetOutput() + array = polydata.GetCellData().GetArray("land_cover_code") + assert array is not None + values = np.array([array.GetValue(i) for i in range(array.GetNumberOfTuples())]) + assert len(values) == polydata.GetNumberOfCells() + lines = polydata.GetNumberOfLines() + assert lines > 0 and not values[:lines].any() + assert {311, 512} <= set(values[lines:].tolist()) + ours = read_vtk(target.read_bytes()).cell_array("land_cover_code").values + assert_array_equal(values, np.asarray(ours)) + + +# ------------------------------------------------------------ the palette + + +def test_palette_out_writes_the_preset(tmp_path: Path) -> None: + """R4: `rasputin palette corine --out FILE` writes the ParaView preset.""" + from tin_engine.palettes import CORINE_NATURAL, paraview_preset + + target = tmp_path / "corine_natural.json" + result = runner.invoke(app, ["palette", "corine", "--out", str(target)]) + assert result.exit_code == 0, result.output + assert json.loads(target.read_text()) == paraview_preset(CORINE_NATURAL, PRESET_NAME) + + +# ------------------------------------------------- what feature_input keeps (R5) + + +class TestWhatFeatureInputKeeps: + DOMAIN = domain_of(Polygon(SQUARE)) + + def open(self, tmp_path: Path, features: list[Feat], map_name: str, **kw: Any) -> Any: + path = write_geojson(tmp_path / f"{map_name}.geojson", features, **kw) + return ff.open_one(path, self.DOMAIN, map_name) + + @pytest.mark.parametrize( + ("name", "codes"), + [ + ("corine", CODES_SYSTEM), + ("corine-water", CODES_SYSTEM), + ("clc18_kode", CODES_SYSTEM), + ("property", ""), + ], + ) + def test_the_maps_name_their_code_system(self, name: str, codes: str) -> None: + assert ff.feature_input().CLASS_MAPS[name].codes == codes + + def test_a_coded_polygon_keeps_its_code_and_polygon(self, tmp_path: Path) -> None: + fs = self.open(tmp_path, [coded(1, WEST, "311")], "corine") + (feature,) = fs.features + assert feature.code == 311 + assert feature.polygon is not None + assert feature.polygon.equals(WEST) + + def test_a_coded_line_keeps_its_code_and_no_polygon(self, tmp_path: Path) -> None: + fs = self.open(tmp_path, [coded("r", ROAD, "122")], "corine") + (feature,) = fs.features + assert (feature.code, feature.polygon) == (122, None) + + def test_the_property_map_keeps_neither(self, tmp_path: Path) -> None: + fs = self.open(tmp_path, [Feat(1, WEST, {"property": "land_cover"})], "property") + (feature,) = fs.features + assert (feature.code, feature.polygon) == (None, None) + + def test_corine_water_keeps_the_water_only(self, tmp_path: Path) -> None: + fs = self.open( + tmp_path, [coded("f", FOREST, "312"), coded("l", LAKE, "512")], "corine-water" + ) + (feature,) = fs.features + assert (feature.fid, feature.code) == ("l", 512) + + def test_a_reprojected_polygon_is_moved_into_the_dems_crs(self, tmp_path: Path) -> None: + lonlat = ff.moved(WEST, "EPSG:25833", "EPSG:4326") + fs = self.open(tmp_path, [coded(1, lonlat, "311")], "corine", crs=None) + (feature,) = fs.features + expected = ff.moved(lonlat, "EPSG:4326") + assert feature.polygon.hausdorff_distance(expected) <= 1e-6 + assert feature.polygon.hausdorff_distance(WEST) <= 1e-3 + + def test_a_covering_polygon_is_kept_and_a_disjoint_one_dropped(self, tmp_path: Path) -> None: + far = rect(900.3, -900.1, 950.7, -850.2) + fs = self.open(tmp_path, [coded("c", COVER, "333"), coded("x", far, "311")], "corine") + (feature,) = fs.features + assert (feature.fid, feature.code, feature.lines) == ("c", 333, ()) + assert feature.polygon.equals(COVER) + assert (fs.outside, fs.clipped) == (1, 0) + + def test_an_uncoded_map_still_drops_a_covering_polygon(self, tmp_path: Path) -> None: + """16b's behaviour for `property`; the coded control keeps it.""" + uncoded = self.open(tmp_path, [Feat("c", COVER, {"property": "land_cover"})], "property") + assert (len(uncoded.features), uncoded.outside) == (0, 1) + kept = self.open(tmp_path, [coded("c", COVER, "333")], "corine") + assert len(kept.features) == 1 diff --git a/tests/python/test_io_ply.py b/tests/python/test_io_ply.py index 22745137..4c096e0a 100644 --- a/tests/python/test_io_ply.py +++ b/tests/python/test_io_ply.py @@ -440,3 +440,55 @@ def test_the_only_first_party_import_is_the_vocabulary(self) -> None: def test_it_is_deterministic(self) -> None: assert write_ply(VERTICES, faces=FACES) == write_ply(VERTICES, faces=FACES) + + +class TestFaceCodes: + """Increment 16c, R3: `write_ply(..., face_codes=...)` puts a face property + `int land_cover_code` after `vertex_indices`, on the face file only. + The header comment `land_cover_codes ...` is the caller's (it arrives in + `comments`), and `test_cli_mesh_landcover.py` pins it in the CLI's file. + + Committed red: `face_codes` is no parameter of `write_ply`, so every call + with it fails on `TypeError`. + """ + + CODES = np.array([311, 2**31 - 1], dtype=np.int64) + + @pytest.mark.parametrize("ascii", [True, False], ids=["ascii", "binary"]) + def test_the_face_property_follows_the_index_list(self, ascii: bool) -> None: + header, _ = read_ply(write_ply(VERTICES, faces=FACES, face_codes=self.CODES, ascii=ascii)) + face = header.element("face") + assert [p.name for p in face.properties] == ["vertex_indices", "land_cover_code"] + assert face.properties[1].type_name == "int" + assert not face.properties[1].is_list + + @pytest.mark.parametrize("ascii", [True, False], ids=["ascii", "binary"]) + def test_the_codes_and_the_faces_round_trip(self, ascii: bool) -> None: + _, data = read_ply(write_ply(VERTICES, faces=FACES, face_codes=self.CODES, ascii=ascii)) + assert_array_equal(data["face"]["land_cover_code"], self.CODES) + assert_array_equal(np.stack(list(data["face"]["vertex_indices"])), FACES) + assert_array_equal(vertex_array(data), VERTICES) + + def test_ascii_and_binary_agree(self) -> None: + _, text = read_ply(write_ply(VERTICES, faces=FACES, face_codes=self.CODES)) + _, packed = read_ply(write_ply(VERTICES, faces=FACES, face_codes=self.CODES, ascii=False)) + assert_array_equal(text["face"]["land_cover_code"], packed["face"]["land_cover_code"]) + + def test_only_when_given(self) -> None: + coded, _ = read_ply(write_ply(VERTICES, faces=FACES, face_codes=self.CODES)) + plain, _ = read_ply(write_ply(VERTICES, faces=FACES)) + assert len(coded.element("face").properties) == 2 + assert [p.name for p in plain.element("face").properties] == ["vertex_indices"] + + def test_refused_with_edges(self) -> None: + with pytest.raises(ValueError, match="face_codes"): + write_ply(VERTICES, edges=EDGES, face_codes=self.CODES) + + @pytest.mark.parametrize("count", [len(FACES) - 1, len(FACES) + 1]) + def test_one_code_per_face(self, count: int) -> None: + with pytest.raises(ValueError, match="face"): + write_ply(VERTICES, faces=FACES, face_codes=np.zeros(count, dtype=np.int64)) + + def test_a_code_outside_int32_is_refused(self) -> None: + with pytest.raises(ValueError, match="int32"): + write_ply(VERTICES, faces=FACES, face_codes=np.array([311, 2**31], dtype=np.int64)) diff --git a/tests/python/test_io_vtk_legacy.py b/tests/python/test_io_vtk_legacy.py index 483261d4..cc77a78d 100644 --- a/tests/python/test_io_vtk_legacy.py +++ b/tests/python/test_io_vtk_legacy.py @@ -518,3 +518,117 @@ def test_point_data_sits_between_the_cells_and_the_cell_data(self) -> None: def test_no_dataset_field_is_named_elevation(self) -> None: assert "elevation" not in read_vtk(write()).field_data + + +class TestLandCoverCode: + """Increment 16c, R3: `write_vtk(..., triangle_codes=...)` writes a cell + array `land_cover_code`, `int`, 0 on every `LINES` cell and each + triangle's code after them, in the cell `FIELD` block beside the + per-feature arrays (whose count includes it), or in a block of its own + when there are none. `feature_mask` stays the only `SCALARS` (13, ruling + 5). `land_cover_codes` joins `RESERVED`. + + Not pinned here: how the text of the `land_cover_codes` dataset string + reaches the writer. R3 reserves the name, so `fields` cannot carry it, + and the design names no parameter for it; `test_cli_mesh_landcover.py` + pins the string in the file. + + Committed red: `triangle_codes` is no parameter of `write_vtk`, so every + call with it fails on `TypeError`, and `land_cover_codes` is not + reserved. + """ + + CODES = np.array([311, 0, 512, 2**31 - 1], dtype=np.int64) + + def coded(self, binary: bool) -> VtkFile: + """Not a fixture, so a missing parameter fails the test, not its setup.""" + return read_vtk(write(triangle_codes=self.CODES, binary=binary)) + + def test_lines_carry_0_then_each_triangle_its_code(self, binary: bool) -> None: + coded = self.coded(binary) + expected = np.concatenate([np.zeros(len(EDGES), dtype=np.int64), self.CODES]) + assert_array_equal(coded.cell_array("land_cover_code").values, expected) + + def test_it_is_an_int_scalar_covering_every_cell(self, binary: bool) -> None: + coded = self.coded(binary) + array = coded.cell_array("land_cover_code") + assert (array.type_name, array.components) == ("int", 1) + assert len(array.values) == len(EDGES) + len(TRIANGLES) + + def test_it_sits_in_the_features_block_beside_the_per_feature_arrays( + self, binary: bool + ) -> None: + coded = self.coded(binary) + assert list(coded.cell_fields) == ["features"] + assert set(coded.cell_fields["features"]) == {"river", "road", "railway", "land_cover_code"} + + def test_feature_mask_stays_the_only_scalar(self, binary: bool) -> None: + coded = self.coded(binary) + assert list(coded.scalars) == ["feature_mask"] + assert_array_equal(coded.cell_array("feature_mask").values, EXPECTED_MASK) + + def test_with_no_feature_arrays_it_is_written_alone(self, binary: bool) -> None: + parsed = read_vtk( + write( + edge_masks=np.zeros(len(EDGES), dtype=np.uint32), + triangle_codes=self.CODES, + binary=binary, + ) + ) + assert len(parsed.cell_fields) == 1 + (arrays,) = parsed.cell_fields.values() + assert list(arrays) == ["land_cover_code"] + + def test_with_no_edges_it_is_the_codes(self, binary: bool) -> None: + parsed = read_vtk( + write( + edges=np.zeros((0, 2), dtype=np.uint32), + edge_masks=np.zeros(0, dtype=np.uint32), + triangle_codes=self.CODES, + binary=binary, + ) + ) + assert_array_equal(parsed.cell_array("land_cover_code").values, self.CODES) + + def test_only_when_given(self) -> None: + with_codes = read_vtk(write(triangle_codes=self.CODES)) + without = read_vtk(write()) + assert "land_cover_code" in with_codes.cell_fields["features"] + with pytest.raises(KeyError): + without.cell_array("land_cover_code") + + def test_ascii_and_binary_agree(self) -> None: + text = read_vtk(write(triangle_codes=self.CODES)) + packed = read_vtk(write(triangle_codes=self.CODES, binary=True)) + assert_array_equal( + text.cell_array("land_cover_code").values, packed.cell_array("land_cover_code").values + ) + + def test_binary_is_big_endian_int32(self) -> None: + blob = write( + edges=np.zeros((0, 2), dtype=np.uint32), + edge_masks=np.zeros(0, dtype=np.uint32), + triangle_codes=self.CODES, + binary=True, + ) + assert struct.pack(">4i", *self.CODES.tolist()) in blob + + @pytest.mark.parametrize("count", [len(TRIANGLES) - 1, len(TRIANGLES) + 1]) + def test_one_code_per_triangle(self, count: int) -> None: + with pytest.raises(ValueError, match="triangle"): + write(triangle_codes=np.zeros(count, dtype=np.int64)) + + def test_a_code_outside_int32_is_refused(self) -> None: + with pytest.raises(ValueError, match="int32"): + write(triangle_codes=np.array([311, 0, 512, 2**31], dtype=np.int64)) + + def test_a_vocabulary_naming_land_cover_code_is_refused(self) -> None: + clash = EdgeVocabulary(properties=(EdgeProperty(name="land_cover_code", bit=0),)) + masks = np.array([1, 0, 0], dtype=np.uint32) + write(vocabulary=clash, edge_masks=masks) # without codes: an ordinary name + with pytest.raises(ValueError, match="land_cover_code"): + write(vocabulary=clash, edge_masks=masks, triangle_codes=self.CODES) + + def test_land_cover_codes_is_reserved(self) -> None: + with pytest.raises(ValueError, match="land_cover_codes"): + write(fields=(("land_cover_codes", "x"),)) diff --git a/tests/python/test_landcover.py b/tests/python/test_landcover.py new file mode 100644 index 00000000..84768b29 --- /dev/null +++ b/tests/python/test_landcover.py @@ -0,0 +1,458 @@ +"""`tin_engine.landcover`: a land-cover class per triangle (increment 16c, R1). + +`docs/increments/16c-landcover-labels.md`, "Tests for @tester": `regions` and +`label_triangles` on hand-made meshes, no `_core` call, no file. Every mesh +here is a rectilinear grid whose lines carry every input boundary, so the +constraint edges are exactly the grid edges lying on the input linework (the +domain's boundary, each polygon's rings, each road), found by distance and not +by the producer. Coordinates sit at UTM 33N magnitudes, off round numbers. + +Each labelling fixture is checked three ways: against the centroid oracle of +`landcover_fixtures` (I2), for I1 (the spread), and against the codes the +design names for it. The fixtures are the design's CLI list as pure meshes: +two squares side by side, a forest with an empty hole, a lake in a holed and +in an unholed forest, a polygon clipped by the domain into two pieces, a road +across a polygon, a polygon covering the domain, boundaries 0.1 mm apart, +determinism, and overlaps (smallest area, ties to the smaller code; D2). + +Pinned beyond the design's text (the design leaves them open): + +- `label_triangles(vertices, triangles, edges, polygons=..., margin=...)`, + `vertices` `(N, 3)` (the trimmed mesh's; only x and y are used), and + `polygons` a sequence of `(geometry, code)` pairs. +- `CoverLabels.regions`, `.outside`, `.overlapped` and `.thin` are counts + (ints), the numbers of R3's stderr line; `.codes` is the `(T,)` array. +- `regions(triangles, edges)` takes no vertex count. + +Committed red: `tin_engine.landcover` does not exist, so every test fails on +`ModuleNotFoundError`, raised per test by `landcover()` below rather than at +collection. +""" + +from __future__ import annotations + +import importlib +from collections.abc import Sequence +from dataclasses import dataclass +from types import ModuleType +from typing import Any + +import numpy as np +import pytest +import shapely +from numpy.testing import assert_array_equal +from shapely.geometry import LineString, MultiLineString, Polygon, box +from shapely.geometry.base import BaseGeometry + +from importscan import first_party_imports +from landcover_fixtures import ( + MARGIN, + edge_keys, + interior_edge_count, + landcover_oracle, + spread_violations, +) + +X0, Y0 = 500_000.3, 6_600_000.7 + + +def landcover() -> ModuleType: + return importlib.import_module("tin_engine.landcover") + + +def rect(x0: float, y0: float, x1: float, y1: float) -> Polygon: + return box(X0 + x0, Y0 + y0, X0 + x1, Y0 + y1) + + +def line(*points: tuple[float, float]) -> LineString: + return LineString([(X0 + x, Y0 + y) for x, y in points]) + + +# ---------------------------------------------------------------- the meshes + + +@dataclass(frozen=True) +class Mesh: + """A trimmed mesh as the writers see it: `(N, 3)` vertices, triangles, and + the constraint edges.""" + + vertices: np.ndarray + triangles: np.ndarray + edges: np.ndarray + + @property + def centroids(self) -> np.ndarray: + return self.vertices[self.triangles][:, :, :2].mean(axis=1) + + def inside(self, geometry: BaseGeometry) -> np.ndarray: + """Triangles whose centroid is strictly inside `geometry`.""" + return np.asarray(shapely.contains_xy(geometry, *self.centroids.T)) + + +def grid_mesh( + xs: Sequence[float], + ys: Sequence[float], + lines: Sequence[BaseGeometry], + domain: Polygon | None = None, +) -> Mesh: + """A rectilinear grid, each cell split on its rising diagonal, keeping the + cells whose centre is in `domain` (default: the whole grid). The + constraint edges are the grid edges lying on `lines` or on the domain's + boundary, within a micrometre at both ends and the midpoint.""" + gx, gy = np.meshgrid(np.asarray(xs) + X0, np.asarray(ys) + Y0, indexing="xy") + vertices = np.column_stack([gx.ravel(), gy.ravel(), np.zeros(gx.size)]) + nx = len(xs) + if domain is None: + domain = box(X0 + xs[0], Y0 + ys[0], X0 + xs[-1], Y0 + ys[-1]) + triangles = [] + for j in range(len(ys) - 1): + for i in range(nx - 1): + v00, v10 = j * nx + i, j * nx + i + 1 + v01, v11 = v00 + nx, v10 + nx + centre = vertices[[v00, v11], :2].mean(axis=0) + if domain.contains(shapely.Point(*centre)): + triangles += [(v00, v10, v11), (v00, v11, v01)] + tri = np.array(triangles, dtype=np.int64) + sides = np.concatenate([tri[:, [0, 1]], tri[:, [1, 2]], tri[:, [2, 0]]]) + _, first = np.unique(edge_keys(sides, len(vertices)), return_index=True) + candidates = sides[np.sort(first)] + linework = shapely.union_all([domain.boundary, *(_linework(g) for g in lines)]) + a, b = vertices[candidates[:, 0], :2], vertices[candidates[:, 1], :2] + near = [ + np.asarray(shapely.distance(linework, shapely.points(p))) <= 1e-6 + for p in (a, b, (a + b) / 2) + ] + on = near[0] & near[1] & near[2] + return Mesh(vertices=vertices, triangles=tri, edges=candidates[on]) + + +def _linework(geometry: BaseGeometry) -> BaseGeometry: + if isinstance(geometry, Polygon): + return MultiLineString([r.coords for r in (geometry.exterior, *geometry.interiors)]) + return geometry + + +def label(mesh: Mesh, polygons: Sequence[tuple[BaseGeometry, int]], margin: float = MARGIN) -> Any: + return landcover().label_triangles( + mesh.vertices, mesh.triangles, mesh.edges, polygons=list(polygons), margin=margin + ) + + +def checked_label( + mesh: Mesh, polygons: Sequence[tuple[BaseGeometry, int]], margin: float = MARGIN +) -> Any: + """`label_triangles`, with I1 and I2 asserted on the way out.""" + labels = label(mesh, polygons, margin) + codes = np.asarray(labels.codes) + assert codes.shape == (len(mesh.triangles),) + assert interior_edge_count(mesh.triangles) > 0 + assert spread_violations(mesh.triangles, mesh.edges, codes) == [] + oracle = landcover_oracle(mesh.vertices, mesh.triangles, polygons, margin) + assert oracle.checked.any() + assert oracle.mismatches(codes) == [] + return labels + + +STEPS = [float(v) for v in range(21)] +DOMAIN = rect(0, 0, 20, 20) + + +# ------------------------------------------------------------------- regions + + +def two_triangles() -> tuple[np.ndarray, np.ndarray]: + return np.array([[0, 1, 2], [0, 2, 3]]), np.array([[0, 1], [1, 2], [2, 3], [3, 0]]) + + +def strip(cells: int) -> np.ndarray: + """`2 * cells` triangles in a row: bottom row 0..cells, top row after it.""" + top = cells + 1 + out = [] + for i in range(cells): + out += [(i, i + 1, top + i + 1), (i, top + i + 1, top + i)] + return np.array(out, dtype=np.int64) + + +class TestRegions: + def test_an_unconstrained_shared_edge_joins_two_triangles(self) -> None: + triangles, hull = two_triangles() + assert_array_equal(landcover().regions(triangles, hull), [0, 0]) + + def test_a_constrained_shared_edge_separates_them(self) -> None: + triangles, hull = two_triangles() + cut = np.vstack([hull, [[0, 2]]]) + assert_array_equal(landcover().regions(triangles, cut), [0, 1]) + + def test_a_constraint_given_backwards_still_blocks(self) -> None: + triangles, hull = two_triangles() + cut = np.vstack([hull, [[2, 0]]]) + assert_array_equal(landcover().regions(triangles, cut), [0, 1]) + + def test_no_constraints_at_all(self) -> None: + triangles, _ = two_triangles() + assert_array_equal(landcover().regions(triangles, np.zeros((0, 2), dtype=np.int64)), [0, 0]) + + @pytest.mark.parametrize("order", ["forward", "reversed", "shuffled"]) + def test_a_strip_of_1000_triangles_is_one_component(self, order: str) -> None: + """The union-find loops to a fixed point: a chain 1 000 long is one + component whatever order its triangles are listed in.""" + triangles = strip(500) + assert len(triangles) == 1000 + if order == "reversed": + triangles = triangles[::-1] + elif order == "shuffled": + triangles = triangles[np.random.default_rng(16).permutation(len(triangles))] + ids = np.asarray(landcover().regions(triangles, np.zeros((0, 2), dtype=np.int64))) + assert ids.shape == (1000,) + assert set(ids.tolist()) == {0} + + def test_ids_are_the_smallest_triangle_index_of_each_component(self) -> None: + cut = line((10, 0), (10, 20)) + mesh = grid_mesh(STEPS, STEPS, [cut]) + order = np.random.default_rng(3).permutation(len(mesh.triangles)) + triangles = mesh.triangles[order] + ids = np.asarray(landcover().regions(triangles, mesh.edges)) + west = np.asarray(shapely.contains_xy(rect(0, 0, 10, 20), *mesh.centroids[order].T)) + assert west.any() and (~west).any() + assert set(ids[west].tolist()) == {int(np.flatnonzero(west).min())} + assert set(ids[~west].tolist()) == {int(np.flatnonzero(~west).min())} + + def test_permuting_and_flipping_the_constraint_rows_changes_nothing(self) -> None: + mesh = grid_mesh(STEPS, STEPS, [rect(4, 4, 16, 16), line((0, 10), (20, 10))]) + rng = np.random.default_rng(7) + shuffled = mesh.edges[rng.permutation(len(mesh.edges))] + flipped = shuffled[:, ::-1] + first = np.asarray(landcover().regions(mesh.triangles, mesh.edges)) + assert len(set(first.tolist())) == 4 + assert_array_equal(landcover().regions(mesh.triangles, shuffled), first) + assert_array_equal(landcover().regions(mesh.triangles, flipped), first) + + +# ---------------------------------------------------------- label_triangles + + +class TestLabelBasics: + def test_codes_are_int32_one_per_triangle(self) -> None: + mesh = grid_mesh(STEPS, STEPS, [rect(4, 4, 16, 16)]) + labels = label(mesh, [(rect(4, 4, 16, 16), 311)]) + codes = np.asarray(labels.codes) + assert codes.dtype == np.int32 + assert codes.shape == (len(mesh.triangles),) + + def test_a_square_cut_by_a_constrained_diagonal(self) -> None: + """Each side of the diagonal gets its own polygon's code.""" + diagonal = line((0, 0), (20, 20)) + lower = Polygon([(X0, Y0), (X0 + 20, Y0), (X0 + 20, Y0 + 20)]) + upper = Polygon([(X0, Y0), (X0 + 20, Y0 + 20), (X0, Y0 + 20)]) + mesh = grid_mesh(STEPS, STEPS, [diagonal]) + labels = checked_label(mesh, [(lower, 311), (upper, 512)]) + codes = np.asarray(labels.codes) + assert set(codes[mesh.inside(lower)].tolist()) == {311} + assert set(codes[mesh.inside(upper)].tolist()) == {512} + assert (labels.regions, labels.outside, labels.overlapped) == (2, 0, 0) + + def test_no_polygons_is_all_zero_and_every_region_outside(self) -> None: + mesh = grid_mesh(STEPS, STEPS, [rect(4, 4, 16, 16), line((0, 10), (20, 10))]) + labels = checked_label(mesh, []) + assert set(np.asarray(labels.codes).tolist()) == {0} + assert labels.regions == 4 + assert labels.outside == labels.regions + assert labels.overlapped == 0 + + def test_a_sliver_component_is_counted_thin_and_still_labelled(self) -> None: + """A 1 mm sliver beside a big triangle, the shared edge constrained: + two components, the sliver's largest inradius about 0.5 mm <= margin.""" + vertices = np.array( + [ + [X0, Y0, 0.0], + [X0 + 100, Y0, 0.0], + [X0 + 100, Y0 + 100, 0.0], + [X0 + 100.001, Y0 + 50, 0.0], + ] + ) + triangles = np.array([[0, 1, 2], [1, 3, 2]]) + edges = np.array([[0, 1], [1, 3], [3, 2], [2, 0], [1, 2]]) + cover = rect(-10, -10, 110, 110) + labels = landcover().label_triangles( + vertices, triangles, edges, polygons=[(cover, 311)], margin=MARGIN + ) + assert_array_equal(labels.codes, [311, 311]) + assert (labels.regions, labels.thin) == (2, 1) + finer = landcover().label_triangles( + vertices, triangles, edges, polygons=[(cover, 311)], margin=1e-5 + ) + assert finer.thin == 0 + + +# ------------------------------------------------------------ the fixtures + + +class TestFixtures: + def test_two_squares_side_by_side(self) -> None: + west, east = rect(2, 4, 10, 16), rect(10, 4, 18, 16) + mesh = grid_mesh(STEPS, STEPS, [west, east]) + labels = checked_label(mesh, [(west, 311), (east, 512)]) + codes = np.asarray(labels.codes) + assert set(codes[mesh.inside(west)].tolist()) == {311} + assert set(codes[mesh.inside(east)].tolist()) == {512} + assert set(codes[~mesh.inside(west.union(east))].tolist()) == {0} + # The triangles touching the shared side carry both codes. + seam = line((10, 5), (10, 15)) + touching = ( + np.asarray( + shapely.distance(seam, shapely.polygons(mesh.vertices[mesh.triangles][:, :, :2])) + ) + == 0 + ) + assert set(codes[touching].tolist()) == {311, 512} + assert (labels.regions, labels.outside, labels.overlapped, labels.thin) == (3, 1, 0, 0) + + def test_a_forest_with_an_empty_hole(self) -> None: + hole = rect(8, 8, 12, 12) + forest = Polygon(rect(2, 2, 18, 18).exterior, [hole.exterior]) + mesh = grid_mesh(STEPS, STEPS, [forest]) + labels = checked_label(mesh, [(forest, 312)]) + codes = np.asarray(labels.codes) + assert set(codes[mesh.inside(hole)].tolist()) == {0} + assert set(codes[mesh.inside(forest)].tolist()) == {312} + assert (labels.regions, labels.outside) == (3, 2) + + def test_a_lake_filling_the_hole_of_a_holed_forest(self) -> None: + lake = rect(8, 8, 12, 12) + forest = Polygon(rect(2, 2, 18, 18).exterior, [lake.exterior]) + mesh = grid_mesh(STEPS, STEPS, [forest, lake]) + labels = checked_label(mesh, [(forest, 312), (lake, 512)]) + codes = np.asarray(labels.codes) + assert set(codes[mesh.inside(lake)].tolist()) == {512} + assert set(codes[mesh.inside(forest)].tolist()) == {312} + assert labels.overlapped == 0 + + def test_a_lake_inside_an_unholed_forest_goes_to_the_smaller(self) -> None: + """D2: the lake's component is in both polygons; the lake is smaller.""" + lake = rect(8, 8, 12, 12) + forest = rect(2, 2, 18, 18) + mesh = grid_mesh(STEPS, STEPS, [forest, lake]) + labels = checked_label(mesh, [(forest, 312), (lake, 512)]) + codes = np.asarray(labels.codes) + assert set(codes[mesh.inside(lake)].tolist()) == {512} + assert set(codes[mesh.inside(forest.difference(lake))].tolist()) == {312} + assert labels.overlapped == 1 + + def test_a_polygon_clipped_by_the_domain_into_two_pieces(self) -> None: + """A band crossing a notch cut into the domain from the north: its two + pieces are separate components, and both carry its code.""" + domain = rect(0, 0, 20, 20).difference(rect(8, 10, 12, 20)) + band = rect(4, 12, 16, 16) + mesh = grid_mesh(STEPS, STEPS, [band], domain=domain) + labels = checked_label(mesh, [(band, 324)]) + codes = np.asarray(labels.codes) + west, east = mesh.inside(rect(4, 12, 8, 16)), mesh.inside(rect(12, 12, 16, 16)) + assert west.any() and east.any() + assert set(codes[west | east].tolist()) == {324} + ids = np.asarray(landcover().regions(mesh.triangles, mesh.edges)) + assert set(ids[west].tolist()).isdisjoint(ids[east].tolist()) + assert (labels.regions, labels.outside) == (3, 1) + + def test_a_road_across_a_polygon(self) -> None: + """The road is a constraint and no polygon: both halves are the forest's.""" + forest = rect(4, 4, 16, 16) + road = line((2, 10), (18, 10)) + mesh = grid_mesh(STEPS, STEPS, [forest, road]) + labels = checked_label(mesh, [(forest, 311)]) + codes = np.asarray(labels.codes) + north, south = mesh.inside(rect(4, 10, 16, 16)), mesh.inside(rect(4, 4, 16, 10)) + assert set(codes[north | south].tolist()) == {311} + ids = np.asarray(landcover().regions(mesh.triangles, mesh.edges)) + assert set(ids[north].tolist()).isdisjoint(ids[south].tolist()) + assert (labels.regions, labels.outside) == (3, 1) + + def test_a_polygon_covering_the_domain(self) -> None: + cover = rect(-5, -5, 25, 25) + mesh = grid_mesh(STEPS, STEPS, []) + labels = checked_label(mesh, [(cover, 333)]) + assert set(np.asarray(labels.codes).tolist()) == {333} + assert (labels.regions, labels.outside, labels.overlapped) == (1, 0, 0) + + def test_boundaries_a_tenth_of_a_millimetre_apart(self) -> None: + """Two squares whose shared side is offset by 0.1 mm, less than the + snap, with nothing merged (the pure form of the CLI fixture, where + the noder may merge them): the sliver column between the two sides + opens onto the outside at both ends, so it is part of the outside + component; away from the seam each square has its own code (I2).""" + xs = [*STEPS[:11], 10.0001, *STEPS[11:]] + west, east = rect(2, 4, 10, 16), rect(10.0001, 4, 18, 16) + mesh = grid_mesh(xs, STEPS, [west, east]) + labels = checked_label(mesh, [(west, 311), (east, 512)]) + codes = np.asarray(labels.codes) + assert set(codes[mesh.inside(rect(2, 4, 9.99, 16))].tolist()) == {311} + assert set(codes[mesh.inside(rect(10.01, 4, 18, 16))].tolist()) == {512} + assert (labels.regions, labels.outside, labels.thin) == (3, 1, 0) + gap = mesh.inside(rect(10, 4.5, 10.0001, 15.5)) + assert gap.any() and set(codes[gap].tolist()) == {0} + + +class TestOverlaps: + """D2: the smallest area wins, ties to the smaller code, in any order.""" + + @pytest.mark.parametrize("reverse", [False, True]) + def test_equal_areas_go_to_the_smaller_code(self, reverse: bool) -> None: + square = rect(4, 4, 16, 16) + mesh = grid_mesh(STEPS, STEPS, [square]) + polygons = [(square, 412), (rect(4, 4, 16, 16), 322)] + labels = checked_label(mesh, polygons[::-1] if reverse else polygons) + assert set(np.asarray(labels.codes)[mesh.inside(square)].tolist()) == {322} + assert labels.overlapped == 1 + + @pytest.mark.parametrize("reverse", [False, True]) + def test_nested_polygons_give_the_inner_code(self, reverse: bool) -> None: + inner, outer = rect(6, 6, 14, 14), rect(-1, -1, 21, 21) + mesh = grid_mesh(STEPS, STEPS, [inner]) + polygons = [(outer, 312), (inner, 512)] + labels = checked_label(mesh, polygons[::-1] if reverse else polygons) + codes = np.asarray(labels.codes) + assert set(codes[mesh.inside(inner)].tolist()) == {512} + assert set(codes[~mesh.inside(inner)].tolist()) == {312} + assert (labels.regions, labels.overlapped, labels.outside) == (2, 1, 0) + + +class TestDeterminism: + """R1: the codes are a function of the triangles, the constraint edges and + the polygons as a set.""" + + @pytest.fixture + def case(self) -> tuple[Mesh, list[tuple[BaseGeometry, int]]]: + lake = rect(8, 8, 12, 12) + forest = rect(2, 2, 18, 18) + bog = rect(12, 2, 18, 6) + road = line((0, 15), (20, 15)) + mesh = grid_mesh(STEPS, STEPS, [forest, lake, bog, road]) + return mesh, [(forest, 312), (lake, 512), (bog, 412)] + + def test_the_same_call_twice_is_the_same_array(self, case: Any) -> None: + mesh, polygons = case + first, second = label(mesh, polygons), label(mesh, polygons) + assert np.asarray(first.codes).tobytes() == np.asarray(second.codes).tobytes() + + def test_permuted_triangles_edges_and_polygons_give_the_same_labels(self, case: Any) -> None: + mesh, polygons = case + base = np.asarray(label(mesh, polygons).codes) + rng = np.random.default_rng(29) + order = rng.permutation(len(mesh.triangles)) + rows = rng.permutation(len(mesh.edges)) + shuffled = Mesh(mesh.vertices, mesh.triangles[order], mesh.edges[rows][:, ::-1]) + again = label(shuffled, polygons[::-1]) + assert_array_equal(np.asarray(again.codes), base[order]) + first = label(mesh, polygons) + assert (again.regions, again.outside, again.overlapped, again.thin) == ( + first.regions, + first.outside, + first.overlapped, + first.thin, + ) + + +class TestPurity: + def test_it_imports_nothing_first_party(self) -> None: + """R1's boundary: numpy and shapely, nothing first-party, never `_core`.""" + module = landcover() + assert first_party_imports(module) == set() diff --git a/tests/python/test_palettes.py b/tests/python/test_palettes.py new file mode 100644 index 00000000..f1390646 --- /dev/null +++ b/tests/python/test_palettes.py @@ -0,0 +1,226 @@ +"""`tin_engine.palettes` and `rasputin palette`: natural colours for CORINE (16c, R4). + +`docs/increments/16c-landcover-labels.md`, R4 and "Tests for @tester". The +colours are Ola's to change (D6), so they are pinned by family, as hue, +saturation and lightness ranges, never as exact triples: forest green, heath a +lighter green than conifers, bare rock grey, peat bog brown, every water class +blue, glaciers near white and not warm. + +Pinned beyond the design's text: + +- An `Annotations` value is compared as `int(value)`, so the preset may carry + codes as strings (ParaView's own presets do) or as numbers. +- Each annotation's label contains the class's `LABEL3` name. +- `NanColor` is present and magenta (R4: "A code the table lacks draws in + `NanColor`, magenta"). + +Committed red: `tin_engine.palettes` does not exist and `rasputin palette` is +no command, so every test fails on `ModuleNotFoundError` (raised per test by +`palettes()`) or on the CLI's usage error for an unknown command. +""" + +from __future__ import annotations + +import colorsys +import importlib +import json +import re +import sqlite3 +from contextlib import closing +from pathlib import Path +from types import ModuleType +from typing import Any + +import pytest +from typer.testing import CliRunner + +from gpkg_fixtures import OLA_NORWAY +from tin_engine.cli import app + +runner = CliRunner(env={"NO_COLOR": "1", "TERM": "dumb"}) + +#: R4's "in the Norway extract" column: 34 codes. +NORWAY = frozenset( + { + *(111, 112, 121, 122, 123, 124, 131, 132, 133, 141, 142), + *(211, 222, 231, 242, 243), + *(311, 312, 313, 321, 322, 324, 331, 332, 333, 334, 335), + *(411, 412, 423), + *(511, 512, 522, 523), + } +) + +#: The 44 CLC level-3 classes (Kosztra et al. 2019). +CLC = frozenset( + { + *(111, 112, 121, 122, 123, 124, 131, 132, 133, 141, 142), + *(211, 212, 213, 221, 222, 223, 231, 241, 242, 243, 244), + *(311, 312, 313, 321, 322, 323, 324, 331, 332, 333, 334, 335), + *(411, 412, 421, 422, 423), + *(511, 512, 521, 522, 523), + } +) + +PRESET_NAME = "rasputin CORINE natural" +HEX = re.compile(r"#[0-9a-fA-F]{6}") + + +def palettes() -> ModuleType: + return importlib.import_module("tin_engine.palettes") + + +def table() -> dict[int, tuple[str, str]]: + return dict(palettes().CORINE_NATURAL) + + +def rgb(code: int) -> tuple[float, float, float]: + """The table's colour for `code`, as r, g, b in 0 .. 1.""" + text = table()[code][1] + assert HEX.fullmatch(text), text + r, g, b = (int(text[i : i + 2], 16) / 255 for i in (1, 3, 5)) + return r, g, b + + +def hls(code: int) -> tuple[float, float, float]: + """Hue in degrees, lightness and saturation (HLS), each 0 .. 1 but hue.""" + h, lightness, s = colorsys.rgb_to_hls(*rgb(code)) + return h * 360, lightness, s + + +# --------------------------------------------------------------- the table + + +class TestTheTable: + def test_every_clc_class_and_zero_has_a_colour(self) -> None: + assert set(table()) == CLC | {0} + + def test_every_norway_code_has_a_colour(self) -> None: + assert len(NORWAY) == 34 and NORWAY <= CLC and len(CLC) == 44 + assert NORWAY - set(table()) == set() + + def test_entries_are_a_name_and_a_hex_colour(self) -> None: + for code, (name, colour) in table().items(): + assert isinstance(name, str) and name, code + assert HEX.fullmatch(colour), (code, colour) + + def test_the_names_are_label3(self) -> None: + # Spot checks from `Legend/CLC_legend.csv` as R4 quotes it. + assert table()[312][0] == "Coniferous forest" + assert table()[412][0] == "Peat bogs" + assert table()[335][0] == "Glaciers and perpetual snow" + assert table()[512][0] == "Water bodies" + + def test_the_colours_are_distinct(self) -> None: + colours = [c.lower() for _, c in table().values()] + assert len(set(colours)) == len(colours) + + +class TestFamilies: + """D6: the families, by hue ranges, not by exact triple.""" + + @pytest.mark.parametrize("code", [311, 312, 313]) + def test_forests_are_green(self, code: int) -> None: + hue, _, saturation = hls(code) + assert 75 <= hue <= 165 and saturation >= 0.2, hls(code) + + def test_heath_is_green_and_lighter_than_conifers(self) -> None: + hue, lightness, saturation = hls(322) + assert 60 <= hue <= 150 and saturation >= 0.15, hls(322) + assert lightness > hls(312)[1] + + def test_bare_rock_is_grey(self) -> None: + _, lightness, saturation = hls(332) + assert saturation <= 0.08 and 0.25 <= lightness <= 0.75, hls(332) + + def test_peat_bogs_are_brown(self) -> None: + hue, lightness, saturation = hls(412) + assert 15 <= hue <= 50 and saturation >= 0.2 and lightness <= 0.55, hls(412) + + @pytest.mark.parametrize("code", sorted(c for c in CLC if c // 100 == 5)) + def test_water_is_blue(self, code: int) -> None: + hue, _, saturation = hls(code) + assert 190 <= hue <= 235 and saturation >= 0.25, hls(code) + + def test_glaciers_are_near_white_and_not_warm(self) -> None: + r, _, b = rgb(335) + _, lightness, _ = hls(335) + assert lightness >= 0.9 and b >= r, rgb(335) + + def test_the_official_legend_is_not_used_for_bogs_or_the_sea(self) -> None: + """Direction 2: the official legend paints peat bogs blue (077-077-255) + and the sea almost white (230-242-255).""" + assert table()[412][1].lower() != "#4d4dff" + assert table()[523][1].lower() != "#e6f2ff" + + +# ---------------------------------------------------------- the preset + + +class TestPreset: + def preset(self) -> dict[str, Any]: + """Not a fixture, so a missing module fails the test, not its setup.""" + out = palettes().paraview_preset(palettes().CORINE_NATURAL, "a name") + assert isinstance(out, list) and len(out) == 1 + assert isinstance(out[0], dict) + return out[0] + + def test_it_carries_the_name(self) -> None: + preset = self.preset() + assert preset["Name"] == "a name" + + def test_annotations_and_colours_have_one_entry_per_code(self) -> None: + preset = self.preset() + entries = len(table()) + assert len(preset["Annotations"]) == 2 * entries + assert len(preset["IndexedColors"]) == 3 * entries + assert all(0.0 <= float(c) <= 1.0 for c in preset["IndexedColors"]) + + def test_they_are_in_the_same_order(self) -> None: + preset = self.preset() + values = [int(v) for v in preset["Annotations"][0::2]] + labels = [str(v) for v in preset["Annotations"][1::2]] + colours = preset["IndexedColors"] + assert sorted(values) == sorted(table()) + for k, code in enumerate(values): + assert table()[code][0] in labels[k], (code, labels[k]) + got = [float(c) for c in colours[3 * k : 3 * k + 3]] + assert got == pytest.approx(rgb(code), abs=1e-6), code + + def test_the_nan_colour_is_magenta(self) -> None: + preset = self.preset() + r, g, b = (float(c) for c in preset["NanColor"]) + assert r >= 0.8 and b >= 0.8 and g <= 0.3, preset["NanColor"] + + def test_it_is_json(self) -> None: + preset = self.preset() + assert json.loads(json.dumps(preset)) == preset + + +# ------------------------------------------------------------- the command + + +class TestCommand: + def test_stdout_is_the_preset(self) -> None: + result = runner.invoke(app, ["palette", "corine"]) + assert result.exit_code == 0, result.output + expected = palettes().paraview_preset(palettes().CORINE_NATURAL, PRESET_NAME) + assert json.loads(result.stdout) == expected + + def test_an_unknown_name_is_a_usage_error_listing_the_known(self) -> None: + result = runner.invoke(app, ["palette", "nosuch"]) + assert result.exit_code == 2, result.output + assert "nosuch" in result.output and "corine" in result.output + assert "No such command" not in result.output + + +# --------------------------------------------------------- Ola's local data + + +@pytest.mark.skipif(not OLA_NORWAY.exists(), reason=f"{OLA_NORWAY} is not here") +def test_every_code_in_olas_norway_extract_has_a_colour() -> None: + """R4's query, rerun: every `code_18` in Ola's Norway GeoPackage.""" + uri = Path(OLA_NORWAY).resolve().as_uri() + "?mode=ro" + with closing(sqlite3.connect(uri, uri=True)) as con: + codes = {int(c) for (c,) in con.execute("SELECT DISTINCT code_18 FROM corine2018")} + assert codes == NORWAY + assert codes <= set(table()) From 0487ed0effba5e7c17518f4d4f11fe457c6edee0 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 03:21:08 +0200 Subject: [PATCH 3/7] green: 16c a land-cover class per triangle, natural colours Makes the red suite of 196147e pass; no test file touched. - landcover.py: regions (edge keys, np.isin, array union-find to a fixed point) and label_triangles (largest-inradius incentre per component, STRtree lookup, smallest area then smaller code), CoverLabels counts. - palettes.py: CORINE_NATURAL (44 CLC classes and 0) and paraview_preset. - feature_input.py: ClassMap.codes; TerrainFeature.code and .polygon for a coded map; codes refused outside 1 .. 2**31 - 1; a coded polygon with no boundary in the domain kept with no lines when it covers it. - io/vtk_legacy.py: triangle_codes -> cell array land_cover_code (0 on lines) in the features FIELD block; land_cover_codes joins RESERVED and its text arrives as the new parameter land_cover_codes. - io/ply.py: face_codes -> face property int land_cover_code; the comment comes from the CLI through comments. - cli.py: the land cover phase and stderr line, the codes to both writers, the palette command, one help sentence. Co-Authored-By: Claude Opus 5.5 --- src_python/tin_engine/cli.py | 64 +++++++++++- src_python/tin_engine/feature_input.py | 42 +++++++- src_python/tin_engine/io/ply.py | 41 ++++++-- src_python/tin_engine/io/vtk_legacy.py | 34 ++++++- src_python/tin_engine/landcover.py | 133 +++++++++++++++++++++++++ src_python/tin_engine/palettes.py | 86 ++++++++++++++++ 6 files changed, 382 insertions(+), 18 deletions(-) create mode 100644 src_python/tin_engine/landcover.py create mode 100644 src_python/tin_engine/palettes.py diff --git a/src_python/tin_engine/cli.py b/src_python/tin_engine/cli.py index 0dbb0d38..70129b01 100644 --- a/src_python/tin_engine/cli.py +++ b/src_python/tin_engine/cli.py @@ -40,6 +40,7 @@ from __future__ import annotations import importlib.metadata +import json import math import os import shlex @@ -75,6 +76,7 @@ from tin_engine.elevation import Trimmed, trim from tin_engine.feature_input import ( CLASS_MAPS, + ClassMap, FeatureError, FeatureRequest, FeatureSet, @@ -86,7 +88,9 @@ from tin_engine.io.models import DemTile, RasterMeta from tin_engine.io.ply import write_ply from tin_engine.io.vtk_legacy import write_vtk +from tin_engine.landcover import label_triangles from tin_engine.mosaic import Bounds, Seam +from tin_engine.palettes import PALETTES, paraview_preset from tin_engine.raster import to_core from tin_engine.stats import PhaseClock, Refinement, Report, Sizes, _exact, quality, render from tin_engine.viz.fixtures import GALLERY, Fixture @@ -696,9 +700,16 @@ def mesh( ``--stats`` (increment 17) adds a report and changes nothing else: the clock always runs, the quality pass and the report only with the flag. + + A ``--features-map`` with class codes (``corine``, ``corine-water``, + ``clc18_kode``) also gives every triangle its polygon's code, in the cell + array ``land_cover_code`` of a ``.vtk`` or the face property of a ``.ply`` + (increment 16c); ``rasputin palette corine`` writes natural colours for it. """ clock = PhaseClock() dem_run: _DemMesh | None = None + codes: npt.NDArray[np.int32] | None = None + codes_text = "" seams: tuple[Seam, ...] = () if (name is None) == (not dem): raise typer.BadParameter( @@ -839,6 +850,9 @@ def mesh( if notice: fields.append(("features_notice", notice)) comments.append(f"features_notice {notice}") + cmap = CLASS_MAPS[features_map or "property"] + if cmap.codes: + codes, codes_text = _land_cover(dem_run.trimmed, found, cmap, snap_spacing, clock) else: assert name is not None if stride is not None: @@ -898,12 +912,18 @@ def mesh( vocabulary=DEFAULT_VOCABULARY, fields=fields, binary=binary, + triangle_codes=codes, + land_cover_codes=codes_text, ) ] else: encoders = [ lambda: write_ply( - vertices, faces=surface_mesh.triangles, ascii=not binary, comments=comments + vertices, + faces=surface_mesh.triangles, + ascii=not binary, + comments=[*comments, *([f"land_cover_codes {codes_text}"] if codes_text else [])], + face_codes=codes, ), lambda: write_ply( vertices, @@ -924,6 +944,48 @@ def mesh( _write_report(clock, report_target, surface_mesh, dem_run, targets, seams) +def _land_cover( + trimmed: Trimmed, found: FeatureSet, cmap: ClassMap, spacing: float, clock: PhaseClock +) -> tuple[npt.NDArray[np.int32], str]: + """16c, R1-R3: a code per triangle from the coded polygons, timed as the + phase ``land cover``, with its stderr line; the codes and their text.""" + polygons = [(f.polygon, f.code) for f in found.features if f.polygon is not None and f.code] + with clock.phase("land cover"): + mesh = (trimmed.vertices, trimmed.triangles, trimmed.edges) + labels = label_triangles(*mesh, polygons=polygons, margin=2 * spacing) + typer.echo( + f"land cover: {labels.regions} regions, {labels.outside} outside every polygon, " + f"{labels.overlapped} in more than one, {labels.thin} thinner than the snap", + err=True, + ) + text = f"{cmap.codes}, attribute {cmap.attribute}, map {cmap.name}; " + return labels.codes, text + "0 = in no polygon, and every constraint line" + + +@app.command() +def palette( + name: Annotated[str, typer.Argument(help=f"The palette: {', '.join(PALETTES)}.")], + out: Annotated[ + Path | None, typer.Option("--out", help="Write the preset here, not to stdout.") + ] = None, +) -> None: + """Write a ParaView colour preset for ``land_cover_code`` (increment 16c). + + Import it in ParaView's Colour Map Editor (Choose Preset, Import), then + colour by ``land_cover_code``; tick Interpret Values As Categories if the + preset does not. + """ + if name not in PALETTES: + raise typer.BadParameter(f"unknown palette {name}; use {', '.join(PALETTES)}") + table, title = PALETTES[name] + text = json.dumps(paraview_preset(table, title), indent=1) + "\n" + if out is None: + typer.echo(text, nl=False) + return + out.write_text(text) + typer.echo(f"{out}") + + def _report_target( stats: str | None, out_parent: Path | None, label: str, meshes: list[Path] ) -> Path | None: diff --git a/src_python/tin_engine/feature_input.py b/src_python/tin_engine/feature_input.py index c72cc34f..54a21beb 100644 --- a/src_python/tin_engine/feature_input.py +++ b/src_python/tin_engine/feature_input.py @@ -36,7 +36,7 @@ import shapely from pydantic import BaseModel, ConfigDict from pyproj import CRS -from shapely.geometry import LineString, Polygon, shape +from shapely.geometry import LineString, MultiPolygon, Polygon, shape from shapely.geometry.base import BaseGeometry from tin_engine.crs import parse_crs, reprojector, transform_definition @@ -55,6 +55,8 @@ #: WGS 84's smallest and largest radii of curvature, metres (R5, "Long edges"). R_MIN, R_MAX = 6_335_439.0, 6_399_594.0 WATER_CODES = ("511", "512", "521", "522", "523") +#: A map whose values are integer class codes names its code system (16c, R5). +CORINE_CODES = "CORINE Land Cover level-3 code" CORINE_NOTICE = ( "Contains modified CORINE Land Cover 2018 data (version 2020_20u1), (c) European Union, " "Copernicus Land Monitoring Service 2018, European Environment Agency (EEA): clipped and " @@ -70,7 +72,9 @@ class FeatureError(ValueError): class ClassMap(BaseModel): """An attribute's values to vocabulary names (R4). ``otherwise`` is what an - unlisted value gets: names, ``"drop"`` (no constraint) or ``"refuse"``.""" + unlisted value gets: names, ``"drop"`` (no constraint) or ``"refuse"``. + ``codes``, when not empty, names the code system the values are integer + codes of; such a map labels triangles (16c, R5).""" model_config = ConfigDict(frozen=True, extra="forbid") @@ -79,6 +83,7 @@ class ClassMap(BaseModel): classes: Mapping[str, tuple[str, ...]] otherwise: Literal["refuse", "drop"] | tuple[str, ...] = "refuse" notice: str = "" + codes: str = "" def _corine(name: str, attribute: str) -> ClassMap: @@ -89,6 +94,7 @@ def _corine(name: str, attribute: str) -> ClassMap: classes=water, otherwise=("land_cover",), notice=CORINE_NOTICE, + codes=CORINE_CODES, ) @@ -105,6 +111,7 @@ def _corine(name: str, attribute: str) -> ClassMap: classes=dict.fromkeys(WATER_CODES, ("water",)), otherwise="drop", notice=CORINE_NOTICE, + codes=CORINE_CODES, ), "clc18_kode": _corine("clc18_kode", "clc18_kode"), } @@ -128,13 +135,17 @@ class FeatureRequest(BaseModel): class TerrainFeature(BaseModel): """One feature: its fid, mask, and clipped lines in the DEM's CRS; a - closed line (first point repeated) is an unclipped ring.""" + closed line (first point repeated) is an unclipped ring. Under a coded + map (16c, R5) it also keeps its class ``code`` and, if polygonal, its + ``polygon`` moved into the DEM's CRS by the transform its lines took.""" model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True) fid: int | str mask: int lines: tuple[LineString, ...] + code: int | None = None + polygon: Polygon | MultiPolygon | None = None class FeatureSet(BaseModel): @@ -333,18 +344,23 @@ def _take(self, source: FeatureSource, own: str, rows: Iterable[tuple[Any, Any, mask = _mask(cmap, self.vocabulary, value, f"{name}: feature {fid}") if mask is None: continue - moved = None + code = _code(value, f"{name}: feature {fid}") if cmap.codes else None + polygonal = code is not None and geometry.geom_type in ("Polygon", "MultiPolygon") + moved = polygon = None if move is not None and bound is not None and bound[1](geometry): # Pre-clipped where its edges are straight, then moved (R5). chains = pre_clip(geometry, bound[2], bound[0]) kept = [shapely.transform(c, move) for c in chains] + polygon = shapely.transform(geometry, move) if polygonal else None else: # moved first, then pre-clipped exactly in the DEM's CRS moved = shapely.transform(geometry, move) if move is not None else geometry kept = [moved] + polygon = moved if polygonal else None if not all(_finite(g) for g in kept): raise FeatureError(f"{name}: feature {fid} has a vertex with no image in the DEM") if moved is not None: kept = list(pre_clip(moved, region)) + coded: dict[str, Any] = {"code": code, "polygon": polygon} lines = tuple( piece for line in kept @@ -352,9 +368,13 @@ def _take(self, source: FeatureSource, own: str, rows: Iterable[tuple[Any, Any, if isinstance(piece, LineString) and piece.length > 0 ) if lines: - self.features.append(TerrainFeature(fid=fid, mask=mask, lines=lines)) + self.features.append(TerrainFeature(fid=fid, mask=mask, lines=lines, **coded)) # A dropped edge lies outside, so a pre-clipped chain is not covered. self.clipped += not all(self.domain.polygon.covers(g) for g in kept) + elif polygon is not None and polygon.intersects(self.domain.polygon.point_on_surface()): + # No boundary crosses the domain, and a point of it is inside: it + # covers the domain, and labels it (R5). Kept, not clipped. + self.features.append(TerrainFeature(fid=fid, mask=mask, lines=(), **coded)) else: self.outside += 1 self.seconds += time.perf_counter() - t0 @@ -364,6 +384,18 @@ def _finite(geometry: BaseGeometry) -> bool: return bool(np.isfinite(shapely.get_coordinates(geometry)).all()) +def _code(value: Any, what: str) -> int: + """R5: a class code is an integer in 1 .. 2**31 - 1; anything else is refused.""" + try: + code = int(str(value)) if not isinstance(value, list) else 0 + except ValueError: + code = 0 + if not 1 <= code < 2**31: + shown = value[0] if isinstance(value, list) and value else value + raise FeatureError(f"{what}: {shown!r} is not a class code in 1 .. 2**31 - 1") + return code + + def _mask(cmap: ClassMap, vocabulary: EdgeVocabulary, value: Any, what: str) -> int | None: """R4: the mask ``value`` maps to, None for ``drop``; a list is a union.""" names: list[str] = [] diff --git a/src_python/tin_engine/io/ply.py b/src_python/tin_engine/io/ply.py index fa49b630..9fee4f74 100644 --- a/src_python/tin_engine/io/ply.py +++ b/src_python/tin_engine/io/ply.py @@ -61,6 +61,7 @@ def write_ply( ascii: bool = True, comments: Sequence[str] = (), vocabulary: EdgeVocabulary | None = None, + face_codes: npt.ArrayLike | None = None, ) -> bytes: """Encode one mesh as a PLY file. @@ -75,6 +76,9 @@ def write_ply( `feature_bit ` comments sorted by bit and a `feature_vocabulary ` comment (increment 13, ruling 9), after `comments`. + face_codes: `(T,)` land-cover codes, written as the face property + `int land_cover_code` after `vertex_indices` (increment 16c, R3). + The comment naming the code system is the caller's, in `comments`. Raises: ValueError: if `vertices` is not `(N, 3)`; if `faces` and `edges` are @@ -83,7 +87,8 @@ def write_ply( contains a control character, any of which forges a header line in a line-oriented format; if a comment is not ASCII, which the header's encoding cannot carry; or if a mask carries a bit - `vocabulary` does not name. + `vocabulary` does not name; if `face_codes` is given with `edges`, + has not one entry per face, or does not fit int32. """ points = np.ascontiguousarray(vertices, dtype=" bytes: return _lines(" ".join(repr(float(c)) for c in point) for point in points) -def _face_block(faces: npt.NDArray[np.uint32], ascii: bool) -> tuple[list[str], bytes]: - """The 2D mesh's block: one variable-length index list per triangle.""" +def _face_block( + faces: npt.NDArray[np.uint32], codes: npt.ArrayLike | None, ascii: bool +) -> tuple[list[str], bytes]: + """The 2D mesh's block: one variable-length index list per triangle, and + optionally each triangle's land-cover code after it (increment 16c, R3).""" + faces = faces.reshape(-1, 3) declaration = [ f"element face {len(faces)}", f"property list uchar {_INDEX} vertex_indices", ] + columns = [np.full(len(faces), 3), faces] + layout: list[tuple[str, str] | tuple[str, str, int]] = [("n", "u1"), ("v", "= 2**31): + raise ValueError("face_codes must fit int32") + declaration.append("property int land_cover_code") + columns.append(values) + layout.append(("c", " bytes: """Encode one mesh and its constraint edges as legacy VTK 4.2 PolyData. @@ -71,12 +80,18 @@ def write_vtk( vocabulary: what each bit of a mask means. fields: extra dataset strings, such as `("crs", "EPSG:25833")`. binary: write packed big-endian records instead of text. + triangle_codes: `(T,)` land-cover codes (increment 16c, R3), written + as the `int` cell array `land_cover_code`, 0 on every line. + land_cover_codes: what the codes are, written as the dataset string + `land_cover_codes` when non-empty and `triangle_codes` is given. Raises: ValueError: if `vertices` is not `(N, 3)`; if `edge_masks` does not have one entry per edge; if a mask carries a bit the vocabulary does not name; if a field name is outside `^[a-z][a-z0-9_]*$` or - reserved; or if a string is not ASCII or has a control character. + reserved; if a string is not ASCII or has a control character; or + if `triangle_codes` has not one entry per triangle, does not fit + int32, or comes with a vocabulary naming `land_cover_code`. """ points = np.asarray(vertices, dtype=np.float64) if points.ndim != 2 or points.shape[1] != 3: @@ -91,6 +106,17 @@ def write_vtk( raise ValueError(f"field name {name!r} is reserved or not ^[a-z][a-z0-9_]*$") table = sorted((prop.bit, prop.name) for prop in vocabulary.properties) + codes = None + if triangle_codes is not None: + codes = np.asarray(triangle_codes).reshape(-1) + if len(codes) != len(polygons): + raise ValueError(f"triangle_codes has {len(codes)} entries, {len(polygons)} triangles") + if len(codes) and (codes.min() < -(2**31) or codes.max() >= 2**31): + raise ValueError("triangle_codes must fit int32") + if any(name == LAND_COVER_CODE for _, name in table): + raise ValueError(f"the vocabulary names {LAND_COVER_CODE!r}, the codes' array") + if land_cover_codes: + fields = (*fields, ("land_cover_codes", land_cover_codes)) used = {name for mask in np.unique(masks) for name in vocabulary.names(int(mask))} cell_masks = np.concatenate([masks, np.zeros(len(polygons), dtype=np.uint32)]) @@ -105,6 +131,10 @@ def write_vtk( for bit, name in table if name in used ] + if codes is not None: + # Ruling 5: every cell array covers every cell; the lines get 0. + cell_codes = np.concatenate([np.zeros(len(lines), dtype=np.int64), codes]) + features.append(_numeric(LAND_COVER_CODE, cell_codes, "int", binary)) out = [ b"# vtk DataFile Version 4.2\nrasputin mesh\n", diff --git a/src_python/tin_engine/landcover.py b/src_python/tin_engine/landcover.py new file mode 100644 index 00000000..b37d9b17 --- /dev/null +++ b/src_python/tin_engine/landcover.py @@ -0,0 +1,133 @@ +"""A land-cover class per triangle: components first, one point per component. + +Increment 16c (`docs/increments/16c-landcover-labels.md`, R1). Pure: numpy and +shapely in, arrays and counts out; nothing first-party, never `_core`, never a +path. + +The triangles are split into connected components across every edge that is +not a constraint edge (the spread of Shewchuk's Triangle `-A`, blocked by +segments). Each component is then looked up once, at the incentre of its +triangle with the largest inradius: that point is at least `r` from every +constraint edge, so when `r` exceeds `margin` (which bounds how far the noder +moved an input boundary) it lies strictly on one side of every input +boundary. A component whose largest `r` is at most `margin` is still labelled +by its point, and counted `thin`. + +Overlaps (Default D2): the polygon of smallest area wins, ties to the smaller +code, so the result does not depend on the polygons' order. +""" + +from __future__ import annotations + +from collections.abc import Sequence +from dataclasses import dataclass + +import numpy as np +import numpy.typing as npt +import shapely +from shapely.geometry.base import BaseGeometry + + +@dataclass(frozen=True) +class CoverLabels: + """The codes, and the counts of R3's stderr line.""" + + codes: npt.NDArray[np.int32] + regions: int + outside: int + overlapped: int + thin: int + + +def regions(triangles: npt.ArrayLike, edges: npt.ArrayLike) -> npt.NDArray[np.int64]: + """Each triangle's component across unconstrained edges, as the smallest + triangle index in that component.""" + tri = np.asarray(triangles, dtype=np.int64).reshape(-1, 3) + cut = np.asarray(edges, dtype=np.int64).reshape(-1, 2) + n = int(max(tri.max(initial=-1), cut.max(initial=-1))) + 1 + sides = np.concatenate([tri[:, [0, 1]], tri[:, [1, 2]], tri[:, [2, 0]]]) + keys = _keys(sides, n) + owner = np.tile(np.arange(len(tri), dtype=np.int64), 3) + free = ~np.isin(keys, _keys(cut, n)) + order = np.argsort(keys[free], kind="stable") + keys, owner = keys[free][order], owner[free][order] + # A manifold mesh: an interior edge's key appears exactly twice. + pair = np.flatnonzero(keys[1:] == keys[:-1]) + u, v = owner[pair], owner[pair + 1] + parent = np.arange(len(tri), dtype=np.int64) + while True: + ru, rv = parent[u], parent[v] + if np.array_equal(ru, rv): + return parent + # Hook the larger root onto the smaller: labels only fall, no cycle. + np.minimum.at(parent, np.maximum(ru, rv), np.minimum(ru, rv)) + while not np.array_equal(parent, jumped := parent[parent]): + parent = jumped + + +def label_triangles( + vertices: npt.ArrayLike, + triangles: npt.ArrayLike, + edges: npt.ArrayLike, + *, + polygons: Sequence[tuple[BaseGeometry, int]], + margin: float, +) -> CoverLabels: + """A code per triangle from `(polygon, code)` pairs; 0 in no polygon. + + Args: + vertices: `(N, 2)` or `(N, 3)`; only x and y are used. + triangles: `(T, 3)` vertex indices. + edges: `(E, 2)` constraint edges; they block the spread. + polygons: the coded polygons, in the vertices' CRS. + margin: how far a constraint edge may lie from its input boundary. + """ + xy = np.asarray(vertices, dtype=np.float64)[:, :2] + tri = np.asarray(triangles, dtype=np.int64).reshape(-1, 3) + ids = regions(tri, edges) + a, b, c = xy[tri[:, 0]], xy[tri[:, 1]], xy[tri[:, 2]] + la, lb, lc = (np.hypot(*(q - p).T) for p, q in ((b, c), (c, a), (a, b))) + perimeter = la + lb + lc + cross = (b[:, 0] - a[:, 0]) * (c[:, 1] - a[:, 1]) - (c[:, 0] - a[:, 0]) * (b[:, 1] - a[:, 1]) + safe = np.where(perimeter > 0, perimeter, 1.0) + r = np.where(perimeter > 0, np.abs(cross) / safe, 0.0) + centre = (la[:, None] * a + lb[:, None] * b + lc[:, None] * c) / safe[:, None] + # Per component, the triangle of largest r, ties to the lowest index. + order = np.lexsort((np.arange(len(tri)), -r, ids)) + best = order[np.unique(ids[order], return_index=True)[1]] + comp_codes, hits = _lookup(centre[best], polygons) + by_id = np.zeros(len(tri), dtype=np.int32) + by_id[ids[best]] = comp_codes + codes = by_id[ids] + return CoverLabels( + codes=codes, + regions=len(best), + outside=int((hits == 0).sum()), + overlapped=int((hits > 1).sum()), + thin=int((r[best] <= margin).sum()), + ) + + +def _keys(pairs: npt.NDArray[np.int64], n: int) -> npt.NDArray[np.int64]: + """Undirected edge keys, `min * n + max`.""" + return np.minimum(pairs[:, 0], pairs[:, 1]) * n + np.maximum(pairs[:, 0], pairs[:, 1]) + + +def _lookup( + points: npt.NDArray[np.float64], polygons: Sequence[tuple[BaseGeometry, int]] +) -> tuple[npt.NDArray[np.int64], npt.NDArray[np.int64]]: + """Each point's code by D2, and how many polygons it lies in.""" + codes = np.zeros(len(points), dtype=np.int64) + hits = np.zeros(len(points), dtype=np.int64) + if not polygons or not len(points): + return codes, hits + tree = shapely.STRtree([p for p, _ in polygons]) + found, which = tree.query(shapely.points(points), predicate="intersects") + rank = np.array([(p.area, code) for p, code in polygons], dtype=[("a", "f8"), ("c", "i8")]) + # Sort the hits by point, then area, then code: each point's first hit wins. + order = np.lexsort((rank["c"][which], rank["a"][which], found)) + found, which = found[order], which[order] + np.add.at(hits, found, 1) + head = np.unique(found, return_index=True)[1] + codes[found[head]] = rank["c"][which[head]] + return codes, hits diff --git a/src_python/tin_engine/palettes.py b/src_python/tin_engine/palettes.py new file mode 100644 index 00000000..c56250f9 --- /dev/null +++ b/src_python/tin_engine/palettes.py @@ -0,0 +1,86 @@ +"""Natural colours for land-cover codes, and a ParaView preset from them. + +Increment 16c (`docs/increments/16c-landcover-labels.md`, R4). Data plus one +function; nothing first-party, nothing about meshes. The colours follow the +natural-colour convention of Patterson and Kelso (2004): the colour the ground +has from above, not the official CLC legend (which paints peat bogs blue and +the sea almost white). They are taste, and Ola's to change (Default D6). +""" + +from __future__ import annotations + +#: CORINE level-3 code -> (CLC `LABEL3`, `#rrggbb`); 0 is no polygon, and +#: every constraint line in a `.vtk`. +# fmt: off +CORINE_NATURAL: dict[int, tuple[str, str]] = { + 0: ("No polygon; constraint lines", "#3c3c3c"), + 111: ("Continuous urban fabric", "#8c5f5a"), + 112: ("Discontinuous urban fabric", "#a88d86"), + 121: ("Industrial or commercial units", "#8e8a96"), + 122: ("Road and rail networks and associated land", "#6e6e6e"), + 123: ("Port areas", "#7d8796"), + 124: ("Airports", "#a8a8a8"), + 131: ("Mineral extraction sites", "#b49b78"), + 132: ("Dump sites", "#8a7d64"), + 133: ("Construction sites", "#bcae98"), + 141: ("Green urban areas", "#86b86e"), + 142: ("Sport and leisure facilities", "#a3cf7e"), + 211: ("Non-irrigated arable land", "#e6d58c"), + 212: ("Permanently irrigated land", "#d9cc6e"), + 213: ("Rice fields", "#cdd89a"), + 221: ("Vineyards", "#9c7a44"), + 222: ("Fruit trees and berry plantations", "#a9ad5e"), + 223: ("Olive groves", "#8f9a52"), + 231: ("Pastures", "#b4d47a"), + 241: ("Annual crops associated with permanent crops", "#dccf94"), + 242: ("Complex cultivation patterns", "#d2c47c"), + 243: ("Land principally occupied by agriculture, with significant areas of natural vegetation", "#bcc47e"), # noqa: E501 + 244: ("Agro-forestry areas", "#a9b574"), + 311: ("Broad-leaved forest", "#4f8f3f"), + 312: ("Coniferous forest", "#1f5a2e"), + 313: ("Mixed forest", "#357438"), + 321: ("Natural grasslands", "#c2d68a"), + 322: ("Moors and heathland", "#a7c47f"), + 323: ("Sclerophyllous vegetation", "#8a9658"), + 324: ("Transitional woodland-shrub", "#7ea65a"), + 331: ("Beaches, dunes, sands", "#e9ddb2"), + 332: ("Bare rocks", "#8f8f8f"), + 333: ("Sparsely vegetated areas", "#b9b8a0"), + 334: ("Burnt areas", "#4b3f3a"), + 335: ("Glaciers and perpetual snow", "#eef6fb"), + 411: ("Inland marshes", "#6e9470"), + 412: ("Peat bogs", "#8a6642"), + 421: ("Salt marshes", "#7f9f8c"), + 422: ("Salines", "#d8d6cc"), + 423: ("Intertidal flats", "#b3bdb3"), + 511: ("Water courses", "#4c8ec4"), + 512: ("Water bodies", "#3e7bb6"), + 521: ("Coastal lagoons", "#5b93b3"), + 522: ("Estuaries", "#5188b4"), + 523: ("Sea and ocean", "#2b5d8e"), +} +# fmt: on + +#: The palettes `rasputin palette` knows, by name, with their preset names. +PALETTES = {"corine": (CORINE_NATURAL, "rasputin CORINE natural")} + +#: A code the table lacks draws magenta, so it is seen rather than blended in. +_NAN_COLOR = [1.0, 0.0, 1.0] + + +def paraview_preset(table: dict[int, tuple[str, str]], name: str) -> list[dict[str, object]]: + """A categorical ParaView colour preset: `Annotations` (value, label, ...) + and `IndexedColors` (r, g, b in 0 .. 1, one triple per value, in order).""" + annotations: list[str] = [] + colours: list[float] = [] + for code, (label, colour) in sorted(table.items()): + annotations += [str(code), f"{code} {label}"] + colours += [int(colour[i : i + 2], 16) / 255 for i in (1, 3, 5)] + return [ + { + "Name": name, + "Annotations": annotations, + "IndexedColors": colours, + "NanColor": _NAN_COLOR, + } + ] From 6c98cd58fb6a4aefcc075cda085814f3e82280d1 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 03:26:36 +0200 Subject: [PATCH 4/7] 16c review fixes (tests): red-step prose past tense; plain imports Co-Authored-By: Claude Opus 5.5 --- tests/python/test_cli_mesh_features.py | 3 +- tests/python/test_cli_mesh_landcover.py | 13 ++++---- tests/python/test_io_ply.py | 4 +-- tests/python/test_io_vtk_legacy.py | 7 ++-- tests/python/test_landcover.py | 43 +++++++++++-------------- tests/python/test_palettes.py | 21 +++++------- 6 files changed, 41 insertions(+), 50 deletions(-) diff --git a/tests/python/test_cli_mesh_features.py b/tests/python/test_cli_mesh_features.py index dcaf843b..a67f8016 100644 --- a/tests/python/test_cli_mesh_features.py +++ b/tests/python/test_cli_mesh_features.py @@ -594,7 +594,8 @@ class TestCommittedExtract: about 54 840 triangles). I7: the tolerance holds, every node covered. Increment 16c adds I1, I2, I3 and I5 on the same run (the `test_16c_` - tests, committed red: the file had no `land_cover_code`); its I4 is + tests, committed red at `196147e`: the file had no `land_cover_code`; + green since `0487ed0`); its I4 is `test_i3_rows_in_reverse_order_give_the_same_file`, unchanged.""" @pytest.fixture(scope="class") diff --git a/tests/python/test_cli_mesh_landcover.py b/tests/python/test_cli_mesh_landcover.py index 8690c70e..65dee2c8 100644 --- a/tests/python/test_cli_mesh_landcover.py +++ b/tests/python/test_cli_mesh_landcover.py @@ -20,10 +20,12 @@ - A `LineString` under a coded map keeps its code and has no polygon. - `rasputin mesh --help` names `land_cover_code` and `rasputin palette`. -Committed red: no code writes `land_cover_code`, `land_cover_codes` or the -stderr line; `rasputin palette` is no command; `ClassMap` has no `codes` and -`TerrainFeature` no `code` or `polygon`; a covering polygon is dropped as -outside; and `corine` accepts any value. Every test fails on one of those. +Committed red at `196147e`: no code wrote `land_cover_code`, +`land_cover_codes` or the stderr line; `rasputin palette` was no command; +`ClassMap` had no `codes` and `TerrainFeature` no `code` or `polygon`; a +covering polygon was dropped as outside; and `corine` accepted any value. +Every test failed on one of those. They landed in `0487ed0` and the suite has +been green since. """ from __future__ import annotations @@ -51,6 +53,7 @@ from test_cli_mesh_dem import USAGE, invoke, write_tiff from test_cli_mesh_domain import COLS, ROWS, SQUARE, geojson from tin_engine.cli import app +from tin_engine.palettes import CORINE_NATURAL, paraview_preset from vtkread import VtkFile, read_vtk runner = CliRunner(env={"NO_COLOR": "1", "TERM": "dumb"}) @@ -463,8 +466,6 @@ def test_vtk_reads_the_array_back(tmp_path: Path, bumpy: Path, plain_square: Pat def test_palette_out_writes_the_preset(tmp_path: Path) -> None: """R4: `rasputin palette corine --out FILE` writes the ParaView preset.""" - from tin_engine.palettes import CORINE_NATURAL, paraview_preset - target = tmp_path / "corine_natural.json" result = runner.invoke(app, ["palette", "corine", "--out", str(target)]) assert result.exit_code == 0, result.output diff --git a/tests/python/test_io_ply.py b/tests/python/test_io_ply.py index 4c096e0a..c09dcef6 100644 --- a/tests/python/test_io_ply.py +++ b/tests/python/test_io_ply.py @@ -448,8 +448,8 @@ class TestFaceCodes: The header comment `land_cover_codes ...` is the caller's (it arrives in `comments`), and `test_cli_mesh_landcover.py` pins it in the CLI's file. - Committed red: `face_codes` is no parameter of `write_ply`, so every call - with it fails on `TypeError`. + Committed red at `196147e`: `face_codes` was no parameter of `write_ply`, + so every call with it failed on `TypeError`. Green since `0487ed0`. """ CODES = np.array([311, 2**31 - 1], dtype=np.int64) diff --git a/tests/python/test_io_vtk_legacy.py b/tests/python/test_io_vtk_legacy.py index cc77a78d..d4d08398 100644 --- a/tests/python/test_io_vtk_legacy.py +++ b/tests/python/test_io_vtk_legacy.py @@ -533,15 +533,14 @@ class TestLandCoverCode: and the design names no parameter for it; `test_cli_mesh_landcover.py` pins the string in the file. - Committed red: `triangle_codes` is no parameter of `write_vtk`, so every - call with it fails on `TypeError`, and `land_cover_codes` is not - reserved. + Committed red at `196147e`: `triangle_codes` was no parameter of + `write_vtk`, so every call with it failed on `TypeError`, and + `land_cover_codes` was not reserved. Green since `0487ed0`. """ CODES = np.array([311, 0, 512, 2**31 - 1], dtype=np.int64) def coded(self, binary: bool) -> VtkFile: - """Not a fixture, so a missing parameter fails the test, not its setup.""" return read_vtk(write(triangle_codes=self.CODES, binary=binary)) def test_lines_carry_0_then_each_triangle_its_code(self, binary: bool) -> None: diff --git a/tests/python/test_landcover.py b/tests/python/test_landcover.py index 84768b29..5dc8811e 100644 --- a/tests/python/test_landcover.py +++ b/tests/python/test_landcover.py @@ -24,17 +24,15 @@ (ints), the numbers of R3's stderr line; `.codes` is the `(T,)` array. - `regions(triangles, edges)` takes no vertex count. -Committed red: `tin_engine.landcover` does not exist, so every test fails on -`ModuleNotFoundError`, raised per test by `landcover()` below rather than at -collection. +Committed red at `196147e`: `tin_engine.landcover` did not exist yet, so +every test failed on `ModuleNotFoundError`. The module landed in `0487ed0` +and the suite has been green since. """ from __future__ import annotations -import importlib from collections.abc import Sequence from dataclasses import dataclass -from types import ModuleType from typing import Any import numpy as np @@ -52,14 +50,11 @@ landcover_oracle, spread_violations, ) +from tin_engine import landcover X0, Y0 = 500_000.3, 6_600_000.7 -def landcover() -> ModuleType: - return importlib.import_module("tin_engine.landcover") - - def rect(x0: float, y0: float, x1: float, y1: float) -> Polygon: return box(X0 + x0, Y0 + y0, X0 + x1, Y0 + y1) @@ -133,7 +128,7 @@ def _linework(geometry: BaseGeometry) -> BaseGeometry: def label(mesh: Mesh, polygons: Sequence[tuple[BaseGeometry, int]], margin: float = MARGIN) -> Any: - return landcover().label_triangles( + return landcover.label_triangles( mesh.vertices, mesh.triangles, mesh.edges, polygons=list(polygons), margin=margin ) @@ -176,21 +171,21 @@ def strip(cells: int) -> np.ndarray: class TestRegions: def test_an_unconstrained_shared_edge_joins_two_triangles(self) -> None: triangles, hull = two_triangles() - assert_array_equal(landcover().regions(triangles, hull), [0, 0]) + assert_array_equal(landcover.regions(triangles, hull), [0, 0]) def test_a_constrained_shared_edge_separates_them(self) -> None: triangles, hull = two_triangles() cut = np.vstack([hull, [[0, 2]]]) - assert_array_equal(landcover().regions(triangles, cut), [0, 1]) + assert_array_equal(landcover.regions(triangles, cut), [0, 1]) def test_a_constraint_given_backwards_still_blocks(self) -> None: triangles, hull = two_triangles() cut = np.vstack([hull, [[2, 0]]]) - assert_array_equal(landcover().regions(triangles, cut), [0, 1]) + assert_array_equal(landcover.regions(triangles, cut), [0, 1]) def test_no_constraints_at_all(self) -> None: triangles, _ = two_triangles() - assert_array_equal(landcover().regions(triangles, np.zeros((0, 2), dtype=np.int64)), [0, 0]) + assert_array_equal(landcover.regions(triangles, np.zeros((0, 2), dtype=np.int64)), [0, 0]) @pytest.mark.parametrize("order", ["forward", "reversed", "shuffled"]) def test_a_strip_of_1000_triangles_is_one_component(self, order: str) -> None: @@ -202,7 +197,7 @@ def test_a_strip_of_1000_triangles_is_one_component(self, order: str) -> None: triangles = triangles[::-1] elif order == "shuffled": triangles = triangles[np.random.default_rng(16).permutation(len(triangles))] - ids = np.asarray(landcover().regions(triangles, np.zeros((0, 2), dtype=np.int64))) + ids = np.asarray(landcover.regions(triangles, np.zeros((0, 2), dtype=np.int64))) assert ids.shape == (1000,) assert set(ids.tolist()) == {0} @@ -211,7 +206,7 @@ def test_ids_are_the_smallest_triangle_index_of_each_component(self) -> None: mesh = grid_mesh(STEPS, STEPS, [cut]) order = np.random.default_rng(3).permutation(len(mesh.triangles)) triangles = mesh.triangles[order] - ids = np.asarray(landcover().regions(triangles, mesh.edges)) + ids = np.asarray(landcover.regions(triangles, mesh.edges)) west = np.asarray(shapely.contains_xy(rect(0, 0, 10, 20), *mesh.centroids[order].T)) assert west.any() and (~west).any() assert set(ids[west].tolist()) == {int(np.flatnonzero(west).min())} @@ -222,10 +217,10 @@ def test_permuting_and_flipping_the_constraint_rows_changes_nothing(self) -> Non rng = np.random.default_rng(7) shuffled = mesh.edges[rng.permutation(len(mesh.edges))] flipped = shuffled[:, ::-1] - first = np.asarray(landcover().regions(mesh.triangles, mesh.edges)) + first = np.asarray(landcover.regions(mesh.triangles, mesh.edges)) assert len(set(first.tolist())) == 4 - assert_array_equal(landcover().regions(mesh.triangles, shuffled), first) - assert_array_equal(landcover().regions(mesh.triangles, flipped), first) + assert_array_equal(landcover.regions(mesh.triangles, shuffled), first) + assert_array_equal(landcover.regions(mesh.triangles, flipped), first) # ---------------------------------------------------------- label_triangles @@ -273,12 +268,12 @@ def test_a_sliver_component_is_counted_thin_and_still_labelled(self) -> None: triangles = np.array([[0, 1, 2], [1, 3, 2]]) edges = np.array([[0, 1], [1, 3], [3, 2], [2, 0], [1, 2]]) cover = rect(-10, -10, 110, 110) - labels = landcover().label_triangles( + labels = landcover.label_triangles( vertices, triangles, edges, polygons=[(cover, 311)], margin=MARGIN ) assert_array_equal(labels.codes, [311, 311]) assert (labels.regions, labels.thin) == (2, 1) - finer = landcover().label_triangles( + finer = landcover.label_triangles( vertices, triangles, edges, polygons=[(cover, 311)], margin=1e-5 ) assert finer.thin == 0 @@ -349,7 +344,7 @@ def test_a_polygon_clipped_by_the_domain_into_two_pieces(self) -> None: west, east = mesh.inside(rect(4, 12, 8, 16)), mesh.inside(rect(12, 12, 16, 16)) assert west.any() and east.any() assert set(codes[west | east].tolist()) == {324} - ids = np.asarray(landcover().regions(mesh.triangles, mesh.edges)) + ids = np.asarray(landcover.regions(mesh.triangles, mesh.edges)) assert set(ids[west].tolist()).isdisjoint(ids[east].tolist()) assert (labels.regions, labels.outside) == (3, 1) @@ -362,7 +357,7 @@ def test_a_road_across_a_polygon(self) -> None: codes = np.asarray(labels.codes) north, south = mesh.inside(rect(4, 10, 16, 16)), mesh.inside(rect(4, 4, 16, 10)) assert set(codes[north | south].tolist()) == {311} - ids = np.asarray(landcover().regions(mesh.triangles, mesh.edges)) + ids = np.asarray(landcover.regions(mesh.triangles, mesh.edges)) assert set(ids[north].tolist()).isdisjoint(ids[south].tolist()) assert (labels.regions, labels.outside) == (3, 1) @@ -454,5 +449,5 @@ def test_permuted_triangles_edges_and_polygons_give_the_same_labels(self, case: class TestPurity: def test_it_imports_nothing_first_party(self) -> None: """R1's boundary: numpy and shapely, nothing first-party, never `_core`.""" - module = landcover() + module = landcover assert first_party_imports(module) == set() diff --git a/tests/python/test_palettes.py b/tests/python/test_palettes.py index f1390646..8e64aca6 100644 --- a/tests/python/test_palettes.py +++ b/tests/python/test_palettes.py @@ -14,27 +14,27 @@ - `NanColor` is present and magenta (R4: "A code the table lacks draws in `NanColor`, magenta"). -Committed red: `tin_engine.palettes` does not exist and `rasputin palette` is -no command, so every test fails on `ModuleNotFoundError` (raised per test by -`palettes()`) or on the CLI's usage error for an unknown command. +Committed red at `196147e`: `tin_engine.palettes` did not exist yet and +`rasputin palette` was no command, so every test failed on +`ModuleNotFoundError` or on the CLI's usage error for an unknown command. Both +landed in `0487ed0` and the suite has been green since. """ from __future__ import annotations import colorsys -import importlib import json import re import sqlite3 from contextlib import closing from pathlib import Path -from types import ModuleType from typing import Any import pytest from typer.testing import CliRunner from gpkg_fixtures import OLA_NORWAY +from tin_engine import palettes from tin_engine.cli import app runner = CliRunner(env={"NO_COLOR": "1", "TERM": "dumb"}) @@ -65,12 +65,8 @@ HEX = re.compile(r"#[0-9a-fA-F]{6}") -def palettes() -> ModuleType: - return importlib.import_module("tin_engine.palettes") - - def table() -> dict[int, tuple[str, str]]: - return dict(palettes().CORINE_NATURAL) + return dict(palettes.CORINE_NATURAL) def rgb(code: int) -> tuple[float, float, float]: @@ -158,8 +154,7 @@ def test_the_official_legend_is_not_used_for_bogs_or_the_sea(self) -> None: class TestPreset: def preset(self) -> dict[str, Any]: - """Not a fixture, so a missing module fails the test, not its setup.""" - out = palettes().paraview_preset(palettes().CORINE_NATURAL, "a name") + out = palettes.paraview_preset(palettes.CORINE_NATURAL, "a name") assert isinstance(out, list) and len(out) == 1 assert isinstance(out[0], dict) return out[0] @@ -203,7 +198,7 @@ class TestCommand: def test_stdout_is_the_preset(self) -> None: result = runner.invoke(app, ["palette", "corine"]) assert result.exit_code == 0, result.output - expected = palettes().paraview_preset(palettes().CORINE_NATURAL, PRESET_NAME) + expected = palettes.paraview_preset(palettes.CORINE_NATURAL, PRESET_NAME) assert json.loads(result.stdout) == expected def test_an_unknown_name_is_a_usage_error_listing_the_known(self) -> None: From da58422f82d179a7c134a76fa6f14e6d87d1eadc Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 03:27:19 +0200 Subject: [PATCH 5/7] 16c review fixes (docs): as built; R5 codes on kept features; project structure Co-Authored-By: Claude Opus 5.5 --- docs/increments/16c-landcover-labels.md | 35 ++++++++++++++++--------- project_structure.md | 24 ++++++++++++++--- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/docs/increments/16c-landcover-labels.md b/docs/increments/16c-landcover-labels.md index 81640440..f4afeb27 100644 --- a/docs/increments/16c-landcover-labels.md +++ b/docs/increments/16c-landcover-labels.md @@ -1,6 +1,7 @@ # Increment 16c — a land-cover class per triangle, in natural colours -Status: **designed** (`@architect`, 2026-09-29, night). Written before +Status: **built (green `0487ed0`), under review.** Designed by `@architect` +2026-09-29, night. Written before `@tester`, per `docs/increments/README.md` step 1, on branch `increment16c-landcover-labels` off master `b4847d7` (16b-1/2 merged, 22 not). Ola was asleep while this was written; every question a design would @@ -465,6 +466,9 @@ not occur there and are left out; they draw in `NanColor`. kept, not clipped. - Uncoded maps behave exactly as in 16b: no polygon is moved or kept, so no cost is added to them. +- Codes are checked only on the features the map keeps: under + `corine-water`, an unlisted value is dropped before its code is read, as + 16b drops any unlisted value. ### R6. The CLI @@ -629,18 +633,23 @@ changes; labelling runs after the mesh is finished. Production lines as `CLAUDE.md` §2 counts them (tests excluded): -| file | lines | -|---|---| -| `landcover.py` (`regions`, incentres, `label_triangles`, `CoverLabels`) | 75 | -| `palettes.py` (45-entry table, `paraview_preset`) | 60 | -| `feature_input.py` (`codes`, `code`, `polygon`, coded refusals, covering polygon) | 30 | -| `io/vtk_legacy.py` (array, fill, field, refusals) | 15 | -| `io/ply.py` (face property, comment, refusal) | 12 | -| `cli.py` (label call and phase, stderr line, fields and comments, `palette` command, help) | 40 | -| **total** | **~230** | - -Worst case about 320, if the writers need more reshaping than estimated. Well -under the 700-line ceiling; one PR. +| file | estimate | as built (green `0487ed0`) | +|---|---|---| +| `landcover.py` (`regions`, incentres, `label_triangles`, `CoverLabels`) | 75 | 83 | +| `palettes.py` (45-entry table, `paraview_preset`) | 60 | 64 | +| `feature_input.py` (`codes`, `code`, `polygon`, coded refusals, covering polygon) | 30 | 22 | +| `io/vtk_legacy.py` (array, fill, field, refusals) | 15 | 19 | +| `io/ply.py` (face property, comment, refusal) | 12 | 16 | +| `cli.py` (label call and phase, stderr line, fields and comments, `palette` command, help) | 40 | 45 | +| **total** | **~230** | **net 249 (262 added, 13 removed)** | + +The estimate's worst case was about 320. Well under the 700-line ceiling; one +PR. + +**As-built writer API.** `write_vtk(..., triangle_codes=None, +land_cover_codes="")`: the codes array and the `land_cover_codes` field text +are separate arguments, the text written only with the array. +`write_ply(..., face_codes=None)`. ## Not in scope diff --git a/project_structure.md b/project_structure.md index 7f5e37ff..60f2355d 100644 --- a/project_structure.md +++ b/project_structure.md @@ -79,7 +79,9 @@ bindings/ src_python/tin_engine/ # public Python API (distribution name: rasputin) __init__.py # re-exports from tin_engine._core - cli.py # Typer entry point declared in pyproject + cli.py # Typer entry point declared in pyproject; + # `rasputin palette NAME [--out FILE]` writes a + # ParaView colour preset (16c) raster.py # the ONLY adapter from decoded data into _core grid_domain.py # DEM extent -> stride-subsampled nodes + outer ring; # pure numpy, never imports _core @@ -103,6 +105,13 @@ src_python/tin_engine/ # public Python API (distribution name: rasputin) # moved to the DEM's CRS and clipped to the domain # as linework -> FeatureSet (16b); opens GeoJSON # and .gml itself; never imports _core + landcover.py # regions, label_triangles: a land-cover code per + # triangle, components across unconstrained edges, + # one point-in-polygon test per component (16c); + # numpy and shapely, never imports _core + palettes.py # CORINE_NATURAL (code -> label, colour) and + # paraview_preset(); data, imports nothing + # first-party (16c) chains.py # start_chains: the domain's rings, then every # feature line, as (indices, role, mask) (16b); # never imports _core @@ -117,11 +126,18 @@ src_python/tin_engine/ # public Python API (distribution name: rasputin) # carries positions rather than feature names svg.py # (Scene, SvgStyle) -> str; the stylesheet lives here fixtures.py # the synthetic gallery, declarative; `rasputin draw - io/ # all file decoding AND encoding lives here + io/ # all file decoding AND encoding lives here, with + # known exceptions to move here in a follow-up: + # `palette`'s JSON is encoded in cli.py (16c), and + # increment 22's GeoJSON writer (on 22's branch) __init__.py - ply.py # arrays -> PLY bytes; takes no path and opens nothing + ply.py # arrays -> PLY bytes; takes no path and opens nothing; + # face_codes= adds the face property + # land_cover_code (16c) vtk_legacy.py # arrays + EdgeVocabulary -> legacy .vtk bytes, for - # ParaView; takes no path and opens nothing + # ParaView; takes no path and opens nothing; + # triangle_codes=, land_cover_codes= add the cell + # array land_cover_code and its field (16c) geotiff.py # TIFF container + GeoKey decoding -> DemTile models.py # Pydantic RasterMeta / DemTile geopackage.py # GeoPackage layer_info / query_features over an From f7d5f14fe34e64cdfd77887394588ad87a0f8cef Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 03:27:45 +0200 Subject: [PATCH 6/7] ROADMAP: 16c built Co-Authored-By: Claude Opus 5.5 --- ROADMAP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index dab0b043..430874f5 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -46,7 +46,7 @@ increment that most needs a picture to check against | — | Release hardening: measure libc++'s `_LIBCPP_HARDENING_MODE_FAST` (and libstdc++'s assertions on the GCC leg), which bounds-check `std::vector`, `span` and the like in Release, on the 1 m benchmark; switch it on if the cost is small. Today an out-of-range index in shipped code the tests miss is undefined behaviour and crashes Python (Ola, 2026-09-27: "I'm surprised we don't have proper memory control"; CI's ASan/UBSan/TSan cover what the tests reach) | to measure (Ola, 2026-09-27); 21d, which it was placed after, is deferred behind the basin work | none yet | | 15 | A DEM in several tiles, and the domain in its own CRS: 15a Norway, many tiles in one projected CRS (Ola's 254 DTM10 UTM33 tiles; only the selected tiles must share a lattice, since 8 of them sit half a cell off); 15b the domain polygon reprojected into the DEM's CRS; 15c and 15d the São Francisco basin (geographic DEMs meshed in the DEM's own lattice frame, not resampled; window decoding and memory) | **15a shipped with branch `increment15-dem-mosaic`'s PR**: `--dem DIR` or several files, `--bbox`, tiles selected and stitched on one lattice, Q2-Q5 refusals; overlaps that disagree (real DTM10 tiles exported on different dates, up to 52 m) are split down the middle and reported per seam (Ola's Q1 revised, 2026-09-28). The design's acceptance box, 9 tiles and 10,051² nodes, meshes: 11.05 M triangles in 31 s on battery. **15b built with branch `increment15b-domain-crs`'s PR**: `--domain` in its own CRS (e.g. EPSG:4326) over a UTM33 mosaic, reprojected vertex by vertex. Then 16b (Ola, 2026-09-27: Norway first) | `docs/increments/15-dem-mosaic.md` | | 16b | Interior polygons and polylines as constraints ("terrain polygons": lakes, land cover, roads, rivers): `--features PATH`, a GeoJSON `FeatureCollection`, each feature naming a vocabulary property; closed or open `Breakline`s, crossings noded, off-node vertices with bilinear z. **Its working example is real data** (Ola, 2026-09-27): CORINE Land Cover 2018 over the benchmark tile `7908_3_10m_z33.tif`. The source is Ola's local copy, `rasputin_data/corine_sql/.../U2018_CLC2018_V2020_20u1.gpkg` (8.2 GB, EPSG:3035, a sibling of this repository, which also holds the 254-tile DTM10 archive for gap 6). It reads without GDAL: sqlite3 over its R-tree, the GeoPackage blob header stripped, `shapely.wkb`, then `pyproj` to 25833, so CRS stops in Python as before. Probed 2026-09-27 in 0.2 s: 60 polygons in 8 classes (heath, bare rock, sparse vegetation, bogs, intertidal flats, water, sea, urban), 11 068 vertices clipped to the tile, median segment 54 m against 10 m cells. The EEA's public ArcGIS service (`image.discomap.eea.europa.eu`, `Corine/CLC2018_WM`) returns the same 11 068 clipped vertices and is the route for anyone without the file. What it forces on 16b's design: neighbouring polygons share their boundaries, so each shared edge arrives twice; the polygons run past the domain and must be clipped; the extract is committed as a fixture with the Copernicus attribution. Placed before 20c because 20c may split constraint segments and should be designed and measured on inputs that have interior ones | **designed** (`@architect`, 2026-09-28): two PRs, 16b-0 (the noder's verifier indexed: it is quadratic, 25 s of a 30 s CORINE run) and 16b-1/2 (GeoJSON and GeoPackage features, class maps, clip as linework, the noder merges shared edges); Q1-Q6 ruled by Ola 2026-09-28 (Q6: the legacy CORINE GML stays and is read). **16b-0 shipped (#107, merged 2026-09-28)**: the verifier's pair search by sort and sweep, always on; `node` on a 48 km CORINE square 24.8 s → 0.067 s. **16b-1/2 built on branch `increment16b12-features`** (2026-09-28): GeoPackage, GeoJSON and the legacy GML (strict reader, fixture repaired) as `--features`; CORINE class maps; the pre-clip keeps whole edges and widens its margin per edge for long ones; the noder merges shared edges; net +642 production lines. Before 20c | `docs/increments/16b-terrain-polygons.md`, `docs/increments/16-domain-polygon.md` (R6) | -| 16c | A land-cover label per triangle: which input polygon (CORINE class) each triangle lies in, by a flood fill over the unconstrained edges. The bits on an edge say what kind of line it is; which class lies on each side is 16c's | **designed** (`@architect`, 2026-09-29): components across unconstrained edges, one point-in-polygon test per component, cell array `land_cover_code`, a natural-colour ParaView preset from `rasputin palette corine`; defaults D1-D7 for Ola to confirm; ~230 production lines. Ruled by Ola 2026-09-28 (16b's Q2) as a separate increment | `docs/increments/16c-landcover-labels.md`, `docs/increments/16b-terrain-polygons.md` (Q2, R6) | +| 16c | A land-cover label per triangle: which input polygon (CORINE class) each triangle lies in, by a flood fill over the unconstrained edges. The bits on an edge say what kind of line it is; which class lies on each side is 16c's | **built** (2026-09-29, overnight; net 249 lines, one PR; Bygdin at 10 m: every triangle labelled, class shares equal to CORINE's clipped areas at two decimals). Designed by `@architect`, 2026-09-29: components across unconstrained edges, one point-in-polygon test per component, cell array `land_cover_code`, a natural-colour ParaView preset from `rasputin palette corine`; defaults D1-D7 for Ola to confirm; ~230 production lines. Ruled by Ola 2026-09-28 (16b's Q2) as a separate increment | `docs/increments/16c-landcover-labels.md`, `docs/increments/16b-terrain-polygons.md` (Q2, R6) | | 16d | A record of the input polylines in the mesh. Today a constraint edge keeps only its type bits; the source feature, its attributes (a river's name or order, a road's class) and which edges belong to one polyline are lost, and where edges from two features merge only the union of their bits survives. Proposed: a feature table (one row per input feature: source ID and attributes) and, per constraint edge, the list of features it came from (a list, since a merged edge belongs to several). The noder already tracks each piece's input chain; the open part is the output: a per-edge list in `.vtk`/`.ply`, or a side file | proposed by Ola 2026-09-28; to be designed by `@architect` once Ola has said what it is for; after 16b | - | | 20c | Soft quality criterion: a penalty that each Steiner node or constraint split must pay for in angle gained, instead of 20's hard 25°; applied at the start and during DEM refinement; may split constraint segments when that improves the mesh. Ola's rulings on 20's C1-C3 | to design after 16b (`@architect` measures cost against 20 first) | `docs/increments/20-start-quality.md` (Ola's rulings) | | — | Auto-catchment: the watershed upstream of a coordinate, computed from the DEM and handed to `--domain`, so a catchment no longer has to be supplied as a file (Ola, 2026-09-27: "not far into the future"). The textbook route is depression handling (Priority-Flood, Barnes, Lehman and Mulla 2014), D8 flow directions (O'Callaghan and Mark 1984) and accumulation, the pour point snapped to the strongest flow nearby, the upstream cells traced and their outline turned into a polygon; the literature check is `@architect`'s. Open for its design: whether it runs in the C++ core (a 10 m tile is 25 M cells); how a stair-stepped cell outline becomes a domain polygon, which meets input coarsening; and that a real catchment crosses tile edges, so it needs gap 6 (a DEM in several tiles) first. Legacy has nothing on it (`grep -rliE "watershed|flow.?acc|flow.?dir|pour.?point|catchment" legacy` returns no files) | to design; after gap 6, which it needs; placed after 20c, can move ahead of it on Ola's word | none yet | From 7c00675e151b6b434fa462333ad05d4880996aa7 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Tue, 29 Sep 2026 03:36:17 +0200 Subject: [PATCH 7/7] 16c acceptance: Bygdin in natural colours Co-Authored-By: Claude Opus 5.5 --- .../benchmarks/2026-09-29/bygdin-landcover.md | 28 ++ .../2026-09-29/bygdin-landcover/README.md | 142 +++++++++++ .../2026-09-29/bygdin-landcover/analyse.py | 212 ++++++++++++++++ .../bygdin_landcover_oblique.png | Bin 0 -> 883809 bytes .../bygdin-landcover/bygdin_landcover_top.png | Bin 0 -> 407034 bytes .../bygdin_reduced_t20.geojson | 1 + .../bygdin-landcover/corine_natural.json | 239 ++++++++++++++++++ .../bygdin-landcover/logs/analysis.txt | 57 +++++ .../bygdin-landcover/logs/commit.txt | 3 + .../bygdin-landcover/logs/exits.txt | 9 + .../bygdin-landcover/logs/lc_t10_ascii.err | 23 ++ .../bygdin-landcover/logs/lc_t10_ascii.out | 2 + .../logs/lc_t10_ascii.stats.md | 67 +++++ .../bygdin-landcover/logs/lc_t10_run1.err | 23 ++ .../bygdin-landcover/logs/lc_t10_run1.out | 2 + .../logs/lc_t10_run1.stats.md | 67 +++++ .../bygdin-landcover/logs/lc_t10_run2.err | 23 ++ .../bygdin-landcover/logs/lc_t10_run2.out | 2 + .../logs/lc_t10_run2.stats.md | 67 +++++ .../bygdin-landcover/logs/lc_t10_run3.err | 23 ++ .../bygdin-landcover/logs/lc_t10_run3.out | 2 + .../logs/lc_t10_run3.stats.md | 67 +++++ .../bygdin-landcover/logs/lc_t1_ascii.err | 23 ++ .../bygdin-landcover/logs/lc_t1_ascii.out | 2 + .../logs/lc_t1_ascii.stats.md | 67 +++++ .../bygdin-landcover/logs/lc_t1_run1.err | 23 ++ .../bygdin-landcover/logs/lc_t1_run1.out | 2 + .../bygdin-landcover/logs/lc_t1_run1.stats.md | 67 +++++ .../bygdin-landcover/logs/lc_t1_run2.err | 23 ++ .../bygdin-landcover/logs/lc_t1_run2.out | 2 + .../bygdin-landcover/logs/lc_t1_run2.stats.md | 67 +++++ .../bygdin-landcover/logs/lc_t1_run3.err | 23 ++ .../bygdin-landcover/logs/lc_t1_run3.out | 2 + .../bygdin-landcover/logs/lc_t1_run3.stats.md | 67 +++++ .../bygdin-landcover/logs/palette.out | 1 + .../bygdin-landcover/logs/pmset_end.txt | 2 + .../bygdin-landcover/logs/pmset_start.txt | 2 + .../bygdin-landcover/logs/pmset_timed_end.txt | 2 + .../2026-09-29/bygdin-landcover/render.py | 155 ++++++++++++ .../2026-09-29/bygdin-landcover/run.sh | 38 +++ 40 files changed, 1627 insertions(+) create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover.md create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/README.md create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/analyse.py create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/bygdin_landcover_oblique.png create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/bygdin_landcover_top.png create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/bygdin_reduced_t20.geojson create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/corine_natural.json create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/analysis.txt create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/commit.txt create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/exits.txt create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t10_ascii.err create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t10_ascii.out create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t10_ascii.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t10_run1.err create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t10_run1.out create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t10_run1.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t10_run2.err create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t10_run2.out create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t10_run2.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t10_run3.err create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t10_run3.out create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t10_run3.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t1_ascii.err create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t1_ascii.out create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t1_ascii.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t1_run1.err create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t1_run1.out create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t1_run1.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t1_run2.err create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t1_run2.out create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t1_run2.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t1_run3.err create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t1_run3.out create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/lc_t1_run3.stats.md create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/palette.out create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/pmset_end.txt create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/pmset_start.txt create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/logs/pmset_timed_end.txt create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/render.py create mode 100644 docs/benchmarks/2026-09-29/bygdin-landcover/run.sh diff --git a/docs/benchmarks/2026-09-29/bygdin-landcover.md b/docs/benchmarks/2026-09-29/bygdin-landcover.md new file mode 100644 index 00000000..96bfe765 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin-landcover.md @@ -0,0 +1,28 @@ +# Increment 16c acceptance on Bygdin: summary (@perf, 2026-09-29) + +**Verdict: ACCEPTED** on the design's three pass criteria. AC power, Apple M1 +Max, commit `f7d5f14`. Method, tables and scripts: +`2026-09-29/bygdin-landcover/README.md`. + +Reduced Bygdin catchment (from 22), DTM10, CORINE 2018 Norway extract with +`--features-map corine`: + +| criterion | 10 m | 1 m | pass | +|---|---|---|---| +| `vtkPolyDataReader` reads it; `land_cover_code` on every cell, 0 on every line | 92,053 cells | 1,159,508 cells | yes | +| seven codes, no code 0, each share within 0.01 pp of CORINE clipped | largest gap 0.000001 pp | the same | yes | +| design's centroid oracle, triangles checked / disagreements | 83,171 / 0 | 1,145,434 / 0 | yes | +| I1: unconstrained edges with differing codes | 0 of 116,398 | 0 of 1,705,117 | yes | + +Shares: 333 42.34 %, 332 21.32 %, 322 17.00 %, 512 16.46 %, 335 2.44 %, +412 0.33 %, 142 0.10 %, equal to 22's table. + +Time, not gated (median of 3): the `land cover` phase takes 0.071 s at 10 m +(83,171 triangles, total 2.08 s) and 1.296 s at 1 m (1,145,434 triangles, +total 3.89 s). At 1 m that is more than `refine` (0.559 s), which is the +condition the design's R2 sets for moving `regions` into the core. + +No bench or thread sweep: 16c's commits change no C++ (design, Acceptance). +Pictures: `bygdin_landcover_oblique.png` (from the south-east, 2× vertical) +and `bygdin_landcover_top.png`. ParaView preset: `corine_natural.json`. The +manual ParaView import (design item 6) is left for Ola. diff --git a/docs/benchmarks/2026-09-29/bygdin-landcover/README.md b/docs/benchmarks/2026-09-29/bygdin-landcover/README.md new file mode 100644 index 00000000..71081328 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin-landcover/README.md @@ -0,0 +1,142 @@ +# Increment 16c acceptance: Bygdin in natural colours (@perf, 2026-09-29) + +The summary and the verdict are in `../bygdin-landcover.md`. This file has the +method, the tables and where every number comes from. The design's acceptance +section (`docs/increments/16c-landcover-labels.md`, "Acceptance") names the +directory `16c-bygdin/`; this run was asked to use `bygdin-landcover/`. + +## What was run + +- **Commit**: `f7d5f14` on `increment16c-landcover-labels` (`logs/commit.txt`). + The 16c commits (`196147e` to `f7d5f14`) change nothing under `include/`, + `src/`, `tests/cpp/`, `tools/` or `CMakeLists.txt` + (`git diff --stat 196147e~1..f7d5f14 -- include src tests/cpp CMakeLists.txt tools` + is empty), so the bench and thread sweep are not required (design, + Acceptance). +- **Build**: `cmake --build build-pyext -j --target _core` (exit 0), copied into + `.venv`; the installed and built `_core` sha256 are equal + (`logs/commit.txt`). Python is the editable `src_python/` tree. +- **Machine**: Apple M1 Max, 10 cores, 32 GB. `mesh` used 10 threads. +- **Power**: AC throughout. `pmset -g batt` at the start, after the timed + runs, and at the end (`logs/pmset_*.txt`): each reads "AC Power, 80 %, AC + attached". +- **Inputs**: + - domain `bygdin_reduced_t20.geojson`, committed here, copied with + `git show increment22-autocatchment:docs/benchmarks/2026-09-29/bygdin/bygdin_reduced_t20.geojson` + (sha256 `8083fb5b...`, the same as 22's); + - DEM `../rasputin_data/DTM10_UTM33_20260925` (10 m, EPSG:25833); + - features `../rasputin_data/corine2018_dtm10_utm33.gpkg`, layer + `corine2018`, `--features-map corine`. + +Scripts, all in this directory: + +- `run.sh`: three timed `--binary` runs at `--tolerance 10` and at + `--tolerance 1`, then one `--ascii` run of each for the quality check, then + `rasputin palette corine --out corine_natural.json`. Every command runs under + `/usr/bin/time -l`; logs and `--stats` reports go to `logs/`. +- `analyse.py`: every table below (`logs/analysis.txt` is its output). It reads + the meshes with VTK's `vtkPolyDataReader`. The CORINE reference is read + straight from the GeoPackage with `sqlite3` and shapely, not through + `tin_engine.feature_input`, so it does not share code with what it checks. + The quality check calls `tools/bench.py`'s `quality()` and + `read_vtk_ascii()` unchanged. +- `render.py`: the two pictures. + +Meshes stay out of the repository. To regenerate: from the repository root +with `.venv` active, `SCRATCH= bash docs/benchmarks/2026-09-29/bygdin-landcover/run.sh`, +then `python .../analyse.py ` and +`python .../render.py /lc_t10_run1.vtk docs/benchmarks/2026-09-29/bygdin-landcover`. + +## 1. The file opens in VTK with `land_cover_code` on every cell + +| run | cells | lines | triangles | `land_cover_code` values | nonzero on lines | `FieldData` `land_cover_codes` | +|---|---|---|---|---|---|---| +| 10 m | 92,053 | 8,882 | 83,171 | 92,053 | 0 | present | +| 1 m | 1,159,508 | 14,074 | 1,145,434 | 1,159,508 | 0 | present | + +The field text reads `CORINE Land Cover level-3 code, attribute Code_18, map +corine; 0 = in no polygon, and every constraint line`. The active `SCALARS` +stays `feature_mask` (13, ruling 5). The ASCII run's codes equal the binary +run's, cell for cell, at both tolerances. + +## 2. Class shares against CORINE clipped to the catchment + +79 CORINE polygons meet the domain; clipped to it they cover 304.909550 km², +the domain's area. The mesh's plan area is 304.909551 km² at both tolerances. +Triangle area (x, y) per code: + +| code | class | mesh km² | mesh share | CORINE clipped share | difference (pp) | 22's table | +|---|---|---|---|---|---|---| +| 333 | sparsely vegetated areas | 129.093253 | 42.33821 % | 42.33821 % | -0.000001 | 42.34 % | +| 332 | bare rocks | 65.011569 | 21.32159 % | 21.32159 % | +0.000000 | 21.32 % | +| 322 | moors and heathland | 51.849126 | 17.00476 % | 17.00476 % | +0.000000 | 17.00 % | +| 512 | water bodies | 50.191119 | 16.46099 % | 16.46099 % | +0.000000 | 16.46 % | +| 335 | glaciers and perpetual snow | 7.452202 | 2.44407 % | 2.44407 % | -0.000000 | 2.44 % | +| 412 | peat bogs | 1.020748 | 0.33477 % | 0.33477 % | +0.000000 | 0.33 % | +| 142 | sport and leisure facilities | 0.291534 | 0.09561 % | 0.09561 % | -0.000000 | 0.10 % | + +The 10 m and 1 m rows agree to the printed digits; the table is both. The +same seven codes, no triangle with code 0, and the largest difference is +0.000001 percentage points (limit 0.01). + +**The check can fail.** Relabelling the 142 region as 333 on the 10 m mesh +moves 333's share to 42.43383 %, 0.096 pp off, and the oracle below then +disagrees on 115 triangles (a one-off probe, not committed). + +## 3. I1 (spread) and I2 (oracle) + +| run | interior unconstrained edges | I1: edges with differing codes | I2: triangles checked (r > 3 mm) | I2: disagreements | +|---|---|---|---|---| +| 10 m | 116,398 | 0 | 83,171 of 83,171 | 0 | +| 1 m | 1,705,117 | 0 | 1,145,434 of 1,145,434 | 0 | + +The oracle is the design's (R1): each triangle's centroid against the CORINE +polygons (read independently, unclipped), smallest area wins; margin +`2 × 1 mm`, so the exact set is `r > 3 mm`, which is every triangle here. +`--stats` stderr, every run: `land cover: 124 regions, 0 outside every +polygon, 0 in more than one, 0 thinner than the snap`. + +## 4. Time (recorded, not gated) + +Median of three binary runs; phase times from each run's `--stats` report, +wall and RSS from `/usr/bin/time -l`. + +| tolerance | triangles | components | union-find rounds | land cover (s) | refine (s) | features clip (s) | total (s) | wall (s) | max RSS | +|---|---|---|---|---|---|---|---|---|---| +| 10 m | 83,171 | 124 | 5 | 0.071 (0.075, 0.071, 0.070) | 0.047 | 1.460 | 2.075 | 2.34 | 719 MB | +| 1 m | 1,145,434 | 124 | 7 | 1.296 (1.303, 1.296, 1.263) | 0.559 | 1.447 | 3.887 | 4.36 | 859 MB | + +"Union-find rounds" counts the hook rounds of `landcover.regions`' outer loop: +`analyse.py` runs a copy of that loop with a counter and checks that its +result equals `regions()` (it does, at both tolerances). + +At 1 m the `land cover` phase (1.30 s) costs more than `refine` (0.56 s). +The design's R2 names this as the condition for moving `regions` into the +core. Where inside the phase the time goes was not profiled. + +## 5. Quality (the ASCII runs, `tools/bench.py`'s `quality()`) + +| tolerance | worst angle | max degree | max error | within tolerance | Delaunay checked / ambiguous / violations | +|---|---|---|---|---|---| +| 10 m | 0.00408° | 16 | 9.99973 m | yes | 116,398 / 1,095 / 0 | +| 1 m | 0.00408° | 29 | 1.0 m | yes | 1,705,117 / 162,591 / 0 | + +The 10 m figures equal 22's "reduced + CORINE" row (83,171 triangles, 0.00408°, +degree 16). 22 has no 1 m run with CORINE features; its 1 m run without them +had max degree 18. Labelling does not touch the mesh, so these are the +refine's figures on this input, not 16c's. + +## 6. The pictures and the ParaView preset + +- `bygdin_landcover_oblique.png`: the 10 m mesh from the south-east, z + exaggerated 2×; `bygdin_landcover_top.png`: map view. `render.py` draws the + triangles only (the constraint lines, code 0, are left out), coloured through + an indexed `vtkLookupTable` built from `tin_engine.palettes.CORINE_NATURAL`, + with a legend of the seven codes present. +- `corine_natural.json`: written by `rasputin palette corine --out`. + **In ParaView**: colour by `land_cover_code` (cells), then Colour Map + Editor → Choose Preset → Import `corine_natural.json`, select "rasputin + CORINE natural", Apply (tick *Interpret Values As Categories* if it is not + on). +- Not done here (design item 6, manual, for Ola): the ParaView import itself, + its version, and whether the preset switches to categorical by itself. diff --git a/docs/benchmarks/2026-09-29/bygdin-landcover/analyse.py b/docs/benchmarks/2026-09-29/bygdin-landcover/analyse.py new file mode 100644 index 00000000..35a03ae1 --- /dev/null +++ b/docs/benchmarks/2026-09-29/bygdin-landcover/analyse.py @@ -0,0 +1,212 @@ +"""Increment 16c acceptance on Bygdin (@perf, 2026-09-29): every table in +README.md comes from this script. Usage, from the repository root, .venv active: + + python docs/benchmarks/2026-09-29/bygdin-landcover/analyse.py + + holds run.sh's meshes. Prints to stdout; run.sh's logs are read from +./logs. The CORINE reference is read here straight from the GeoPackage with +sqlite3 and shapely, not through tin_engine.feature_input, so it is independent +of the code under test. +""" + +from __future__ import annotations + +import re +import sqlite3 +import statistics +import sys +from pathlib import Path + +import numpy as np +import shapely +from vtkmodules.util.numpy_support import vtk_to_numpy +from vtkmodules.vtkIOLegacy import vtkPolyDataReader + +HERE = Path(__file__).resolve().parent +ROOT = HERE.parents[3] +sys.path.insert(0, str(ROOT / "tools")) +import bench # noqa: E402 + +from tin_engine.landcover import regions # noqa: E402 + +GPKG = ROOT.parent / "rasputin_data" / "corine2018_dtm10_utm33.gpkg" +MARGIN = 2 * 1e-3 # cli: margin = 2 * snap spacing, default 1 mm +# 22's table (bygdin/README.md on increment22-autocatchment), percent. +TABLE_22 = {333: 42.34, 332: 21.32, 322: 17.00, 512: 16.46, 335: 2.44, 412: 0.33, 142: 0.10} + + +def gpkg_geometry(blob: bytes) -> shapely.Geometry: + """A GeoPackage binary geometry: 8-byte header, envelope, then WKB.""" + flags = blob[3] + envelope = {0: 0, 1: 32, 2: 48, 3: 48, 4: 64}[(flags >> 1) & 0b111] + return shapely.from_wkb(blob[8 + envelope :]) + + +def corine(domain: shapely.Geometry) -> list[tuple[shapely.Geometry, int]]: + """Every CORINE polygon whose envelope meets the domain's, unclipped.""" + x0, y0, x1, y1 = domain.bounds + con = sqlite3.connect(f"file:{GPKG}?mode=ro", uri=True) + rows = con.execute( + "select c.geom, c.code_18 from corine2018 c join rtree_corine2018_geom r on c.fid = r.id " + "where r.maxx >= ? and r.minx <= ? and r.maxy >= ? and r.miny <= ?", + (x0, x1, y0, y1), + ).fetchall() + out = [(gpkg_geometry(g), int(code)) for g, code in rows] + return [(g, c) for g, c in out if g.intersects(domain)] + + +def read(path: Path) -> dict: + r = vtkPolyDataReader() + r.SetFileName(str(path)) + r.ReadAllFieldsOn() + r.Update() + pd = r.GetOutput() + arr = pd.GetCellData().GetArray("land_cover_code") + n_lines, n_polys = pd.GetNumberOfLines(), pd.GetNumberOfPolys() + lines = vtk_to_numpy(pd.GetLines().GetConnectivityArray()).reshape(-1, 2) + tris = vtk_to_numpy(pd.GetPolys().GetConnectivityArray()).reshape(-1, 3) + fd = pd.GetFieldData().GetAbstractArray("land_cover_codes") + return dict( + cells=pd.GetNumberOfCells(), n_lines=n_lines, n_polys=n_polys, + codes=None if arr is None else vtk_to_numpy(arr).astype(np.int64), + points=vtk_to_numpy(pd.GetPoints().GetData()).astype(np.float64), + tris=tris.astype(np.int64), lines=lines.astype(np.int64), + text=None if fd is None else fd.GetValue(0), + scalars=pd.GetCellData().GetScalars().GetName() if pd.GetCellData().GetScalars() else None, + ) # fmt: skip + + +def rounds(tri: np.ndarray, edges: np.ndarray) -> tuple[int, np.ndarray]: + """landcover.regions' loop, counting its hook rounds (outer iterations + that hooked something). Checked equal to regions() by the caller.""" + n = int(max(tri.max(), edges.max())) + 1 + key = lambda p: np.minimum(p[:, 0], p[:, 1]) * n + np.maximum(p[:, 0], p[:, 1]) # noqa: E731 + sides = np.concatenate([tri[:, [0, 1]], tri[:, [1, 2]], tri[:, [2, 0]]]) + keys, owner = key(sides), np.tile(np.arange(len(tri)), 3) + free = ~np.isin(keys, key(edges)) + order = np.argsort(keys[free], kind="stable") + keys, owner = keys[free][order], owner[free][order] + pair = np.flatnonzero(keys[1:] == keys[:-1]) + u, v = owner[pair], owner[pair + 1] + parent, count = np.arange(len(tri)), 0 + while True: + ru, rv = parent[u], parent[v] + if np.array_equal(ru, rv): + return count, parent + count += 1 + np.minimum.at(parent, np.maximum(ru, rv), np.minimum(ru, rv)) + while not np.array_equal(parent, jumped := parent[parent]): + parent = jumped + + +def oracle(xy, tri, polys) -> tuple[np.ndarray, np.ndarray]: + """The design's oracle (R1): each triangle's centroid against the + polygons, smallest area wins, ties to the smaller code; and the mask of + triangles with inradius > 1.5 * margin, where it is exact.""" + a, b, c = xy[tri[:, 0]], xy[tri[:, 1]], xy[tri[:, 2]] + per = sum(np.hypot(*(q - p).T) for p, q in ((b, c), (c, a), (a, b))) + cross = (b[:, 0] - a[:, 0]) * (c[:, 1] - a[:, 1]) - (c[:, 0] - a[:, 0]) * (b[:, 1] - a[:, 1]) + r = np.abs(cross) / per + tree = shapely.STRtree([p for p, _ in polys]) + found, which = tree.query(shapely.points((a + b + c) / 3), predicate="intersects") + area = np.array([p.area for p, _ in polys]) + code = np.array([k for _, k in polys]) + order = np.lexsort((code[which], area[which], found)) + found, which = found[order], which[order] + head = np.unique(found, return_index=True)[1] + out = np.zeros(len(tri), dtype=np.int64) + out[found[head]] = code[which[head]] + return out, r > 1.5 * MARGIN + + +def stats_phases(path: Path) -> dict[str, float]: + text = path.read_text() + out = {m[1]: float(m[2]) for m in re.finditer(r"^\| ([^|]+?) \| ([0-9.]+) \| [0-9.]+ % \|$", text, re.M)} + total = re.search(r"^\| \*?\*?total\*?\*? \| \*?\*?([0-9.]+)", text, re.M) + if total: + out["total"] = float(total[1]) + return out + + +def wall(path: Path) -> tuple[float, int]: + text = path.read_text() + return float(re.search(r"([0-9.]+) real", text)[1]), int(re.search(r"(\d+)\s+maximum resident", text)[1]) + + +def main(scratch: Path) -> None: + domain = shapely.from_geojson((HERE / "bygdin_reduced_t20.geojson").read_text()) + domain = shapely.union_all([g for g in shapely.get_parts(domain)]) if domain.geom_type == "GeometryCollection" else domain + polys = corine(domain) + ref: dict[int, float] = {} + for p, k in polys: + ref[k] = ref.get(k, 0.0) + shapely.intersection(p, domain).area + ref_total = sum(ref.values()) + print(f"CORINE polygons meeting the domain: {len(polys)}; clipped area {ref_total / 1e6:.6f} km2, domain {domain.area / 1e6:.6f} km2") + + for tol in ("10", "1"): + tag = f"lc_t{tol}" + print(f"\n== {tol} m ==") + m = read(scratch / f"{tag}_run1.vtk") + codes, tri, lines, pts = m["codes"], m["tris"], m["lines"], m["points"] + print(f"vtkPolyDataReader: {m['cells']} cells = {m['n_lines']} lines + {m['n_polys']} triangles; active SCALARS {m['scalars']}") + assert codes is not None, "land_cover_code missing" + print(f"land_cover_code: {len(codes)} values (one per cell: {len(codes) == m['cells']}); " + f"nonzero on lines: {int((codes[: m['n_lines']] != 0).sum())}") # fmt: skip + print(f"FieldData land_cover_codes: {m['text']!r}") + tc = codes[m["n_lines"] :] + xy = pts[:, :2] + a, b, c = xy[tri[:, 0]], xy[tri[:, 1]], xy[tri[:, 2]] + area = 0.5 * np.abs((b[:, 0] - a[:, 0]) * (c[:, 1] - a[:, 1]) - (c[:, 0] - a[:, 0]) * (b[:, 1] - a[:, 1])) + total = area.sum() + print(f"mesh area {total / 1e6:.6f} km2; triangles with code 0: {int((tc == 0).sum())}") + print("| code | mesh km2 | mesh share % | CORINE clipped share % | diff (pp) | 22's table % |") + worst = 0.0 + for k in sorted(set(ref) | set(np.unique(tc).tolist()), key=lambda k: -ref.get(k, 0)): + ms = 100 * area[tc == k].sum() / total + rs = 100 * ref.get(k, 0.0) / ref_total + worst = max(worst, abs(ms - rs)) + print(f"| {k} | {area[tc == k].sum() / 1e6:.6f} | {ms:.5f} | {rs:.5f} | {ms - rs:+.6f} | {TABLE_22.get(k, '-')} |") + print(f"largest share difference: {worst:.6f} pp; codes equal: {set(np.unique(tc).tolist()) == set(ref)}") + # I1: across every interior non-constraint edge, the codes agree. + n = len(pts) + key = lambda p: np.minimum(p[:, 0], p[:, 1]) * n + np.maximum(p[:, 0], p[:, 1]) # noqa: E731 + sides = np.concatenate([tri[:, [0, 1]], tri[:, [1, 2]], tri[:, [2, 0]]]) + k3, own = key(sides), np.tile(np.arange(len(tri)), 3) + free = ~np.isin(k3, key(lines)) + o = np.argsort(k3[free], kind="stable") + ks, ow = k3[free][o], own[free][o] + pr = np.flatnonzero(ks[1:] == ks[:-1]) + print(f"I1: {len(pr)} interior unconstrained edges, {int((tc[ow[pr]] != tc[ow[pr + 1]]).sum())} with differing codes") + # I2: the centroid oracle. + want, exact = oracle(xy, tri, polys) + print(f"I2: oracle checks {int(exact.sum())} of {len(tri)} triangles (r > {1.5 * MARGIN} m); " + f"disagreements {int((want[exact] != tc[exact]).sum())}; " + f"disagreements on the {int((~exact).sum())} unchecked: {int((want[~exact] != tc[~exact]).sum())}") # fmt: skip + nr, parent = rounds(tri, lines) + same = np.array_equal(parent, regions(tri, lines)) + print(f"union-find: {nr} hook rounds; {len(np.unique(parent))} components; equal to landcover.regions: {same}") + # The ASCII run: same codes, and bench.quality. + asc = read(scratch / f"{tag}_ascii.vtk") + print(f"ASCII run codes equal to binary run1: {np.array_equal(asc['codes'], codes)}") + vm = bench.read_vtk_ascii(scratch / f"{tag}_ascii.vtk") + err = (HERE / "logs" / f"{tag}_ascii.err").read_text() + max_err = float(re.search(r"achieved max error ([0-9.e+-]+)", err)[1]) + q = bench.quality(vm.points, vm.triangles, vm.edges, float(tol), max_err) + print(f"quality: worst angle {q.worst_angle:.5f} deg, max degree {q.max_degree}, " + f"max error {max_err} (within: {q.within_tolerance}), Delaunay checked/ambiguous/violations " + f"{q.delaunay_checked}/{q.delaunay_ambiguous}/{q.delaunay_violations}") # fmt: skip + # Times over the three binary runs. + runs = [stats_phases(HERE / "logs" / f"{tag}_run{i}.stats.md") for i in (1, 2, 3)] + walls = [wall(HERE / "logs" / f"{tag}_run{i}.err") for i in (1, 2, 3)] + for ph in ("land cover", "refine", "features clip", "total"): + vals = [r.get(ph, float("nan")) for r in runs] + print(f"time {ph}: median {statistics.median(vals):.3f} s (runs {', '.join(f'{v:.3f}' for v in vals)})") + print(f"wall: median {statistics.median(w for w, _ in walls):.2f} s (runs {', '.join(f'{w:.2f}' for w, _ in walls)}); " + f"max RSS median {statistics.median(r for _, r in walls) / 1e6:.0f} MB") # fmt: skip + for i in (1, 2, 3): + line = [ln for ln in (HERE / "logs" / f"{tag}_run{i}.err").read_text().splitlines() if ln.startswith("land cover")] + print(f"run{i} stderr: {line[0]}") + + +if __name__ == "__main__": + main(Path(sys.argv[1])) diff --git a/docs/benchmarks/2026-09-29/bygdin-landcover/bygdin_landcover_oblique.png b/docs/benchmarks/2026-09-29/bygdin-landcover/bygdin_landcover_oblique.png new file mode 100644 index 0000000000000000000000000000000000000000..fcb7defc6cbc509f3a71ff78ceb7a815f46a4edb GIT binary patch literal 883809 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

tkOkis1qa36%5}(` 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

N!oqXoE1}I}H$nsMl*>Vw z#|~t7K%oxfexjMZ}_t|$+2^Pgo*y1y8Ii30yHF9WM&!AV*Y$j`UUYvVQ zhntG4juMN}aow7<+cXR|96eu6Z+c;Tiw&tzVH2dVIe)*N zC)uyuIrXYq#xjgCrS*;X;!Z?lHpYe=YI$u{dHEIb)5!gJtdr`nMov|&$gWJ2ietY- z*IHMok#@1S*oMTdRWY1uuw&0RG?98yYu#(KCziL9sU9n&%RELHbHY`cq1of>EIA$G7CMEs~~_L$I6D*>!)!)`I5_`-cc~RbkT8j z79IN_*EY25EaN*{7qnDLkfq%h!b;{Qv(5Ykj#YmQP4}&wGO>J@Muawvh#Ft03`ZMh z6qHqeHLBc}_49BS1Vv+5+1wfKKZd!1{q&oA13J_rA#b^p)z>w^b7vG3FQdBELF8@U z--X!_HX*(ip)i5@H%(X1Y~KEs^11$(N#pvd`d&BPAayhZ_3wd z511p0&4KJ#bLapQ>L#ws#Mj}#-ebd+2k3&3tVr8MI!F<-7!*RcB4Kzw|LJ(1&><58 zbFTM7B0yq-=!iT6Nv%zWg?*5#`f&=lcY~YJnC;Sr00Dhs^mX#3Ta_anR`ErpEnm_D z6qDN5KXKkUVoc;1y>sB15+;N&cmS9#GbPSrEU}EG5!oyeO=EOe5Jo^(tMR$uoySi& z=O|GW#dYU!eeqRG5y6RVBH|S3d_U&Za6V5-EL1O`L;x^P@yXk_A3XW!z(*fEr3B1x zz4bVcY=|-DqJ2(M@5AS5xDatdq7)9p>G|o0Z@Ks1yM5=;quUrG0F2}G`R6b0@6Qhp z=d=z@B%(Q{;C+lK#yF1C`Er?Kj42%sd+%~5!U{Kd=aY+w%XmgTg^`HzILrzR5i<}W zA`vA_D_(=lE5cCcrlV%&TRMY88Gu(RgzVPlGJiCs`C)iDhC7}aCJ-Ye7w@}^w}Ezn zcZAG|a(?ce7jv@Ius$4$URz}L&NJ|@{k88L4*T=@ z^7{3|{mV~op1j}n(OmqL;!^MiH2(%T)@A}=+%Y%E1;5EycSiDZ-^{W0<}8<)rKeEZ zQ{A!ww@wP=K)ojT)rP0E(D*n+OCMcN3DQP5X5uCU3o^iYH*Jv1(9>ykDmg}X3od1mGgIgXp$ zusArB^XPlG+xoH%`wT(P7=W#ze$q#-b3L(hVbH9O2@dQLXG^L_IiXUXHttPF-ZrmA zoq{)R6t@FZxA)kZEv-bQ(o{LEP)yS1&MP1qjZ9nz6ifL-^DxHs>R)NjH?Cvtt|sTW z&RQ@C|I$;WjF+{Q+bafFogX$k+Zov+AW~%Rdt!MexdR-)hHDhJ1eYXXec$WO@-Iwj zxN^G8`?5R}y;bF|+hIu~?Ca(&W1Cn3SnKdGhA;BIW=gPJd3C5Zq8u5Gv5E`XoageH zFJJ!g{rSORov`4T$sr}2G11bbAoUF(^U`w`?YswuG0mQwC*>j%BJ8q+0t9w$M2Cel zu*fkwmr)0q(m`e-q)1GR2^WpV6*4XokIp+_h>%j^Jl`Rg3d7FNQzT|7xKL)UFhfd7 zYPQSqi?xjcfElK7$}EpO0yCSorJEpE_;Jyy%-{<;jB|Q;7-NjXZeW1jJ~-zP7?H<^ z$qzox2>?zv`^V}1TYfqWftfFtX}{m+2cD)mB|4psS%x6`%bD&5@>xhS;yl59nEd_C zr$aj4-rme}yj&&#IGv8W{cgM`9r^$OAOJ~3K~%RNF6S|&6cf$UjBZAN5GbXv+YK?( z9L71NR7PFqfeTp&o`l!UNH2WY5?vJh)n+bCAl zVI?&z0+GdusMP(u8~o!b+(#Ux8}YOYv>VdNIb3uRq|znJk)6PjQL$njx-o90yFi`I z%(}I#3adzXfh5XMI6Dk^@QQaIe)#UAM>in^@BQ(3n5Oyi>W4Q^-unWE5qo$u_A?5b zDr_nQaFw)F5ycm_(g^lfI$yo{5(vF9d++>P>(SI? zijJD5iY_*yMuaWT0zD5dITRhyHuL%1=XNSRKWwIKm{3#un$BoNmBw_|+}Z*%N*MlNT&ZQ3Gn0c${x(3g1NjnApiAR;+#OOW+Y^QhmP?dQvnRo8fRRPsnPoC3IVRfH(xW}qWr zM>$}^n1fzDt@A}_)^lsi%4*9qhCe}0Lu3BrO|jQ-#T2o(O6?_Qjrit|y>{lCb$ipd zz(&YouPT?2q87|LftrCxxrMRCU!VQ({^hIBmmUEG?_HJ{*zJdK2;fB;wn`x&q8~hn zp+-0dLgJBR`zs;ENEiVSW<(DzL|2AQ6w1ItDPN@+oJUHO7FB{g5+L*XanX$SEgc9k z()nQ=A0`I&o{p#e?l5FiHJxMF`6B(m%#Vi0!?27%9)>{cP|q0SG*83u1Xg5(5>aqj z+7KN8yLpWBF5cb#+41370{~{uyIWjT( zL@AK>$vY1KS*0R`P-GYK0nGzL42&>Ob8;>O3W4&ndHpk>83EAb=R$5&<@_M{05yfr zR0JZLU~soS9OmI6;grvvPrD(Vh$)ZDRB%WAe3l#ualF?#TID=qx0H`t0d$?#$yM^f zAvkm)tVoV`S)d`NL`0X%I1FLG-=X*Al1dNGt%5RNNmmvQ-U@;P_iQ*fiIb(WR^2k& ziPewMh1OS(Aqd*o@)bCK%c*rdhLA%3k(7iOY5lH@ElD^vZOa^YPg7LOO2;==yr=1 zI%H)PJDIZOLXuI@9rvI&yDuVWho0s?uCoi9rC=jOX6|(jidgLkc9k7kQ@#=&^j_$UQ`|N+2GZr$g!b|1UPjHE&sMv=SJsO7aLXN# zfQVd#^@7*-arrv-o@yF>K7lf}H;dUdEFC5bYkwjCzEMBkuI-ggG3f@IT-c;4%ZIE( zz&lx+2nJ-{D!)A_E5p#K-U#-5jabluN)&h6S|>(I&B2d&@-;6pC=V z#AL5TFtt$AJLM)u+$i8|+N>5p=|CaSr)Egw1zohYL2Zm1v*8i&mFNEpLJUGEUd0(r zqHgLNCRS*pv}Qr9;g$p&6r@2@lM(CFDb3XplE7lLpN+iD=GI_>vA@L|6-!(&TSegrb1QB z-_ArY_TS&r6Xpd^$qTalE-)}Zesp^{?C<`&Ap%be16*(sw4dAz0LJyS|y${#v~ zI{+BsiHRYLh**?CehbU5Uc^Kszh#5UYK0pR5x{%DqB0a?3c=@gq;fHH&(1hbyZr#b zDW!3ocEb>cu-^?h{zzxkeI{TrR-0u{k)drdp^VL3K9f(Wn6}fF`w*D77(phNokT<$clk;5RhV)bYtD&<(I%Hu} z^d4-LC`>HD@CAZ$6ZJ8gP`eZLu?@jhc>N|oXFJyg#qed7dFE1z1e$Gu8rXF+s)u((9QWY^`XU)NC;h8m7m3Jy~4@6s5s7 zuc;n?T~aex0)$3`FbAkhk>zAG?gX3r18lLe#fKT{fl8YhHmainDOkz&E19?(4Qz5R zOYwwjPkM(CQnGQ>hNFm9Gb1!#-EuL+4|IV>E#$y4Y?h8z$l~ z&Q&^2{t0O``7%i?s)1VJ?6 zuk>(RMG_gZ2m@V}kHnxkc=p~$&))k;0%w2e=YRG0fB(OHefNJ&W8CkM9QxpyCRd3tm6=&;*`VHW@}#yCxBxAV>;re&~&$72X#`D%y3J6fkvBH%Ct?;SBA;0O{eG7_GV(fl=V&*qYLPV0b4s)h zu2UVn;zj_vsq9h(*8pmn38xn!{{?R*6o(d<$Z{`4Q`UsoDYo4Rbd2DcOJ3^ps4ba5K z)lxwxc(+feMyuO1z@5@Xr&z7B9sO*Q4ch!-p(*tYd#PtsKi*?Ua-UqF-(rJ6%KZXe z4R}~#88s@~Ovu^9)2d3LDT;=YUC#-0*w-~`>TTSb59|0F!8CyuS7NG1U8Ko4K9jPR zhp{!lT7pt*UIgE3UXefFRI0d!zIwDrrRg4WObb$k2Ee4a>yZYq zXjkbkVBV}vss&NgDVVz_mh};;%VI~8o2Lsl95D>i1&p`PK|yI;U2 z>E((-W=Rc1qlJ9+^+Yvt|7DP~DK1J#Lc*gG z7v3tTEu{jeholi)P#BzQGwF4K+0t*Xrf3*&by)+J z2N%_Vbx{%f>1c`T6icM}hfbCyffcT5vb?%$A*c%m?2vfF(&jDUYW-fS7sui+42XI# zs%rC+r?3v-(mAK`#IifMu%YS)bEU*1Yt%;EN2yTI3P5NYf(c2*9gudjVMUrrVUl2} z?HY(%4h;O;|L8Mm(1nhTjk>tksUgP5sLFo1u5CYp5SD3%)zneKYKj!<02&pg2)c+~ zJus{RGSQ3Jah9n06kP(C4QLw5qM?2op*&Eo)PxJgt9||JfBhf+qu)OfpI?me;Erz{ za`m2hoqlM<(LL}U;EdyW^1-D@01$ykjS2M%ThB#y7yt+k2t18hz=l4)BODTi|5Z@z4zWbr_*to zW+K|{h8QD}%dCKmI3%JFd>&xA$|4ZcVrNl~4k1ZGfGpy`tGH${_yCX+CCU(k6Og$Q zRx{9p;8O_M_w`QY7~eU_jWi$-fruj_5V1>xkHan=g{b86UsMGlr2Hg^S)5obTQfJ_ z!U_&G!k9mB0b3ZwRB99k`QdztbDZXRnr3(b<3#Vh_raq_@7&#InoN4};t$W4za3M0 z`~5HH&wujlLtuXN^xcvH=BxUD{=NVCcmK!#Y8Zxb96$QjFMj9Oe&e0@KFlsa78v_v zdr=sXwJd02bWlS*WdTZI_iMHwN5++z5@?D6O~9wJoz|+ODnmg_#m8LQs_DeF#D1gi zy;UV(BVKAzY3{eS$!+T9crq-JIck;8H0X#1aa8qqgJ6h4xk|)wiZ>_9*L7$Ln$}RL zd2h!@)Bq3aR6(-G3tb0R)%D&0G2n4hXaCusWZlSK0&E=HQQkrhK0I2KI=BRLGNU;vHK=!$@d8rB`YV>4{odz zL%&J14WIxTSyLdX)X6|gKPeUHE`BGL7uvxaMTuA(#ZYXk@b21D3v|FHuB=9;)g(#| zx-}V&7!nHtrn1vH%UKjDRyotCyDpKd!`oYCQF z<_L+6C|NXHcaBPeRt8(Bt52-1qK?2&%)mu1LvhvCu!j8gJ@2sLT1&d3q|6%)BPeWVsAFo!>??7O`!UQ=ZA7QMx;n8?#`q z%*wwKM$b%iM@qau49C;%G2fi;$Jd{o_fK}tyDS4)qt$gdBoh+?@0c81E>kX&JH)UH z&LKDigaQ5J&ck6p{Q!vhbUL8JojXyY8Ri&q$HOo`8ukpk#KR2x7=q7$0y;-C>1YQ4 zfQeI4Y7p)5v`*iYmk9yE0T7UbI**Z7tO1!A(IWyP)iqvHUj(2PEv25O)4} z+WFuSfe7N9Ql#B}aBgYk$o}9EPM6`8b8i0*kb9V7@@~kfyAN5E5t%vqk%*QN4?@r& zC5lswiH@h;Tkju+3*pP-`Q__}hx6;#_kZ-qfAaLL$8W#$76ZqWQcC4%r)gs5GDsq= zgC$b_7Lje%4wnMr0XW2A2zeOi`7$!m-Tm3S-1C9x93?uQ4jB$$rJSK9w2C2P9qilT z?NxucG{5Ah%lwt&^puEVOeN&2N&(9lO%l?Kl>-Q_a_cKbpzE4YSRRX72dfWi^ePy5 z?HO?(=4CR`_rL!`X1<&+Z#{kgOJDlRFbw7tTY9r9I7{T(EC?&lu3v{uE-KJ1RMC|9ebA}PDqAj6H8azXZO&kMrrPn9 zpVUN7>(szXt_zkWs#0^0mXCF6sW>VU-*2Iu1tUw#)QouthL$g@`H;oU5%F#m& zsx7xoNdkb-qyU}jn;1!Mt3VG+j3={(wvR*pZx9=;l4qSq2uh9=d0~Zl0m?!zboBxx zj|1wEJr(|~FJb{2L3cH)UCU6i5G6r0P$g0W`qbiFlk5n}5X7;*=-Wi?Qh zQI?RQVFp>I`I5kv9u|Ws2o{4aWQvnq!J2hdm+cal6b&iEoCHa!(>MhiuupAwr?#gc zTFw3tnu1^z{tQ(|BZJCF)_h-xUZK9o!9I{^V<}F31oyf;CjJI(1OYntDIiv#oFemO6e-e~m;g`scsvXicy#uAYL% z(RK%+Bs#8!yB$@MDf_n0pdt*|guQfElw9%d=~CsC`UxyTG?vhE{*$}A@8wQ%CJuwY z<-_^q!~?jUlR!g#r&QBmCfXeaO2ovJC`Ast;1IocH%|@}iT5dfLd1I}4Ct4n6o81C z*u~&^4-?QOrsF+@`H&uypC-5vu?}CCm(6_>ytYmd!K%y0-~Z&@$2Sj%@aX0tH=>|J zWMo=~YXY(?Yddhx0pN<0BOt22(}fX_pgHc#rg`r|2pMH?&dqb2=NN`8w23LE5`Mip zeg81*7MX*{Pw7iUoKhYki6Mlg34$0LFf0<7OK(SFN-4&4Jnavs9U!EeyTlLaj$=%p zeg5L<)5lNWdNS;SL+8DVF=cQhAAoV3a+}EtH>5;48?V%YwD*1DQ4KZ0IL{zk=Lp8@xGXMX=*^grAKDv`g-pv zr7R9o+Gp$t2r7V+IlZ-OLgEfe#h`FkyMGZo=f-iG=LiV9VaU3V`Jnvpho2Hr@Xq=0 z&O7fvdi3mgJRS~*5W$NQ?(WVxcRHPZ^I!k>-g)=KTK{C*xhid4i$O@DS;-~rP$KC8wGWaJi(>y3 zQ>Qx=3~;Yna}AZRIKIlk7L-)9X3bC`N^cw^T;fe zjjD;%2T@>wsBTzDkyC#?KT25 z*&5dbNrvl|wxHr_mH8BWWH~UpT8oG+a|iXI1sxs9M@3V@6yIw?IKj11yc_v2wo?_X zOq+rB#gL$;-&vP)s!5Udr%g24%-`IX<`+@0ebE$38;U{QAi)h^y;zkiP-JrrZIoX` z(;rmZ{6$;`RiwAp^PAk>yuQSdL0J!V&Q-f~W58ASr3Nk;qz(Xs&b$yRqePxJT^_F2 z5Jh!xEq$rnP}Zu89b=tyLDOe#5=nD@mcpt$B;`VhmNe7=T$^~x%t%s}Or5&{GHfCo z+B6$hAV8C&RM{DY>ynpHIU!b?!OcN8H%F|jq_~K_k>b=1b4L0Mef>0~##*N6A|wYLw>fw`*}>`-58utjwvkF8X!S~ zArYiVN>(UrMbIy@A{^leJ^0N(!G80rBmCm9_2OVyQdkN{I5gO@WXY0(ltm3700CeV zAb>_sRb5?O)2%z4bM}y#%MZDC=31HiHYJhOWTW~H=j@Z?THpGJY&=+2GX zi$zn_rPf+2WsJ4X_WfXt)mo!ceY>}#8ix(-yJAV}@}eeKm8MNur8x~{9L zDwR^E*_JCP+q&*5RBE$c0no+et}M%=qXXw?x7!6)+(ipF?O&CF!^GSyhos2(M34*_tp}4ZHL0m{^2UR5v9x#v1S^Qigg)pju~($i5^hY zc;lH8&MK0U(>Zd-L|7o4g?N&LO&lJfUFEE)fkg5!JIWzKL)J5}s`BB(~$!kJN@BBRJ&ml>u1kIh*ez z;UofSq{W{#-v!C*jmGGhe{Z4=GG$s8xn|fW2^Y|?viV$R^N0y|iJj~Kq^zb#2U=mf zIWN}A4A!?rRG^HpKxFn7ZFyvG7Fr`>X%`p6g+i>Tag`a?yT&YQTCCKHcBHzOz3S?A z)z+5)r20}1rReuF4W+f#SaL3&?`|4Q-6`t|XH~hs(yVfG$8+V@rz-Dy`zNe!?AaK@iA_73ALgX@zu{9PI z2C*A4^HoGF3O$V|dXj}_HzXQLrIk`>t@C{-^+N5-Dm3>ar}Xv0AILEZws078d}3 zh2AgQWqq_ThgvHls;g2VDuu?_<+2%!vBo*)n9J$Zc(K3m-d5J0c^*q2$L3S3*go~c%XE`XJ zh0Y|1VAJwp7_@M6oD~)GoGKw9N0XNDb~4*RCdI)klKGI^;2fncrC?C#G)VbTJf9Ne z%yu4!B)6)pa6l&36M7`J!%&7>m~cI0D2Oa~pGjBp0!(tf@^Cca+h0-r3r1flK{8Qc zDrL`IE<_7POW_mk1Ej79 zX{I2O4zYbiE}YyEy!JerK_)*Zat%uIt0X_?i2U5=OqhzSM_V>q$q>VgHeoJ?5Yzx^ zv4PAmC82YNTO{*_h-(?S6kqy|J-qV^;JirhMpgnQsX+V z3T8(Vr-KW2^4H-MvYExh&+$o~#~O{o9FQyT#lREdcr(mI5@ILvPnyPI%%2lxuGOSL zcciLP#CgS&N3Wvv#gb@1q!w~8fv$}H^N+^%`1gPJZ!3i_K7Pli&3IXC>pcKMzao9v zQR}SjJF~*0$}TU=r6cmPh(edT@SsCgoK**PO)4tev5U}qEKVNr_ehTTI;;c zgm-fs`!Xp&!pYom#2xzLe1Hr8fJ!M#)+30Hi8TVs;GENi(uH>8=c3(ZyT9zZ*1({O zlVNeKs+It*b35&vtLqAn`a<=qt|`i4Z`~S~hesQ4XM>Z;0`TnhI@ zV~o~#_wKExsZp&Ft=76MOXu8vzqi(QT^EQ9N-5u>wd=aB>n<)Xo;-PSd3jkBh1S{_ z%Q51Du*LB@qJcIcd8fA_R=g*%Y;=%Dvmge!oH3<{Acn?A} zst0LNghC6Jv+?=Gfs`U3Npv;;{K9lrC^{KinAggP$B-z&ZegjDq+u+9;mk^LQxicN z7YV|1cz~P=3YmWdlVK=oKQ#Or~-UT>@#umEGoP2q$jsH2zFWSt;hl zU|*2UbA?K07G;QpU1Tu~q?0=nBxF-2bTrwZ3AK07s3+({E&xer6f=uCN7M4kZ>}3< zyBL#?lTc`Z%^+S;9FnBwm_QQ9$T`9ISnLNgm9RAanaTYqpH0Z={i(1n>{jA+MP_)0 zC{YXo;#nD{Wx_Of7h59@v7&9BOc#m!`UpTeo+sIsNy2iB^BW19GMNFz=2s5*vc9u-h>e%=XIo~R` z_Mc;u&3rCQNx>q17Kdu=ngm&af2MK?S;Che)H!J+7Mqj&%X#npCKYqOxje%z+^? zuqPs|H7P(urHC9k<;bG%%jn(y2ji^`!A(*DZxAq!J4PN{Jo(0MmLk1F!1K2FNAE5D zXMBBazIda1u{-|u;~Jm?>3~Bp>&WHLD8(j_fB>}q_-yg;Wdm^JpXT0i`-NAYD~(47 ztu7P+Sp(Ks0Psx@NRiMxkN<~7i2&NWY5L4CTWv|dE-DJGaBA}jUK}y?%H@gxf{_{k zpp?>u0wU+YIkJQ6+M(|a0BK!dWt2wesIATx8m{ZxR1NOHkgct?bXKdK+P0Su9(+=k zWmy)8Se8Xul}%mMb)~f~3aym7cI_~rHNHiK&&+rAxz@_I)*4G%DW%A|Jze(2t}F}X zl%=-5Z|w_LZWmf>AGMgl41=-WKf_xzPU4ZIiDYOP!98RNfO&9?phEnQX`r}_!Vx)Z zt#j5`0`RR;5wOGpMjr}5Mrcr4g~-y;6+l>6jLr!nC<|?kwTq_NY}VWD?zPwMES9TJ zA8&_Y003i*F~&L9w(WkuC!(?}J%ZEsy)gz6Pft%@ym;X!`r_hZwOTC}3nH@C9v>fn z{p(*}tyVs_Of`>$>&Hy*JkD}#pYPW2#&I&hPZ!N%Jhwdy6iCLF7Eth=gdB?OA&!d9yQXHwZt?a*Da3e02PI2*a_M=Vm5VX4vPzRT zk#F;)6hqU!RC&rAkBMXMQVatLk(0MA{49b4OY&og#nOpcIDnm@V(rai2T3@MDZ?8R z=4_f*CrHOk2~9?)!1-^PbSSF_$jO>PzSvCBOuaFVmuPU#M@Hgoq#Ami~Q95Xcnot85I-Qv^_!kz+_XX9I5QkYwoiCttrVw_*Lw^gSlEO) zB9ndg1fj(_Ap>S>Fa7zM?G4fsC|PGI)_B#_lK^zstswRnN9E$0mjyTw<4Ei@nFpuR z6GMQaG=nh-u`9_n^Uwa_Z~gR#zg^S_0Q-Ii0CmySw9;B@($10352GpvV>3dflrFsa zo$|SyQmXLjC8Mb#0xC5of>VF5fZGKHgs&}2Z)%7+AJK;|ns0qtr#E(nr_V>;bDTsm zNdehKHLCd)zJ6jp|LXID)rGU9v{D+Cn*OcEDmW@s2$QMcsQ7Tk`SWT4 zT;73ZC@$B}FQ1=2KWoeem0C2lQpyqaUBBJ#0imi&^uCBhh*(t>2B2f)JE+B?0U%op zm(8brXZmhnCS|0xDoX9WIs6cn`!!xH!XyoYF~$zY0MY0)5!z}-UITHYAmA*Fw!qN^ z_7z5I7$p*pth1gn5EmFTl~hUz(1*jWlWKz~_mR&yU{m82W9-vsFOH87-+be~QrPwV z{d;#2(HQ&e`R@7i*1J5mZM)m;thGf^6h-0b4Axozc=6)J#l^)i48|BwXK0!R5ic(< zfAv>?_3-e}n@1XBcDvnXvnh&#i0=H%*Kgkc*(BHc(f7Xj_{aZd7zWRM2&NDF{r=+O z;!9uql?CqGw*AVV`!9(f*CdFb+3gJT*h{=<;-8n#9v_|DPP3g%YC%H(EJ6#8!KZDf z#0^C=XDLUYXR@97^h4f#FpFiR_hUl*lr`M^#;2J(8fJ;;QRExTWk;ccE56m*cJ;k0@;r67oj6vz`mOU-7v^mX76X!X(NVDh6Dk~xv6TI}9 z>^H4{WCTe95n;~J8zr_yDe#;mOT7OtD=#GrAa;pmS3Dci$WcmLFeY`lE8zptD1?!N zvzSK`NGS)KSWF_LM3JcTkT{F*vmi~*vRGYg$$?zTkc4?j%Prusx=35(g3LBsiVzXN zl?PqYh%-sJaHcCcBN<@(fH*TB$&$Y-foiXnJwcoCWeSY9c?z zlpvf!umrNSE6Qdf457FWK=jpP*9@kW6H18a z$T4B^c;rMqM9NQL#OOIO*``9o{=9MA-`?+IXc_l2uL~CN-=1HcwEr2zc;LDtCevF5;UpG<6 z!6{Bmz~cX&cqfkjYH6!m$$$V#ZVdkh-~FHe3;5&x`Gx=bZ*iFc3tFe4klxNo4{nUJ z09<%UvG#?5?2vgqgl`dJ)AOb{2@h6u()E(K;Dada8F;_$S%fvx0K^r@eS#@L7A8VX za;Xr4183BJ=trfzqQZ8thh5?7sZu*}%K>~_1pnKPT^-#l|lfptF0!ra!3B7l%*X8Qw&4j zdxQe5^Z#~%mh5<+snEt`YEdcig~(-K5QFn>pw?RJTs86q#%s|d3ouzN_}@?%ysKrC z31GImLGO|1_z)4mSnF*yx4Yfp;rf-kx5}yjqSb2Q0VZQ?Q^PO}=jZ3==jVOj13=R> z!!QJo1|oX+@Zm5Fh**~8a=F}WHp4Is!*FtP^2@*c%bU%{_h%V~!5`SeFtlykw(Yn6 z=3l-4v;PrGg6RD}_~vV`y|!E~o2KzZ31iHDzxO|1mL+zlRM!CT-gkfdb6@>6-pM$n z&P6S>Or@sd<>}*(0qDl$VG#uLoj z2gRI6V%*#a8Ck3F#2A8-T#@NTNEVJ*7Q%d3$4?lWzex{^P+Uw!sXnwIg4Co?7F3bP z5GcqDAXi6T9&_Z8K$LupmDK zLE|D}g;~JZy9%SmQ!+;)8pyz!tX1K(EJC9eN}}Q%L1T5`1d?D`*%Tl_4i&_Hu`^YD z#_?oyOIFsxEEZx=MIb?h4zWz6z@p;JWk5Q0MPqypAFwPGDS>a;)+4FF2Y!t30@4(L zr|f{Npa>FIB{tfuDjDCM7s6dmDk2RBFq@adK&M~DuPgiNC5oMqnM^9Naz^Ox% z)0mK^C3c7;U8*J6&yEtx9I9sBa@gNvxsF&TsL&CXU@k6+_#88nnka?|lnUIzU1C`L zbek&49B-pA{^KRVBrA<=?&Jy)U^cPtFIVuA|2_W@e}35lXIhgXsHKQO zOczEBV=>;@T--rX=oI^&CtxZIOJWQWE}opTAb3C#ALKiT_>oU3ua88BSAdJtPal8$ z{y+WOfA#GA;wyjZKfU+nm)`%;?|=Q*{~`k1y!|Qxot{2Ihk8<>lXhok58wOAJGXBC zjI;E^cm5#&6s0aE=R)dDwXT```* z^snSUeTPX#lt3hfqYF->6~1Kfgi+8UofZ(pM%Fz*A{Nrw4&7a16pbV%WhLW9($(K+aKJl}%Fz=){J` zm~L#r?U&TZqZ#Ejv0?QDFR%_;0dyc5jIqWVJ6fgIO$7v1RTYKqyJ53h8DsZtx4qmc zRi2-pKYH{C5seuDaKGOV!=RM%;6h#3hlht+>#C}}S%Vi-eB&G6@OZ)4$HZFa+%OEz zxwEsg%gf70j~?B)apT3q9|a`nCrSQJHBmQB-~J$~=O zkH2^CXTCZ%=>t)43vCYq>Mcu=Bg!9As&NKU)~{H05byS z%|Qj6S0S=?w?sn4Da_}?FA=A#$M7Kb;K69y#zHlU-~=Uhz=W(!#}%D|V=r7Pco;y; zjf1AnnaPasHt*ve2w*pFk~*EF7(W6*n(vE(^ISoTB;|v5e zFaIv-mjY;uA0b8stPYe4>?Iz7g3G{iMHG~TAns!h#;L7HC=??X<3Q#ymJm4-qD^FY zlXOdor8TjtQTj4Oh9R*mj!@*8B8f=!_N=5wfkA@2TQr8_B}_$vqY#rz1h^#^E~KsE z<1!Ph&nZ77UbCDRTu6o&bDlwx3z8esQP3V^gefT%)UzId53lDMf8T8$KfM~)1~FIb=5gys+O$U;ap{4tRU6?C2xQ4}xC zCkukHC5!J9P4j;ncO|NwIS}nG1eE z5bt}(q)hD2KoTP&q{}29E=_FMnZ*ykP#zCuXK~y!-U0930{kLRS#ddX6I(B!)O3;{ z=S!>35KKr2mNi@@6geXjXO%}846iv5cXT99kdS085&&u((ikN$+aiAG@a0J2&L$Qd zFkT)?_O_%#K-QC17PrHGCRu6-bpxSKBayE{E>BD(`UIl!IP1D97=|&i8yif6=th?m zjuA5*7;(cD-U|94(Idgs>dlf|O>>F@vcM?d@*uYc~9zx%)Zw`Q zU9@dy7WKN;i=j9D&^t%{(0j%^zk;<=x+t_ltu;}g_g?`wu)mMZEManBWQ`B}5B;4# zJOls$iJOe=yMMiV>y69fYlAms)OtL5 zlvbdGK}|?e$3B$o4SjD11Ox>xj3?W7V;xxuPK?j$<0C8NzKN@K1BgniqSU(7O5uLj z7Z*$0c5U0WZM$Btma9e6)W#T}f)0!AzI;iJz}Q89pp+s4r5pebs_mimk2Klq zmz2ud?;af;c?<#(-+%x8J9qAEHk-06 zi=yyc6>kY~c6RpGTW_^(>s=xI&jY~u(+3yN9(w#@b8_#kU;6WqLDhmYjBxtwF%dlZ zaiKtJAT6pDYs>dTi#)ak|fXS-lhvH$pOqeAHheS9% zz6L^?4oOe`kiaMPiX_4WhQ|?)gM@W;4Bd5#>hNCrLdbtv-}E=uyL96`*0_>zYd8Y1B)M*J`#(NB(8 zBp-38B*H<-H6saLhAo@0p2M_q+p|P8O+xzlLaoHIjQAKLGeeN{ z@R=Ga`^c0vw-B%?%NC`Y3OQKtkYybKW?a+U-vUYG7CorK+kh&lH@)5xHM^xOEb~PdFkIq>tB$B$`HY(SmSA z4>KNi3gXO~L7+r+#$Gd#ANz+qd;a*NAN?DxbzN7tZ=WpA>fOG5_VjeKSrgH>{`voT z?b_jT*=)BvW9*CPFF*M3VO`fnp@+c?gK^Gz7X8uTrXz4mvSnxXWG#VFnlxa&L(}3J z0C-tMT~&$wo}YVT3@K%-i$WcgaDy-4`&4f-Iqa>=;Aj&|Kj~e`-#6>i=VqVzSb9|1_Gs2sc`DwG|~;iO-@A42~VyOF!fvpI|T-@btZbQgqq?8a+IJ9 zo_b)78+vPvGlM-iT6u^@YgH858ateUyL9_~I~Y?IMO{~AQE08(w(D_!(LC0L)}TPc zpwF#po8fR!ol*N9_s%-&gxCGh8LL&*j2sSKDnPK_@<6L;Ne~eAtx<@&&}J}QI~Zfh zQAf`gcpaQ}&e`$*$951ATd)ysnBawR0MF0c%gc+^sv$>JU7BHp7%ne&RbBRdZw5OU z3qam-qU-2IZx&4zR@7)EqLudl)*6T$FU=7MKx0zDI7i-3!V6w3Glg)@b!~U=wYxWN zT=z{zoV7%5yWMH6&(1H~ef!~ukIJh2m2Z6Y+4EE95YTw9(5k9}8Kh?!SZlqp!k51E zrGtZmx~@H5fQTcoG7P)j&KR>?F3--+j*gD%y7nj4hY8srgALC**h9b&&I?6}5SiN}VRmxOQXzz}FQ;Tu zQuP!PS23_4f&^YLXBkl%81gVoFxr!#pI9!JS1A*>#>BjC3Q7g`!IMT&XJ+B0_7j=g}cLToI3@>5QtOpB5&6PG=SV&_QG zq1iAjDnw*n7Uc#cQ`}T{z&Yj|K?uK_Kq367NnFoB*0*KWhAAN^DZpIOzfOA5VcPnK zC@Ce%svd_6iOX$RsvTDg@s>R^kv7tdmRWcZa7_))d8?BU(Q_;&$iV})B_4u7OX64f z^P`X#knF=mnWs!R5Fqc_NtxtdsId7f&o-t9Q8W#cRmenIXggh*7~0W+#qy@$+*ccP2V;!wy6*V+=-Knr zrm2q(4}4C$+wIG;0D`)z>Z~9HplDL!NIyL%f3fri@rNQJHIsM z0RWX{vCz$a-w}~r9ew^S1c2Mu;o7s4KX`OJ-+~AKT~?E1PGWRP@LYliFRO>&+aRi6 zeB6GW35?#4~4euF4W^7BJkQ3XNkjR&f5Lf zmSs^?x-7M42arYg*qd!?)q6`i<++k+o)o6RZ_P2J4gQF^i8xhUYIX zZr;3qa#9=|^h9)ddAZ%T`~7~u-#>r;vZ|`8a_jZ_@MyK!Y~FqM-QA9y^IbxUCr`H? z5onrXvuQjKLk=9dqA2RREQ^9+UE^j8tucu}DJ-;BW#O?fYn`*B4Mbg6=jWIA?!R(! za;+?jvMBs}`ED1xUE6p4`yV{qZ0f)87yiuq?>{KZ^5ViS7K^c6D1&mXwMwbJ@0+H% zbLY<8yLUax5KefHGIU+H+wI!6y?y(3Q51{C!Z*u2Jw1K)?AiVM_p7R^s*2fGI1ddp zO{2B0>w3H0dQ}B4+h*4IVndB8RL`kM5 zsv9tSH9jfHY^L#2R;%FC2U2Cgv3rpgN~td;%>Fd65kvr9(#JVb9?Yb>X<;UtCYsHf zAgi}$D~-o^AQbs5sY_da&qZ;_r2ENkN{U`dNRzxQuj64B6p5|GIY*d(Sn@0oZZ*R z(eP})%zzhjTU)FyNV=H_QO(R+1%x8*bBeUO&j@T2_ulxaK^OJEuX3hmn3f7+) zgNw>}Iy78Xm8DgL93LW}1X)Qs{BZu^RcUte5s30QV6sY@Mg}Q}E4UCYqAHMc0gy4z zvV#m{L!l%oohgkhK0rNA%>(5xSv*(&P+==Ec68;KESFe_J(%5qpYhmpUd#!ojG`A6AOQUM`~Uj= zAO1_NmF?_+s0%#|Mr&ObMOBt%QPfpc*HzaKK%@(;wLUo5paNs;Yp>n;*6;oPV2ri4 zEDEJ{Sr%HUqAXO9F-(__SIaweV3@PxWbf#a0j=D2*N(OWV@U(aIZKX+5LIa6NCei} zAbxL$4lF8-KL7DRL*MuNw%e>%ufBSFxoEW3MOmC&KRP&A_k%HmF~$u3>5Xmil86?E zg}W^KQ}^ur>68BE$NMAip*YjG3Fd=<2;y*tP+&PuWcyLH;KdIB-+uoZ0^U36-@12t zd~Hue&N@VdN`u9zKt7s;A%L1%Y$Cug*fI5;3UH%sgBp<+|8sU2VVdNR>5UnTv(6e? z9@QSc@a-@z&-eWW6qiKg5OG`*W#L^UUX~wIM@cIypGcF9-iSDaKSvon7oqP4N8}*}XPtA@(sKnD^|02i^!kDkYOpT+n`S)gId^$^`Qev2@_Bd@>@#VY443=S1xzHpIcKM5ZRxcQ(&vZ5=r7^2|Y zl*ejG>_QS~OklFlj(!mE3f%lpp2%=jg@o-XN+7u_kC-?Glm~w}kB>?Ohw!S4XL{?D z@4%NmO8!pHl1f0UpZUt4x_;;0ClB6#^6)1={LVk|3Axsqi0Znk>k6P$S~ZK>oAz02 z3CMdLe(=G=YsZJ1&Fby9zj%Ij`NKc_>Ep*whheCz3YBW=D)fyQjH&A?*ei_81Ffb{ zgXG3CeqEKL=-!e~bv*4{Av!X~n7Xbkn8l`OFS>3&=$el2t`CN5V6-Qvmt{fDp;C)w zbF5WWRb^2WrS`P{<#M64T2%`mFxC$JaOqy6D%J;!<+>q9);KemTTkCQ`uI~id-sXD zYpV^n+n&4kL{apGILg#SPG-$67j>Q#e>tkzi|Kgo$HUvLMyF6c054^NPv0@ zgAsEHGE2b%nhfaW?)rLr%_oy}UHXCa z(g9=aU~ExpkX~1)P?54^AoP78F7GA}cK9z?^U`U%lyF2|SnKN5?PEOw%;ng~YE7FKsBx za6wbg>&xFdw+2E&7WhjCX+dg=EyU|z_~kxXGhU>JcWii^bpb*cDIWB zf#%@gNJONd2^9A-3Ats)SC;t%zt2@A z=MuQEVzb%gdLEV;QVQRQXgtPy$eLo$Km+N=oCOW(3YOzcTXOdBsVyodxi<#=IefN+ zIF973iXm(PW2K;dU1 z*YogS4gyBA^`ugsp@B*6kYg5djqwhKX(^Fqe>9I6;T6?WiOBO2E<&m|H^iJPOS3yC zM`z`$q5LkUN=V{gl$Qj;{6de1K5G&aFyI`~k3)uYHzHxOXA%%})R0A5g;+7sKqzh) zag_>Z4>oipDd-6V!>u4VK7y&-DpvZ~EAT{d7LT0i{F))V`M3DSzA9-?q85Qo5OKgS z#|089gm`_z6l4|X62)E=^L?|UIX!cJ%KLT+#0q99O8-GTh8Y$N#_M)`HVBtJALkSr zW38m*Tv9SV1QIfsjd&7|ugT&GS!IFZLuNN@4D;j?BFUh+e&g=-8+YJq-}sH+{3}l% ze+&RmKK|hGgCBqJ!+%j#RZ~|!kst!4)i3?|zoIo->wfQ_{Eg?&Pk;HBzvdiuZLhWZ z!sqXA*2@PE9}mL-0Qg-5qVih! zqR`s6QMAq&GZ@ptRxNjBNjGjE+MT|5**0~xUM~Sq7hXHBrvFje|F%{LxZUkNhJs3I zt=y4a)_@3Qx%B6iE?m7R>!vC6D|P+!?CkNwXP-Q>cU*ID^_8Bgt70i9h{Hnn90CA0 zK@u4IkpU2Z9lGE9@$x_W)98p4fEi329tGgIvm+9b9@QuiyKW#%oDNTzKx?h%gp3c! z00(2N?<)xa9yI^}rEq((H^xC<;7|}yT~$hs{B4IG1w(;B#XD zI5;@i?RJ-!mvvp2WjPG4ZHK;ht7Ws@t@`v}<7byL>_v|cTXvM9@< zEQ_Mh-q6Ar+qK;=m}k#V+pbfHM~4URzxUB*y>eu&wZ@pDC;|WvLGZq_z)!J<7(wOXx+sPFrWi;M8Xo>;M1Ed0xc=M2nn?Tf7CU1 z)ga6Q)A9;t2`%?#%R!Bq*d}-=qxAZp5Ez&g{ved<^W~6)1Q9|t=BNoWPxdqW^wUQ_ z{_gL7<1hT>Xq_+(ASSt1mWpS^aBjZ9&SgxxYC}4zxk3?A=3r3X&8*BTWU(3fg?JxL z?uzA$#Hm{Zh(I?QX%XfzWSZ`hgy$$$ML7XI3{-6R7Gm~4q5wY0MuV{@_d%hIK?5k1 zLo5lGGJZa-$l<)?k!DNNl8{R%o4K-97Hp+SAN$#2J3}}y>@}8vP}oH52nz6AXatdS zg`_VGCe@~hmCPpvqHY}=A+FTW`xS;+cEdZt@XV2u5#~XYB;utMo`_S-PK=}=m(i%_ zyg}GeGc8wSDdBvM6 zN#f49q7;Kk1ExO_#YDcUqh~3l-d`D>l(S>9?SMI1G+Eeke7T7VVXkTg!HM8cM$Abj zdaHz+5@RnEj6!1o-^>yaZr*n4C5^tPAp?${G8-iGyhw)LZjrc;OGfZejWmeEv&XIG& zNN4b-3{x)&W9^eqPfuU&Kli!M9UmX>_xrYOUw{4N?w!NBt~Z-iQD^|Xd9!)(qVT;E zCWgY(uIpASRadC=swnis2T%IJ?e_x!EEWsjGtxQd8%b`rTMt!O3;TB1xBb!4avEq8dc!sA(D@@l@n)EUZS~zlf zBW~8b9j?eWB}qQJ`eR8CG3iz*N=G;oE~PaL9?gMBN~f}?OpT;PmuO&i6-!%&BB3`a zYpTvKO`NArgqWTPv&(Zf#0}o}3|<$M`zMbd{I&n#zxmC-{8zYboqXB3!Y?LsN4YzJ zxHwrrl~sL1q{rwoFjGzwsceks0He5olMM+Z9Dt%mNC^+u&n|;MFaZO`bSN9@1e?ID z_7*A}vvl#Sk6B*5lM^GtQTf9F3124RU$7qt66uTw%{tOUf+C4N0h7l(VVVQ;a%mU@ zxMI_V7HgOos7^2nWzir>4N9o;rlTrpA7DI=kbEIx zwp=OMdYGW$vC+;$w8S_l)LVZ!(-X*cD>RfM0vau#_j#xTnQ8ejkq|`FWh=zFHeK}DoUwl|?S$xQ%P$k+hdw1& zLX5XU6JLT1X#zF^S=aCMsO2FG&c%g@*=+==`i$fzMRutv$qGj19T5Dcat3t=aAO{bEry*Eh!41~<2_ z-#)%}s0$4Q&XFVM-3^LD7e(R!jpOw}!8t&o+U<7DYT=3$4gpNp(`aBZ86_a1b7W&SKlQMjm<47Ctq(Zc0?gFzQIj6sA^^2gh1PA` zX{|jRqjho8-1^|`Odq?lEWCxs$U7LT)~@akh^Q^j7;V9M+XeE&>>N38(wf2BF^rCw z)3<=2G^?U-oO6RSF(01%AidYalc%ReQSMv1b?eq@wd%Ug81u#(uYCT^>t$JZO9CPw za(8YY>~}9;o^_tiu)Tb?TEd%e+&VZ|mt}zn{a_9b*4Eg5FnwpAKI<+nF50&BmWpR* zXLVg~Hk-C>yRJJsyI8N+#+2{B|M=)|d2q0*>T<+<)YKOfW3yzXRaKT%Rq8@JM~;jS zfBRv0@%;43(-&1$)^#-)Q#bW;vDok1P1T&fJog3)0R`~$H4K9%Su{3ll}y{OsM1d{Bwe&ZV4?EB1 zV25GyF$l;_|8sTlPF$9OPYrG6#@QTF)EQDrtF2PdCGWVcdjyZz(2uYlu6zV8- z0T8Q#!5AWgO&UbXU<`S5m$l#E(R;pMxZee^<(>3@<0Uan5wj~R0F>+px&9GlfiNkg zasCcV8s__-1x$hFqc32nS>i5Dm{t}M<54hWyp(ZvVp5a#3<#?++0>YIt4I=80kQHk z4Y)P`eEYYg?uc1|9A=EsdHfmYf)#Pu;hdHF5^Go1cn?^iGhq~^^nA>T`D~Mp?~6PY zN?!+MX2gqnvlsz!6ct1J`RRs`94->~mn4(0odV2#X4+1fj|r#s0YH&vWE=}jM~B~( zVzorfI?*^x-H9=vgjd=uvW$o^pJFDA?78!oqFC6DLnl&YV2=H<32jJC39`E?DHzLM zro^aI$eSedP9(7}jyxjxUZr?%#p*IkL`59H2_~{Yke^a6LJSHODvQ=yOJoOQh)65dc70uy%hj^3 z%6;4I_ib4g>-CBd6o4M(|KlG3P>2Kco$Wffv@MY%L|iUwA~1tlsLjdx#(K4^s>*X2 zJkWrET5DaXLhC7gV%HP_B0hQYtSAaVr?ldYSA*%G#~QZ7B_W-h9QVU;es*zqxH&z2 z`D^#-+wY%WEM9>zJHl*-CzQ=UnoR?YZ-C%y)MSn6yP+sFD%7foB{d#LgodRA^}$+WfuQQw-ZH>r7W;jxwepPvjImnl#iCxj z>lYV~m+QK!OGI?wJUoN|+Ei7yCL(L>zBu!;h$(?3@}>&RltHwFBqA3{5aqDc{eqku z$dDUN4MV>PrSxvMfAZvXzjp@*2hJ5ICr3WT-R*X_Z{Pm>o43~M%FmKgVg|zZPu?Kf^+1*g$T8Ei>B`T!5Dja*@T4-gRv&Q*N*LB^)k3a3ZzG>>ZuJ-%( zmAkhA=;GpXwOU?Y4Bi33GZegB!uv<~Rt`r;M_!2#>=k{W>8%<3pQdSiA4 zCWA8zmn}HwypKawRsJpf^79Yi*HQo-xQ4=$L_DIw%@@3m$vOAVJMTPwjQf6a001BW zNklcWv7Fw^LU-#%F|I`t!Dp>5J1 zEI!^e0!g=Zsm_!4dtlN_s3P>DjA~_!lzWs+2;jhc^5h&pN>$$s2O&iG`~UlIJ^0bL zzx?%ITWqeeQ+HIFWmlMgefB88(HWF-79N>UBCWtAA|_G6%2^dM9FEI1#Y5Po%)-17 zI`ickN>FSel1v0>F8@iL7f|vgb4EsZ7>-jUoFI~{UcP!0L@A~W!83vnbsY2fGZG1k zr4-^JR>EB2B^%_LmDI0#ftq>g})j!)biG)=WgTM!74I3~_wh$~u366Hk7L@2lJ5pOvUWv&|&b0Sbw}$fc%VNu@?s)QYbFfiZakPKC}dh zaaNrkK1DC6-i(AaKxqr#&#x>C>l`BX{ZJN#t`z`mcROz} z=m`jASt5Y5PAN4M?be)Ct73@@=iJbnF$o!(b$PH{E;a|NX3;3Ee6}-wx6oQE?OhtQ z_AL?^T^-*4;N#Qo>GCG7nnk0_n0}907CGzMeSg`VFK<-Kb#tv~_D}1J^X+cCKR8$e z!EbzZ`NM~2@3zMomw}RdPB=FRv1lAwnsnMg7LW)XSa)Z$y}tPvs8mXoRZ*0>tTblL z*<;M?$RQyS5h?{E%HW8h3==iL6E|X4NJoq`X9nkKDcV+O3rCK~DTilg=ZN_H`Ah#f zf+gh9wM}(UT)O>gL)KWOl{MB3);XfW8r3ONmi>Ad>`t9px^~eWfHzJ-}l3Aw|{wdzTcbW^6=>B*3l7!9*TbbpPZb0{>__5M@yeShaGK< zMZ~V_5#8QvQ4-@bi#c<57fZ{GluJEJjXzu$X`fp?nlD2!J??DzZ7TGEpd zf=7cfrt7*JH*Opp9B}sp-Wwv}GK4QqTMztXO+GKAl-lq2_4LQT3vX%Z@jm~A0I=R1 z?zY=6zx@@h6~CKFkk&Efc`^DbzZrSNNx&J7EL$XAFqC;-JaMIX1*4FN64y9pc6z)bLJ9qq`6uw_;jNX3g>WoHMoI~r z$gL{;J(UDMyb%8X{2%}8JKy|g%jNRzU;6cs3L+QI$HR8Ahal-7fHTFaJUHTUT3W$D z0a-)}`V@D`NRmLBP4de$ly*cy%C228J2Qram?i2#FA)(FCI}G391QH4_(@s8At{Iw zV?*Gy@+k=>Wz8(9i~+pH%f@Y&Y$QIf)H%Mt~q> z2rQ4}x*-z5YFr^P^I}m$$V4O4pVGuEohu?)!Diqi@tR!1h>1WR2SXwxha6oc$+!_v zX;K7@_0AFT2{Akf&L<_r@+sj;;LX_NiybD(2}U#RI%c#O#P=_Y%V@-u14$uXB7sQF zAU@!LFHxdvA$|-}GHYD%&g{YYamJ7o6r>+YopgYN@iwKv%=9|4Yc3N|!~-WbDFXmR zK~Thm^&rj^BH}`QmOO`?HmKxC0NfD(OR*@->A>ucNjB{GRZ8@PY;i=Oh!_Z%m@RO) zhAZ^qI|ere|km$!V>s zvM|OvqNc9a&KYB$Jv$|!swx4%5rK1Bsj8|FfgJVyV67zrt*p|Dh*qo3we``n?vtT6 z&XLv%y0Wy3gM&?36zlbJym#CCAjx?=S%U^iYybbp6erHSdffQX| z-9L(^lNmD}#E3OaD_-fMtSz22%Y9iEWmNzIxzRl`4SHE|a%>7{g4TVcX0S}&yHt$b z96S;cE(qsFJ^~R9*mm_JXUX+?u{}CFyV$qwtFPQXK0dP6wp~B;!(hzNo9-!-9z_S&_C=kFXDV~4>QW2&m~ zzpIoQg)`)EN-}*7zwbN$yAK~eeDUIiwbq*{E*1-)#(SqpU&;6XIaw_h&z@a=^2zCX zy*|FSDa!Kjur5oD0J_lDI%BPKZWzqP`6U4;g>_vwbzKy?tjfBov{q+l7mH;Bpgf(y z|CvCL^5EcLu~@wJ>gvv&<9GhxxzF&ewO$7itTDYErUx3HKYxC5auV|Y^?E(ptW0zU zuAcB5gzUVFhbLD(cmTzN6dvsG3YS2I@ck^#&(GVo^&$@64dmLjYYE7qweEjhP zt@Xvl1p#fh+qZu1=bm5ou{%=c&qI{mc3fJD5vfPaf+)t4P9&59=95MN3sLmw1Efkz+ZHVvLkU|A{Mi^lWZ&= zha+eDv-O5NT!#b{NY0=6x+X}*Ek}t-a2zlx%}VYqB_we%6f`lMf?N(5vk4O|23NPG z3Ys{`BucZ>F!Dq@)=I|_4zai7<3MVZABxR_T9mo>G7JtSDlUZh81Wxg>lsPA-jQw=C2^f;| ztU%@)lybx4SHi6El@}OO$>A8*Nuq1X(1A#HNO}PWqKD@SvxpFZ@`x}-IdOzVoR}LL zAkB0c{Nuw7*M?@ZInH^t=DJR=Oy3QRueoec4$lb#!!KogK{q6rxhjxxrX#ZPV0?rbfhm z7<8c@JorRwU6ti%XQy)qb^URNs`QS*#je=&CFa4>xsP zPGH<;0Rqax^DE{HyRQ4wfBLOAKKDxBo5RC3^1vFucsRf}aX`ejZ9jc7G)=SJHd^c3x7S*a4xPSdgw}ey-K|#3rmlQ$zi67esfK>o z?b^-3>g>GpRD~c_Xr@2cudi?1*i=>d$}5}4k9VHwpqMwO2R;^y1rfDvyI!yT-vOZO zI(DUwY=Qtcgg^f4L0gdsC6Y!A9$5$v(6+7Lk7ZeUzR7;Szqq()nr5+B1WbghCw~4H zf9X5UJ^uK?>u-MHqYpn?t=EJ#N@{k9?Qrh6V@yur(*Y8IVC-Pz*hHX0NGu+I`~UizAN=rJel`B$FaNp4!LiKZDA7$2rL!D; z04PP?XYO}MgSuo`lf;QH8(tgZ+K6C(9;vb@rhbNU&aI$A2#RSY5R<@I(UX`0W4JJt z4M-$q3d010frQGPFo8c9F<&5e2#}PEFs;eSM2n=JI_ASD#)koc2x&n`C{y^FkWh!M1Evl8S)YAsSB=6!k^O6ZlC?!Lp2GiBh)iiJcn6 z?bt(!n{~VA8leyw%F0~2sRdbsA7AWjq&)gfDgCp9GOHO#b#d1@ql5;4sf!wOEtR%`<&M`0x9$7RZk@M$+>j*Uz911lX% z5)fC*q|P-g_a>TgLy&Hla-Sne2LlWan=?L=G*jwNW+6hA4BrxT5^*X?QxA|721(wV z-O?eAq!Fy0;;+;{gkOqDH9n^$B*2*PVh}B_N#wV&TMt(vz+6Zt%Tq*L(UJjp4D6Um zzyJ9135wnaNhKsTBVvFTWh}Tvw3t{dCxJnF5u$`?!pjm1n53sVj~c~ZmI#8tutB&m zWJDq9DMR$(kG>5EKlO9nF}!vH1?Ve(8*=$=mM+-gZj($NV!R@M8X&e^A`_C80Nd2{s}_(|%c z9V)C^>s#OYR%5-1h^2C|S}9LVj0S#42vSPA8l*&8YTm<001*hKj^iLfw2By`OFi4O zD5a%R!Sh@IOcRf`1+2x^1qa*K6JFreSVGP}- zlD#&o3d+!>40KyL$jmb;s-?U`IDoG1zlg$^1tgA>T|2jpj}4EF4I`2$iprHz5+_N* z0?`&MjD*if1OS9psi-0#<6MeOcB?apD=I)qXf-=HNYB+k`4P^!6&0<>W1J^(630Ot zOB{pAKd~%YqJ`SZ+SKUy_17OP6ueX^;LO>zv~uC58?QL9f38+rEfxz(5kbcq$Y;Zp4fKRD!h-px1PJT^AAyu2)#3u|j@`}gm! z)oQI)YjScjj^lE?-1UZmCoZqo~nnSr!GLK@ia{TL%DK*PWa!jtrNYE%4lq@aM#M0RYaQZ}9G3NJ$FS zY83!#wVKqDNZAT<0Z2SW5=PRt5ora4q&yfM_0k3tDHxHqqDa36xu(=1-izaA9nGGLEAViU7P9YbidW84PFQY$GreN|~hQ z_3KsV+4ChmYSjZmn1oC>GlXa?&%k>WtAKtlR(znk)z1S6lg`jh(c3T)7)djCr-U_rJ&e+K8?4f z0DQ{FQKv^|GVPkQ3#z3FfcZJvOgw^FX?VoG*+G@nk9nOUve`wULO}|uVhFOE<@E+T z9&=_>t1{09COyN_V=Q!`B?KKXG8QLjM23`)FNEUcrwQ*AG|bu)wJJ)Zjpy^+wJf6$ zS^gk(>at+-%K#0wvjNVx~(IW_5`5fz3XZZq`ML9i+&a)?UC6-86Ily8OS$yo*9QNG)%7qLnO) z4vVHtCsi6STVG%>$%U?ipkeUZKI~CEjKY3pXNsIayL9%@4?YEmmSv?6!8ivY62@B1cAO-F3qpuja4m~YOpf_}xKdjKgrT8| zW7{%-M1q`)Mx&J^Nwrd5sjZdEC5zHt$f7K=Zj%_0XW1MuT z5F*76(w8GZfB^7Lz>`+y%iO7~KM%twieiC+GoB6 z7Z;W*m9nhwXt#a&c9fFI$+2f%L${o}c<71q%ieBIEJ7$!io`-fJIyDvqA`YGzF}H9 z10e)wh@``DAOw({yoe_kCgw&fduD6Hl@_6d5JD-+ZwN(+BS5bUK-yR`jX8S|gWyWc z&{fXsfe}n3aRPv?)o^-ZW_Y+#Dix)eBBj(W0TWRmYH^gXFcAqujsOtRrX6`0Q?7hT zlgPP1VMQ#K{YqU{j+1z0ea)i<$F(V?cI0s(A|5l&<2Z@JNH!uJ9UDoK1cX3B!Z4cK zwq@teZMI{lYbOk&Mk4@#R;zWz<#V&MV?u~9iid_OvO$L0Uo(zl-w$PCMONGZVD~Wr z5#u-_gd9G6eEZJ*4;;F%R$HE&oE#n=o|u^6oZGe?MGF z6$%B{b;rlYj~qE7J5w;meBY1bI7y;Hp(KQ$R4gnk%I2Jfg6Dd!>)2h+0xgybl#nD& zf*|w?ZV(6=rAs=)a7;W{Y60(Ci2Ne1+S*#9(U7bPsTL_O zGPiTrsT0TLV!i5`>lPQ6PMkPiTV9%;+uI7c7H;d|lTsM3<>Axhq-h2Bf=|s4Y@A3S`M!rf&viX;U9n7aU8kQ%H{IZ)Kt6es{vK6ud64bF*i^*64wS6 zFY+QQQ-VjAWdqC!qI5tI+ifnz5QQK>16se_8#va5>198lDeo(~SwXzq;@mh0Aeli=%czy&^ z?gW*Lk**eECe?I$5$aJD>dAHl9mO*^0+{L1mkH3aVV4FY3cZ70wzoibWg?nVp?Qne z3_J~%oN-funVHpuT@?FjtUX;+Ma|$MWRv%qr|QFcHma)CLL>uUHcDO6XViKtXAtOq zQG6EIHLOI|zA4C5MuY*tQrBy@uE8|oXao-#E2x<{C1IXr6&mYFueUK*TfR_fuXe?h zw8>wKB9Bs`mo~IHmsBhR^1ua^d6@AP za*mm&h0c&GbeS3g!%@)cO0;r$Q!pb(mnzd@E9;2l*g><2RF@1U`nm=xd0tmKkd!9=up@hmbtQ2(gJU0wOza2DL%XMAHu{q<6 zbBoeyrCh76PEL+3EG!pG6Qm0$bO9X|X#hkdlvtEnl-iC(2w@3J5G{*xAyp4MW{Gj8 zS!dva3qc4X=_=m!S~gl1jf<@~PJC=e=<`^{dOQjfIp)-&NJuxc-|0gUTE1;r2nzRHSIP0+*JChpUBx*9|gxi>~_R8cVnpQwdA}bVMAz_K1ivVM(VsB9}ccWTQl$ zFb2Gkh=_zvWT6oDm@@?cgc0P}X)VeaL3A8jYI(>Q6@-YQczC$Fb!JMcG1wLr0Gx?1 zik6p~VHn!By=TwNu3a;niy#O+&rPq8;tWxggi+iLfKzEqCixWd$CcIk#S4qidEqZy zeeHFxdaeD?{r5h6-`x_WYPZ|6g99N%mP<%5Yjt&XW@ct}byXq-GOBkRXV0EJ=g*&C zUtf0|r&6gDJd4n{)$)Z1f&c-aSS*xFMbC2_$3{YA-<3kavn(3Nal)8wTg5^lieg!G zFg8{g87a7~1AvYMCJ5=$c4!pELc|hGkzCV-^?xRoNB{~ z68SWH1D?=6pydNlg`0DaQH>I>J_?fjQj67TA{nPH5gSq7IO1LojUY0EUs zscO`#9{TzcxgYAKi}gS;>lgK=62YjI7kPKY+>Y30S+2Ukgi%nCvoSGWOGOQ~vy97Z z6qKcc_5-T0kE@eSbg5}1LlnYbgJPy3pwaObgjofE*w@iA&{DvhBvv+_98f5%*(;H6 zKn!RsYk9Qd^gee|Wgm@fZ=YnF@e)1XU(QLxiLcFhk2MAzd^84a3p1`e>%>CQpu zdmH1O4c$1i$|VDR4M^;o6^4E067ni4&G}Y(Wuih{$sUUNRUv)uo3&P%Tld&Q#_Qyo zp3VF|cX{O!LlA*;{@%C!&Lj8#_^&?n#p$g(03h(&zwzo@q;L1PANktE^h~E_{?v)j ze)RTZk3Z}YmH5e#7s-tvWoe8J9jQB+F!3 zS~&maUwPS;*WUP+|M#EN4}beBpZ@3v{_=HieDh0x_LtPVXQ~(1CABJ@wGXC?dL}JDAC*~7+BV5Qp9UUC{j7(8V~^c)>Flv2NqY5X zLI@$KCFKt^*?cLc*!>m>A(m}LQ7rpPMo}EcaibZz!P_z5001BWNklI%OLB_cVV;SWpNn$&;W7}w3gh<&1fsBIy#yLq? zm&T0*QTAVyHR@94kTI4N+kUXhxkzFrW8)-dQ5Yj2p-3%42oi#v3EA0EaERk1juLkS zFP&c=9T^IvXnnmoJXGaEluE^Nxi~#FQLnGh&TgsKkH#wt%e*9~g@ZrbOL3dt%&^x|;;ibzR%GY^i8U2qDC_ZQHg8Awdv=0M~T@Nf%Q*~4-ZQMNV!}_ z#H+5ojwO&JvF8;|&(EJZbGp8^wsh&z^(>9Tc+4_R z!4&rEF)H)}u8#p%+zH&kXUM1iVTSeq`My(ruqR9gs|jtW`S6V5lAbQgIkMAKP;T}J z^NK+*+jTw@wi#X@0Nj7qot9;-udk1cj5HdJ$;rvp)z$q|GXUmEoh{YOPe-L0Z0ze2 z2)lT5sJv21{L;JJAiI7zQX)!nZ8dyU~Y05 zHMyvH?qQ%lxdGYc9&!VCJAKz^-+GN~;ZHu3f1pvrepl%Gc=|;?^`ZAYa{rHQ$DNv) zlOJ2DE$+Si>Z6Z6r5$MwDTYe};M7)_zA%A3$ouHM8N;!U4kto4t6}oidn;h_JSN z@tdE1kIe0Mi1acY4L}er%c6uxB!F~tq@CD^bZfN-csvxSX-HnnqCp%B0rLjb1WF-gLh!x(oQVUvU<->Xu!M~79Fix$TQvY zK~W$OIhmq!Na2I0hnMSxy|cBUN=pzz1PXzKNTDYU`!GK3(+l`=n6nG z#w7tn2;sPnT}_ zLa9`Y;<(_s6BA?IsRYI(lps1a90G(8M~=*I-8wNdGnFLCvE%1ilGN9OMkAq=-uj%| zrnk%>LZM*Enb^8*_9d_Uxo>>=3ldYXZM#tDw8NC51Fcr8(P%6$FCRR3Fbu=d(a}bu zA&CHEV`GTeZnwh-9G4JE{Xih%P_=-B6g-PiLMgQ9&gzZ?=B5P|grF3GV zG&WjHnbfZ9P>PgN+oG~OL`L!{(I*H3$8jV{K@PsuT$FVmvTtSDgwpr@TCFxcJuRDP zNZ_DcE=zVs5Cjsl@H{UIB}mXgPi6PCSJV27ZdppazP|4J{?@Hq<(2myxNK->xH2^6 z`~LcR{o?ua3kwU4_4=ht3rmX&vwN=Ed&TuS)2E-?(ASn`iN5((IvX9CStg<8(gsk< z^%fP`Gb_h88xYtG$jIg4$f!2%#6C&j%{#|^@bN(jfPRpQfE+cb0XduWAs@N#hsO^6 zcye-badB~Yc(_m~6pO{RwY6L=I^4)R9pL#4jvqEWa0mQ(Ur4Aw0X7%k89W`r$N*#C zG}r)CWs`4_9o78U1o9)dTo5qJXaLN|R+MKG{dqA1!Js~P&4!bkTRQ`J@RnLZoU


7w4*3~=LZo(pQ^Osj@c3LR!md2oIHRwsXLJQ0{5qYYm32Jm4L z-t%g`bI~Hzf6gYh2fU^RaAW?m$qO91(xoDw=)GZ}F(6n5=rS4%ip~b80YYwCY%|H> zjjR&p9h{o5h(b9NgLw`8Q49ea&@r+Hf8L~muI-S2wZ3pn?m4~TVCqe?Au}x69n>L` zUqAfzSN`J*9|M5d9lKLj>dfqp-+SwOfA@7SnBBf7g+D)h`#Y03dfVT8WcPuCU;W%i zKlP#aJ@(Mu5@<-TDItJg`kg)bA5 z^`AX;TmlUlfdi$dXrBRB?~?ZzhT_u34$xEk%4`9xiEW;MLJri9I0$su1gio!vuFEw zt~n2uW#|~!$*8iVt;P@$D50{n!E;?H&fm%XA`rw@D@hWF6H%>Nq{|?X2fk%d&Mhf! z!#KArYT1@$S@n9ON!nv$!<{}91hHG_F7wQTnNnS@gKWW=B#Z@qAcHi5l7^514HEl6 z`TH=87-LDoWc5T0p%`vqPs0w}q}p6dJkKe{Q)DP%Zi{mvlkIVmv=;b?J-MZt5Z_t$ zR##T)<6|S0kwR@{jSEpM6_PlPqBx2Z%eIR}c*ZrCA3c5S{8HT>*;S890DwpY=Sawg zY_NVdm*QUoOO`?IUg*|n{6Bf;L;m1?C$1cD&Y?Pk@}@#^X?^wPA15YmyM z65XN+*^NR-h|yNjX?aPUSi(w@mfN(0AWR~X zuz)49Aq62)r~xx<&{@o8%3d{^kb*%rjiAvuGMO?pMng| zE0v0j*^L1|+nVHEDFad(yM{jJP9A;CD-@se;#WQT zz)wycKm7cc|NK`!`w;;6@a^yP3dNhB`_islkvKblj8gj8gFpH3?eCmF`q;$u)>ps& zx39bTx$-eipEz>ICqH!ZiAUo&y7HQ5z3z>FI6OKopW(r~zklZ)pPWDXSY>GVg|B$s z*FOKzB#!^#lixmd?C>Z4`ETZrJwCgA&u{(7yQXHgJ?$%hBdA57B0XdjzYTN~`}iW8 zGMc$dF8hrt{A(C%F@T|yH*~1(=;5)MYj68Si6g9BnqNM92s%RXw&U0d1I}0wupnf< zpQQS2oX0~$j%Qm!aEYDC08A!1?G7%$B)7cTY@a@LeqwU0S}Aqt?6UCzK}8E)m`%jH z@(o>`NK6<*Kv`!m8RMNeN2b>S06@lU5<8CLB#L4Iq9fWL+E575WrPqf9ATVTwk2aE zFR3IAx9MCG0+1loy+Y#ETMge2JkK2(s)k{-xKt~QP;^8XM)%$O$mGPBlp!d1E!jv)!oN<=$B;itVvru#;;*`*^<*7S3=n0@Q7Y5*a*e7o&lv_U05lpc#(9#&b8|DcV>^!R zI!>unXf#@p5a7CNYwPFFUlJfHm2$CAD0r^z*tTuOahx#jd6aV=MiHgt=+V;_wH|zE zzFaQLZj@0JB}u{<3&ZfX+inYjAPhsJnJ?$hpP!nVT3=tUR;!3uC=?ovhTIE?I6OQoSq75(trQ=TX6OzLQQNX4 zG$uoI%c20lIQQF8x$Mr&RHYy*A|OG>bu7!0+KQAC(a|JHrB88U+&gpDPoZl0d)fIy z7D}Xr53*=NHov5lCP@;ZRU4=(_{PlXs9V)c+f=-=f_zgPc&Gd*H3|7tk+j1c`nYLPYX8xkcp3NQ7i&AV)W zPcl{3&msc(*`V}m@#49!fBK&$Cnp`pSzB8h8XB@JORm^ru{bulMb9kta}LcZ1Kd

p5ndqjuhuEp5E}vOPa!!Wavcdhg`Riu&H3To?hJ+#vPqw+ZyG7*!#$<)e%H)N zpBT|;22m1&9t`!%=4K7WW?oj8CN%SogMB|E;Xh~*W^^2c)>k{zV0%OGDDwdUPcBcE ziO%%Jmi4hW7|H%v8+Qcc7)3hLSkxlOr+zg-ESU;I~K@E?$zDyyc&MyaGwCYw&_!ij zb+ZB}Q1tN?a&67#KHP64af7UdQjn%yd39y!{cnHM&b^o2aLWt+_y2nDww-&`&zw7b z8~|o_?3Ev$nBMySkAFjcpTzNhe)V$z@Qi0ZTfe3_cj`E$baG~PdGX>W{@33Cz!lGU z765$ZGavrUhi|8rwSCvV<;9CX`p>VOoj?AL+dn}q`;Jfk^XLEVAE{-}?c96e?EL3G z`T+pgd->H~q3Aen97k8*aO>^^2h-QAEnW=#765$o1Ml9pbC2VCXHFh{|2y9N*7tsR z-xbgJ)>ky0V?*VA7lltgnUihXw#~_IvTaVbIhm7fO}356O}6Vf_lu`5(3{%--(GvI zbqU_xAosw)ZuR(l-Il-$y-o+@WC}gT1w0qq@%;KY5!$>N3wVSZs^0MUC3y2^MDUpq z!0ov;MK_WDc{}4z7Fk$7#sE51y-tRIefEC67&^FL|C(k3%9ltB2aj6i})s#D}C8aCB0WjO@4Mbgr4r!0={e%n6{DQI9aKZ^A%Aj+R|Z z`$?~eCgYdD4?adv$6!;g!#(4YD{T=B11>vJ#~xOAYa-}-hydD1rT zS&qlX?syLx>(VrL^6&7_66dETF;8ir2pFu0q%cM<8cD8ZHmerlGYQtfk*S@-kaaq+ zRp+ARz6Sfa`4Gem^)%=_f~SJjW@cr*U#j&xmSFOA#~8Y3x$?Rkb!>L5Z~9uVe)7F? z+%)L&&EoGNPGl9K|DgsC<+GO%B8oJYcomb1A`JMG`R(aE{z>3!{BB7W9u+84@|~AD zhi11Cr{NK)VnEQvYK^IG%OSU7(VT7bx{XaN zxpABgY#>M!hgzX)3;a%xF;KSvONt;i{N1nH{r+Sv^;@qVV|;9^gi4CV3aI8V+TgyB z89}!??bwUv;%m2g#nAsyW5`6NP;pBZeODeXxM75d=l;)rJa627M@>w!IS~v__k^&Y zm6Z%QqWcqFoavfJqiTh8dje=fMRdeJZm4i9&nWl704w@#yQR}i zu+dO14uj7_kJ)Uu;biNjgC;&PV4nz#iN{jRt&y2XC`3q_OW@3q?yX%p+uz?uYX=4g zx?FnY`AWU?C5aX`e6!t1BaV(@Ehb9Px;*ETJ(NJ1d-@;-S~ssRE~eORh(?T6RJeCO z{7EDOMakv%EbU65gKKMR!;QIFSV)LizZ-`395*wH3C*I}54@-ljBUCQaoJsj%?}W- zpi3X74XJTYv&K`s`UAKg-l$xN;kIIjo8sR=yd5(_$@{woOs2fI4G@AvLrb5cDj75b zZJM*hE3BJ!S{G&|^Ny1VHEzgj0#AP|X2|M?lLl|-Kn?Fw=6a_S9>_T9--e1|tSiik47Tqh< z2Hd0&StiJctWIjKFJ9eLFjdBCs(gClCaPcRzbc8_$URW#bTzfS&nF7N?)FSL-@TZP86H*eS}BG?@|ccPp8)?1o|L{I{nYNF= zMFFl`18#hfw?97AMv$pw-d_UVc9$k+)}d-xiyeA0nD{jcdyejo5ey=S_H4YqIlM7}fk=W#;b zZx{P&Y)%-T@mwGCr|3%jkGorOXMU$@MC&eV8V;XC0frSwt=X}y@52Gwy4rag%~!L2 zLbhCZLJx&nJ)fySw{(izSM9v|MvE7FPJr%27yZxPW!}Z)*3%A53;PDef8n7TZuy*wqps<`gCx zLs^3Vxc?45mQ{sj5DvcaR+mGw*TtBNk|54mEs+?n0<2oUsip}P1SboYOH+h zo`s8dIlr(k>Zr+yKmj7n?eur*)ns z*T%ST;Q1(e5sIx57$yl{B;aU!NuohoeKO!xBBpQ&qhb5QVqNAiDkzY~RTppmD9^?l zPhi6HSfFPSffz4#2&r0a#4i$xP?jSYV{FNYHorH`2~f#AZ=3=!vJeC?y_mAFF*bfV z&y1eZv^iTo1)-Wo=n=?cpx`gug{f&g?ThAKKamMDGcu&vjvL~HF_F%< zI<{6SyM z9F^+aT^Te+zm&@EV7#e*4~af>gn}{!zkZ^mp1$H)i>SX%b-0e?Kb6Ct8Y~+jDBS+O zS`y}R<+*U?(DoO4#EH5Dl%t3u6?fIg0t_&3x(%@z?<(_aw+f!E^{{wK8?sLt=-LhnHBL zdOVeM~aa%blp1XH#;vsmMl5QK{h#f3A9m>-H9i1ec&*I}aq zg`x9@Y!0#Z!n!y{Tgq1DcJ%#u+>YD*vk&269l;W0QJ`>X;(78|_-%4xLNB|2@Q>#h&>0GiN@hg#61;m-G1C z5CBn!bocuRDHuUDUQ4{M9vlZ9;hmjZpvnfc7=da|hoWbkS?Wx9(O2`6)o) zzBlN_Hvu}ITE5lzrBDA-+$*1GJUM3~`Lp)254irf^7UBrp+#CKaf;Z=|8-{g>{+$; z96u8f!}56M@EUznfdlD^|MU+p&+qjQx((d~@{UhB%3k{C#xKpv!CR8r_8Xp^m-XXg z_lq6YP6E{b7G)_SpX>jYry+iG_1pXIK}q6LEdh*D0S|9 zghVc8H(~p$#G!Se(r9b`!NgFZ3uV)>F$f9@v1+nXQa>--pJtz*rQ6kmQ~n`G>k-iwRi9JB1}Uq~WF&>K79x2}KwIgsYNuNbU0c9dJh{H|o3f|kxb5TAe|zN;s$ zG5}(eerSN2Dmj2f?i*5o;r3%*6^s!l)mpAXYNWyP@AtHaXvdp(Io$Wb#Y3BIdK4S2 zS3J%KW)*r}O>O+Ftn552X^SDLLl0FBn|G@pcX=y>m3jL7^@b>v-2L_$t*CL=X3H%o zqRcoKuyFv#uQAz@+43~qoP2R(6!W_%1JhaiF-Yl@AgUaeX|y*Is%tUFf<^2JdDURTH2|ad?4q2RnDj~g@exijzPSz<(ktW&9ck~tx zAWY6Wt<)<;j*dRG)cjK%eUCehEI&Bp7LNz_Hb_fLYh-x%;QBFC1C@fafED+ku|iYA zu8daYr~L*h<(u|6Z7S#naC380RlCk3AfQLxmeQ^uspmbmh(!NQ74 zSM{*7vQ+CZEVZh*&{_|Rn)xEntxRBLhh`u4)-W(3#4F*^FWGx^A?gL!*v;JJI-6pr z_Etg~)fw~8GF6kl`ULYZ^t^>r|5JV%Q zRt|GO?PT8Oce(%f0*Iiy>Qj0n9)_j?)kP^G z=qK`?aZA%$%X!mf2{QQ8zdt#OV-BiT^tWHS_PXY?WVdrok=Y-JmkC@Ad{*iHPZ#Yd~IO`dDM*X!tVb5}fz`d5Ea4$h|<-r$S_>oKs6*3>Lj3Y%RWQxU@s} zfi7L0X|F$(c`yO9?>nOPXkhIjy4L!dsNzfrN9Zk1V(vF_M_|^Zr-Uoi6(J zh+a|1Ym2%T!XwY;U3!lR_0o^dvuwlH&s-0A`^=AFEFmxeXu~Z@@&9M|dAYK4k7c!e zx_OE&Mz+sNpU$PDt@C`=`Z?XcKxOb8W%yj%`;jL9mkhL~g98jcvkYG{S9(PJa$ol2 zA^H$odjeJE^pSOs-E(;#xk)eAJtTOy^z9=2lG^L+Mz+~K(=?2J9LIzAP5Y&n)w)=G zVgqbuW7&s zli}OI2;uY1li*|j(|PCnzrqG@4aG;Br zncUdXf1{off>(&0SknygOf7Pv%H-l&;=YyS7x?}VSN?->d5l7)U%aGsp}H*;WXk=W zf|$qiVrS06ko!4}NaMmlQCrw_mRfU_;!u=eLK?oaK@k*akyU$`v$H9SMz_BM6vD2y zy33anNBJ1#=Gt+lE@Pa3#UC>;wnB-pHk}AI>UoZHZbawq_($%#LhG4hf{8%|ZhF8S zWE;9F^nJ3>`G*I|9P*qS*%C9pNAIE~`xmDqV!$yu@Pa#vP{=on1*A7-+ikE8q-XlB zo$rEVMLzfQ??rRigoJ5+)L^?fD?&DBo9-y2*B^aPP*<|yt{y0+G8jX~7*ZSMz;@qiTJYL zGCx(obqZ4!7a_>aZqyY&~&3{B7PHv{R=N#Uxsyr4bDheV)Fc_D&EbEvtZ1&f&|#3i*P z?p3xXBPU%>PW)gktyg3z^r>DOI42WkrR$$`)z^alY42m4PRQXs1f{L zEU>^&|J#pWt!fZaa2G3oC6XNcHd(}ZEXv>R6qNe*{nTpx7iW>73U*g#&Z;S3cE0=j z%(d03>dNS2{_Erw3Gx}5c7DAO(HJ*wY#_?p2IXWvvbSt8TjD>>e+se|W}$JIW&2eI zRCd0kp>d1lQgZ)pfHx-)LSTVI(Z?WpL(aj~;xiIs-av<&2>LT`qDI#6(1Bv3sXRBS545aG|WLrH(R`Gsx)y&T6@9=Tgv-XUC0if*(W+z9;-*d)X z%WEj9#{9WuZKAMUU zUJ~pA#^c@5&XQ{gMaP3yVJyY%cuHY7X}j&Asb`9Dht~9@qMV9-sTM^H7grEysokvK zN?R|qyB*7y6qi>_ae}0;gbx3fW&X8?7xS@g`hyK2zEU4ui1JefpNfvI`ayCMn@rr( z-ue8wf?)wY&JK%T3z$eAQy-TvcKju+59g{TbBma~RN{=W6kuOA0XbT~X11yWcPR<2C~WrD;z z&YW+tv_&DL7*^vtC(2w~CJDdit<8My=4MWF!b1-am4>9@!n_O>mL$c5Ynwc0g4n>3 zag3WjPP;@ei8Lbb*m*jcTLxP!SoE4(_T60=HnuP$AxY&H72 z+69Z1NQl+H7kQ&`yumY_@JCcIp#MG+@QLjR-S>5)6-YlNyrZwlh;XK+2nU{{^Kf#e zR7PPOLQbt2R_<>TZSBep6Ln0d0ScAAMcJHpEV z1rdO97(U;UrC6%|iVDp1kq@(5? zPTqq@+m%-NNepDvYjd1nm}z^=^1C%2KTJX?{9eb{Yg=`KY1mdnmxwE4o$x>tn$S3c zWT!@YvAHU2IjyulqQaN^CfcpdgzthBv@n8{>v_!wI=s`Fl9@L8usKOfG1k{Ne8