Iteration extending controlledburn from polygon-only to a unified
geometry rasterizer over polygon, line, and point input. Output schema
gains type-pure tables; the unification follows the geometric structure
of measure-on-a-grid as one operation parameterised by Hausdorff
dimension. Design philosophy and decisions are recorded in
inst/docs-design/unified-geometry-rasterization.md.
Polygon boundary cells: $edges$weight renamed to $edges$fraction
to reflect what the column carries (a dimensionless fraction in
[0, 1]). [materialise_chunk()] reads either name for forward
compatibility with cached results, but new code should use
$edges$fraction.
burn_sparse() now errors on line and point input. Previously it
fell through to a path that produced silent-empty (points) or
silently-mistyped (lines) output. The error message redirects to
burn_scanline().
burn_scanline() accepts LINESTRING, MULTILINESTRING, POINT, and
MULTIPOINT alongside the existing POLYGON / MULTIPOLYGON support.
Output schema gains $lines (row, col, length, id; absolute
length in CRS units) and $points (row, col, id; no measure
column — implicit weight = 1).
materialise_chunk() handles all four tables. Line cells contribute
length, point cells contribute count, polygon cells contribute
fraction. Mixed-kind input errors rather than silently combining
units; filter to one kind via id = first.
print.controlledburn reports $lines and $points counts when
present.
GeometryCollection input is rejected with a warning. Mixed-dimension input would produce a sparse table where rows from different kinds carry different units — caller must split into homogeneous groups and run separate burns. Curved types (CircularString, CompoundCurve, etc.) must be linearised by the caller before passing in.
walk_polyline(): cell-stepping walker extracted from walk_ring().
Geometry-agnostic; takes a closed parameter that selects whether
to push back coords for the start-cell-reentry case (true for rings,
false for open lines). The polygon path runs the existing
winding-sweep post-processing on the walker's output; the line path
sums per-cell segment lengths from the same CellMap.
process_line(): walks lines on the full grid (no shrink_to_fit),
because horizontal/vertical lines have a degenerate bbox that
Box::empty() reports as true. Walker cost is O(cells touched), so
the bbox-shrinking optimisation is dead weight for lines anyway.
process_point(): trivial. Computes cell index via
Grid::get_row / get_column, drops out-of-extent silently.
Edge zoo (tests/testthat/test-edge-zoo.R) pins canonical
rasterizer edge cases by category (Horizontal, Sub-pixel,
Alignment, Precision, Topology, Collinear, CRS-boundary). Each
test pins a convention, not just a numerical expectation, so
the test surface doubles as API contract documentation.
Complete rewrite of controlledburn using the exactextract algorithm (Daniel Baston, vendored from exactextractr) for exact polygon-grid coverage fractions.
burn_scanline(): O(perimeter) scanline sweep with winding-number interior
classification and exact boundary coverage fractions. No dense matrix
allocation — output is sparse runs + edges tables.
burn_sparse(): Reference implementation using the exactextract dense
algorithm, compressed to the same sparse output format.
materialise_chunk(): Opt-in expansion to dense matrix or vector, with
per-polygon-id filtering.
Default grid parameters: extent derived from geometry bbox via wk::wk_bbox(),
dimension auto-fitted to 256 cells on the long axis preserving aspect ratio,
or specified as resolution.
Geometry input via wk::wkb(), geos_geometry, sfc, blob, or raw WKB
list (compatible with vapour/gdalraster output).
Moved from Rcpp to cpp11, using libgeos for GEOS access.
Scanline algorithm: lightweight walk using Box::crossing() directly (no
Cell class allocation), winding-count interior classification, analytical
single-traversal coverage via perimeter_distance().
Validated against burn_sparse across 52 test cases: simple shapes, NC counties, shared boundaries, edge cases (grid-aligned edges, slivers, extent clipping, degenerate shapes).
O(perimeter) scaling confirmed by benchmark: 17× faster than dense at 3200×3200 resolution for complex shapes. Memory: sparse output ~50 MB vs ~2 GB dense for real-world 32K×16K grids.
Derived from fasterize (Noam Ross, EcoHealth Alliance) — original scanline edge logic for polygon rasterization.
Previous controlledburn (0.0.2) returned binary in/out run-length indexes
without coverage fractions. That code is archived at
archive0-2026-02-13 branch.
Basic function working, return a list of triplets of zero-index start,end,line index.
Raster package objects and use removed.
Converted from fasterize edge scanline logic, to not materialize any raster data. Armadillo usage removed.