-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'rasterize' into 'master'
Add rasterize_polygons See merge request isciences/exactextractr!60
- Loading branch information
Showing
12 changed files
with
269 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# Copyright (c) 2022 ISciences, LLC. | ||
# All rights reserved. | ||
# | ||
# This software is licensed under the Apache License, Version 2.0 (the "License"). | ||
# You may not use this file except in compliance with the License. You may | ||
# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
setGeneric("rasterize_polygons", function(x, y, ...) | ||
standardGeneric("rasterize_polygons")) | ||
|
||
#' Create a raster approximation of a polygon coverage | ||
#' | ||
#' Returns a raster whose values indicate the index of the polygon covering | ||
#' each cell. Where multiple polygons cover the same cell, the index of the | ||
#' polygon covering the greatest area will be used, with the lowest index | ||
#' returned in the case of ties. Cells that are not covered by any polygon, | ||
#' or whose total covered fraction is less than \code{min_coverage}, will be | ||
#' set to \code{NA}. | ||
#' | ||
#' @param x a \code{sf} or \code{sfc} object with polygonal geometries | ||
#' | ||
#' @param y a (possibly empty) \code{RasterLayer} whose resolution and | ||
#' extent will be used for the generated \code{RasterLayer}. | ||
#' @param min_coverage minimum fraction of a cell that must be covered by | ||
#' polygons to be included in the output | ||
#' @return a \code{RasterLayer} or \code{SpatRaster}, consistent with the type of \code{y} | ||
#' @name rasterize_polygons | ||
NULL | ||
|
||
.rasterize_polygons <- function(x, y, min_coverage = 0) { | ||
num_rows <- terra::nrow(y) | ||
num_cols <- terra::ncol(y) | ||
|
||
if (min_coverage == 1.0) { | ||
# account for error in sum of single-precision plots | ||
min_coverage <- (min_coverage - 1e-6) | ||
} | ||
|
||
max_coverage_values <- matrix(0.0, nrow = num_rows, ncol = num_cols) | ||
max_coverage_indexes <- matrix(NA_integer_, nrow = num_rows, ncol = num_cols) | ||
tot_coverage <- matrix(0.0, nrow = num_rows, ncol = num_cols) | ||
|
||
dest_extent <- .extent(y) | ||
dest_res <- .res(y) | ||
|
||
geoms <- sf::st_geometry(x) | ||
|
||
for (i in seq_along(geoms)) { | ||
wkb <- sf::st_as_binary(geoms[[i]], EWKB=TRUE) | ||
|
||
CPP_update_max_coverage(dest_extent, dest_res, max_coverage_values, max_coverage_indexes, tot_coverage, wkb, i) | ||
} | ||
|
||
max_coverage_indexes[tot_coverage < min_coverage] <- NA | ||
|
||
if (inherits(y, 'SpatRaster')) { | ||
ret <- terra::rast(y) | ||
terra::values(ret) <- max_coverage_indexes | ||
} else { | ||
ret <- raster::raster(y) | ||
raster::values(ret) <- max_coverage_indexes | ||
} | ||
|
||
return(ret) | ||
} | ||
|
||
#' @import sf | ||
#' @import raster | ||
#' @useDynLib exactextractr | ||
#' @rdname rasterize_polygons | ||
#' @export | ||
setMethod('rasterize_polygons', signature(x='sf', y='RasterLayer'), .rasterize_polygons) | ||
|
||
#' @useDynLib exactextractr | ||
#' @rdname rasterize_polygons | ||
#' @export | ||
setMethod('rasterize_polygons', signature(x='sf', y='SpatRaster'), .rasterize_polygons) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Copyright (c) 2022 ISciences, LLC. | ||
// All rights reserved. | ||
// | ||
// This software is licensed under the Apache License, Version 2.0 (the "License"). | ||
// You may not use this file except in compliance with the License. You may | ||
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// [[Rcpp::plugins("cpp14")]] | ||
#include <Rcpp.h> | ||
|
||
#include "geos_r.h" | ||
#include "raster_utils.h" | ||
|
||
#include "exactextract/src/raster_cell_intersection.h" | ||
|
||
// [[Rcpp::export]] | ||
void CPP_update_max_coverage(Rcpp::NumericVector & extent, | ||
Rcpp::NumericVector & res, | ||
Rcpp::NumericMatrix & max_coverage, | ||
Rcpp::IntegerMatrix & max_coverage_index, | ||
Rcpp::NumericMatrix & tot_coverage, | ||
const Rcpp::RawVector & wkb, | ||
int index) | ||
{ | ||
GEOSAutoHandle geos; | ||
|
||
auto grid = make_grid(extent, res); | ||
|
||
auto coverage_fraction = exactextract::raster_cell_intersection(grid, geos.handle, read_wkb(geos.handle, wkb).get()); | ||
|
||
auto ix = grid.row_offset(coverage_fraction.grid()); | ||
auto jx = grid.col_offset(coverage_fraction.grid()); | ||
|
||
for (size_t i = 0; i < coverage_fraction.rows(); i++) { | ||
for (size_t j = 0; j < coverage_fraction.cols(); j++) { | ||
auto cov = coverage_fraction(i, j); | ||
if (cov > 0) { | ||
tot_coverage(i + ix, j + jx) += cov; | ||
if (cov > max_coverage(i + ix, j + jx)) { | ||
max_coverage(i + ix, j + jx) = cov; | ||
max_coverage_index(i + ix, j + jx) = index; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# Copyright (c) 2022 ISciences, LLC. | ||
# All rights reserved. | ||
# | ||
# This software is licensed under the Apache License, Version 2.0 (the "License"). | ||
# You may not use this file except in compliance with the License. You may | ||
# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
context('rasterize_polygons') | ||
|
||
test_that('value is assigned to polygon with greatest coverage area', { | ||
polys <- | ||
st_as_sf( | ||
data.frame(id = 1:3, | ||
geom = c( | ||
'POLYGON ((10 0, 10 5, 5 5, 10 0))', | ||
'POLYGON ((0 0, 10 0, 5 5, 1 10, 0 10, 0 0))', | ||
'POLYGON ((5 5, 10 5, 10 10, 1 10, 5 5))' | ||
)), | ||
wkt = 'geom' | ||
) | ||
|
||
rt <- terra::rast(xmin = 0, xmax = 10, ymin = 0, ymax = 10, res = 2) | ||
|
||
r <- rasterize_polygons(polys, rt) | ||
|
||
# lower-right is a tie, so it goes to the first feature encountered | ||
expect_equal( | ||
extract(r, cbind(9, 1))[[1]], | ||
1) | ||
|
||
# center tell is touched by all three, goes to polygon that covers the greatest area | ||
expect_equal( | ||
extract(r, cbind(5, 5))[[1]], | ||
2) | ||
}) | ||
|
||
test_that('min_coverage excludes cells with small coverage area', { | ||
rt <- terra::rast(xmin = 0, xmax = 10, ymin = 0, ymax = 10, res = 1) | ||
circ <- make_circle(5, 5, 3.5, crs=st_crs(rt)) | ||
circ_pieces <- st_sf(st_intersection(circ, st_make_grid(circ, 1))) | ||
|
||
cfrac <- coverage_fraction(rt, circ, crop = FALSE)[[1]] | ||
|
||
# by default, all touched cells are included in output | ||
r <- rasterize_polygons(circ_pieces, rt) | ||
expect_equal( | ||
values(cfrac) > 0, | ||
!is.na(values(r)) | ||
) | ||
|
||
# min_coverage excludes cells with small coverage area | ||
r <- rasterize_polygons(circ_pieces, rt, min_coverage = 0.5) | ||
expect_equal( | ||
values(cfrac) > 0.5, | ||
!is.na(values(r)) | ||
) | ||
}) | ||
|
||
test_that('input type is preserved', { | ||
rt <- terra::rast(xmin = 0, xmax = 10, ymin = 0, ymax = 10, res = 2) | ||
rr <- raster::raster(rt) | ||
|
||
circ <- st_sf(make_circle(5, 5, 3.5, crs=st_crs(rt))) | ||
|
||
r <- rasterize_polygons(circ, rt) | ||
expect_s4_class(r, 'SpatRaster') | ||
|
||
r <- rasterize_polygons(circ, rr) | ||
expect_s4_class(r, 'RasterLayer') | ||
}) | ||
|
||
test_that('no error when polygon does not intersect raster', { | ||
rt <- terra::rast(xmin = 0, xmax = 10, ymin = 0, ymax = 10, res = 2, crs=NA) | ||
|
||
circ <- st_sf(make_circle(500, 500, 3.5, crs=st_crs(rt))) | ||
|
||
r <- rasterize_polygons(circ, rt) | ||
|
||
expect_true(all(is.na(values(r)))) | ||
}) | ||
|
||
test_that('no error when polygon partially intersects raster', { | ||
rt <- terra::rast(xmin = 0, xmax = 10, ymin = 0, ymax = 10, res = 2, crs=NA) | ||
|
||
circ <- st_sf(make_circle(10, 5, 3.5, crs=st_crs(rt))) | ||
|
||
expect_invisible( | ||
r <- rasterize_polygons(circ, rt) | ||
) | ||
}) |