Skip to content

feat(spatial): "cover" polygon cell selection for thin/concave areas - #20

Merged
npillar merged 2 commits into
mainfrom
feat/polygon-cover-selection
Jul 17, 2026
Merged

npillar merged 2 commits into
mainfrom
feat/polygon-cover-selection

Conversation

@npillar

@npillar npillar commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

What

Adds an opt-in selection: "cover" to PolygonReadOptions (resolvePolygonCells / readPolygon).

Today the reader keeps a cell only when its center falls inside the ring. For a thin or concave polygon narrower than the grid step this selects very few — or zero — cells, so the consumer (area meteogram, spec 058) drops to a degenerate single-cell fallback. For a concave ring that fallback cell can even resolve outside the drawn shape.

"cover" adds conservative rasterization: alongside the center test, it also keeps any cell whose footprint overlaps the polygon (corner-in-ring ∪ ring-vertex-in-cell ∪ edge-crossing). The selection then tracks the cells the shape visibly covers, and is non-empty whenever the polygon overlaps any cell.

Why

Consumers need a meaningful "distribution over the zone" band for any area shape, including thin/route-like corridors. Center-only membership makes those areas fragile (few cells) or wrong (empty → fallback outside the ring).

Details

  • selection defaults to "center" — existing behaviour is unchanged (verified by a back-compat test).
  • Cell footprints come from neighbour midpoints (one-sided/mirrored at the grid edge). A coverBBox scan box keys off footprints (a superset of the center-bbox), so a sub-cell polygon that contains no center still resolves.
  • Implemented for 1d-rectilinear and 2d-curvilinear layouts; npoints degrades to "center".

Validation

New tests/unit/polygon-reader.cover.test.ts (7 cases, 1d + 2d incl. a skewed curvilinear grid): cover ⊇ center; coarse grid where center collapses to empty but cover stays populated; sub-cell polygon between four centers. Full polygon-reader suite (48) still green.

Driven against a real thin/concave area on the production wrf3km (2d-curvilinear) grid: center 12 cells → cover 41, producing a fuller min/median/max band.

Follow-up (separate PR, consumer side)

Wire selection: "cover" into the nautilus area-meteogram read path (readAreaVariable: resolvePolygonCells + readPolygon, speed + direction) and bump the @i4sea/zarr-node dependency once this is published.

npillar added 2 commits July 16, 2026 15:46
The polygon reader keeps a cell only when its center falls inside the ring
(`pointInPolygon`). A thin or concave polygon narrower than the grid step then
selects very few — or zero — cells, forcing the caller into a degenerate
single-cell fallback (which, for a concave ring, can resolve to a point
outside the drawn shape).

Add an opt-in `selection: "cover"` to PolygonReadOptions. Alongside the center
test, it keeps any cell whose FOOTPRINT overlaps the polygon (conservative
rasterization: corner-in-ring, ring-vertex-in-cell, or edge-crossing). This
tracks the cells a shape visibly covers and guarantees a non-empty selection
whenever the polygon overlaps any cell — the right membership for an area
"distribution over the zone" reduction.

- `selection` defaults to "center"; existing behaviour is unchanged.
- Footprints come from neighbour midpoints; a `coverBBox` scan box keys off
  footprints (superset of the center-bbox) so a sub-cell polygon that contains
  no center still resolves.
- Implemented for the 1d-rectilinear and 2d-curvilinear layouts; npoints
  degrades to "center".

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds an opt-in polygon cell selection mode (selection: "cover") to make resolvePolygonCells / readPolygon robust for thin and concave polygons that often select too few (or zero) cells under the existing center-only rule.

Changes:

  • Extend PolygonReadOptions with selection?: "center" | "cover" (defaulting to "center" for back-compat).
  • Implement conservative “cover” membership via per-cell footprint bounds + polygon/rect overlap predicate for 1d-rectilinear and 2d-curvilinear layouts (npoints falls back to center).
  • Add a dedicated unit test suite covering hard cases for both 1d and 2d layouts.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
tests/unit/polygon-reader.cover.test.ts Adds unit tests validating "cover" behavior (cover ⊇ center; non-empty selections on coarse grids; 1d + 2d cases).
src/spatial/polygon-reader.ts Introduces the "cover" selection path (cell footprint computation + overlap test) and wires it into selection resolution.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +160 to +170
const d1 = cross(b1, b2, a1);
const d2 = cross(b1, b2, a2);
const d3 = cross(a1, a2, b1);
const d4 = cross(a1, a2, b2);
if (d1 > 0 !== d2 > 0 && d3 > 0 !== d4 > 0) return true;
// Colinear / touching cases.
if (d1 === 0 && onSeg(b1, a1, b2)) return true;
if (d2 === 0 && onSeg(b1, a2, b2)) return true;
if (d3 === 0 && onSeg(a1, b1, a2)) return true;
if (d4 === 0 && onSeg(a1, b2, a2)) return true;
return false;
@npillar
npillar merged commit 9d6d461 into main Jul 17, 2026
3 checks passed
npillar added a commit that referenced this pull request Jul 17, 2026
Minor release adding the "cover" polygon cell selection from PR #20:
PolygonReadOptions.selection: "center" | "cover" (default "center",
back-compat). "cover" keeps any cell whose footprint overlaps the ring
(conservative rasterization), guaranteeing a non-empty selection for thin
or concave areas where center-in-ring picks too few — or zero — cells and
the centroid fallback can resolve outside the shape. Implemented for the
1d-rectilinear and 2d-curvilinear layouts; npoints degrades to "center".
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