Skip to content

Add polygon-to-morton coverage function#21

Merged
espg merged 6 commits intomainfrom
feature/polygon-coverage
Feb 26, 2026
Merged

Add polygon-to-morton coverage function#21
espg merged 6 commits intomainfrom
feature/polygon-coverage

Conversation

@espg
Copy link
Owner

@espg espg commented Feb 26, 2026

This a human designed algorithm (see #20 ) , implemented by claude... with lots of hand holding. Mostly because claude will change unit tests to pass when they fail, instead of fixing why they fail =P

Summary

  • Implements morton_coverage(lats, lons, order) — computes morton indices that completely cover a polygon defined by lat/lon vertices (closes Polygon to morton coverage #20)
  • Core algorithm in Rust (coverage.rs): contiguous boundary construction via great-circle interpolation, connected-component inner/outer classification with stereographic PIP, recursive inward fill, and a PIP sweep fallback for completeness
  • Python wrapper (coverage.py) with input validation and closed-polygon handling
  • Handles concave polygons, antimeridian crossings, and polar regions

Test plan

  • 18 synthetic tests pass (pytest mortie/tests/test_coverage.py -v -m "not slow")
  • 55 Rust unit tests pass (cargo test --lib)
  • Rust benchmarks compile (cargo bench --bench coverage_bench --no-run)
  • Real-data tests with Antarctic drainage basins (marked slow)
  • Python codspeed benchmarks run (pytest benchmarks/test_bench_coverage.py -v)
  • CI passes (codspeed workflow updated to include new benchmarks)

espg and others added 2 commits February 25, 2026 21:42
Antarctic drainage basins at order 6 cover fewer cells than initially
assumed (e.g., Basin 24 ≈ 24 cells, Basin 1 ≈ 76 cells). Thresholds
verified against brute-force PIP grid sampling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codecov
Copy link

codecov bot commented Feb 26, 2026

Codecov Report

❌ Patch coverage is 98.68996% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.30%. Comparing base (22ab907) to head (3db1e34).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
mortie/tests/test_coverage.py 98.98% 2 Missing ⚠️
mortie/coverage.py 96.66% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main      #21      +/-   ##
==========================================
+ Coverage   85.82%   87.30%   +1.47%     
==========================================
  Files          13       15       +2     
  Lines        1771     2000     +229     
==========================================
+ Hits         1520     1746     +226     
- Misses        251      254       +3     
Flag Coverage Δ
unittests 87.30% <98.68%> (+1.47%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
mortie/__init__.py 77.77% <100.00%> (+2.77%) ⬆️
mortie/coverage.py 96.66% <96.66%> (ø)
mortie/tests/test_coverage.py 98.98% <98.98%> (ø)

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 22ab907...3db1e34. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@codspeed-hq
Copy link
Contributor

codspeed-hq bot commented Feb 26, 2026

Merging this PR will not alter performance

✅ 5 untouched benchmarks
🆕 6 new benchmarks
⏩ 9 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
🆕 test_coverage_circle100_order6 N/A 708.4 µs N/A
🆕 test_coverage_triangle_order8 N/A 379.7 s N/A
🆕 test_coverage_square_order6 N/A 5.2 s N/A
🆕 test_coverage_triangle_order6 N/A 5.3 s N/A
🆕 test_coverage_circle500_order6 N/A 1.1 ms N/A
🆕 test_coverage_triangle_order4 N/A 95.8 ms N/A

Comparing feature/polygon-coverage (88c51be) with main (22ab907)

Open in CodSpeed

Footnotes

  1. 9 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@espg
Copy link
Owner Author

espg commented Feb 26, 2026

PR #21 Review & Improvement Plan

What's working well

  • Algorithm (Phases A-C) is correct and robust
  • 29 tests all pass (18 synthetic + 8 real-data + 3 multipart)
  • Good test coverage including boundary, interior superset, concave, equatorial, southern hemisphere
  • Benchmarks for both Rust (criterion) and Python (codspeed)
  • README documentation with examples
  • Multipart polygon support
  • Clean Python wrapper with proper error handling

Issues to address before merge

1. Replace stereographic PIP with gnomonic projection + 2D winding number

The current PIP uses south polar stereographic (hardcoded center -90°,0°) which only works reliably for southern-hemisphere polygons. Per the spherical PIP research in issue #20, the recommended approach is gnomonic projection centered on each test point + 2D winding number:

  • Project polygon vertices onto tangent plane at the test point (not a fixed center)
  • Great circles become straight lines in gnomonic projection
  • Run standard 2D winding number algorithm
  • Works for any polygon within one hemisphere of the test point
  • Zero trig functions per edge after initial vertex→unit-vector conversion
  • No special cases for antimeridian/poles

Architecture change: Currently all polygon vertices are projected ONCE via stereographic at startup, then reused for every PIP test. With gnomonic, the projection is centered on each test point, so we re-project per test. Cost is low — gnomonic projection is just dot products.

New functions: latlon_to_unit_vec(), gnomonic_pip(), winding_number_2d()

Functions to delete: stereographic_project(), point_in_polygon_ray_cast(), polygon_centroid() (dead code)

classify_components() signature update: Remove px, py, center_lat, center_lon parameters; replace with poly_verts: &[[f64; 3]].

Rust test updates: Remove stereo/raycast-specific tests, add gnomonic/winding-number tests including south pole edge case.

2. PR cleanup

  • Dead code: polygon_centroid() defined but never called — remove
  • Empty test: test_holes_raises is a pass statement — replace with pytest.skip("Hole detection not yet implemented")
  • No NaN/inf validation: Add np.isfinite check in _single_coverage() before calling Rust
  • Missing docs: Add Notes section to morton_coverage() docstring covering: self-intersecting polygons undefined, holes not supported, algorithm description

3. Fix pole-touching cell rendering in notebook

In the Circle plots (cell 6), some morton cells near the south pole render as "bowties". The healpy.boundaries() vertex longitudes fan wildly at lat≈±90° and the sequential lon_diff > 180 wrapping heuristic breaks.

Fix: Detect pole-touching cells (|lat| > 89.5°). For those, project boundary vertices to polar stereographic (sx, sy), build the Shapely polygon in projected space, then inverse-project back to (lon, lat). Non-pole cells keep the existing sequential unwrapping.

4. Rename notebook to example + add documentation

coverage_debug.ipynbexamples/morton_coverage_example.ipynb with narrative markdown cells explaining what morton_coverage does, what parameters mean, what to observe in each plot section, and performance characteristics.

Execution order

Task 1 (gnomonic PIP) → Task 2 (cleanup) → Task 3 (pole rendering) → Task 4 (notebook docs)

Files to modify

File Task Action
src_rust/src/coverage.rs 1, 2 Replace PIP, remove dead code, update tests
mortie/coverage.py 2 Add NaN validation, improve docstrings
mortie/tests/test_coverage.py 2 Fix placeholder test, add NaN/inf tests
coverage_debug.ipynbexamples/morton_coverage_example.ipynb 3, 4 Fix pole rendering, add docs, rename

Verification

  1. maturin develop --release — builds
  2. cargo test --lib — Rust tests pass
  3. pytest mortie/tests/test_coverage.py -v — all Python tests pass
  4. Open notebook, run all cells, visually confirm pole-touching cells render correctly
  5. git add all changed files

@espg
Copy link
Owner Author

espg commented Feb 26, 2026

good job @claude , we implemented a major new feature in a day that a team of scientists and engineers at NASA didn't have enough time to do in the pre-LLM coding days!

@espg espg merged commit 0302cb1 into main Feb 26, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Polygon to morton coverage

1 participant