| Title: | Sparse Polygon Rasterization with Exact Coverage Fractions |
|---|---|
| Description: | Rasterize polygons without materializing any pixel values. Computes exact coverage fractions for polygon-grid intersections and returns results in a sparse two-table format: run-length encoded interior cells and individually weighted boundary cells. The scanline algorithm is O(perimeter) in both time and memory. Uses the exactextract algorithm by Daniel Baston, vendored from the 'exactextractr' package. Derived from the 'fasterize' package by Noam Ross. |
| Authors: | Michael Sumner [aut, cre] (ORCID: <https://orcid.org/0000-0002-2471-7511>), Noam Ross [aut] (ORCID: <https://orcid.org/0000-0002-2136-0000>), EcoHealth Alliance [cph] (Original fasterize scanline logic), Jim Hester [ctb] (Wrote CollectorList.h for early version), Daniel Baston [cph] (exactextract C++ algorithm (vendored)), ISciences, LLC [cph] (exactextract C++ algorithm (vendored)) |
| Maintainer: | Michael Sumner <[email protected]> |
| License: | Apache License (>= 2) |
| Version: | 0.1.3 |
| Built: | 2026-05-12 00:06:27 UTC |
| Source: | https://github.com/hypertidy/controlledburn |
Rasterizes geometry into a sparse output schema, with one type-pure
table per geometry kind. Polygons produce run-length-encoded interior
cells ($runs) and weighted boundary cells ($edges, with a
coverage fraction in [0, 1]). Lines produce one record per touched
cell with the absolute length of line within the cell, in CRS units
($lines). Points produce one record per touched cell, with no
measure column ($points). See the unified geometry rasterization
design doc in inst/docs-design/.
burn_scanline(x, extent = NULL, dimension = NULL, resolution = NULL)burn_scanline(x, extent = NULL, dimension = NULL, resolution = NULL)
x |
geometry input, one of:
|
extent |
numeric vector |
dimension |
integer vector |
resolution |
numeric, cell size (scalar for square cells, or
|
Mixed geometry-kind input (e.g. a vector containing both polygons
and lines) is permitted and produces tables populated for whichever
kinds are present. Each table's measure column means exactly one
thing — polygon $edges$fraction is dimensionless, $lines$length
is in CRS units, points have no measure — so combining them in a
single materialized matrix would silently mix units.
materialize_chunk() therefore refuses mixed-kind input; filter to
one kind via id = first, or run separate burns for each kind.
GeometryCollection input warns and is skipped. Curved types (CircularString, CompoundCurve, etc.) must be linearised by the caller before calling.
Memory usage is O(perimeter) rather than O(bounding-box area), so
no tile_size parameter is required (in contrast to burn_sparse(),
which is polygon-only and tile-bounded).
A list with class "controlledburn" containing:
runsdata.frame with columns row, col_start, col_end,
id — run-length encoded polygon interior cells (always full
coverage). Empty for line/point input.
edgesdata.frame with columns row, col, fraction,
id — polygon boundary cells with partial coverage; fraction
is in (0, 1). Empty for line/point input.
linesdata.frame with columns row, col, length,
id — line cells; length is the absolute length of the line
within the cell, in CRS units. Empty for polygon/point input.
pointsdata.frame with columns row, col, id —
point cells; no measure column (a point is either in a cell or
it isn't). Empty for polygon/line input.
extentthe raster extent (c(xmin, xmax, ymin, ymax))
dimensionthe grid dimensions (c(ncol, nrow))
Row and column indices are 1-based. Row 1 is the top (ymax) row.
The id column is a 1-based index into the input geometry vector.
The measure is computed in the CRS the geometry and grid are specified in — not the geodesic measure on the Earth. Geometry in degrees produces area in square degrees and length in degrees. For geodesic measures, project to an appropriate CRS first.
if (requireNamespace("geos", quietly = TRUE)) { library(geos) # Polygon: produces $runs (interior) and $edges (fraction in [0, 1]) poly <- as_geos_geometry("POLYGON ((0.5 0.5, 2.5 0.5, 2.5 2.5, 0.5 2.5, 0.5 0.5))") r <- burn_scanline(poly, extent = c(0, 3, 0, 3), dimension = c(3, 3)) # Line: produces $lines with absolute length in CRS units line <- as_geos_geometry("LINESTRING (0.5 0.5, 2.5 2.5)") r <- burn_scanline(line, extent = c(0, 3, 0, 3), dimension = c(3, 3)) # Point: produces $points with row/col only (no measure column) pt <- as_geos_geometry("POINT (1.5 1.5)") r <- burn_scanline(pt, extent = c(0, 3, 0, 3), dimension = c(3, 3)) # Defaults: extent from bbox, 256-cell fitted grid r <- burn_scanline(poly) # By resolution r <- burn_scanline(poly, resolution = 0.01) }if (requireNamespace("geos", quietly = TRUE)) { library(geos) # Polygon: produces $runs (interior) and $edges (fraction in [0, 1]) poly <- as_geos_geometry("POLYGON ((0.5 0.5, 2.5 0.5, 2.5 2.5, 0.5 2.5, 0.5 0.5))") r <- burn_scanline(poly, extent = c(0, 3, 0, 3), dimension = c(3, 3)) # Line: produces $lines with absolute length in CRS units line <- as_geos_geometry("LINESTRING (0.5 0.5, 2.5 2.5)") r <- burn_scanline(line, extent = c(0, 3, 0, 3), dimension = c(3, 3)) # Point: produces $points with row/col only (no measure column) pt <- as_geos_geometry("POINT (1.5 1.5)") r <- burn_scanline(pt, extent = c(0, 3, 0, 3), dimension = c(3, 3)) # Defaults: extent from bbox, 256-cell fitted grid r <- burn_scanline(poly) # By resolution r <- burn_scanline(poly, resolution = 0.01) }
Computes exact coverage fractions for polygon-grid intersections via an exactextract-style flood-fill, returning sparse two-table output: run-length-encoded interior cells and individually weighted boundary cells.
burn_sparse( x, extent = NULL, dimension = NULL, resolution = NULL, tile_size = 4096L )burn_sparse( x, extent = NULL, dimension = NULL, resolution = NULL, tile_size = 4096L )
x |
geometry input, one of:
|
extent |
numeric vector |
dimension |
integer vector |
resolution |
numeric, cell size (scalar for square cells, or
|
tile_size |
integer, maximum tile dimension (default 4096). The grid is
processed in tiles of at most |
Polygon-only. Line and point input are rejected with an error directing
the caller to burn_scanline(), which produces the unified
(polygon / line / point) output schema.
Compared to burn_scanline(), this function is older and uses a
bounding-box-bounded dense intermediate (hence the tile_size
parameter). For polygon input the two functions produce numerically
identical results; burn_scanline() is preferred for new code.
controlledburn does not validate input geometry. It rasterizes what you pass it, valid or not, and trusts you to judge the result.
A list with class "controlledburn" containing:
runsdata.frame with columns row, col_start, col_end, id —
run-length encoded interior cells (full coverage)
edgesdata.frame with columns row, col, fraction, id —
boundary cells with partial coverage, fraction in (0, 1)
extentthe raster extent
dimensionthe grid dimensions
Row and column indices are 1-based. Row 1 is the top (ymax) row.
The id column is a 1-based index into the input geometry vector.
if (requireNamespace("geos", quietly = TRUE)) { library(geos) poly <- as_geos_geometry("POLYGON ((0.5 0.5, 2.5 0.5, 2.5 2.5, 0.5 2.5, 0.5 0.5))") # Explicit extent and dimension result <- burn_sparse(poly, extent = c(0, 3, 0, 3), dimension = c(3, 3)) # Defaults: extent from bbox, 256-cell fitted grid result <- burn_sparse(poly) # Specify resolution (cell size) result <- burn_sparse(poly, resolution = 0.1) }if (requireNamespace("geos", quietly = TRUE)) { library(geos) poly <- as_geos_geometry("POLYGON ((0.5 0.5, 2.5 0.5, 2.5 2.5, 0.5 2.5, 0.5 0.5))") # Explicit extent and dimension result <- burn_sparse(poly, extent = c(0, 3, 0, 3), dimension = c(3, 3)) # Defaults: extent from bbox, 256-cell fitted grid result <- burn_sparse(poly) # Specify resolution (cell size) result <- burn_sparse(poly, resolution = 0.1) }
Expands a sparse burn_sparse() / burn_scanline() result into a dense
matrix, optionally over a subwindow of the parent grid. The matrix's
values depend on which kind of geometry produced the result:
materialize_chunk( x, target = NULL, id = NULL, type = c("matrix", "vector"), max_cells = 1e+08 ) materialise_chunk( x, target = NULL, id = NULL, type = c("matrix", "vector"), max_cells = 1e+08 )materialize_chunk( x, target = NULL, id = NULL, type = c("matrix", "vector"), max_cells = 1e+08 ) materialise_chunk( x, target = NULL, id = NULL, type = c("matrix", "vector"), max_cells = 1e+08 )
x |
a |
target |
numeric extent |
id |
integer geometry id (or vector of ids) to materialize, or |
type |
character, one of |
max_cells |
maximum number of cells to allocate (default 1e8). Set to
|
Polygon ($runs + $edges): coverage fraction in [0, 1].
Interior cells are 1; boundary cells are the partial fraction.
Line ($lines): absolute length within each cell, in CRS units.
Multiple lines crossing the same cell sum.
Point ($points): integer count of points landing in each
cell. Multiple points in one cell sum.
If a result holds tables for more than one geometry kind (which can
happen when a single burn was given a mixed-kind input vector),
materialize_chunk() errors rather than silently combining different
units in one matrix. Filter with id = to one kind first, or run
separate burns for each kind.
A numeric matrix (nrow × ncol) or vector (length nrow*ncol).
The cell unit depends on geometry kind (see Description). When target
is specified, the matrix dimensions correspond to the snapped subwindow.
The snapped extent is available as attr(result, "extent").