Skip to content

fix: don't drop multipolygon parts whose seed lands in an earlier part - #30

Merged
mailletf merged 1 commit into
mainfrom
fix/multipolygon-dropped-parts
Aug 25, 2026
Merged

mailletf merged 1 commit into
mainfrom
fix/multipolygon-dropped-parts

Conversation

@mailletf

@mailletf mailletf commented Aug 25, 2026 •

Copy link
Copy Markdown
Member

Problem

polygons_to_geohashes shares accepted_geohashes across the parts of a MultiPolygon, but the early continue skipped neighbour expansion, not just re-testing. A part whose seed cell had already been accepted while walking an earlier part terminated on its own seed and contributed nothing.

With A = rect(-73.60, 45.50, -73.55, 45.55) and B = rect(-73.590, 45.520, -73.520, 45.525) — B's centroid falls inside A — precision 7 returned A's 1369 cells where the union of the parts is 1479. 110 cells (7%) silently missing, and 66 of 1291 with fully_contained_only. No error raised.

Fix

Track a per-polygon visited set that gates the geometry test while accepted stays global, and always expand from any cell that intersects. Each part now gets a full walk regardless of what earlier parts covered, which also makes the separate rejected set redundant.

Two papercuts in the same loop:

  • neighbor.to_string() cloned an already-owned String — 8 wasted allocations per visited cell.
  • polygon.unsigned_area() was recomputed per candidate cell; it is O(vertices) and constant. Hoisting it is worth ~8% on verdun p9 with fully_contained_only.

Tests

Two regression tests, both failing on main: the union property, and order-independence of the parts.

polygons_to_geohashes shares `accepted_geohashes` across the parts of a
multipolygon, but the early `continue` skipped neighbour expansion as well
as re-testing. A part whose seed cell had already been accepted while
walking an earlier part therefore terminated on its own seed and
contributed nothing to the output.

With A = rect(-73.60, 45.50, -73.55, 45.55) and
B = rect(-73.590, 45.520, -73.520, 45.525), B's centroid falls inside A,
and at precision 7 the function returned A's 1369 cells where the union of
the parts is 1479 — 110 cells (7%) silently missing, and 66 of 1291 with
fully_contained_only.

Track a per-polygon `visited` set that gates the geometry test while
`accepted` stays global, and always expand from any cell that intersects.
Each part now gets a full walk regardless of what earlier parts covered,
which also makes the separate `rejected` set redundant.

Two smaller fixes in the same loop:

- `neighbor.to_string()` cloned an already-owned String, costing eight
  wasted allocations per visited cell.
- `polygon.unsigned_area()` was recomputed inside the loop for every
  candidate cell; it is O(vertices) and constant, so hoist it. Worth ~8%
  on verdun p9 with fully_contained_only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes an under-coverage bug in polygons_to_geohashes when processing multiple polygon parts (e.g., MultiPolygon parts) where a later part’s BFS would previously terminate early if its seed cell had already been accepted while walking an earlier part. It also removes some avoidable per-cell overhead and adds regression tests to prevent reintroducing the issue.

Changes:

  • Introduces a per-polygon visited_geohashes set so each polygon part gets a full BFS walk regardless of globally accepted cells.
  • Removes the per-polygon rejected_geohashes tracking and avoids unnecessary String allocations during neighbor expansion.
  • Hoists polygon.unsigned_area() out of the inner loop and adds two regression tests (union property + order-independence).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@mailletf
mailletf merged commit 66e3ce1 into main Aug 25, 2026
17 checks passed
@mailletf
mailletf deleted the fix/multipolygon-dropped-parts branch September 16, 2026 20:02
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.

2 participants