Increment 15b: the domain in its own CRS - #106
Merged
Merged
Conversation
Failing suites for 15-dem-mosaic.md R9, R11 [15b] and "Tests for @tester" (15b); no production code. Names the design left open are pinned in the new "Pinned by the red suite (15b)" subsection. - test_crs.py (new, 22): parse_crs, reprojector bit-equal to pyproj's always_xy transform, and exactly one from_crs site, in crs.py. Run against a scratch crs.py (not committed): 31/31 with test_always_xy.py, and six mutants killed (always_xy False or dropped, input or output axes swapped, CRSError leaking, float32 round trip). - test_domain.py (rewritten, 54): read_domain(path, crs) without the DEM, crs: str, to_crs (vertices only, re-oriented, same CRS by CRS equality not to_epsg, no image refused), check_extent; check_crs gone. - test_dem_input_domain.py (new, 22): DemRequest.domain, bounds from the moved domain, the needed region (grown by one cell, pinned away from its edge), extent refusals before any load, a real DTM10 seam in EPSG:25832. - test_cli_mesh_domain_crs.py (new, 18): 4326, 25832, WKT 3035, a CRS with no EPSG code and CRS84 over a 25833 mosaic; domain_crs and domain_transform; --bbox with --domain refused; axis order able to fail; same-CRS digests recorded at d34d79d, before any 15b change. - test_refine_golden.py calls read_domain(path); it and three test_cli_constraint_feet.py tests that use it are red on the signature alone. test_cli_mesh_domain.py: two must-match tests renamed for the extent check that now refuses them, assertions unchanged. Full suite: 86 failed, 20 errors (test_crs.py: ModuleNotFoundError in a fixture), 1914 passed, 7 skipped. ruff check, ruff format --check, mypy and check_citations clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
15-dem-mosaic.md R9, R11 [15b] and "Pinned by the red suite (15b)"; no test
file touched.
- crs.py (new): parse_crs (CRSError becomes a ValueError naming the text),
reprojector (pyproj's always_xy transform, bit for bit), crs_label (EPSG:n
only for an exact code, else pyproj's own text) and transform_description.
One Transformer.from_crs site in src_python/ (I8).
- domain.py: read_domain(path, crs=None) no longer takes the DEM;
DomainPolygon.crs is text, and any CRS pyproj reads is accepted. to_crs
moves vertices only, re-orients, returns self for an equal pyproj.CRS
(not an equal to_epsg(), so +axis=wnu UTM 33 is transformed), and refuses
a vertex with no image naming both CRSs. check_crs is gone; the extent
check is public, check_extent(domain, meta).
- dem_input.py: DemRequest.domain (exclusive with bounds), DemInput.domain in
the DEM's CRS. open_dem plans on the moved domain's bounds, with the polygon
grown by the cell diagonal (mitred) as the needed region, checks the extent
against the plan, then assembles; refusals keep their type and name both
CRSs. One addition the pins did not name: 15a's window snaps a box edge
within 1e-6 cell of a node line onto it, which cut off a domain vertex
1e-6 m past a node line (test_cli_mesh_domain.py's near-node vertex); such
an edge is moved half a cell out and the plan redone.
- cli.py: --bbox with --domain is a usage error; the domain is read before
the DEM is opened; domain_crs and domain_transform ("none" when the CRS is
the DEM's, with no transformer made) are recorded.
Production LOC (CLAUDE.md s2 unit, against 372bb99): +149 -51, net 98;
estimate 115, worst 160.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ayout check_crs is gone at the green commit, so 15-dem-mosaic.md R9's domain.py:97 now names the tree it was true of. project_structure.md: domain.py no longer checks the CRS, dem_input.py takes a domain, and crs.py is listed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ettled for the domain, 15 row Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…'s R1), CI runs the codecs-gated CLI and golden suites Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…inned, multi-EPSG refusal A test amendment after green, from @Reviewer's CHANGES REQUESTED on 15b. B1 (red): dem_input grows the needed region by footprints[0]'s spacing, the first tile by name in the repository, not the chosen plan's. Two-spacing, same-EPSG repositories, both name orders: - 10 m L with the SE block missing, NEAR_THE_HOLE, a far 1 m tile: refused without it and with z_fine.tif; with a_fine.tif it is accepted (load reached). Red. - The mirror, a 1 m L and a far 10 m tile, AROUND_THE_HOLE scaled by 0.1 (3.5 m from the nearest missing node): filler without it and with z_coarse.tif; a_coarse.tif grows by 14 m and refuses 36 nodes. Red. - An unselected far tile, either order, leaves the plan unchanged (pin). A scratch fix taking the spacing from the selected lattice turns all green. S1 (pins): domain window == --bbox window with every edge on a node line and with a vertex 2e-6 cell past one; one line wider on that side only at 1e-7 cell past, each of the four edges. `<` -> `<=` (and `>` -> `>=`) in _past fails 13 of 13 affected cases; _past disabled fails the four in-band cases. S2 (pin): tiles in EPSG:25832 and 25833 with a domain are refused before any load, naming both codes and "a domain needs one". Deleting the check fails it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
B1 from @Reviewer: _domain_plan grew the domain by footprints[0]'s cell, the first tile by name in the repository, so a tile the domain does not select, and where its name sorts, changed what was needed. Now it plans on the polygon itself, grows by that plan's cell diagonal, and re-plans while the lattice chosen has a larger one; the reach only grows, so it ends. All header-only, before any load; _past and the snap band are unchanged. domain.py's docstring names what it now imports (numpy, pydantic, tin_engine.crs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd, one CRS "Pinned by the red suite (15b)" in 15-dem-mosaic.md gains the test amendment's pins (6143d87): the needed region is grown by the chosen plan's cell, independent of unselected tiles and name order (B1); the snap-band cases (S1); and the refusal of tiles in several CRSs with a domain (S2). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… re-plan loop and three open edge cases recorded; 15b LOC as built Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nt, the two-PR reason Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Test amendment. The 15b review found nothing pinning that `DemInput.seams` survives `open_dem`'s domain path: a resolution that set `seams=()` whenever `request.domain` is set passed the whole suite. - test_dem_input_domain.py, TestSeamsOnTheDomainPath: a quadrant pair planted to disagree (+0.5, +2.0, and +0.0005, below the 1 mm rule, on the shared column) opened with a domain in EPSG:25833, 4326 and 25832 reports ne.tif | nw.tif, nodes 2, max 2.0, median 1.25; the report equals the node-by-node oracle and the --bbox run at the moved domain's bounds; agreeing quadrants with a domain report (). - test_cli_mesh_domain_crs.py, TestSeamsWithADomain: `dem_seams` is written on the --domain path (4326 domain): the pair when planted, `none` when the overlaps agree. Under the mutant (`seams=()` when a domain is given in open_dem) the five disagreeing-case tests fail; the agreeing ones pass, as they must. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed in the status line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… region only Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ps still disagree Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ola ruled on 2026-09-28 that with --domain the seam report counts only nodes inside the needed region (the domain grown by the plan's cell diagonal, mitred): an irregular catchment's bounding box can be several times its area, and a disagreement bilinear z never reads is noise in the report. No production code here; the amendment pins the ruling first. - A thin diagonal strip whose plan is the whole grid but whose region misses the planted overlap: seams == () and dem_seams "none" (red). - An L whose region takes two of the planted nodes: exactly those, against seams_of masked by the needed region (red). - A rectangle 7 m short of the overlap still counts it (pin). - --bbox at the same bounds reports every planted node, and the mosaic values are unchanged by the domain (pins). mosaic_fixtures.seams_of takes an optional node mask. Mutants against a scratch implementation: ungrown-polygon mask and no mask both killed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…Ola) Ola's ruling of 2026-09-28: with --domain the seam report counts only the overlap nodes inside the needed region, the domain grown by the chosen plan's cell diagonal, mitred. Without a domain the report is unchanged, and which tile's value a node takes is unchanged everywhere. - `_domain_plan` also returns the grown polygon it already computed; `open_dem` hands it to `assemble` (None without a domain). - `assemble` takes an optional `needed` geometry, as `plan_mosaic` does, and masks only each pair's overlap strip before `_seam` (`shapely.intersects_xy`, closed, prepared once); `_decide` never sees it. No canvas-sized mask; `mosaic.py` stays file- and CRS-free. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e's pair count Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR #106's CI failed on Linux x86 (GCC, Python 3.12/3.13/3.14) in TestTheSameCrs::test_the_mesh_is_16s_bit_for_bit: the square's digest, recorded on macOS arm64 at d34d79d, is not what Linux produces for the random float32 terrain. A recorded digest pinned a platform, not the behaviour. The quarter-circle digest was never reached there (the codecs step was skipped) and had the same exposure. The guarantee is now relational, on one machine, for both cases: - the run equals (SHA-256 of points, cells, cell and point arrays) the same run with cli.open_dem replaced by 16's data flow: the whole file, opened as without a domain, and the domain object exactly as read_domain returned it, never through DomainPolygon.to_crs or the window cut; - the domain handed to _dem_mesh has the read vertices bit for bit; - Transformer.from_crs is never called (the no-transformer fixture, unchanged). The platform-stable absolute anchor is increment 18's test_refine_golden.py CLI quarter circle, a same-CRS domain; referenced in the docstring and the design's pin, not duplicated. On macOS the reference run reproduces the two dropped digests exactly (seen under mutant M3 below). Mutants (production files restored after each): - M1, to_crs round-trips a same-CRS domain through EPSG:4326: all three fail on the no-transformer check; with that fixture neutralised the vertex check still kills both, and with the vertex check also removed the square passes (the 1 mm snap absorbs the round trip) while the quarter circle is refused by the extent check. The vertex check is what makes the "not transformed" half bite without the fixture. - M3, the domain path's tile +0.001 m: both digest relations fail. Full suite 2103 passed, 7 skipped; ruff check, ruff format, mypy clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
Author
|
Fix for the red CI at 8f46f13: Note on 24d178c's commit message (not rewritten): under the round-trip mutant two of the three 🤖 Generated with Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Increment 15b (
docs/increments/15-dem-mosaic.md, R9/R11). Python only; 162 production lines (CLAUDE.md§2).--domainreads a polygon in its own CRS (GeoJSONcrsmember, 4326 default, or--domain-crs) and reprojects it into the DEM's CRS vertex by vertex (crs.py:parse_crs,reprojector, the oneTransformer.from_crssite,always_xy). CRS equality bypyproj.CRS, which fixes a latent bug: a west-pointing UTM33 reported EPSG 25833 and was accepted untransformed.--bboxand--domainexclude each other;domain_crsanddomain_transformrecorded.--domain, the seam report counts only nodes inside the needed region (Ola, 2026-09-28).Review
Red
372bb99→ green0011741, test amendments after each finding/ruling; @Reviewer through several rounds, APPROVED at8f46f13. Local: pytest 2103 passed, mypy, ruff, governance gates.🤖 Generated with Claude Code