| 1 |
# Copyright (c) 2018-2020 ISciences, LLC. |
|
| 2 |
# All rights reserved. |
|
| 3 |
# |
|
| 4 |
# This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
# You may not use this file except in compliance with the License. You may |
|
| 6 |
# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
# |
|
| 8 |
# Unless required by applicable law or agreed to in writing, software |
|
| 9 |
# distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
# See the License for the specific language governing permissions and |
|
| 12 |
# limitations under the License. |
|
| 13 | ||
| 14 |
if (!isGeneric("exact_extract")) {
|
|
| 15 | 136x |
setGeneric("exact_extract", function(x, y, ...)
|
| 16 | 136x |
standardGeneric("exact_extract"))
|
| 17 |
} |
|
| 18 | ||
| 19 |
#' Extract or summarize values from Raster* objects |
|
| 20 |
#' |
|
| 21 |
#' Extracts the values of cells in a Raster* that are covered by a |
|
| 22 |
#' simple feature collection containing polygonal geometries, as well as the |
|
| 23 |
#' fraction of each cell that is covered by the polygon. Returns either |
|
| 24 |
#' the result of a summary operation or function applied to the values |
|
| 25 |
#' and coverage fractions (if \code{fun} is specified), or a data frame
|
|
| 26 |
#' containing the values and coverage fractions themselves (if \code{fun}
|
|
| 27 |
#' is \code{NULL}.)
|
|
| 28 |
#' |
|
| 29 |
#' The value of \code{fun} may be set to a string (or vector of strings)
|
|
| 30 |
#' representing summary operations supported by the exactextract library. |
|
| 31 |
#' If the input raster has a single layer and a single summary operation |
|
| 32 |
#' is specified, \code{exact_extract} will return a vector with the result
|
|
| 33 |
#' of the summary operation for each feature in the input. If the input |
|
| 34 |
#' raster has multiple layers, or if multiple summary operations are specified, |
|
| 35 |
#' \code{exact_extract} will return a data frame with a row for each feature
|
|
| 36 |
#' and a column for each summary operation / layer combination. (The |
|
| 37 |
#' \code{force_df} can be used to always return a data frame instead of a vector.)
|
|
| 38 |
#' In all of the summary operations, \code{NA} values in the raster are ignored
|
|
| 39 |
#' (i.e., \code{na.rm = TRUE}.)
|
|
| 40 |
#' |
|
| 41 |
#' The following summary operations are supported: |
|
| 42 |
#' |
|
| 43 |
#' \itemize{
|
|
| 44 |
#' \item{\code{min} - the minimum defined value in any raster cell wholly or
|
|
| 45 |
#' partially covered by the polygon} |
|
| 46 |
#' \item{\code{max} - the maximum defined value in any raster cell wholly or
|
|
| 47 |
#' partially covered by the polygon} |
|
| 48 |
#' \item{\code{count} - the sum of fractions of raster cells with defined values
|
|
| 49 |
#' covered by the polygon} |
|
| 50 |
#' \item{\code{sum} - the sum of defined raster cell values, multiplied by
|
|
| 51 |
#' the fraction of the cell that is covered by the polygon} |
|
| 52 |
#' \item{\code{mean} - the mean cell value, weighted by the fraction of each cell
|
|
| 53 |
#' that is covered by the polygon} |
|
| 54 |
#' \item{\code{median} - the median cell value, weighted by the fraction of each
|
|
| 55 |
#' cell that is covered by the polygon} |
|
| 56 |
#' \item{\code{quantile} - arbitrary quantile(s) of cell values, specified in
|
|
| 57 |
#' \code{quantiles}, weighted by the fraction of each
|
|
| 58 |
#' cell that is covered by the polygon} |
|
| 59 |
#' \item{\code{mode} - the most common cell value, weighted by the fraction of
|
|
| 60 |
#' each cell that is covered by the polygon. Where multiple |
|
| 61 |
#' values occupy the same maximum number of weighted cells, |
|
| 62 |
#' the largest value will be returned.} |
|
| 63 |
#' \item{\code{majority} - synonym for \code{mode}}
|
|
| 64 |
#' \item{\code{minority} - the least common cell value, weighted by the fraction
|
|
| 65 |
#' of each cell that is covered by the polygon. Where |
|
| 66 |
#' multiple values occupy the same minimum number of |
|
| 67 |
#' weighted cells, the smallest value will be returned.} |
|
| 68 |
#' \item{\code{variety} - the number of distinct values in cells that are wholly
|
|
| 69 |
#' or partially covered by the polygon.} |
|
| 70 |
#' \item{\code{variance} - the population variance of cell values, weighted by the
|
|
| 71 |
#' fraction of each cell that is covered by the polygon.} |
|
| 72 |
#' \item{\code{stdev} - the population standard deviation of cell values, weighted
|
|
| 73 |
#' by the fraction of each cell that is covered by the polygon.} |
|
| 74 |
#' \item{\code{coefficient_of_variation} - the population coefficient of variation of
|
|
| 75 |
#' cell values, weighted by the fraction of each cell that is |
|
| 76 |
#' covered by the polygon.} |
|
| 77 |
#' \item{\code{weighted_mean} - the mean cell value, weighted by the product of
|
|
| 78 |
#' the fraction of each cell covered by the polygon |
|
| 79 |
#' and the value of a second weighting raster provided |
|
| 80 |
#' as \code{weights}}
|
|
| 81 |
#' \item{\code{weighted_sum} - the sum of defined raster cell values, multiplied by
|
|
| 82 |
#' the fraction of each cell that is covered by the polygon |
|
| 83 |
#' and the value of a second weighting raster provided |
|
| 84 |
#' as \code{weights}}
|
|
| 85 |
#' } |
|
| 86 |
#' |
|
| 87 |
#' Alternatively, an R function may be provided as \code{fun}. The function will be
|
|
| 88 |
#' called for each feature with with vectors of cell values and weights as arguments. |
|
| 89 |
#' \code{exact_extract} will then return a vector of the return values of \code{fun}.
|
|
| 90 |
#' |
|
| 91 |
#' If \code{fun} is not specified, \code{exact_extract} will return a list with
|
|
| 92 |
#' one data frame for each feature in the input feature collection. The data |
|
| 93 |
#' frame will contain a column with values from each layer in the input `Raster*`, |
|
| 94 |
#' and a final column indicating the fraction of the cell that is covered by the |
|
| 95 |
#' polygon. |
|
| 96 |
#' |
|
| 97 |
#' @param x a \code{RasterLayer}, \code{RasterStack}, or \code{RasterBrick}
|
|
| 98 |
#' @param y a sf object with polygonal geometries |
|
| 99 |
#' @param fun an optional function or character vector, as described below |
|
| 100 |
#' @param weights a weighting raster to be used with the \code{weighted_mean}
|
|
| 101 |
#' and \code{weighted_sum} summary operations.
|
|
| 102 |
#' @param quantiles quantiles to be computed when \code{fun == 'quantile'}
|
|
| 103 |
#' @param append_cols when \code{fun} is not \code{NULL}, an optional
|
|
| 104 |
#' character vector of columns from \code{y} to be
|
|
| 105 |
#' included in returned data frame. |
|
| 106 |
#' @param force_df always return a data frame instead of a vector, even if |
|
| 107 |
#' \code{x} has only one layer and \code{fun} has length 1
|
|
| 108 |
#' @param full_colnames include the names of \code{x} in the names of the
|
|
| 109 |
#' returned data frame, even if \code{x} has only one
|
|
| 110 |
#' layer. This is useful when the results of multiple |
|
| 111 |
#' calls to \code{exact_extract} are combined with
|
|
| 112 |
#' \code{cbind}.
|
|
| 113 |
#' @param include_cell if \code{TRUE}, and \code{fun} is \code{NULL}, augment
|
|
| 114 |
#' the returned data frame for each feature with a column |
|
| 115 |
#' for the cell index (\code{cell}). If \code{TRUE} and
|
|
| 116 |
#' \code{fun} is not \code{NULL}, add \code{cell} to the
|
|
| 117 |
#' data frame passed to \code{fun} for each feature.
|
|
| 118 |
#' @param include_cols an optional character vector of column names in |
|
| 119 |
#' \code{y} to be added to the data frame for each
|
|
| 120 |
#' feature that is either returned (when \code{fun} is
|
|
| 121 |
#' \code{NULL}) or passed to \code{fun}.
|
|
| 122 |
#' @param include_xy if \code{TRUE}, and \code{fun} is \code{NULL}, augment
|
|
| 123 |
#' the returned data frame for each feature with columns |
|
| 124 |
#' for cell center coordinates (\code{x} and \code{y}). If
|
|
| 125 |
#' \code{TRUE} and \code{fun} is not \code{NULL}, add
|
|
| 126 |
#' \code{x} and {y} to the data frame passed to \code{fun}
|
|
| 127 |
#' for each feature. |
|
| 128 |
#' @param stack_apply if \code{TRUE}, apply \code{fun} to each layer of
|
|
| 129 |
#' \code{x} independently. If \code{FALSE}, apply \code{fun}
|
|
| 130 |
#' to all layers of \code{x} simultaneously.
|
|
| 131 |
#' @param max_cells_in_memory the maximum number of raster cells to load at |
|
| 132 |
#' a given time when using a named summary operation |
|
| 133 |
#' for \code{fun} (as opposed to a function defined using
|
|
| 134 |
#' R code). If a polygon covers more than \code{max_cells_in_memory}
|
|
| 135 |
#' raster cells, it will be processed in multiple chunks. |
|
| 136 |
#' @param progress if \code{TRUE}, display a progress bar during processing
|
|
| 137 |
#' @param ... additional arguments to pass to \code{fun}
|
|
| 138 |
#' @return a vector or list of data frames, depending on the type of \code{x} and the
|
|
| 139 |
#' value of \code{fun} (see Details)
|
|
| 140 |
#' @examples |
|
| 141 |
#' rast <- raster::raster(matrix(1:100, ncol=10), xmn=0, ymn=0, xmx=10, ymx=10) |
|
| 142 |
#' poly <- sf::st_as_sfc('POLYGON ((2 2, 7 6, 4 9, 2 2))')
|
|
| 143 |
#' |
|
| 144 |
#' # named summary operation on RasterLayer, returns vector |
|
| 145 |
#' exact_extract(rast, poly, 'mean') |
|
| 146 |
#' |
|
| 147 |
#' # two named summary operations on RasterLayer, returns data frame |
|
| 148 |
#' exact_extract(rast, poly, c('min', 'max'))
|
|
| 149 |
#' |
|
| 150 |
#' # named summary operation on RasterStack, returns data frame |
|
| 151 |
#' stk <- raster::stack(list(a=rast, b=sqrt(rast))) |
|
| 152 |
#' exact_extract(stk, poly, 'mean') |
|
| 153 |
#' |
|
| 154 |
#' # named weighted summary operation, returns vector |
|
| 155 |
#' weights <- raster::raster(matrix(runif(100), ncol=10), xmn=0, ymn=0, xmx=10, ymx=10) |
|
| 156 |
#' exact_extract(rast, poly, 'weighted_mean', weights=weights) |
|
| 157 |
#' |
|
| 158 |
#' # custom summary function, returns vector |
|
| 159 |
#' exact_extract(rast, poly, function(value, cov_frac) length(value[cov_frac > 0.9])) |
|
| 160 |
#' |
|
| 161 |
#' @name exact_extract |
|
| 162 |
NULL |
|
| 163 | ||
| 164 |
#' @import sf |
|
| 165 |
#' @import raster |
|
| 166 |
#' @useDynLib exactextractr |
|
| 167 |
#' @rdname exact_extract |
|
| 168 |
#' @export |
|
| 169 |
setMethod('exact_extract', signature(x='Raster', y='sf'),
|
|
| 170 |
function(x, y, fun=NULL, ..., |
|
| 171 |
include_xy=FALSE, |
|
| 172 |
progress=TRUE, |
|
| 173 |
max_cells_in_memory=30000000, |
|
| 174 |
include_cell=FALSE, |
|
| 175 |
force_df=FALSE, |
|
| 176 |
full_colnames=FALSE, |
|
| 177 |
stack_apply=FALSE, |
|
| 178 |
append_cols=NULL, |
|
| 179 |
include_cols=NULL, |
|
| 180 |
quantiles=NULL) {
|
|
| 181 | 9x |
.exact_extract(x, y, fun=fun, ..., |
| 182 | 9x |
include_xy=include_xy, |
| 183 | 9x |
progress=progress, |
| 184 | 9x |
max_cells_in_memory=max_cells_in_memory, |
| 185 | 9x |
include_cell=include_cell, |
| 186 | 9x |
force_df=force_df, |
| 187 | 9x |
full_colnames=full_colnames, |
| 188 | 9x |
stack_apply=stack_apply, |
| 189 | 9x |
append_cols=append_cols, |
| 190 | 9x |
include_cols=include_cols, |
| 191 | 9x |
quantiles=quantiles) |
| 192 |
}) |
|
| 193 | ||
| 194 |
# Return the number of standard (non-...) arguments in a supplied function that |
|
| 195 |
# do not have a default value. This is used to fail if the summary function |
|
| 196 |
# provided by the user cannot accept arguments of values and weights. |
|
| 197 |
.num_expected_args <- function(fun) {
|
|
| 198 | 28x |
a <- formals(args(fun)) |
| 199 | 28x |
a <- a[names(a) != '...'] |
| 200 | 28x |
sum(sapply(a, nchar) == 0) |
| 201 |
} |
|
| 202 | ||
| 203 |
emptyVector <- function(rast) {
|
|
| 204 | 12x |
switch(substr(raster::dataType(rast), 1, 3), |
| 205 | ! |
LOG=logical(), |
| 206 | 3x |
INT=integer(), |
| 207 | 9x |
numeric()) |
| 208 |
} |
|
| 209 | ||
| 210 |
.exact_extract <- function(x, y, fun=NULL, ..., |
|
| 211 |
weights=NULL, |
|
| 212 |
include_xy=FALSE, |
|
| 213 |
progress=TRUE, |
|
| 214 |
max_cells_in_memory=30000000, |
|
| 215 |
include_cell=FALSE, |
|
| 216 |
force_df=FALSE, |
|
| 217 |
full_colnames=FALSE, |
|
| 218 |
stack_apply=FALSE, |
|
| 219 |
append_cols=NULL, |
|
| 220 |
include_cols=NULL, |
|
| 221 |
quantiles=NULL) {
|
|
| 222 | 131x |
if(!is.null(append_cols)) {
|
| 223 | 2x |
if (!inherits(y, 'sf')) {
|
| 224 | ! |
stop(sprintf('append_cols only supported for sf arguments (received %s)',
|
| 225 | ! |
paste(class(y), collapse = ' '))) |
| 226 |
} |
|
| 227 | ||
| 228 | 2x |
force_df <- TRUE |
| 229 |
} |
|
| 230 | ||
| 231 | 131x |
if(sf::st_geometry_type(y, by_geometry = FALSE) == 'GEOMETRY') {
|
| 232 | 2x |
if (!all(sf::st_dimension(y) == 2)) {
|
| 233 | 1x |
stop("Features in sfc_GEOMETRY must be polygonal")
|
| 234 |
} |
|
| 235 | 1x |
y <- sf::st_cast(y, 'MULTIPOLYGON') |
| 236 |
} |
|
| 237 | ||
| 238 | 130x |
if(!is.null(weights)) {
|
| 239 | 28x |
if (!startsWith(class(weights), 'Raster')) {
|
| 240 | 1x |
stop("Weights must be a Raster object.")
|
| 241 |
} |
|
| 242 | ||
| 243 | 27x |
if (!is.character(fun)) {
|
| 244 | 1x |
stop("Weighting raster can only be used with named summary operations.")
|
| 245 |
} |
|
| 246 | ||
| 247 | 26x |
if (!any(startsWith(fun, "weighted"))) {
|
| 248 | 9x |
warning("Weights provided but no requested operations use them.")
|
| 249 |
} |
|
| 250 | ||
| 251 | 26x |
if (!is.na(sf::st_crs(x))) {
|
| 252 | 17x |
if (is.na(sf::st_crs(weights))) {
|
| 253 | 1x |
warning("No CRS specified for weighting raster; assuming it has the same CRS as the value raster.")
|
| 254 | 16x |
} else if (sf::st_crs(x) != sf::st_crs(weights)) {
|
| 255 | 1x |
stop("Weighting raster does not have the same CRS as value raster.")
|
| 256 |
} |
|
| 257 |
} |
|
| 258 |
} |
|
| 259 | ||
| 260 | 127x |
if(is.na(sf::st_crs(x)) && !is.na(sf::st_crs(y))) {
|
| 261 | 1x |
warning("No CRS specified for raster; assuming it has the same CRS as the polygons.")
|
| 262 | 126x |
} else if(is.na(sf::st_crs(y)) && !is.na(sf::st_crs(x))) {
|
| 263 | 2x |
warning("No CRS specified for polygons; assuming they have the same CRS as the raster.")
|
| 264 | 124x |
} else if(sf::st_crs(x) != sf::st_crs(y)) {
|
| 265 | 1x |
y <- sf::st_transform(y, sf::st_crs(x)) |
| 266 | 1x |
warning("Polygons transformed to raster CRS (EPSG:", sf::st_crs(x)$epsg, ")")
|
| 267 |
} |
|
| 268 | ||
| 269 | 127x |
if (!is.null(fun) && !is.character(fun) && .num_expected_args(fun) < 2) {
|
| 270 | 5x |
stop("exact_extract was called with a function that does not appear to ",
|
| 271 | 5x |
"be of the form `function(values, coverage_fractions, ...)`") |
| 272 |
} |
|
| 273 | ||
| 274 | 122x |
if (is.character(fun)) {
|
| 275 | 87x |
if (length(list(...)) > 0) {
|
| 276 | 1x |
stop("exact_extract was called with a named summary operation that ",
|
| 277 | 1x |
"does not accept additional arguments ...") |
| 278 |
} |
|
| 279 | ||
| 280 | 86x |
if (include_xy) {
|
| 281 | 1x |
stop("include_xy must be FALSE for named summary operations")
|
| 282 |
} |
|
| 283 | ||
| 284 | 85x |
if (include_cell) {
|
| 285 | 1x |
stop("include_cell must be FALSE for named summary operations")
|
| 286 |
} |
|
| 287 | ||
| 288 | 84x |
if (!is.null(include_cols)) {
|
| 289 | 1x |
stop("include_cols not supported for named_summary operations (see argument append_cols)")
|
| 290 |
} |
|
| 291 |
} |
|
| 292 | ||
| 293 | 118x |
if (progress && length(y) > 1) {
|
| 294 | 1x |
n <- length(y) |
| 295 | 1x |
pb <- utils::txtProgressBar(min = 0, max = n, initial=0, style=3) |
| 296 | 1x |
update_progress <- function() {
|
| 297 | 2x |
i <- 1 + utils::getTxtProgressBar(pb) |
| 298 | 2x |
utils::setTxtProgressBar(pb, i) |
| 299 | 2x |
if (i == n) {
|
| 300 | ! |
close(pb) |
| 301 |
} |
|
| 302 |
} |
|
| 303 |
} else {
|
|
| 304 | 117x |
update_progress <- function() {}
|
| 305 |
} |
|
| 306 | ||
| 307 | 118x |
tryCatch({
|
| 308 | 118x |
x <- raster::readStart(x) |
| 309 | 118x |
if (!is.null(weights)) {
|
| 310 | 25x |
weights <- raster::readStart(weights) |
| 311 |
} |
|
| 312 | ||
| 313 | 118x |
if (is.character(fun)) {
|
| 314 |
# Compute all stats in C++ |
|
| 315 | 83x |
results <- sapply(sf::st_as_binary(sf::st_geometry(y), EWKB=TRUE), function(wkb) {
|
| 316 | 91x |
ret <- CPP_stats(x, weights, wkb, fun, max_cells_in_memory, quantiles) |
| 317 | 81x |
update_progress() |
| 318 | 81x |
return(ret) |
| 319 |
}) |
|
| 320 | ||
| 321 | 73x |
if (length(fun) == 1 && |
| 322 | 73x |
raster::nlayers(x) == 1 && |
| 323 | 73x |
(is.null(quantiles) || length(quantiles) == 1) && |
| 324 | 73x |
!force_df) {
|
| 325 |
# Just return a vector of stat results |
|
| 326 | 60x |
return(as.vector(results)) |
| 327 |
} else {
|
|
| 328 |
# Return a data frame with a column for each stat |
|
| 329 | 13x |
colnames <- .cppStatColNames(x, fun, full_colnames, quantiles) |
| 330 | ||
| 331 | 13x |
if (is.matrix(results)) {
|
| 332 | 11x |
results <- t(results) |
| 333 |
} else {
|
|
| 334 | 2x |
results <- matrix(results, nrow=length(results)) |
| 335 |
} |
|
| 336 | ||
| 337 | 13x |
dimnames(results) <- list(NULL, colnames) |
| 338 | 13x |
ret <- as.data.frame(results) |
| 339 | ||
| 340 | 13x |
if (!is.null(append_cols)) {
|
| 341 | 1x |
ret <- cbind(sf::st_drop_geometry(y[, append_cols]), ret) |
| 342 |
} |
|
| 343 | ||
| 344 | 13x |
return(ret) |
| 345 |
} |
|
| 346 |
} else {
|
|
| 347 | 35x |
geoms <- sf::st_geometry(y) |
| 348 | ||
| 349 | 35x |
ret <- lapply(seq_along(geoms), function(feature_num) {
|
| 350 | 45x |
wkb <- sf::st_as_binary(geoms[[feature_num]], EWKB=TRUE) |
| 351 | ||
| 352 | 45x |
ret <- CPP_exact_extract(x, wkb) |
| 353 | ||
| 354 | 45x |
if (length(ret$weights) > 0) {
|
| 355 | 39x |
vals <- raster::getValuesBlock(x, |
| 356 | 39x |
row=ret$row, |
| 357 | 39x |
col=ret$col, |
| 358 | 39x |
nrow=nrow(ret$weights), |
| 359 | 39x |
ncol=ncol(ret$weights)) |
| 360 | ||
| 361 | 39x |
if(is.matrix(vals)) {
|
| 362 | 7x |
vals <- as.data.frame(vals) |
| 363 |
} else {
|
|
| 364 | 32x |
vals <- data.frame(value=vals) |
| 365 |
} |
|
| 366 |
} else {
|
|
| 367 |
# Polygon does not intersect raster. |
|
| 368 |
# Construct a zero-row data frame with correct column names/types. |
|
| 369 | 6x |
vals <- do.call(data.frame, lapply(seq_len(raster::nlayers(x)), |
| 370 | 6x |
function(i) emptyVector(x[[i]]))) |
| 371 | 6x |
if (raster::nlayers(x) == 1) {
|
| 372 | 3x |
names(vals) <- 'value' |
| 373 |
} else {
|
|
| 374 | 3x |
names(vals) <- names(x) |
| 375 |
} |
|
| 376 |
} |
|
| 377 | ||
| 378 | 45x |
if (include_xy) {
|
| 379 | 10x |
vals <- .appendXY(vals, x, ret$row, nrow(ret$weights), ret$col, ncol(ret$weights)) |
| 380 |
} |
|
| 381 | ||
| 382 | 45x |
if (include_cell) {
|
| 383 | 4x |
vals <- .appendCell(vals, x, ret$row, nrow(ret$weights), ret$col, ncol(ret$weights)) |
| 384 |
} |
|
| 385 | ||
| 386 | 45x |
if (!is.null(include_cols)) {
|
| 387 |
# use vals as first argument to cbind, then rearrange names so that |
|
| 388 |
# include_cols come first |
|
| 389 | 2x |
vals <- cbind(vals, |
| 390 | 2x |
sf::st_drop_geometry(y[feature_num, include_cols]), |
| 391 | 2x |
row.names = NULL)[, c(include_cols, names(vals))] |
| 392 |
} |
|
| 393 | ||
| 394 | 45x |
cov_fracs <- as.vector(t(ret$weights)) |
| 395 | 45x |
vals <- vals[cov_fracs > 0, , drop=FALSE] |
| 396 | 45x |
cov_fracs <- cov_fracs[cov_fracs > 0] |
| 397 | ||
| 398 | 45x |
update_progress() |
| 399 | ||
| 400 | 45x |
if (is.null(fun)) {
|
| 401 | 13x |
vals$coverage_fraction <- cov_fracs |
| 402 | 13x |
return(vals) |
| 403 |
} else {
|
|
| 404 | 32x |
if (ncol(vals) == 1) {
|
| 405 |
# Only one layer, nothing appended (cells or XY) |
|
| 406 | 24x |
return(fun(vals[,1], cov_fracs, ...)) |
| 407 |
} else {
|
|
| 408 | 8x |
if (stack_apply) {
|
| 409 |
# Pass each layer in stack to callback individually |
|
| 410 | 4x |
nlay <- raster::nlayers(x) |
| 411 | 4x |
appended_cols <- seq_len(ncol(vals))[-seq_len(nlay)] |
| 412 | ||
| 413 | 4x |
if (length(appended_cols) == 0) {
|
| 414 | 2x |
result <- lapply(seq_len(nlay), function(z) |
| 415 | 2x |
fun(vals[, z], cov_fracs, ...)) |
| 416 |
} else {
|
|
| 417 | 2x |
result <- lapply(seq_len(nlay), function(z) |
| 418 | 2x |
fun(cbind(data.frame(value=vals[, z]), |
| 419 | 2x |
vals[, c(appended_cols)]), cov_fracs, ...)) |
| 420 |
} |
|
| 421 | ||
| 422 | 4x |
names(result) <- paste('fun', names(x), sep='.')
|
| 423 | 4x |
return(do.call(data.frame, result)) |
| 424 |
} else {
|
|
| 425 |
# Pass all layers to callback, to be handled together |
|
| 426 | 4x |
return(fun(vals, cov_fracs, ...)) |
| 427 |
} |
|
| 428 |
} |
|
| 429 |
} |
|
| 430 |
}) |
|
| 431 | ||
| 432 | 34x |
if (!is.null(fun)) {
|
| 433 | 22x |
if (class(ret[[1]]) == 'data.frame') {
|
| 434 | 4x |
if (requireNamespace('dplyr', quietly = TRUE)) {
|
| 435 | 4x |
ret <- dplyr::bind_rows(ret) # handle column name mismatches |
| 436 |
} else {
|
|
| 437 | ! |
ret <- do.call(rbind, ret) |
| 438 |
} |
|
| 439 |
} else {
|
|
| 440 | 18x |
ret <- simplify2array(ret) |
| 441 | ||
| 442 | 18x |
if (force_df) {
|
| 443 | 3x |
ret <- data.frame(result = ret) |
| 444 |
} |
|
| 445 |
} |
|
| 446 |
} |
|
| 447 | ||
| 448 | 34x |
if (!is.null(append_cols)) {
|
| 449 | 1x |
ret <- cbind(sf::st_drop_geometry(y[, append_cols]), ret) |
| 450 |
} |
|
| 451 | ||
| 452 | 34x |
return(ret) |
| 453 |
} |
|
| 454 | 118x |
}, finally={
|
| 455 | 118x |
raster::readStop(x) |
| 456 | 118x |
if (!is.null(weights)) {
|
| 457 | 25x |
raster::readStop(weights) |
| 458 |
} |
|
| 459 |
}) |
|
| 460 |
} |
|
| 461 | ||
| 462 |
.appendXY <- function(vals_df, rast, first_row, nrow, first_col, ncol) {
|
|
| 463 | 10x |
if (nrow(vals_df) == 0) {
|
| 464 | 2x |
vals_df$x <- numeric() |
| 465 | 2x |
vals_df$y <- numeric() |
| 466 |
} else {
|
|
| 467 | 8x |
x_coords <- raster::xFromCol(rast, col=seq(first_col, first_col + ncol - 1)) |
| 468 | 8x |
y_coords <- raster::yFromRow(rast, row=seq(first_row, first_row + nrow - 1)) |
| 469 | ||
| 470 | 8x |
vals_df$x <- rep(x_coords, times=nrow) |
| 471 | 8x |
vals_df$y <- rep(y_coords, each=ncol) |
| 472 |
} |
|
| 473 | ||
| 474 | 10x |
return(vals_df) |
| 475 |
} |
|
| 476 | ||
| 477 |
.appendCell <- function(vals_df, rast, first_row, nrow, first_col, ncol) {
|
|
| 478 | 4x |
if (nrow(vals_df) == 0) {
|
| 479 | 2x |
vals_df$cell <- numeric() |
| 480 |
} else {
|
|
| 481 | 2x |
rows <- rep(seq(first_row, first_row + nrow - 1), each = ncol) |
| 482 | 2x |
cols <- rep(seq(first_col, first_col + ncol - 1), times = nrow) |
| 483 | ||
| 484 | 2x |
vals_df$cell <- raster::cellFromRowCol(rast, row=rows, col=cols) |
| 485 |
} |
|
| 486 | ||
| 487 | 4x |
return(vals_df) |
| 488 |
} |
|
| 489 | ||
| 490 |
.cppStatColNames <- function(rast, stat_names, full_colnames, quantiles) {
|
|
| 491 | 13x |
quantile_index = which(stat_names == 'quantile') |
| 492 | 13x |
if (length(quantile_index) != 0) {
|
| 493 | 1x |
stat_names <- c(stat_names[seq_along(stat_names) < quantile_index], |
| 494 | 1x |
sprintf('q%02d', as.integer(100 * quantiles)),
|
| 495 | 1x |
stat_names[seq_along(stat_names) > quantile_index]) |
| 496 |
} |
|
| 497 | ||
| 498 | 13x |
if (raster::nlayers(rast) > 1 || full_colnames) {
|
| 499 | 6x |
z <- expand.grid(names(rast), stat_names, stringsAsFactors=TRUE) |
| 500 | 6x |
mapply(paste, z[[2]], z[[1]], MoreArgs=list(sep='.')) |
| 501 |
} else {
|
|
| 502 | 7x |
stat_names |
| 503 |
} |
|
| 504 |
} |
|
| 505 | ||
| 506 |
#' @useDynLib exactextractr |
|
| 507 |
#' @rdname exact_extract |
|
| 508 |
#' @export |
|
| 509 |
setMethod('exact_extract', signature(x='Raster', y='sfc_MULTIPOLYGON'), .exact_extract)
|
|
| 510 | ||
| 511 |
#' @useDynLib exactextractr |
|
| 512 |
#' @rdname exact_extract |
|
| 513 |
#' @export |
|
| 514 |
setMethod('exact_extract', signature(x='Raster', y='sfc_POLYGON'), .exact_extract)
|
|
| 515 | ||
| 516 |
#' @useDynLib exactextractr |
|
| 517 |
#' @rdname exact_extract |
|
| 518 |
#' @export |
|
| 519 |
setMethod('exact_extract', signature(x='Raster', y='sfc_GEOMETRY'), .exact_extract)
|
| 1 |
# Copyright (c) 2020 ISciences, LLC. |
|
| 2 |
# All rights reserved. |
|
| 3 |
# |
|
| 4 |
# This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
# You may not use this file except in compliance with the License. You may |
|
| 6 |
# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
# |
|
| 8 |
# Unless required by applicable law or agreed to in writing, software |
|
| 9 |
# distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
# See the License for the specific language governing permissions and |
|
| 12 |
# limitations under the License. |
|
| 13 | ||
| 14 |
if (!isGeneric("exact_resample")) {
|
|
| 15 | 6x |
setGeneric("exact_resample", function(x, y, ...)
|
| 16 | 6x |
standardGeneric("exact_resample"))
|
| 17 |
} |
|
| 18 | ||
| 19 |
#' Resample a raster to a new grid |
|
| 20 |
#' |
|
| 21 |
#' @param x a \code{RasterLayer} to be resampled
|
|
| 22 |
#' @param y a \code{RasterLayer} with a grid definition to which \code{x}
|
|
| 23 |
#' should be resampled |
|
| 24 |
#' @param fun a named summary operation to be used for the resampling |
|
| 25 |
#' @return a resampled version of \code{x}
|
|
| 26 |
#' |
|
| 27 |
#' @name exact_resample |
|
| 28 |
NULL |
|
| 29 | ||
| 30 |
#' @import sf |
|
| 31 |
#' @import raster |
|
| 32 |
#' @useDynLib exactextractr |
|
| 33 |
#' @rdname exact_resample |
|
| 34 |
#' @export |
|
| 35 |
setMethod('exact_resample',
|
|
| 36 |
signature(x='RasterLayer', y='RasterLayer'), |
|
| 37 |
function(x, y, fun) {
|
|
| 38 | 6x |
if (sf::st_crs(x) != sf::st_crs(y)) {
|
| 39 | 3x |
if (is.na(sf::st_crs(x))) {
|
| 40 | 1x |
warning("No CRS specified for source raster; assuming it has the same CRS as destination raster.")
|
| 41 | 2x |
} else if (is.na(sf::st_crs(y))) {
|
| 42 | 1x |
warning("No CRS specified for destination raster; assuming it has the same CRS as source raster.")
|
| 43 |
} else {
|
|
| 44 | 1x |
stop('Destination raster must have same CRS as source.')
|
| 45 |
} |
|
| 46 |
} |
|
| 47 | ||
| 48 | 5x |
x <- raster::readStart(x) |
| 49 | 5x |
tryCatch({
|
| 50 | 5x |
CPP_resample(x, y, fun) |
| 51 | 5x |
}, finally={
|
| 52 | 5x |
raster::readStop(x) |
| 53 |
}) |
|
| 54 |
}) |
| 1 |
# Copyright (c) 2018-2020 ISciences, LLC. |
|
| 2 |
# All rights reserved. |
|
| 3 |
# |
|
| 4 |
# This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
# You may not use this file except in compliance with the License. You may |
|
| 6 |
# obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
# |
|
| 8 |
# Unless required by applicable law or agreed to in writing, software |
|
| 9 |
# distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
# See the License for the specific language governing permissions and |
|
| 12 |
# limitations under the License. |
|
| 13 | ||
| 14 |
if (!isGeneric('coverage_fraction')) {
|
|
| 15 | 12x |
setGeneric('coverage_fraction', function(x, y, crop=FALSE, ...)
|
| 16 | 12x |
standardGeneric('coverage_fraction'))
|
| 17 |
} |
|
| 18 | ||
| 19 |
.coverage_fraction <- function(x, y, crop) {
|
|
| 20 | 12x |
if(is.na(sf::st_crs(x)) && !is.na(sf::st_crs(y))) {
|
| 21 | 1x |
warning("No CRS specified for raster; assuming it has the same CRS as the polygons.")
|
| 22 | 11x |
} else if(is.na(sf::st_crs(y)) && !is.na(sf::st_crs(x))) {
|
| 23 | 1x |
warning("No CRS specified for polygons; assuming they have the same CRS as the raster.")
|
| 24 | 10x |
} else if(sf::st_crs(x) != sf::st_crs(y)) {
|
| 25 | 1x |
y <- sf::st_transform(y, sf::st_crs(x)) |
| 26 | 1x |
warning("Polygons transformed to raster CRS (EPSG:", sf::st_crs(x)$epsg, ")")
|
| 27 |
} |
|
| 28 | ||
| 29 | 12x |
lapply(sf::st_as_binary(y, EWKB=TRUE), function(wkb) {
|
| 30 | 12x |
CPP_coverage_fraction(x, wkb, crop) |
| 31 |
}) |
|
| 32 |
} |
|
| 33 | ||
| 34 |
#' Compute the fraction of raster cells covered by a polygon |
|
| 35 |
#' |
|
| 36 |
#' @param x a (possibly empty) \code{RasterLayer} whose resolution and
|
|
| 37 |
#' extent will be used for the generated \code{RasterLayer}.
|
|
| 38 |
#' @param y a \code{sf} object with polygonal geometries
|
|
| 39 |
#' @param crop if \code{TRUE}, each generated \code{RasterLayer} will be
|
|
| 40 |
#' cropped to the extent of its associated feature. |
|
| 41 |
#' @return a list with a \code{RasterLayer} for each feature in \code{y}.
|
|
| 42 |
#' Values of the raster represent the fraction of each |
|
| 43 |
#' cell in \code{x} that is covered by \code{y}.
|
|
| 44 |
#' @examples |
|
| 45 |
#' rast <- raster::raster(matrix(1:100, ncol=10), xmn=0, ymn=0, xmx=10, ymx=10) |
|
| 46 |
#' poly <- sf::st_as_sfc('POLYGON ((2 2, 7 6, 4 9, 2 2))')
|
|
| 47 |
#' |
|
| 48 |
#' cov_frac <- coverage_fraction(rast, poly)[[1]] |
|
| 49 |
#' @name coverage_fraction |
|
| 50 |
NULL |
|
| 51 | ||
| 52 |
#' @import sf |
|
| 53 |
#' @import raster |
|
| 54 |
#' @useDynLib exactextractr |
|
| 55 |
#' @rdname coverage_fraction |
|
| 56 |
#' @export |
|
| 57 |
setMethod('coverage_fraction', signature(x='RasterLayer', y='sf'), function(x, y, crop=FALSE) {
|
|
| 58 | ! |
coverage_fraction(x, sf::st_geometry(y), crop) |
| 59 |
}) |
|
| 60 | ||
| 61 |
#' @rdname coverage_fraction |
|
| 62 |
#' @export |
|
| 63 |
setMethod('coverage_fraction', signature(x='RasterLayer', y='sfc_MULTIPOLYGON'), .coverage_fraction)
|
|
| 64 | ||
| 65 |
#' @rdname coverage_fraction |
|
| 66 |
#' @export |
|
| 67 |
setMethod('coverage_fraction', signature(x='RasterLayer', y='sfc_POLYGON'), .coverage_fraction)
|
|
| 68 |
| 1 |
// Copyright (c) 2018-2020 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
// [[Rcpp::plugins("cpp14")]]
|
|
| 15 |
#include <memory> |
|
| 16 | ||
| 17 |
#include <Rcpp.h> |
|
| 18 |
#include <geos_c.h> |
|
| 19 | ||
| 20 |
#include "exactextract/src/geos_utils.h" |
|
| 21 |
#include "exactextract/src/grid.h" |
|
| 22 |
#include "exactextract/src/matrix.h" |
|
| 23 |
#include "exactextract/src/raster_cell_intersection.h" |
|
| 24 |
#include "exactextract/src/raster_source.h" |
|
| 25 |
#include "exactextract/src/raster_stats.h" |
|
| 26 | ||
| 27 |
using geom_ptr= std::unique_ptr<GEOSGeometry, std::function<void(GEOSGeometry*)>>; |
|
| 28 |
using wkb_reader_ptr = std::unique_ptr<GEOSWKBReader, std::function<void(GEOSWKBReader*)>>; |
|
| 29 | ||
| 30 |
using exactextract::Box; |
|
| 31 |
using exactextract::Matrix; |
|
| 32 |
using exactextract::Grid; |
|
| 33 |
using exactextract::subdivide; |
|
| 34 |
using exactextract::bounded_extent; |
|
| 35 |
using exactextract::Raster; |
|
| 36 |
using exactextract::RasterView; |
|
| 37 |
using exactextract::raster_cell_intersection; |
|
| 38 |
using exactextract::RasterStats; |
|
| 39 |
using exactextract::RasterSource; |
|
| 40 | ||
| 41 | 183x |
static Grid<bounded_extent> make_grid(const Rcpp::S4 & rast) {
|
| 42 | 549x |
Rcpp::Environment raster = Rcpp::Environment::namespace_env("raster");
|
| 43 | ||
| 44 | 366x |
Rcpp::S4 extent = rast.slot("extent");
|
| 45 | ||
| 46 | 549x |
Rcpp::Function resFn = raster["res"]; |
| 47 | ||
| 48 | 183x |
Rcpp::NumericVector res = resFn(rast); |
| 49 | ||
| 50 |
return {{
|
|
| 51 | 366x |
extent.slot("xmin"),
|
| 52 | 366x |
extent.slot("ymin"),
|
| 53 | 366x |
extent.slot("xmax"),
|
| 54 | 366x |
extent.slot("ymax"),
|
| 55 |
}, |
|
| 56 | 183x |
res[0], |
| 57 | 366x |
res[1] |
| 58 |
}; |
|
| 59 |
} |
|
| 60 | ||
| 61 |
// Construct a Raster using an R vector for storage |
|
| 62 |
class NumericVectorRaster : public exactextract::AbstractRaster<double> {
|
|
| 63 |
public: |
|
| 64 | 663x |
NumericVectorRaster(const Rcpp::NumericVector & vec, const Grid<bounded_extent> & g) : |
| 65 |
AbstractRaster<double>(g), |
|
| 66 | 663x |
m_vec(vec) |
| 67 |
{}
|
|
| 68 | ||
| 69 | 538823x |
double operator()(size_t row, size_t col) const final {
|
| 70 | 538823x |
return m_vec[row*cols() + col]; |
| 71 |
} |
|
| 72 | ||
| 73 |
private: |
|
| 74 |
const Rcpp::NumericVector m_vec; |
|
| 75 |
}; |
|
| 76 | ||
| 77 |
// Read raster values from an R raster object |
|
| 78 |
class S4RasterSource {
|
|
| 79 |
public: |
|
| 80 | 118x |
S4RasterSource(Rcpp::S4 rast) : |
| 81 | 118x |
m_grid(Grid<bounded_extent>::make_empty()), |
| 82 |
m_rast(rast), |
|
| 83 | 118x |
m_last_box(0, 0, 0, 0) |
| 84 |
{
|
|
| 85 | 118x |
m_grid = make_grid(rast); |
| 86 |
} |
|
| 87 | ||
| 88 | 109x |
const Grid<bounded_extent> &grid() const {
|
| 89 | 109x |
return m_grid; |
| 90 |
} |
|
| 91 | ||
| 92 | 663x |
std::unique_ptr<exactextract::AbstractRaster<double>> read_box(const exactextract::Box & box, int layer) {
|
| 93 | 663x |
auto cropped_grid = m_grid.crop(box); |
| 94 | ||
| 95 | 663x |
if (!(box == m_last_box)) {
|
| 96 | 658x |
m_last_box = box; |
| 97 | ||
| 98 | 1974x |
Rcpp::Environment raster = Rcpp::Environment::namespace_env("raster");
|
| 99 | 1974x |
Rcpp::Function getValuesBlockFn = raster["getValuesBlock"]; |
| 100 | ||
| 101 | 658x |
if (cropped_grid.empty()) {
|
| 102 | 77x |
m_rast_values = Rcpp::no_init(0, 0); |
| 103 |
} else {
|
|
| 104 |
// Instead of reading only values for the requested band, we read values |
|
| 105 |
// for all requested bands and then cache them to return from subsequent |
|
| 106 |
// calls to read_box. There are two reasons for this: |
|
| 107 |
// 1) Use of the 'lyrs` argument to getValuesBlock does not work for |
|
| 108 |
// a single band in the CRAN version of raster |
|
| 109 |
// 2) Reading everything once avoids the very significant per-call |
|
| 110 |
// overhead of getValuesBlock, which largely comes from operations |
|
| 111 |
// like setting names on the result vector. |
|
| 112 | 581x |
m_rast_values = getValuesBlockFn(m_rast, |
| 113 | ! |
1 + cropped_grid.row_offset(m_grid), |
| 114 | 581x |
cropped_grid.rows(), |
| 115 | 581x |
1 + cropped_grid.col_offset(m_grid), |
| 116 | 581x |
cropped_grid.cols(), |
| 117 | 1162x |
Rcpp::Named("format", "m"));
|
| 118 |
} |
|
| 119 |
} |
|
| 120 | ||
| 121 | 663x |
if (cropped_grid.empty()) {
|
| 122 | 77x |
return std::make_unique<NumericVectorRaster>(m_rast_values, cropped_grid); |
| 123 |
} else {
|
|
| 124 | 1172x |
return std::make_unique<NumericVectorRaster>(m_rast_values(Rcpp::_, layer), |
| 125 | 586x |
cropped_grid); |
| 126 |
} |
|
| 127 |
} |
|
| 128 | ||
| 129 |
private: |
|
| 130 |
Grid<bounded_extent> m_grid; |
|
| 131 |
Rcpp::S4 m_rast; |
|
| 132 |
Rcpp::NumericMatrix m_rast_values; |
|
| 133 |
exactextract::Box m_last_box; |
|
| 134 |
}; |
|
| 135 | ||
| 136 |
// GEOS warning handler |
|
| 137 | ! |
static void geos_warn(const char* fmt, ...) {
|
| 138 | ! |
char buf[BUFSIZ] = { '\0' };
|
| 139 | ||
| 140 |
va_list msg; |
|
| 141 | ! |
va_start(msg, fmt); |
| 142 | ! |
vsnprintf(buf, BUFSIZ*sizeof(char), fmt, msg); |
| 143 | ! |
va_end(msg); |
| 144 | ||
| 145 | ! |
Rcpp::Function warning("warning");
|
| 146 | ! |
warning(buf); |
| 147 |
} |
|
| 148 | ||
| 149 |
// GEOS error handler |
|
| 150 | ! |
static void geos_error(const char* fmt, ...) {
|
| 151 | ! |
char buf[BUFSIZ] = { '\0' };
|
| 152 | ||
| 153 |
va_list msg; |
|
| 154 | ! |
va_start(msg, fmt); |
| 155 | ! |
vsnprintf(buf, BUFSIZ*sizeof(char), fmt, msg); |
| 156 | ! |
va_end(msg); |
| 157 | ||
| 158 | ! |
Rcpp::stop(buf); |
| 159 |
} |
|
| 160 | ||
| 161 |
// GEOSContextHandle wrapper to ensure finishGEOS is called. |
|
| 162 |
struct GEOSAutoHandle {
|
|
| 163 | 148x |
GEOSAutoHandle() {
|
| 164 | 148x |
handle = initGEOS_r(geos_warn, geos_error); |
| 165 |
} |
|
| 166 | ||
| 167 | 296x |
~GEOSAutoHandle() {
|
| 168 | 148x |
finishGEOS_r(handle); |
| 169 |
} |
|
| 170 | ||
| 171 |
GEOSContextHandle_t handle; |
|
| 172 |
}; |
|
| 173 | ||
| 174 |
// Return a smart pointer to a Geometry, given WKB input |
|
| 175 | 142x |
static geom_ptr read_wkb(const GEOSContextHandle_t & context, const Rcpp::RawVector & wkb) {
|
| 176 | 426x |
wkb_reader_ptr wkb_reader{ GEOSWKBReader_create_r(context), [context](GEOSWKBReader* r) { GEOSWKBReader_destroy_r(context, r); } };
|
| 177 | ||
| 178 |
geom_ptr geom{ GEOSWKBReader_read_r(context,
|
|
| 179 |
wkb_reader.get(), |
|
| 180 | 284x |
std::addressof(wkb[0]), |
| 181 | 142x |
wkb.size()), |
| 182 | 284x |
[context](GEOSGeometry* g) { GEOSGeom_destroy_r(context, g); } };
|
| 183 | ||
| 184 | 142x |
if (geom.get() == nullptr) {
|
| 185 | ! |
Rcpp::stop("Failed to parse WKB geometry");
|
| 186 |
} |
|
| 187 | ||
| 188 | 284x |
return geom; |
| 189 |
} |
|
| 190 | ||
| 191 |
// [[Rcpp::export]] |
|
| 192 | 45x |
Rcpp::List CPP_exact_extract(Rcpp::S4 & rast, const Rcpp::RawVector & wkb) {
|
| 193 | 90x |
GEOSAutoHandle geos; |
| 194 | ||
| 195 | 45x |
auto grid = make_grid(rast); |
| 196 | 90x |
auto coverage_fractions = raster_cell_intersection(grid, geos.handle, read_wkb(geos.handle, wkb).get()); |
| 197 | ||
| 198 | 45x |
size_t nrow = coverage_fractions.rows(); |
| 199 | 45x |
size_t ncol = coverage_fractions.cols(); |
| 200 | ||
| 201 | 45x |
Rcpp::NumericMatrix weights = Rcpp::no_init(nrow, ncol); |
| 202 | 159x |
for (size_t i = 0; i < nrow; i++) {
|
| 203 | 507x |
for (size_t j = 0; j < ncol; j++) {
|
| 204 | 393x |
weights(i, j) = coverage_fractions(i ,j); |
| 205 |
} |
|
| 206 |
} |
|
| 207 | ||
| 208 | 45x |
if (nrow > 0) {
|
| 209 | 39x |
size_t row_us = 1 + coverage_fractions.grid().row_offset(grid); |
| 210 | 39x |
if (row_us > static_cast<size_t>(std::numeric_limits<int>::max())) {
|
| 211 | ||
| 212 |
} |
|
| 213 |
} |
|
| 214 | ||
| 215 | 45x |
int row = NA_INTEGER; |
| 216 | 45x |
int col = NA_INTEGER; |
| 217 | 45x |
if (nrow > 0) {
|
| 218 | 39x |
size_t row_us = (1 + coverage_fractions.grid().row_offset(grid)); |
| 219 | 39x |
if (row_us > static_cast<size_t>(std::numeric_limits<int>::max())) {
|
| 220 | ! |
throw std::runtime_error("Cannot represent row offset as an R integer");
|
| 221 |
} |
|
| 222 | 39x |
row = static_cast<int>(row_us); |
| 223 |
} |
|
| 224 | 45x |
if (ncol > 0) {
|
| 225 | 39x |
size_t col_us = (1 + coverage_fractions.grid().col_offset(grid)); |
| 226 | 39x |
if (col_us > static_cast<size_t>(std::numeric_limits<int>::max())) {
|
| 227 | ! |
throw std::runtime_error("Cannot represent column offset as an R integer");
|
| 228 |
} |
|
| 229 | 39x |
col = static_cast<int>(col_us); |
| 230 |
} |
|
| 231 | ||
| 232 |
return Rcpp::List::create( |
|
| 233 | 90x |
Rcpp::Named("row") = row,
|
| 234 | 90x |
Rcpp::Named("col") = col,
|
| 235 | 90x |
Rcpp::Named("weights") = weights
|
| 236 |
); |
|
| 237 |
} |
|
| 238 | ||
| 239 |
// [[Rcpp::export]] |
|
| 240 | 12x |
Rcpp::S4 CPP_coverage_fraction(Rcpp::S4 & rast, const Rcpp::RawVector & wkb, bool crop) |
| 241 |
{
|
|
| 242 | 24x |
GEOSAutoHandle geos; |
| 243 | 36x |
Rcpp::Environment raster = Rcpp::Environment::namespace_env("raster");
|
| 244 | 36x |
Rcpp::Function rasterFn = raster["raster"]; |
| 245 | 36x |
Rcpp::Function crsFn = raster["crs"]; |
| 246 | ||
| 247 | 12x |
auto grid = make_grid(rast); |
| 248 | 24x |
auto coverage_fraction = raster_cell_intersection(grid, geos.handle, read_wkb(geos.handle, wkb).get()); |
| 249 | ||
| 250 | 12x |
if (crop) {
|
| 251 | 2x |
grid = coverage_fraction.grid(); |
| 252 |
} |
|
| 253 | 24x |
RasterView<float> coverage_view(coverage_fraction, grid); |
| 254 | ||
| 255 | 12x |
Rcpp::NumericMatrix weights{static_cast<int>(grid.rows()),
|
| 256 | 12x |
static_cast<int>(grid.cols())}; |
| 257 | ||
| 258 | 629x |
for (size_t i = 0; i < grid.rows(); i++) {
|
| 259 | 159018x |
for (size_t j = 0; j < grid.cols(); j++) {
|
| 260 | 158401x |
weights(i, j) = coverage_view(i, j); |
| 261 | 158401x |
if (!crop && std::isnan(weights(i, j))) {
|
| 262 | 139692x |
weights(i, j) = 0; |
| 263 |
} |
|
| 264 |
} |
|
| 265 |
} |
|
| 266 | ||
| 267 |
return rasterFn(weights, |
|
| 268 | 24x |
Rcpp::Named("xmn")=grid.xmin(),
|
| 269 | 24x |
Rcpp::Named("xmx")=grid.xmax(),
|
| 270 | 24x |
Rcpp::Named("ymn")=grid.ymin(),
|
| 271 | 24x |
Rcpp::Named("ymx")=grid.ymax(),
|
| 272 | 48x |
Rcpp::Named("crs")=crsFn(rast));
|
| 273 |
} |
|
| 274 | ||
| 275 | 115x |
static int get_nlayers(Rcpp::S4 & rast) {
|
| 276 | 345x |
Rcpp::Environment raster = Rcpp::Environment::namespace_env("raster");
|
| 277 | 345x |
Rcpp::Function nlayersFn = raster["nlayers"]; |
| 278 | ||
| 279 | 115x |
Rcpp::NumericVector nlayersVec = nlayersFn(rast); |
| 280 | ||
| 281 | 230x |
return static_cast<int>(nlayersVec[0]); |
| 282 |
} |
|
| 283 | ||
| 284 | 500250x |
static double get_stat_value(const RasterStats<double> & stats, const std::string & stat_name) {
|
| 285 |
if (stat_name == "mean") return stats.mean(); |
|
| 286 | ||
| 287 | 500250x |
else if (stat_name == "sum") return stats.sum(); |
| 288 | ! |
else if (stat_name == "count") return stats.count(); |
| 289 | ||
| 290 | ! |
else if (stat_name == "min") return stats.min().value_or(NA_REAL); |
| 291 | ! |
else if (stat_name == "max") return stats.max().value_or(NA_REAL); |
| 292 | ||
| 293 | ! |
else if (stat_name == "mode") return stats.mode().value_or(NA_REAL); |
| 294 | ! |
else if (stat_name == "majority") return stats.mode().value_or(NA_REAL); |
| 295 | ! |
else if (stat_name == "minority") return stats.minority().value_or(NA_REAL); |
| 296 | ||
| 297 | ! |
else if (stat_name == "variety") return stats.variety(); |
| 298 | ! |
else if (stat_name == "weighted_mean") return stats.weighted_mean(); |
| 299 | ! |
else if (stat_name == "weighted_sum") return stats.weighted_sum(); |
| 300 | ||
| 301 | ! |
else if (stat_name == "variance") return stats.variance(); |
| 302 | ! |
else if (stat_name == "stdev") return stats.stdev(); |
| 303 | ! |
else if (stat_name == "coefficient_of_variation") return stats.coefficient_of_variation(); |
| 304 | ||
| 305 | ! |
else Rcpp::stop("Unknown stat: " + stat_name);
|
| 306 |
} |
|
| 307 | ||
| 308 | ||
| 309 |
// [[Rcpp::export]] |
|
| 310 | 91x |
Rcpp::NumericMatrix CPP_stats(Rcpp::S4 & rast, |
| 311 |
Rcpp::Nullable<Rcpp::S4> weights, |
|
| 312 |
const Rcpp::RawVector & wkb, |
|
| 313 |
const Rcpp::StringVector & stats, |
|
| 314 |
int max_cells_in_memory, |
|
| 315 |
const Rcpp::Nullable<Rcpp::NumericVector> & quantiles) {
|
|
| 316 |
try {
|
|
| 317 | 182x |
GEOSAutoHandle geos; |
| 318 | ||
| 319 | 91x |
if (max_cells_in_memory < 1) {
|
| 320 | 1x |
Rcpp::stop("Invalid value for max_cells_in_memory: %d", max_cells_in_memory);
|
| 321 |
} |
|
| 322 | ||
| 323 | 90x |
int nlayers = get_nlayers(rast); |
| 324 | ||
| 325 | 180x |
S4RasterSource rsrc(rast); |
| 326 | ||
| 327 | 90x |
std::unique_ptr<S4RasterSource> rweights; |
| 328 | 90x |
bool weighted = false; |
| 329 | 90x |
if (weights.isNotNull()) {
|
| 330 | 26x |
Rcpp::S4 weights_s4 = weights.get(); |
| 331 | ||
| 332 | 25x |
if (get_nlayers(weights_s4) != 1) {
|
| 333 | 1x |
Rcpp::stop("Weighting raster must have only a single layer.");
|
| 334 |
} |
|
| 335 | ||
| 336 | 24x |
rweights = std::make_unique<S4RasterSource>(weights_s4); |
| 337 | 24x |
weighted = true; |
| 338 |
} |
|
| 339 | ||
| 340 | 89x |
bool store_values = false; |
| 341 | 89x |
int stat_result_size = 0; |
| 342 | 187x |
for (const auto & stat : stats) {
|
| 343 |
// explicit construction of std::string seems necessary to avoid ambiguous overload error |
|
| 344 | 204x |
if (stat == std::string("mode") ||
|
| 345 | 199x |
stat == std::string("majority") ||
|
| 346 | 196x |
stat == std::string("minority") ||
|
| 347 | 192x |
stat == std::string("variety") ||
|
| 348 | 484x |
stat == std::string("median") ||
|
| 349 | 183x |
stat == std::string("quantile")) {
|
| 350 | 29x |
store_values = true; |
| 351 |
} |
|
| 352 | ||
| 353 | 179x |
if (!weighted && |
| 354 | 179x |
(stat == std::string("weighted_mean") ||
|
| 355 | 178x |
stat == std::string("weighted_sum"))) {
|
| 356 | 2x |
Rcpp::stop("Weighted stat requested but no weights provided.");
|
| 357 |
} |
|
| 358 | ||
| 359 | 100x |
if (stat == std::string("quantile")) {
|
| 360 | 8x |
int num_quantiles = 0; |
| 361 | ||
| 362 | 8x |
if (quantiles.isNotNull()) {
|
| 363 | 7x |
Rcpp::NumericVector qvec = quantiles.get(); |
| 364 | 7x |
num_quantiles = qvec.size(); |
| 365 |
} |
|
| 366 | ||
| 367 | 8x |
if (num_quantiles == 0) {
|
| 368 | 2x |
Rcpp::stop("Quantiles not specified.");
|
| 369 |
} |
|
| 370 | ||
| 371 | 6x |
stat_result_size += num_quantiles; |
| 372 |
} else {
|
|
| 373 | 92x |
stat_result_size += 1; |
| 374 |
} |
|
| 375 |
} |
|
| 376 | ||
| 377 | 170x |
std::vector<RasterStats<double>> raster_stats; |
| 378 | 85x |
raster_stats.reserve(nlayers); |
| 379 | 175x |
for (int i = 0; i < nlayers; i++) {
|
| 380 | 90x |
raster_stats.emplace_back(store_values); |
| 381 |
} |
|
| 382 | ||
| 383 | 170x |
Rcpp::NumericMatrix stat_results = Rcpp::no_init(nlayers, stat_result_size); |
| 384 | ||
| 385 | 170x |
auto geom = read_wkb(geos.handle, wkb); |
| 386 | 85x |
auto bbox = exactextract::geos_get_box(geos.handle, geom.get()); |
| 387 | ||
| 388 | 85x |
auto grid = weighted ? rsrc.grid().common_grid(rweights->grid()) : rsrc.grid(); |
| 389 | ||
| 390 | 84x |
if (bbox.intersects(grid.extent())) {
|
| 391 | 75x |
auto cropped_grid = grid.crop(bbox); |
| 392 | ||
| 393 | 185x |
for (const auto &subgrid : subdivide(cropped_grid, max_cells_in_memory)) {
|
| 394 | 220x |
auto coverage_fraction = raster_cell_intersection(subgrid, geos.handle, geom.get()); |
| 395 | 110x |
auto& cov_grid = coverage_fraction.grid(); |
| 396 | ||
| 397 | 110x |
if (!cov_grid.empty()) {
|
| 398 | 110x |
if (weighted) {
|
| 399 | 46x |
auto weights = rweights->read_box(cov_grid.extent(), 0); |
| 400 | ||
| 401 | 47x |
for (int i = 0; i < nlayers; i++) {
|
| 402 | 48x |
auto values = rsrc.read_box(cov_grid.extent(), i); |
| 403 | 24x |
raster_stats[i].process(coverage_fraction, *values, *weights); |
| 404 |
} |
|
| 405 |
} else {
|
|
| 406 | 178x |
for (int i = 0; i < nlayers; i++) {
|
| 407 | 182x |
auto values = rsrc.read_box(cov_grid.extent(), i); |
| 408 | 91x |
raster_stats[i].process(coverage_fraction, *values); |
| 409 |
} |
|
| 410 |
} |
|
| 411 |
} |
|
| 412 |
} |
|
| 413 |
} |
|
| 414 | ||
| 415 | 170x |
for (int j = 0; j < nlayers; j++) {
|
| 416 | 89x |
const auto& rs = raster_stats[j]; |
| 417 | ||
| 418 | 89x |
int i = 0; |
| 419 | 190x |
for(const auto& stat : stats) {
|
| 420 | 104x |
if (stat == std::string("mean")) stat_results(j, i++) = rs.mean();
|
| 421 | ||
| 422 | 83x |
else if (stat == std::string("sum")) stat_results(j, i++) = rs.sum();
|
| 423 | 68x |
else if (stat == std::string("count")) stat_results(j, i++) = rs.count();
|
| 424 | ||
| 425 | 62x |
else if (stat == std::string("min")) stat_results(j, i++) = rs.min().value_or(NA_REAL);
|
| 426 | 56x |
else if (stat == std::string("max")) stat_results(j, i++) = rs.max().value_or(NA_REAL);
|
| 427 | ||
| 428 | 50x |
else if (stat == std::string("median")) stat_results(j, i++) = rs.quantile(0.5).value_or(NA_REAL);
|
| 429 | ||
| 430 | 49x |
else if (stat == std::string("mode")) stat_results(j, i++) = rs.mode().value_or(NA_REAL);
|
| 431 | 44x |
else if (stat == std::string("majority")) stat_results(j, i++) = rs.mode().value_or(NA_REAL);
|
| 432 | 41x |
else if (stat == std::string("minority")) stat_results(j, i++) = rs.minority().value_or(NA_REAL);
|
| 433 | ||
| 434 | 37x |
else if (stat == std::string("variety")) stat_results(j, i++) = rs.variety();
|
| 435 | 25x |
else if (stat == std::string("weighted_mean")) stat_results(j, i++) = rs.weighted_mean();
|
| 436 | 13x |
else if (stat == std::string("weighted_sum")) stat_results(j, i++) = rs.weighted_sum();
|
| 437 | ||
| 438 | 10x |
else if (stat == std::string("variance")) stat_results(j, i++) = rs.variance();
|
| 439 | 9x |
else if (stat == std::string("stdev")) stat_results(j, i++) = rs.stdev();
|
| 440 | 8x |
else if (stat == std::string("coefficient_of_variation")) stat_results(j, i++) = rs.coefficient_of_variation();
|
| 441 | ||
| 442 | 7x |
else if (stat == std::string("quantile")) {
|
| 443 | 12x |
Rcpp::NumericVector qvec = quantiles.get(); |
| 444 | 15x |
for (double q : qvec) {
|
| 445 | 11x |
stat_results(j, i++) = rs.quantile(q).value_or(NA_REAL); |
| 446 |
} |
|
| 447 |
} |
|
| 448 | ||
| 449 | 4x |
else Rcpp::stop("Unknown stat: " + stat);
|
| 450 |
} |
|
| 451 |
} |
|
| 452 | ||
| 453 | 162x |
return stat_results; |
| 454 | 20x |
} catch (std::exception & e) {
|
| 455 |
// throw predictable exception class |
|
| 456 | 10x |
Rcpp::stop(e.what()); |
| 457 |
} |
|
| 458 |
} |
|
| 459 | ||
| 460 |
// [[Rcpp::export]] |
|
| 461 | 5x |
Rcpp::S4 CPP_resample(Rcpp::S4 & rast_in, |
| 462 |
Rcpp::S4 & rast_out, |
|
| 463 |
const Rcpp::StringVector & stat) {
|
|
| 464 | 15x |
Rcpp::Environment raster = Rcpp::Environment::namespace_env("raster");
|
| 465 | 15x |
Rcpp::Function rasterFn = raster["raster"]; |
| 466 | 15x |
Rcpp::Function valuesFn = raster["values<-"]; |
| 467 | ||
| 468 | 5x |
if (stat.size() != 1) {
|
| 469 | 1x |
Rcpp::stop("Only a single operation may be used for resampling.");
|
| 470 |
} |
|
| 471 | ||
| 472 | 8x |
S4RasterSource rsrc(rast_in); |
| 473 | ||
| 474 | 4x |
Rcpp::S4 out = rasterFn(rast_out); |
| 475 | ||
| 476 | 4x |
auto grid_in = make_grid(rast_in); |
| 477 | 4x |
auto grid_out = make_grid(rast_out); |
| 478 | ||
| 479 | 8x |
std::string stat_name = Rcpp::as<std::string>(stat[0]); |
| 480 | ||
| 481 | 8x |
Rcpp::NumericMatrix values_out = Rcpp::no_init(grid_out.rows(), grid_out.cols()); |
| 482 | ||
| 483 | 529x |
for (size_t row = 0; row < grid_out.rows(); row++) {
|
| 484 |
// Read enough source raster data to process an entire destination row at |
|
| 485 |
// a time, since getValuesBlock calls have a lot of overhead. |
|
| 486 | 525x |
auto y = grid_out.y_for_row(row); |
| 487 | 525x |
auto ymin = y - grid_out.dy(); |
| 488 | 525x |
auto ymax = y + grid_out.dy(); |
| 489 | ||
| 490 | 525x |
Box row_box{ grid_out.xmin(), ymin, grid_out.xmax(), ymax };
|
| 491 | 1050x |
auto values = rsrc.read_box(row_box, 0); |
| 492 | ||
| 493 | 500775x |
for (size_t col = 0; col < grid_out.cols(); col++) {
|
| 494 | 1000500x |
RasterStats<double> stats; |
| 495 | ||
| 496 | 500250x |
Box cell = grid_cell(grid_out, row, col); |
| 497 | 500250x |
auto coverage_fraction = raster_cell_intersection(grid_in, cell); |
| 498 | ||
| 499 | 500250x |
auto& cov_grid = coverage_fraction.grid(); |
| 500 | ||
| 501 | 500250x |
if (!cov_grid.empty()) {
|
| 502 | 382698x |
stats.process(coverage_fraction, *values); |
| 503 |
} |
|
| 504 | ||
| 505 | 500250x |
values_out(row, col) = get_stat_value(stats, stat_name); |
| 506 |
} |
|
| 507 |
} |
|
| 508 | ||
| 509 | 4x |
out = valuesFn(out, values_out); |
| 510 | 8x |
return out; |
| 511 |
} |
| 1 |
// Copyright (c) 2018-2019 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#ifndef EXACTEXTRACT_BOX_H |
|
| 15 |
#define EXACTEXTRACT_BOX_H |
|
| 16 | ||
| 17 |
#include "coordinate.h" |
|
| 18 |
#include "crossing.h" |
|
| 19 |
#include "side.h" |
|
| 20 | ||
| 21 |
#include <limits> |
|
| 22 | ||
| 23 |
namespace exactextract {
|
|
| 24 | ||
| 25 |
struct Box {
|
|
| 26 |
double xmin; |
|
| 27 |
double ymin; |
|
| 28 |
double xmax; |
|
| 29 |
double ymax; |
|
| 30 | ||
| 31 | 8379714x |
Box(double p_xmin, double p_ymin, double p_xmax, double p_ymax) : |
| 32 |
xmin{p_xmin},
|
|
| 33 |
ymin{p_ymin},
|
|
| 34 |
xmax{p_xmax},
|
|
| 35 | 8379714x |
ymax{p_ymax} {}
|
| 36 | ||
| 37 |
static Box maximum_finite() {
|
|
| 38 |
return {
|
|
| 39 |
std::numeric_limits<double>::lowest(), |
|
| 40 |
std::numeric_limits<double>::lowest(), |
|
| 41 |
std::numeric_limits<double>::max(), |
|
| 42 |
std::numeric_limits<double>::max() |
|
| 43 |
}; |
|
| 44 |
} |
|
| 45 | ||
| 46 | 167x |
static Box make_empty() {
|
| 47 | 167x |
return {0, 0, 0, 0};
|
| 48 |
} |
|
| 49 | ||
| 50 | 6112695x |
double width() const {
|
| 51 | 6112695x |
return xmax - xmin; |
| 52 |
} |
|
| 53 | ||
| 54 | 6112695x |
double height() const {
|
| 55 | 6112695x |
return ymax - ymin; |
| 56 |
} |
|
| 57 | ||
| 58 | 6109487x |
double area() const {
|
| 59 | 6109487x |
return width() * height(); |
| 60 |
} |
|
| 61 | ||
| 62 | 1604x |
double perimeter() const {
|
| 63 | 1604x |
return 2 * width() + 2 * height(); |
| 64 |
} |
|
| 65 | ||
| 66 | 383901x |
bool intersects(const Box & other) const {
|
| 67 | 383901x |
if (other.ymin > ymax) |
| 68 | 48x |
return false; |
| 69 | 383853x |
if (other.ymax < ymin) |
| 70 | 44x |
return false; |
| 71 | 383809x |
if (other.xmin > xmax) |
| 72 | ! |
return false; |
| 73 | 383809x |
if (other.xmax < xmin) |
| 74 | ! |
return false; |
| 75 | ||
| 76 | 383809x |
return true; |
| 77 |
} |
|
| 78 | ||
| 79 | 3937853x |
Box intersection(const Box & other) const {
|
| 80 |
return {
|
|
| 81 | 3937853x |
std::max(xmin, other.xmin), |
| 82 | 3937853x |
std::max(ymin, other.ymin), |
| 83 | 3937853x |
std::min(xmax, other.xmax), |
| 84 | 3937853x |
std::min(ymax, other.ymax) |
| 85 |
}; |
|
| 86 |
} |
|
| 87 | ||
| 88 |
Box translate(double dx, double dy) const {
|
|
| 89 |
return {
|
|
| 90 |
xmin + dx, |
|
| 91 |
ymin + dy, |
|
| 92 |
xmax + dx, |
|
| 93 |
ymax + dy |
|
| 94 |
}; |
|
| 95 |
} |
|
| 96 | ||
| 97 |
Coordinate upper_left() const {
|
|
| 98 |
return Coordinate{xmin, ymax};
|
|
| 99 |
} |
|
| 100 | ||
| 101 |
Coordinate upper_right() const {
|
|
| 102 |
return Coordinate{xmax, ymax};
|
|
| 103 |
} |
|
| 104 | ||
| 105 |
Coordinate lower_left() const {
|
|
| 106 |
return Coordinate{xmin, ymin};
|
|
| 107 |
} |
|
| 108 | ||
| 109 |
Coordinate lower_right() const {
|
|
| 110 |
return Coordinate{xmax, ymin};
|
|
| 111 |
} |
|
| 112 | ||
| 113 |
Side side(const Coordinate &c) const; |
|
| 114 | ||
| 115 |
Crossing crossing(const Coordinate &c1, const Coordinate &c2) const; |
|
| 116 | ||
| 117 | 500587x |
bool empty() const {
|
| 118 | 500587x |
return xmin >= xmax || ymin >= ymax; |
| 119 |
} |
|
| 120 | ||
| 121 | 3x |
Box expand_to_include(const Box & other) const {
|
| 122 | 3x |
if (empty()) {
|
| 123 | ! |
return other; |
| 124 |
} |
|
| 125 | ||
| 126 | 3x |
if (other.empty()) {
|
| 127 | ! |
return *this; |
| 128 |
} |
|
| 129 | ||
| 130 | 3x |
return { std::min(xmin, other.xmin),
|
| 131 | 3x |
std::min(ymin, other.ymin), |
| 132 | 3x |
std::max(xmax, other.xmax), |
| 133 | 3x |
std::max(ymax, other.ymax) }; |
| 134 |
} |
|
| 135 | ||
| 136 |
bool contains(const Box &b) const; |
|
| 137 | ||
| 138 |
bool contains(const Coordinate &c) const; |
|
| 139 | ||
| 140 |
bool strictly_contains(const Coordinate &c) const; |
|
| 141 | ||
| 142 | 833x |
bool operator==(const Box& other) const {
|
| 143 | 833x |
return xmin == other.xmin && xmax == other.xmax && ymin == other.ymin && ymax == other.ymax; |
| 144 |
} |
|
| 145 | ||
| 146 |
friend std::ostream &operator<<(std::ostream &os, const Box &c); |
|
| 147 |
}; |
|
| 148 | ||
| 149 |
std::ostream &operator<<(std::ostream &os, const Box &c); |
|
| 150 | ||
| 151 |
} |
|
| 152 | ||
| 153 |
#endif |
| 1 |
// Copyright (c) 2018 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#ifndef EXACTEXTRACT_GRID_H |
|
| 15 |
#define EXACTEXTRACT_GRID_H |
|
| 16 | ||
| 17 |
#include <numeric> |
|
| 18 |
#include <stdexcept> |
|
| 19 |
#include <vector> |
|
| 20 | ||
| 21 |
#include "box.h" |
|
| 22 | ||
| 23 |
namespace exactextract {
|
|
| 24 |
struct infinite_extent {
|
|
| 25 |
static const size_t padding = 1; |
|
| 26 |
}; |
|
| 27 | ||
| 28 |
struct bounded_extent {
|
|
| 29 |
static const size_t padding = 0; |
|
| 30 |
}; |
|
| 31 | ||
| 32 | 95x |
static inline bool is_integral(double d, double tol) {
|
| 33 | 95x |
return std::abs(d - std::round(d)) <= tol; |
| 34 |
} |
|
| 35 | ||
| 36 |
template<typename extent_tag> |
|
| 37 |
class Grid {
|
|
| 38 | ||
| 39 |
public: |
|
| 40 | 1767770x |
Grid(const Box & extent, double dx, double dy) : |
| 41 |
m_extent{extent},
|
|
| 42 |
m_dx{dx},
|
|
| 43 |
m_dy{dy},
|
|
| 44 | 1767770x |
m_num_rows{2*extent_tag::padding + (extent.ymax > extent.ymin ? static_cast<size_t>(std::round((extent.ymax - extent.ymin) / dy)) : 0)},
|
| 45 | 3535540x |
m_num_cols{2*extent_tag::padding + (extent.xmax > extent.xmin ? static_cast<size_t>(std::round((extent.xmax - extent.xmin) / dx)) : 0)}
|
| 46 |
{}
|
|
| 47 | ||
| 48 | 117753x |
static Grid make_empty() {
|
| 49 | 117753x |
return Grid({0, 0, 0, 0}, 0, 0);
|
| 50 |
} |
|
| 51 | ||
| 52 | 4594238x |
size_t get_column(double x) const {
|
| 53 |
if (extent_tag::padding) {
|
|
| 54 | 6122328x |
if (x < m_extent.xmin) |
| 55 | 858x |
return 0; |
| 56 | 6121470x |
if (x > m_extent.xmax) |
| 57 | 922x |
return m_num_cols - 1; |
| 58 | 6120548x |
if (x == m_extent.xmax) // special case, returning the cell for which xmax is the right |
| 59 | 3060882x |
return m_num_cols - 2; |
| 60 |
} else {
|
|
| 61 | 1533074x |
if (x < m_extent.xmin || x > m_extent.xmax) |
| 62 | ! |
throw std::out_of_range("x");
|
| 63 | ||
| 64 | 1533074x |
if (x == m_extent.xmax) |
| 65 | 767040x |
return m_num_cols - 1; |
| 66 |
} |
|
| 67 | ||
| 68 |
// Since we've already range-checked our x value, make sure that |
|
| 69 |
// the computed column index is no greater than the column index |
|
| 70 |
// associated with xmax. |
|
| 71 | 6887601x |
return std::min( |
| 72 | 2295867x |
extent_tag::padding + static_cast<size_t>(std::floor((x - m_extent.xmin) / m_dx)), |
| 73 | 4591734x |
get_column(m_extent.xmax)); |
| 74 |
} |
|
| 75 | ||
| 76 | 4592776x |
size_t get_row(double y) const {
|
| 77 |
if (extent_tag::padding) {
|
|
| 78 | 6119514x |
if (y > m_extent.ymax) |
| 79 | 1848x |
return 0; |
| 80 | 6117666x |
if (y < m_extent.ymin) |
| 81 | 1848x |
return m_num_rows - 1; |
| 82 | 6115818x |
if (y == m_extent.ymin) // special case, returning the cell for which ymin is the bottom |
| 83 | 3058966x |
return m_num_rows - 2; |
| 84 |
} else {
|
|
| 85 | 1533019x |
if (y < m_extent.ymin || y > m_extent.ymax) |
| 86 | ! |
throw std::out_of_range("y");
|
| 87 | ||
| 88 | 1533019x |
if (y == m_extent.ymin) |
| 89 | 767040x |
return m_num_rows - 1; |
| 90 |
} |
|
| 91 | ||
| 92 |
// Since we've already range-checked our y value, make sure that |
|
| 93 |
// the computed row index is no greater than the column index |
|
| 94 |
// associated with ymin. |
|
| 95 | 6883215x |
return std::min( |
| 96 | 2294405x |
extent_tag::padding + static_cast<size_t>(std::floor((m_extent.ymax - y) / m_dy)), |
| 97 | 4588810x |
get_row(m_extent.ymin)); |
| 98 |
} |
|
| 99 | ||
| 100 | 1002193x |
bool empty() const { return m_num_rows <= 2*extent_tag::padding && m_num_cols <= 2*extent_tag::padding; }
|
| 101 | ||
| 102 | 10147838x |
size_t rows() const { return m_num_rows; }
|
| 103 | ||
| 104 | 11435670x |
size_t cols() const { return m_num_cols; }
|
| 105 | ||
| 106 | 75x |
size_t size() const { return rows()*cols(); }
|
| 107 | ||
| 108 | 5038091x |
double xmin() const { return m_extent.xmin; }
|
| 109 | ||
| 110 | 3695133x |
double xmax() const { return m_extent.xmax; }
|
| 111 | ||
| 112 | 3553218x |
double ymin() const { return m_extent.ymin; }
|
| 113 | ||
| 114 | 5273503x |
double ymax() const { return m_extent.ymax; }
|
| 115 | ||
| 116 | 5537474x |
double dx() const { return m_dx; }
|
| 117 | ||
| 118 | 5774461x |
double dy() const { return m_dy; }
|
| 119 | ||
| 120 | 2151202x |
const Box& extent() const { return m_extent; }
|
| 121 | ||
| 122 | 383521x |
size_t row_offset(const Grid & other) const { return static_cast<size_t>(std::round(std::abs(other.m_extent.ymax - m_extent.ymax) / m_dy)); }
|
| 123 | ||
| 124 | 383482x |
size_t col_offset(const Grid & other) const { return static_cast<size_t>(std::round(std::abs(m_extent.xmin - other.m_extent.xmin) / m_dx)); }
|
| 125 | ||
| 126 | 76x |
double x_for_col(size_t col) const { return m_extent.xmin + ((col - extent_tag::padding) + 0.5) * m_dx; }
|
| 127 | ||
| 128 | 601x |
double y_for_row(size_t row) const { return m_extent.ymax - ((row - extent_tag::padding) + 0.5) * m_dy; }
|
| 129 | ||
| 130 | 738x |
Grid<extent_tag> crop(const Box & b) const {
|
| 131 | 738x |
if (extent().intersects(b)) {
|
| 132 | 661x |
return shrink_to_fit(b.intersection(extent())); |
| 133 |
} else {
|
|
| 134 | 77x |
return make_empty(); |
| 135 |
} |
|
| 136 |
} |
|
| 137 | ||
| 138 | 383520x |
Grid<extent_tag> shrink_to_fit(const Box & b) const {
|
| 139 | 383520x |
if (b.xmin < m_extent.xmin || b.ymin < m_extent.ymin || b.xmax > m_extent.xmax || b.ymax > m_extent.ymax) {
|
| 140 | ! |
throw std::range_error("Cannot shrink extent to bounds larger than original.");
|
| 141 |
} |
|
| 142 | ||
| 143 | 383520x |
size_t col0 = get_column(b.xmin); |
| 144 | 383520x |
size_t row1 = get_row(b.ymax); |
| 145 | ||
| 146 |
// Shrink xmin and ymax to fit the upper-left corner of the supplied extent |
|
| 147 | 383520x |
double snapped_xmin = m_extent.xmin + (col0 - extent_tag::padding) * m_dx; |
| 148 | 383520x |
double snapped_ymax = m_extent.ymax - (row1 - extent_tag::padding) * m_dy; |
| 149 | ||
| 150 |
// Make sure x0 and y1 are within the reduced extent. Because of |
|
| 151 |
// floating point round-off errors, this is not always the case. |
|
| 152 | 383520x |
if (b.xmin < snapped_xmin) {
|
| 153 | ! |
snapped_xmin -= m_dx; |
| 154 | ! |
col0--; |
| 155 |
} |
|
| 156 | ||
| 157 | 383520x |
if (b.ymax > snapped_ymax) {
|
| 158 | ! |
snapped_ymax += m_dy; |
| 159 | ! |
row1--; |
| 160 |
} |
|
| 161 | ||
| 162 | 383520x |
size_t col1 = get_column(b.xmax); |
| 163 | 383520x |
size_t row0 = get_row(b.ymin); |
| 164 | ||
| 165 | 383520x |
size_t num_rows = 1 + (row0 - row1); |
| 166 | 383520x |
size_t num_cols = 1 + (col1 - col0); |
| 167 | ||
| 168 |
// If xmax or ymin falls cleanly on a cell boundary, we don't |
|
| 169 |
// need as many rows or columns as we otherwise would, because |
|
| 170 |
// we assume that the rightmost cell of the grid is a closed |
|
| 171 |
// interval in x, and the lowermost cell of the grid is a |
|
| 172 |
// closed interval in y. |
|
| 173 | 383520x |
if (num_rows > 2 && (snapped_ymax - (num_rows-1)*m_dy <= b.ymin)) {
|
| 174 | 87x |
num_rows--; |
| 175 |
} |
|
| 176 | 383520x |
if (num_cols > 2 && (snapped_xmin + (num_cols-1)*m_dx >= b.xmax)) {
|
| 177 | 101x |
num_cols--; |
| 178 |
} |
|
| 179 | ||
| 180 |
// Perform offsets relative to the new xmin, ymax origin |
|
| 181 |
// points. If this is not done, then floating point roundoff |
|
| 182 |
// error can cause progressive shrink() calls with the same |
|
| 183 |
// inputs to produce different results. |
|
| 184 | 383520x |
Box reduced_box = {
|
| 185 |
snapped_xmin, |
|
| 186 | 383520x |
std::min(snapped_ymax - num_rows * m_dy, b.ymin), |
| 187 | 383520x |
std::max(snapped_xmin + num_cols * m_dx, b.xmax), |
| 188 |
snapped_ymax |
|
| 189 |
}; |
|
| 190 | ||
| 191 |
// Fudge computed xmax and ymin, if needed, to prevent extent |
|
| 192 |
// from growing during a shrink operation. |
|
| 193 | 383520x |
if (reduced_box.xmax > m_extent.xmax) {
|
| 194 | ! |
if (std::round((reduced_box.xmax - reduced_box.xmin)/m_dx) == |
| 195 | ! |
std::round((m_extent.xmax - reduced_box.xmin)/m_dx)) {
|
| 196 | ! |
reduced_box.xmax = m_extent.xmax; |
| 197 |
} else {
|
|
| 198 | ! |
throw std::runtime_error("Shrink operation failed.");
|
| 199 |
} |
|
| 200 |
} |
|
| 201 | 383520x |
if (reduced_box.ymin < m_extent.ymin) {
|
| 202 | ! |
if (std::round((reduced_box.ymax - reduced_box.ymin)/m_dy) == |
| 203 | ! |
std::round((reduced_box.ymax - m_extent.ymin)/m_dy)) {
|
| 204 | ! |
reduced_box.ymin = m_extent.ymin; |
| 205 |
} else {
|
|
| 206 | ! |
throw std::runtime_error("Shrink operation failed.");
|
| 207 |
} |
|
| 208 |
} |
|
| 209 | ||
| 210 | 383520x |
Grid<extent_tag> reduced{reduced_box, m_dx, m_dy};
|
| 211 | ||
| 212 |
if (b.xmin < reduced.xmin() || b.ymin < reduced.ymin() || b.xmax > reduced.xmax() || b.ymax > reduced.ymax()) {
|
|
| 213 | ! |
throw std::runtime_error("Shrink operation failed.");
|
| 214 |
} |
|
| 215 | ||
| 216 | 767040x |
return reduced; |
| 217 |
} |
|
| 218 | ||
| 219 |
template<typename extent_tag2> |
|
| 220 | 24x |
bool compatible_with(const Grid<extent_tag2> &b) const {
|
| 221 |
// Define a tolerance for grid compatibility, to be used in the following ways: |
|
| 222 |
// Grid resolutions must be integer multiples of each other within a factor of 'compatibility_tol' |
|
| 223 |
// Grid origin points must differ by an integer multiple of the smaller of the two grid resolutions, |
|
| 224 |
// within a factor of 'compatibility_tol'. |
|
| 225 |
// Perhaps it's possible to remove this hardcoded tolerance and express something in terms of |
|
| 226 |
// std::numeric_limits<double>::epsilon(), but I wasn't able to make that work. |
|
| 227 | 24x |
constexpr double compatability_tol = 1e-6; |
| 228 | ||
| 229 |
if (empty() || b.empty()) {
|
|
| 230 | ! |
return true; |
| 231 |
} |
|
| 232 | ||
| 233 |
// Check x-resolution compatibility |
|
| 234 | 24x |
if (!is_integral(std::max(m_dx, b.m_dx) / std::min(m_dx, b.m_dx), std::min(m_dx, b.m_dx)*compatability_tol)) {
|
| 235 | ! |
return false; |
| 236 |
} |
|
| 237 | ||
| 238 |
// Check y-resolution compatibility |
|
| 239 | 24x |
if (!is_integral(std::max(m_dy, b.m_dy) / std::min(m_dy, b.m_dy), std::min(m_dy, b.m_dy)*compatability_tol)) {
|
| 240 | ! |
return false; |
| 241 |
} |
|
| 242 | ||
| 243 |
// Check left-hand boundary compatibility |
|
| 244 | 24x |
if (!is_integral(std::abs(b.m_extent.xmin - m_extent.xmin) / std::min(m_dx, b.m_dx), std::min(m_dx, b.m_dx)*compatability_tol)) {
|
| 245 | 1x |
return false; |
| 246 |
} |
|
| 247 | ||
| 248 |
// Check upper boundary compatibility |
|
| 249 | 23x |
if (!is_integral(std::abs(b.m_extent.ymax - m_extent.ymax) / std::min(m_dy, b.m_dy), std::min(m_dy, b.m_dy)*compatability_tol)) {
|
| 250 | ! |
return false; |
| 251 |
} |
|
| 252 | ||
| 253 | 23x |
return true; |
| 254 |
} |
|
| 255 | ||
| 256 |
template<typename extent_tag2> |
|
| 257 | 24x |
Grid<extent_tag> common_grid(const Grid<extent_tag2> &b) const {
|
| 258 | 24x |
if (!compatible_with(b)) {
|
| 259 | 1x |
throw std::runtime_error("Incompatible extents.");
|
| 260 |
} |
|
| 261 | ||
| 262 | 23x |
if (b.empty()) {
|
| 263 | ! |
return *this; |
| 264 |
} |
|
| 265 | ||
| 266 | 23x |
const double common_dx = std::min(m_dx, b.m_dx); |
| 267 | 23x |
const double common_dy = std::min(m_dy, b.m_dy); |
| 268 | ||
| 269 | 23x |
const double common_xmin = std::min(m_extent.xmin, b.m_extent.xmin); |
| 270 | 23x |
const double common_ymax = std::max(m_extent.ymax, b.m_extent.ymax); |
| 271 | ||
| 272 | 23x |
double common_xmax = std::max(m_extent.xmax, b.m_extent.xmax); |
| 273 | 23x |
double common_ymin = std::min(m_extent.ymin, b.m_extent.ymin); |
| 274 | ||
| 275 | 23x |
const long nx = static_cast<long>(std::round((common_xmax - common_xmin) / common_dx)); |
| 276 | 23x |
const long ny = static_cast<long>(std::round((common_ymax - common_ymin) / common_dy)); |
| 277 | ||
| 278 | 23x |
common_xmax = std::max(common_xmax, common_xmin + nx*common_dx); |
| 279 | 23x |
common_ymin = std::min(common_ymin, common_ymax - ny*common_dy); |
| 280 | ||
| 281 | 23x |
return {{ common_xmin, common_ymin, common_xmax, common_ymax}, common_dx, common_dy };
|
| 282 |
} |
|
| 283 | ||
| 284 |
template<typename extent_tag2> |
|
| 285 |
Grid<extent_tag> overlapping_grid(const Grid<extent_tag2> &b) const {
|
|
| 286 |
if (!compatible_with(b)) {
|
|
| 287 |
throw std::runtime_error("Incompatible extents.");
|
|
| 288 |
} |
|
| 289 | ||
| 290 |
if (empty() || b.empty()) {
|
|
| 291 |
return make_empty(); |
|
| 292 |
} |
|
| 293 | ||
| 294 |
const double common_dx = std::min(m_dx, b.m_dx); |
|
| 295 |
const double common_dy = std::min(m_dy, b.m_dy); |
|
| 296 | ||
| 297 |
const double common_xmin = std::max(m_extent.xmin, b.m_extent.xmin); |
|
| 298 |
const double common_ymax = std::min(m_extent.ymax, b.m_extent.ymax); |
|
| 299 | ||
| 300 |
double common_xmax = std::min(m_extent.xmax, b.m_extent.xmax); |
|
| 301 |
double common_ymin = std::max(m_extent.ymin, b.m_extent.ymin); |
|
| 302 | ||
| 303 |
const long nx = static_cast<long>(std::round((common_xmax - common_xmin) / common_dx)); |
|
| 304 |
const long ny = static_cast<long>(std::round((common_ymax - common_ymin) / common_dy)); |
|
| 305 | ||
| 306 |
common_xmax = std::max(common_xmax, common_xmin + nx*common_dx); |
|
| 307 |
common_ymin = std::min(common_ymin, common_ymax - ny*common_dy); |
|
| 308 | ||
| 309 |
return {{ common_xmin, common_ymin, common_xmax, common_ymax}, common_dx, common_dy };
|
|
| 310 |
} |
|
| 311 | ||
| 312 |
bool operator==(const Grid<extent_tag> &b) const {
|
|
| 313 |
return |
|
| 314 |
m_extent == b.m_extent && |
|
| 315 |
m_dx == b.m_dx && |
|
| 316 |
m_dy == b.m_dy; |
|
| 317 |
} |
|
| 318 | ||
| 319 |
bool operator!=(const Grid<extent_tag> &b) const {
|
|
| 320 |
return !(*this == b); |
|
| 321 |
} |
|
| 322 | ||
| 323 |
private: |
|
| 324 |
Box m_extent; |
|
| 325 | ||
| 326 |
double m_dx; |
|
| 327 |
double m_dy; |
|
| 328 | ||
| 329 |
size_t m_num_rows; |
|
| 330 |
size_t m_num_cols; |
|
| 331 |
}; |
|
| 332 | ||
| 333 |
Box grid_cell(const Grid<bounded_extent> & grid, size_t row, size_t col); |
|
| 334 |
Box grid_cell(const Grid<infinite_extent> & grid, size_t row, size_t col); |
|
| 335 | ||
| 336 |
Grid<infinite_extent> make_infinite(const Grid<bounded_extent> & grid); |
|
| 337 |
Grid<bounded_extent> make_finite(const Grid<infinite_extent> & grid); |
|
| 338 | ||
| 339 |
std::vector<Grid<bounded_extent>> subdivide(const Grid<bounded_extent> & grid, size_t max_size); |
|
| 340 | ||
| 341 |
template<typename T> |
|
| 342 |
Grid<bounded_extent> common_grid(T begin, T end) {
|
|
| 343 |
if (begin == end) {
|
|
| 344 |
return Grid<bounded_extent>::make_empty(); |
|
| 345 |
} else if (std::next(begin) == end) {
|
|
| 346 |
return begin->grid(); |
|
| 347 |
} |
|
| 348 |
return std::accumulate( |
|
| 349 |
std::next(begin), |
|
| 350 |
end, |
|
| 351 |
begin->grid(), |
|
| 352 |
[](auto& acc, auto& op) {
|
|
| 353 |
return acc.common_grid(op.grid()); |
|
| 354 |
}); |
|
| 355 |
} |
|
| 356 | ||
| 357 |
} |
|
| 358 | ||
| 359 |
#endif //EXACTEXTRACT_INFINITEGRID_H |
| 1 |
// Copyright (c) 2018-2020 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#ifndef EXACTEXTRACT_MATRIX_H |
|
| 15 |
#define EXACTEXTRACT_MATRIX_H |
|
| 16 | ||
| 17 |
#include <iomanip> |
|
| 18 |
#include <iterator> |
|
| 19 |
#include <memory> |
|
| 20 |
#include <cstring> |
|
| 21 |
#include <vector> |
|
| 22 | ||
| 23 |
namespace exactextract {
|
|
| 24 | ||
| 25 |
template<typename T> |
|
| 26 |
class Matrix {
|
|
| 27 | ||
| 28 |
public: |
|
| 29 |
using value_type = T; |
|
| 30 | ||
| 31 | 1766558x |
Matrix(size_t rows, size_t cols) : |
| 32 |
m_rows{rows},
|
|
| 33 | 1766558x |
m_cols{cols}
|
| 34 |
{
|
|
| 35 | 1766558x |
if (m_rows > 0 && m_cols > 0) {
|
| 36 |
// new T[]() initializes to zero |
|
| 37 |
m_data = std::unique_ptr<T[]>(new T[m_rows * m_cols]()); |
|
| 38 |
} |
|
| 39 |
} |
|
| 40 | ||
| 41 | 117x |
Matrix(size_t rows, size_t cols, T value) : |
| 42 |
m_rows{rows},
|
|
| 43 | 117x |
m_cols{cols}
|
| 44 |
{
|
|
| 45 | 117x |
if (m_rows > 0 && m_cols > 0) {
|
| 46 |
// new T[] does not initialize |
|
| 47 |
m_data = std::unique_ptr<T[]>(new T[m_rows * m_cols]); |
|
| 48 |
} |
|
| 49 | ||
| 50 | 117x |
std::fill(m_data.get(), m_data.get() + m_rows*m_cols, value); |
| 51 |
} |
|
| 52 | ||
| 53 |
explicit Matrix(const std::vector<std::vector<T>> & data) : |
|
| 54 |
m_rows{data.size()},
|
|
| 55 |
m_cols{data[0].size()}
|
|
| 56 |
{
|
|
| 57 |
m_data = std::unique_ptr<T[]>(new T[m_rows*m_cols]()); |
|
| 58 | ||
| 59 |
auto lastpos = m_data.get(); |
|
| 60 |
for (auto& row : data) {
|
|
| 61 |
lastpos = std::copy(row.begin(), row.end(), lastpos); |
|
| 62 |
} |
|
| 63 |
} |
|
| 64 | ||
| 65 | 500417x |
Matrix(Matrix<T>&& m) noexcept : |
| 66 |
m_rows{m.rows()},
|
|
| 67 | 500417x |
m_cols{m.cols()}
|
| 68 |
{
|
|
| 69 | 500417x |
m_data = std::move(m.m_data); |
| 70 |
} |
|
| 71 | ||
| 72 | 1739674x |
T& operator()(size_t row, size_t col) {
|
| 73 | 1739674x |
check(row, col); |
| 74 | 1739674x |
return m_data[row*m_cols + col]; |
| 75 |
} |
|
| 76 | ||
| 77 | 1114002x |
T operator()(size_t row, size_t col) const {
|
| 78 | 1114002x |
check(row, col); |
| 79 | 1114002x |
return m_data[row*m_cols + col]; |
| 80 |
} |
|
| 81 | ||
| 82 |
bool operator==(const Matrix<T> & other) const {
|
|
| 83 |
if (m_rows != other.m_rows) {
|
|
| 84 |
return false; |
|
| 85 |
} |
|
| 86 |
if (m_cols != other.m_cols) {
|
|
| 87 |
return false; |
|
| 88 |
} |
|
| 89 | ||
| 90 |
return 0 == memcmp(m_data.get(), other.m_data.get(), m_rows*m_cols*sizeof(T)); |
|
| 91 |
} |
|
| 92 | ||
| 93 | 557135x |
void increment(size_t row, size_t col, const T & val) {
|
| 94 | 557135x |
check(row, col); |
| 95 | 557135x |
m_data[row*m_cols + col] += val; |
| 96 |
} |
|
| 97 | ||
| 98 | 1369663x |
size_t rows() const { return m_rows; }
|
| 99 | 1605617x |
size_t cols() const { return m_cols; }
|
| 100 | ||
| 101 |
T* row(size_t row) {
|
|
| 102 |
return &(m_data[row*m_cols]); |
|
| 103 |
} |
|
| 104 | ||
| 105 |
T* data() {
|
|
| 106 |
return m_data.get(); |
|
| 107 |
} |
|
| 108 | ||
| 109 |
#ifdef MATRIX_CHECK_BOUNDS |
|
| 110 |
void check(size_t row, size_t col) const {
|
|
| 111 |
if (row + 1 > m_rows) {
|
|
| 112 |
throw std::out_of_range("Row " + std::to_string(row) + " is out of range.");
|
|
| 113 |
} |
|
| 114 |
if (col + 1 > m_cols) {
|
|
| 115 |
throw std::out_of_range("Col " + std::to_string(col) + " is out of range.");
|
|
| 116 |
} |
|
| 117 |
} |
|
| 118 |
#else |
|
| 119 | 3410811x |
void check(size_t, size_t) const {}
|
| 120 |
#endif |
|
| 121 | ||
| 122 |
private: |
|
| 123 |
std::unique_ptr<T[]> m_data; |
|
| 124 | ||
| 125 |
size_t m_rows; |
|
| 126 |
size_t m_cols; |
|
| 127 | ||
| 128 |
}; |
|
| 129 | ||
| 130 |
template<typename T> |
|
| 131 |
std::ostream& operator<<(std::ostream & os, const Matrix<T> & m) {
|
|
| 132 |
for (size_t i = 0; i < m.rows(); i++) {
|
|
| 133 |
for (size_t j = 0; j < m.cols(); j++) {
|
|
| 134 |
if (m(i, j) != 0) {
|
|
| 135 |
os << std::right << std::fixed << std::setw(10) << std::setprecision(6) << |
|
| 136 |
m(i, j) << " "; |
|
| 137 |
} else {
|
|
| 138 |
os << " "; |
|
| 139 |
} |
|
| 140 |
} |
|
| 141 |
os << std::endl; |
|
| 142 |
} |
|
| 143 | ||
| 144 |
return os; |
|
| 145 |
} |
|
| 146 | ||
| 147 |
} |
|
| 148 | ||
| 149 |
#endif |
| 1 |
// Copyright (c) 2018-2020 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#ifndef EXACTEXTRACT_RASTER_STATS_H |
|
| 15 |
#define EXACTEXTRACT_RASTER_STATS_H |
|
| 16 | ||
| 17 |
#include <algorithm> |
|
| 18 |
#include <iostream> |
|
| 19 |
#include <limits> |
|
| 20 |
#include <unordered_map> |
|
| 21 | ||
| 22 |
#include "raster_cell_intersection.h" |
|
| 23 |
#include "weighted_quantiles.h" |
|
| 24 |
#include "variance.h" |
|
| 25 | ||
| 26 |
#include "../vend/optional.hpp" |
|
| 27 | ||
| 28 |
namespace exactextract {
|
|
| 29 | ||
| 30 |
template<typename T> |
|
| 31 |
class RasterStats {
|
|
| 32 | ||
| 33 |
public: |
|
| 34 |
/** |
|
| 35 |
* Compute raster statistics from a Raster representing intersection percentages, |
|
| 36 |
* a Raster representing data values, and (optionally) a Raster representing weights. |
|
| 37 |
* and a set of raster values. |
|
| 38 |
*/ |
|
| 39 | 500340x |
explicit RasterStats(bool store_values = false) : |
| 40 | 500340x |
m_min{std::numeric_limits<T>::max()},
|
| 41 | 500340x |
m_max{std::numeric_limits<T>::lowest()},
|
| 42 |
m_sum_ciwi{0},
|
|
| 43 |
m_sum_ci{0},
|
|
| 44 |
m_sum_xici{0},
|
|
| 45 |
m_sum_xiciwi{0},
|
|
| 46 | 500340x |
m_store_values{store_values} {}
|
| 47 | ||
| 48 | 382789x |
void process(const Raster<float> & intersection_percentages, const AbstractRaster<T> & rast) {
|
| 49 | 765578x |
RasterView<T> rv{rast, intersection_percentages.grid()};
|
| 50 | ||
| 51 | 856167x |
for (size_t i = 0; i < rv.rows(); i++) {
|
| 52 | 1009653x |
for (size_t j = 0; j < rv.cols(); j++) {
|
| 53 | 536275x |
float pct_cov = intersection_percentages(i, j); |
| 54 |
T val; |
|
| 55 | 536275x |
if (pct_cov > 0 && rv.get(i, j, val)) {
|
| 56 | 535693x |
process_value(val, pct_cov, 1.0); |
| 57 |
} |
|
| 58 |
} |
|
| 59 |
} |
|
| 60 |
} |
|
| 61 | ||
| 62 | 24x |
void process(const Raster<float> & intersection_percentages, const AbstractRaster<T> & rast, const AbstractRaster<T> & weights) {
|
| 63 |
// Process the entire intersection_percentages grid, even though it may |
|
| 64 |
// be outside the extent of the weighting raster. Although we've been |
|
| 65 |
// provided a weighting raster, we still need to calculate correct values |
|
| 66 |
// for unweighted stats. |
|
| 67 | 24x |
auto& common = intersection_percentages.grid(); |
| 68 | ||
| 69 | 24x |
if (common.empty()) |
| 70 | ! |
return; |
| 71 | ||
| 72 | 48x |
RasterView<float> iv{intersection_percentages, common};
|
| 73 | 48x |
RasterView<T> rv{rast, common};
|
| 74 | 48x |
RasterView<T> wv{weights, common};
|
| 75 | ||
| 76 | 134x |
for (size_t i = 0; i < rv.rows(); i++) {
|
| 77 | 1993x |
for (size_t j = 0; j < rv.cols(); j++) {
|
| 78 | 1883x |
float pct_cov = iv(i, j); |
| 79 |
T weight; |
|
| 80 |
T val; |
|
| 81 | ||
| 82 | 1883x |
if (pct_cov > 0 && rv.get(i, j, val)) {
|
| 83 | 1562x |
if (wv.get(i, j, weight)) {
|
| 84 | 1559x |
process_value(val, pct_cov, weight); |
| 85 |
} else {
|
|
| 86 |
// Weight is NODATA, convert to NAN |
|
| 87 | 3x |
process_value(val, pct_cov, std::numeric_limits<double>::quiet_NaN()); |
| 88 |
} |
|
| 89 |
} |
|
| 90 |
} |
|
| 91 |
} |
|
| 92 |
} |
|
| 93 | ||
| 94 |
/** |
|
| 95 |
* The mean value of cells covered by this polygon, weighted |
|
| 96 |
* by the percent of the cell that is covered. |
|
| 97 |
*/ |
|
| 98 | 21x |
float mean() const {
|
| 99 | 21x |
return sum() / count(); |
| 100 |
} |
|
| 101 | ||
| 102 |
/** |
|
| 103 |
* The mean value of cells covered by this polygon, weighted |
|
| 104 |
* by the percent of the cell that is covered and a secondary |
|
| 105 |
* weighting raster. |
|
| 106 |
* |
|
| 107 |
* If any weights are undefined, will return NAN. If this is undesirable, |
|
| 108 |
* caller should replace undefined weights with a suitable default |
|
| 109 |
* before computing statistics. |
|
| 110 |
*/ |
|
| 111 | 12x |
float weighted_mean() const {
|
| 112 | 12x |
return weighted_sum() / weighted_count(); |
| 113 |
} |
|
| 114 | ||
| 115 |
/** The fraction of weighted cells to unweighted cells. |
|
| 116 |
* Meaningful only when the values of the weighting |
|
| 117 |
* raster are between 0 and 1. |
|
| 118 |
*/ |
|
| 119 |
float weighted_fraction() const {
|
|
| 120 |
return weighted_sum() / sum(); |
|
| 121 |
} |
|
| 122 | ||
| 123 |
/** |
|
| 124 |
* The raster value occupying the greatest number of cells |
|
| 125 |
* or partial cells within the polygon. When multiple values |
|
| 126 |
* cover the same number of cells, the greatest value will |
|
| 127 |
* be returned. Weights are not taken into account. |
|
| 128 |
*/ |
|
| 129 | 8x |
nonstd::optional<T> mode() const {
|
| 130 | 8x |
if (variety() == 0) {
|
| 131 | 1x |
return nonstd::nullopt; |
| 132 |
} |
|
| 133 | ||
| 134 | 14x |
return std::max_element(m_freq.cbegin(), |
| 135 |
m_freq.cend(), |
|
| 136 | 46x |
[](const auto &a, const auto &b) {
|
| 137 | 46x |
return a.second < b.second || (a.second == b.second && a.first < b.first); |
| 138 | 7x |
})->first; |
| 139 |
} |
|
| 140 | ||
| 141 |
/** |
|
| 142 |
* The minimum value in any raster cell wholly or partially covered |
|
| 143 |
* by the polygon. Weights are not taken into account. |
|
| 144 |
*/ |
|
| 145 | 6x |
nonstd::optional<T> min() const {
|
| 146 | 6x |
if (m_sum_ci == 0) {
|
| 147 | 1x |
return nonstd::nullopt; |
| 148 |
} |
|
| 149 | 5x |
return m_min; |
| 150 |
} |
|
| 151 | ||
| 152 |
/** |
|
| 153 |
* The maximum value in any raster cell wholly or partially covered |
|
| 154 |
* by the polygon. Weights are not taken into account. |
|
| 155 |
*/ |
|
| 156 | 6x |
nonstd::optional<T> max() const {
|
| 157 | 6x |
if (m_sum_ci == 0) {
|
| 158 | 1x |
return nonstd::nullopt; |
| 159 |
} |
|
| 160 | 5x |
return m_max; |
| 161 |
} |
|
| 162 | ||
| 163 |
/** |
|
| 164 |
* The given quantile (0-1) of raster cell values. Coverage fractions |
|
| 165 |
* are taken into account but weights are not. |
|
| 166 |
*/ |
|
| 167 | 12x |
nonstd::optional<T> quantile(double q) const {
|
| 168 | 12x |
if (m_sum_ci == 0) {
|
| 169 | ! |
return nonstd::nullopt; |
| 170 |
} |
|
| 171 | ||
| 172 |
// The weighted quantile computation is not processed incrementally. |
|
| 173 |
// Create it on demand and retain it in case we want multiple quantiles. |
|
| 174 | 12x |
if (!m_quantiles) {
|
| 175 | 7x |
m_quantiles = std::make_unique<WeightedQuantiles>(); |
| 176 | ||
| 177 | 62x |
for (const auto& entry : m_freq) {
|
| 178 | 55x |
m_quantiles->process(entry.first, entry.second); |
| 179 |
} |
|
| 180 |
} |
|
| 181 | ||
| 182 | 22x |
return m_quantiles->quantile(q); |
| 183 |
} |
|
| 184 | ||
| 185 |
/** |
|
| 186 |
* The sum of raster cells covered by the polygon, with each raster |
|
| 187 |
* value weighted by its coverage fraction. |
|
| 188 |
*/ |
|
| 189 | 500286x |
float sum() const {
|
| 190 | 500286x |
return (float) m_sum_xici; |
| 191 |
} |
|
| 192 | ||
| 193 |
/** |
|
| 194 |
* The sum of raster cells covered by the polygon, with each raster |
|
| 195 |
* value weighted by its coverage fraction and weighting raster value. |
|
| 196 |
* |
|
| 197 |
* If any weights are undefined, will return NAN. If this is undesirable, |
|
| 198 |
* caller should replace undefined weights with a suitable default |
|
| 199 |
* before computing statistics. |
|
| 200 |
*/ |
|
| 201 | 15x |
float weighted_sum() const {
|
| 202 | 15x |
return (float) m_sum_xiciwi; |
| 203 |
} |
|
| 204 | ||
| 205 |
/** |
|
| 206 |
* The number of raster cells with a defined value |
|
| 207 |
* covered by the polygon. Weights are not taken |
|
| 208 |
* into account. |
|
| 209 |
*/ |
|
| 210 | 27x |
float count() const {
|
| 211 | 27x |
return (float) m_sum_ci; |
| 212 |
} |
|
| 213 | ||
| 214 |
/** |
|
| 215 |
* The population variance of raster cells touched |
|
| 216 |
* by the polygon. Cell coverage fractions are taken |
|
| 217 |
* into account; values of a weighting raster are not. |
|
| 218 |
*/ |
|
| 219 | 1x |
float variance() const {
|
| 220 | 1x |
return static_cast<float>(m_variance.variance()); |
| 221 |
} |
|
| 222 | ||
| 223 |
/** |
|
| 224 |
* The population standard deviation of raster cells |
|
| 225 |
* touched by the polygon. Cell coverage fractions |
|
| 226 |
* are taken into account; values of a weighting |
|
| 227 |
* raster are not. |
|
| 228 |
*/ |
|
| 229 | 1x |
float stdev() const {
|
| 230 | 1x |
return static_cast<float>(m_variance.stdev()); |
| 231 |
} |
|
| 232 | ||
| 233 |
/** |
|
| 234 |
* The population coefficient of variation of raster |
|
| 235 |
* cells touched by the polygon. Cell coverage fractions |
|
| 236 |
* are taken into account; values of a weighting |
|
| 237 |
* raster are not. |
|
| 238 |
*/ |
|
| 239 | 1x |
float coefficient_of_variation() const {
|
| 240 | 1x |
return static_cast<float>(m_variance.coefficent_of_variation()); |
| 241 |
} |
|
| 242 | ||
| 243 |
/** |
|
| 244 |
* The sum of weights for each cell covered by the |
|
| 245 |
* polygon, with each weight multiplied by the coverage |
|
| 246 |
* coverage fraction of each cell. |
|
| 247 |
* |
|
| 248 |
* If any weights are undefined, will return NAN. If this is undesirable, |
|
| 249 |
* caller should replace undefined weights with a suitable default |
|
| 250 |
* before computing statistics. |
|
| 251 |
*/ |
|
| 252 | 12x |
float weighted_count() const {
|
| 253 | 12x |
return (float) m_sum_ciwi; |
| 254 |
} |
|
| 255 | ||
| 256 |
/** |
|
| 257 |
* The raster value occupying the least number of cells |
|
| 258 |
* or partial cells within the polygon. When multiple values |
|
| 259 |
* cover the same number of cells, the lowest value will |
|
| 260 |
* be returned. |
|
| 261 |
* |
|
| 262 |
* Cell weights are not taken into account. |
|
| 263 |
*/ |
|
| 264 | 4x |
nonstd::optional<T> minority() const {
|
| 265 | 4x |
if (variety() == 0) {
|
| 266 | 2x |
return nonstd::nullopt; |
| 267 |
} |
|
| 268 | ||
| 269 | 4x |
return std::min_element(m_freq.cbegin(), |
| 270 |
m_freq.cend(), |
|
| 271 | 11x |
[](const auto &a, const auto &b) {
|
| 272 | 11x |
return a.second < b.second || (a.second == b.second && a.first < b.first); |
| 273 | 2x |
})->first; |
| 274 |
} |
|
| 275 | ||
| 276 |
/** |
|
| 277 |
* The number of distinct defined raster values in cells wholly |
|
| 278 |
* or partially covered by the polygon. |
|
| 279 |
*/ |
|
| 280 | 24x |
size_t variety() const {
|
| 281 | 24x |
return m_freq.size(); |
| 282 |
} |
|
| 283 | ||
| 284 |
bool stores_values() const {
|
|
| 285 |
return m_store_values; |
|
| 286 |
} |
|
| 287 | ||
| 288 |
private: |
|
| 289 |
T m_min; |
|
| 290 |
T m_max; |
|
| 291 | ||
| 292 |
// ci: coverage fraction of pixel i |
|
| 293 |
// wi: weight of pixel i |
|
| 294 |
// xi: value of pixel i |
|
| 295 |
double m_sum_ciwi; |
|
| 296 |
double m_sum_ci; |
|
| 297 |
double m_sum_xici; |
|
| 298 |
double m_sum_xiciwi; |
|
| 299 |
WestVariance m_variance; |
|
| 300 | ||
| 301 |
mutable std::unique_ptr<WeightedQuantiles> m_quantiles; |
|
| 302 | ||
| 303 |
std::unordered_map<T, float> m_freq; |
|
| 304 | ||
| 305 |
bool m_store_values; |
|
| 306 | ||
| 307 | 537255x |
void process_value(const T& val, float coverage, double weight) {
|
| 308 | 537255x |
m_sum_ci += static_cast<double>(coverage); |
| 309 | 537255x |
m_sum_xici += val*static_cast<double>(coverage); |
| 310 | ||
| 311 | 537255x |
m_variance.process(val, coverage); |
| 312 | ||
| 313 | 537255x |
double ciwi = static_cast<double>(coverage)*weight; |
| 314 | 537255x |
m_sum_ciwi += ciwi; |
| 315 | 537255x |
m_sum_xiciwi += val * ciwi; |
| 316 | ||
| 317 | 537255x |
if (val < m_min) {
|
| 318 | 449401x |
m_min = val; |
| 319 |
} |
|
| 320 | ||
| 321 | 537255x |
if (val > m_max) {
|
| 322 | 450227x |
m_max = val; |
| 323 |
} |
|
| 324 | ||
| 325 |
// TODO should weights factor in here? |
|
| 326 | 537255x |
if (m_store_values) {
|
| 327 | 224x |
m_freq[val] += coverage; |
| 328 | 224x |
m_quantiles.reset(); |
| 329 |
} |
|
| 330 |
} |
|
| 331 |
}; |
|
| 332 | ||
| 333 |
template<typename T> |
|
| 334 |
std::ostream& operator<<(std::ostream& os, const RasterStats<T> & stats) {
|
|
| 335 |
os << "{" << std::endl;
|
|
| 336 |
os << " \"count\" : " << stats.count() << "," << std::endl; |
|
| 337 | ||
| 338 |
os << " \"min\" : "; |
|
| 339 |
if (stats.min().has_value()) {
|
|
| 340 |
os << stats.min().value(); |
|
| 341 |
} else {
|
|
| 342 |
os << "null"; |
|
| 343 |
} |
|
| 344 |
os << "," << std::endl; |
|
| 345 | ||
| 346 |
os << " \"max\" : "; |
|
| 347 |
if (stats.max().has_value()) {
|
|
| 348 |
os << stats.max().value(); |
|
| 349 |
} else {
|
|
| 350 |
os << "null"; |
|
| 351 |
} |
|
| 352 |
os << "," << std::endl; |
|
| 353 | ||
| 354 |
os << " \"mean\" : " << stats.mean() << "," << std::endl; |
|
| 355 |
os << " \"sum\" : " << stats.sum() << "," << std::endl; |
|
| 356 |
os << " \"weighted_mean\" : " << stats.weighted_mean() << "," << std::endl; |
|
| 357 |
os << " \"weighted_sum\" : " << stats.weighted_sum(); |
|
| 358 |
if (stats.stores_values()) {
|
|
| 359 |
os << "," << std::endl; |
|
| 360 |
os << " \"mode\" : "; |
|
| 361 |
if (stats.mode().has_value()) {
|
|
| 362 |
os << stats.mode().value(); |
|
| 363 |
} else {
|
|
| 364 |
os << "null"; |
|
| 365 |
} |
|
| 366 |
os << "," << std::endl; |
|
| 367 | ||
| 368 |
os << " \"minority\" : "; |
|
| 369 |
if (stats.minority().has_value()) {
|
|
| 370 |
os << stats.minority().value(); |
|
| 371 |
} else {
|
|
| 372 |
os << "null"; |
|
| 373 |
} |
|
| 374 |
os << "," << std::endl; |
|
| 375 | ||
| 376 |
os << " \"variety\" : " << stats.variety() << std::endl; |
|
| 377 |
} else {
|
|
| 378 |
os << std::endl; |
|
| 379 |
} |
|
| 380 |
os << "}" << std::endl; |
|
| 381 |
return os; |
|
| 382 |
} |
|
| 383 | ||
| 384 | ||
| 385 |
} |
|
| 386 | ||
| 387 |
#endif |
| 1 |
// Copyright (c) 2018-2020 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#ifndef EXACTEXTRACT_RASTER_H |
|
| 15 |
#define EXACTEXTRACT_RASTER_H |
|
| 16 | ||
| 17 |
#include <cmath> |
|
| 18 |
#include <limits> |
|
| 19 | ||
| 20 |
#include "grid.h" |
|
| 21 |
#include "matrix.h" |
|
| 22 | ||
| 23 |
namespace exactextract {
|
|
| 24 |
template<typename T> |
|
| 25 |
class AbstractRaster {
|
|
| 26 |
public: |
|
| 27 |
using value_type = T; |
|
| 28 | ||
| 29 | 1767906x |
explicit AbstractRaster(const Grid<bounded_extent> & ex) : |
| 30 |
m_grid{ex},
|
|
| 31 | 1767906x |
m_nodata{std::is_floating_point<T>::value ? std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::min()},
|
| 32 | 1767906x |
m_has_nodata{false}
|
| 33 |
{}
|
|
| 34 | ||
| 35 |
AbstractRaster(const Grid<bounded_extent> & ex, const T& nodata_val) : m_grid{ex}, m_nodata{nodata_val}, m_has_nodata{true} {}
|
|
| 36 | ||
| 37 | 883953x |
virtual ~AbstractRaster() = default; |
| 38 | ||
| 39 | 3005210x |
size_t rows() const {
|
| 40 | 3005210x |
return m_grid.rows(); |
| 41 |
} |
|
| 42 | ||
| 43 | 4267154x |
size_t cols() const {
|
| 44 | 4267154x |
return m_grid.cols(); |
| 45 |
} |
|
| 46 | ||
| 47 | 765746x |
double xres() const {
|
| 48 | 765746x |
return m_grid.dx(); |
| 49 |
} |
|
| 50 | ||
| 51 | 765746x |
double yres() const {
|
| 52 | 765746x |
return m_grid.dy(); |
| 53 |
} |
|
| 54 | ||
| 55 | 765746x |
double xmin() const {
|
| 56 | 765746x |
return m_grid.xmin(); |
| 57 |
} |
|
| 58 | ||
| 59 |
double ymin() const {
|
|
| 60 |
return m_grid.ymin(); |
|
| 61 |
} |
|
| 62 | ||
| 63 |
double xmax() const {
|
|
| 64 |
return m_grid.xmax(); |
|
| 65 |
} |
|
| 66 | ||
| 67 | 765746x |
double ymax() const {
|
| 68 | 765746x |
return m_grid.ymax(); |
| 69 |
} |
|
| 70 | ||
| 71 | 883292x |
const Grid<bounded_extent>& grid() const {
|
| 72 | 883292x |
return m_grid; |
| 73 |
} |
|
| 74 | ||
| 75 |
virtual T operator()(size_t row, size_t col) const = 0; |
|
| 76 | ||
| 77 | 765746x |
bool has_nodata() const { return m_has_nodata; }
|
| 78 | ||
| 79 | 279412x |
T nodata() const { return m_nodata; }
|
| 80 | ||
| 81 | 538837x |
bool get(size_t row, size_t col, T & val) {
|
| 82 | 538837x |
val = operator()(row, col); |
| 83 | ||
| 84 |
if (m_has_nodata && val == m_nodata) {
|
|
| 85 | ! |
return false; |
| 86 |
} |
|
| 87 | 538837x |
if (std::is_floating_point<T>::value && std::isnan(val)) {
|
| 88 | 23x |
return false; |
| 89 |
} |
|
| 90 | ||
| 91 | 538814x |
return true; |
| 92 |
} |
|
| 93 | ||
| 94 | ! |
void set_nodata(const T & val) {
|
| 95 | ! |
m_has_nodata = true; |
| 96 | ! |
m_nodata = val; |
| 97 |
} |
|
| 98 | ||
| 99 |
bool operator==(const AbstractRaster<T> & other) const {
|
|
| 100 |
if (rows() != other.rows()) |
|
| 101 |
return false; |
|
| 102 | ||
| 103 |
if (cols() != other.cols()) |
|
| 104 |
return false; |
|
| 105 | ||
| 106 |
if (xres() != other.xres()) |
|
| 107 |
return false; |
|
| 108 | ||
| 109 |
if (yres() != other.yres()) |
|
| 110 |
return false; |
|
| 111 | ||
| 112 |
if (xmin() != other.xmin()) |
|
| 113 |
return false; |
|
| 114 | ||
| 115 |
if (ymin() != other.ymin()) |
|
| 116 |
return false; |
|
| 117 | ||
| 118 |
// Do the rasters differ in their definition of NODATA? If so, we need to do some more detailed |
|
| 119 |
// checking below. |
|
| 120 |
bool nodata_differs = has_nodata() != other.has_nodata() || |
|
| 121 |
(has_nodata() && other.has_nodata() && nodata() != other.nodata()); |
|
| 122 | ||
| 123 |
for (size_t i = 0; i < rows(); i++) {
|
|
| 124 |
for (size_t j = 0; j < cols(); j++) {
|
|
| 125 |
if(operator()(i, j) != other(i, j)) {
|
|
| 126 |
// Override default behavior of NAN != NAN |
|
| 127 |
if (!std::isnan(operator()(i, j)) || !std::isnan(other(i, j))) {
|
|
| 128 |
return false; |
|
| 129 |
} |
|
| 130 |
} else if (nodata_differs && (operator()(i, j) == nodata() || other(i, j) == other.nodata())) {
|
|
| 131 |
// For data types that do not have NAN, or even for floating point types where the user has |
|
| 132 |
// selected some other value to represent NODATA, we need to reverse a positive equality test |
|
| 133 |
// where the value is considered to be NODATA in one raster but not the other. |
|
| 134 |
return false; |
|
| 135 |
} |
|
| 136 |
} |
|
| 137 |
} |
|
| 138 | ||
| 139 |
return true; |
|
| 140 |
} |
|
| 141 | ||
| 142 |
bool operator!=(const AbstractRaster<T> & other) const {
|
|
| 143 |
return !(operator==(other)); |
|
| 144 |
} |
|
| 145 |
private: |
|
| 146 |
Grid<bounded_extent> m_grid; |
|
| 147 |
T m_nodata; |
|
| 148 |
bool m_has_nodata; |
|
| 149 |
}; |
|
| 150 | ||
| 151 |
template<typename T> |
|
| 152 |
class Raster : public AbstractRaster<T> {
|
|
| 153 |
public: |
|
| 154 |
Raster(Matrix<T>&& values, const Box & box) : AbstractRaster<T>( |
|
| 155 |
Grid<bounded_extent>( |
|
| 156 |
box, |
|
| 157 |
(box.xmax - box.xmin) / values.cols(), |
|
| 158 |
(box.ymax - box.ymin) / values.rows())), |
|
| 159 |
m_values{std::move(values)} {}
|
|
| 160 | ||
| 161 | 500417x |
Raster(Matrix<T>&& values, const Grid<bounded_extent> & g) : |
| 162 |
AbstractRaster<T>(g), |
|
| 163 | 500417x |
m_values{std::move(values)} {}
|
| 164 | ||
| 165 |
Raster(const Box & box, size_t nrow, size_t ncol) : |
|
| 166 |
AbstractRaster<T>(Grid<bounded_extent>(box, (box.xmax-box.xmin) / ncol, (box.ymax-box.ymin) / nrow)), |
|
| 167 |
m_values{nrow, ncol}
|
|
| 168 |
{}
|
|
| 169 | ||
| 170 |
explicit Raster(const Grid<bounded_extent> & ex) : |
|
| 171 |
AbstractRaster<T>(ex), |
|
| 172 |
m_values{ex.rows(), ex.cols()}
|
|
| 173 |
{}
|
|
| 174 | ||
| 175 |
Matrix<T>& data() {
|
|
| 176 |
return m_values; |
|
| 177 |
} |
|
| 178 | ||
| 179 | 393x |
T& operator()(size_t row, size_t col) {
|
| 180 | 393x |
return m_values(row, col); |
| 181 |
} |
|
| 182 | ||
| 183 | 556867x |
T operator()(size_t row, size_t col) const override {
|
| 184 | 556867x |
return m_values(row, col); |
| 185 |
} |
|
| 186 | ||
| 187 |
private: |
|
| 188 |
Matrix<T> m_values; |
|
| 189 |
}; |
|
| 190 | ||
| 191 |
template<typename T> |
|
| 192 |
class RasterView : public AbstractRaster<T>{
|
|
| 193 |
public: |
|
| 194 |
// Construct a view of a raster r at an extent ex that is larger |
|
| 195 |
// and/or of finer resolution than r |
|
| 196 | 765746x |
RasterView(const AbstractRaster<T> & r, Grid<bounded_extent> ex) : AbstractRaster<T>(ex), m_raster{r} {
|
| 197 | 765746x |
double disaggregation_factor_x = r.xres() / ex.dx(); |
| 198 | 765746x |
double disaggregation_factor_y = r.yres() / ex.dy(); |
| 199 | ||
| 200 |
if (std::abs(disaggregation_factor_x - std::floor(disaggregation_factor_x)) > 1e-6 || |
|
| 201 | 765746x |
std::abs(disaggregation_factor_y - std::floor(disaggregation_factor_y)) > 1e-6) {
|
| 202 | ! |
throw std::runtime_error("Must construct view at resolution that is an integer multiple of original.");
|
| 203 |
} |
|
| 204 | ||
| 205 | 765746x |
if (disaggregation_factor_x < 0 || disaggregation_factor_y < 0) {
|
| 206 | ! |
throw std::runtime_error("Must construct view at equal or higher resolution than original.");
|
| 207 |
} |
|
| 208 | ||
| 209 | 765746x |
m_x_off = static_cast<long>(std::round((ex.xmin() - r.xmin()) / ex.dx())); |
| 210 | 765746x |
m_y_off = static_cast<long>(std::round((r.ymax() - ex.ymax()) / ex.dy())); |
| 211 | 765746x |
m_rx = static_cast<size_t>(disaggregation_factor_x); |
| 212 | 765746x |
m_ry = static_cast<size_t>(disaggregation_factor_y); |
| 213 | ||
| 214 | 765746x |
if (r.has_nodata()) {
|
| 215 | ! |
this->set_nodata(r.nodata()); |
| 216 |
} |
|
| 217 |
} |
|
| 218 | ||
| 219 | 699121x |
T operator()(size_t row, size_t col) const override {
|
| 220 |
if (m_x_off < 0 && static_cast<size_t>(-m_x_off) > col) {
|
|
| 221 |
return this->nodata(); |
|
| 222 |
} |
|
| 223 | 665403x |
if (m_y_off < 0 && static_cast<size_t>(-m_y_off) > row) {
|
| 224 | 19144x |
return this->nodata(); |
| 225 |
} |
|
| 226 | ||
| 227 | 646259x |
size_t i0 = (row + m_y_off) / m_ry; |
| 228 | 646259x |
size_t j0 = (col + m_x_off) / m_rx; |
| 229 | ||
| 230 | 646259x |
if (i0 > m_raster.rows() - 1 || j0 > m_raster.cols() - 1) {
|
| 231 | 86844x |
return this->nodata(); |
| 232 |
} |
|
| 233 | ||
| 234 | 559415x |
return m_raster(i0, j0); |
| 235 |
} |
|
| 236 | ||
| 237 |
private: |
|
| 238 |
const AbstractRaster<T>& m_raster; |
|
| 239 | ||
| 240 |
long m_x_off; |
|
| 241 |
long m_y_off; |
|
| 242 |
size_t m_rx; |
|
| 243 |
size_t m_ry; |
|
| 244 |
}; |
|
| 245 | ||
| 246 | ||
| 247 |
template<typename T> |
|
| 248 |
std::ostream& operator<<(std::ostream & os, const AbstractRaster<T> & m) {
|
|
| 249 |
for (size_t i = 0; i < m.rows(); i++) {
|
|
| 250 |
for (size_t j = 0; j < m.cols(); j++) {
|
|
| 251 |
if (m(i, j) != 0) {
|
|
| 252 |
os << std::right << std::fixed << std::setw(10) << std::setprecision(6) << |
|
| 253 |
m(i, j) << " "; |
|
| 254 |
} else {
|
|
| 255 |
os << " "; |
|
| 256 |
} |
|
| 257 |
} |
|
| 258 |
os << std::endl; |
|
| 259 |
} |
|
| 260 | ||
| 261 |
return os; |
|
| 262 |
} |
|
| 263 |
} |
|
| 264 | ||
| 265 |
#endif //EXACTEXTRACT_RASTER_H |
| 1 |
// Copyright (c) 2020 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#ifndef EXACTEXTRACT_VARIANCE_H |
|
| 15 |
#define EXACTEXTRACT_VARIANCE_H |
|
| 16 | ||
| 17 |
#include <cmath> |
|
| 18 | ||
| 19 |
namespace exactextract {
|
|
| 20 | ||
| 21 |
class WestVariance {
|
|
| 22 |
/** \brief Implements an incremental algorithm for weighted standard |
|
| 23 |
* deviation, variance, and coefficient of variation, as described in |
|
| 24 |
* formula WV2 of West, D.H.D. (1979) "Updating Mean and Variance |
|
| 25 |
* Estimates: An Improved Method". Communications of the ACM 22(9). |
|
| 26 |
*/ |
|
| 27 | ||
| 28 |
private: |
|
| 29 |
double sum_w = 0; |
|
| 30 |
double mean = 0; |
|
| 31 |
double t = 0; |
|
| 32 | ||
| 33 |
public: |
|
| 34 |
/** \brief Update variance estimate with another value |
|
| 35 |
* |
|
| 36 |
* @param x value to add |
|
| 37 |
* @param w weight of `x` |
|
| 38 |
*/ |
|
| 39 | 537255x |
void process(double x, double w) {
|
| 40 | 537255x |
double mean_old = mean; |
| 41 | ||
| 42 | 537255x |
sum_w += w; |
| 43 | 537255x |
mean += (w / sum_w) * (x - mean_old); |
| 44 | 537255x |
t += w * (x - mean_old) * (x - mean); |
| 45 |
} |
|
| 46 | ||
| 47 |
/** \brief Return the population variance. |
|
| 48 |
*/ |
|
| 49 | 3x |
constexpr double variance() const {
|
| 50 | 3x |
return t / sum_w; |
| 51 |
} |
|
| 52 | ||
| 53 |
/** \brief Return the population standard deviation |
|
| 54 |
*/ |
|
| 55 | 2x |
double stdev() const {
|
| 56 | 2x |
return std::sqrt(variance()); |
| 57 |
} |
|
| 58 | ||
| 59 |
/** \brief Return the population coefficient of variation |
|
| 60 |
*/ |
|
| 61 | 1x |
double coefficent_of_variation() const {
|
| 62 | 1x |
return stdev() / mean; |
| 63 |
} |
|
| 64 | ||
| 65 |
}; |
|
| 66 | ||
| 67 |
} |
|
| 68 | ||
| 69 |
#endif //EXACTEXTRACT_VARIANCE_H |
| 1 |
// Copyright (c) 2019-2020 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#ifndef EXACTEXTRACT_WEIGHTED_QUANTILES_H |
|
| 15 |
#define EXACTEXTRACT_WEIGHTED_QUANTILES_H |
|
| 16 | ||
| 17 |
#include <algorithm> |
|
| 18 |
#include <cmath> |
|
| 19 |
#include <stdexcept> |
|
| 20 |
#include <vector> |
|
| 21 | ||
| 22 |
namespace exactextract {
|
|
| 23 | ||
| 24 |
class WeightedQuantiles {
|
|
| 25 |
// Compute weighted quantiles based on https://stats.stackexchange.com/a/13223 |
|
| 26 | ||
| 27 |
public: |
|
| 28 | ||
| 29 | 55x |
void process(double x, double w) {
|
| 30 | 55x |
if (w < 0) {
|
| 31 | ! |
throw std::runtime_error("Weighted quantile calculation does not support negative weights.");
|
| 32 |
} |
|
| 33 | ||
| 34 | 55x |
if (!std::isfinite(w)) {
|
| 35 | ! |
throw std::runtime_error("Weighted quantile does not support non-finite weights.");
|
| 36 |
} |
|
| 37 | ||
| 38 | 55x |
m_ready_to_query = false; |
| 39 | ||
| 40 | 55x |
m_elems.emplace_back(x, w); |
| 41 |
} |
|
| 42 | ||
| 43 |
double quantile(double q) const; |
|
| 44 | ||
| 45 |
private: |
|
| 46 |
struct elem_t {
|
|
| 47 | 65x |
elem_t(double _x, double _w) : x(_x), w(_w), cumsum(0), s(0) {}
|
| 48 | ||
| 49 |
double x; |
|
| 50 |
double w; |
|
| 51 |
double cumsum; |
|
| 52 |
double s; |
|
| 53 |
}; |
|
| 54 | ||
| 55 |
void prepare() const; |
|
| 56 | ||
| 57 |
mutable std::vector<elem_t> m_elems; |
|
| 58 |
mutable double m_sum_w; |
|
| 59 |
mutable bool m_ready_to_query; |
|
| 60 |
}; |
|
| 61 | ||
| 62 |
} |
|
| 63 | ||
| 64 |
#endif //EXACTEXTRACT_WEIGHTED_QUANTILES_H |
| 1 |
// |
|
| 2 |
// Copyright (c) 2014-2018 Martin Moene |
|
| 3 |
// |
|
| 4 |
// https://github.com/martinmoene/optional-lite |
|
| 5 |
// |
|
| 6 |
// Distributed under the Boost Software License, Version 1.0. |
|
| 7 |
// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
|
| 8 | ||
| 9 |
#pragma once |
|
| 10 | ||
| 11 |
#ifndef NONSTD_OPTIONAL_LITE_HPP |
|
| 12 |
#define NONSTD_OPTIONAL_LITE_HPP |
|
| 13 | ||
| 14 |
#define optional_lite_MAJOR 3 |
|
| 15 |
#define optional_lite_MINOR 2 |
|
| 16 |
#define optional_lite_PATCH 0 |
|
| 17 | ||
| 18 |
#define optional_lite_VERSION optional_STRINGIFY(optional_lite_MAJOR) "." optional_STRINGIFY(optional_lite_MINOR) "." optional_STRINGIFY(optional_lite_PATCH) |
|
| 19 | ||
| 20 |
#define optional_STRINGIFY( x ) optional_STRINGIFY_( x ) |
|
| 21 |
#define optional_STRINGIFY_( x ) #x |
|
| 22 | ||
| 23 |
// optional-lite configuration: |
|
| 24 | ||
| 25 |
#define optional_OPTIONAL_DEFAULT 0 |
|
| 26 |
#define optional_OPTIONAL_NONSTD 1 |
|
| 27 |
#define optional_OPTIONAL_STD 2 |
|
| 28 | ||
| 29 |
#if !defined( optional_CONFIG_SELECT_OPTIONAL ) |
|
| 30 |
# define optional_CONFIG_SELECT_OPTIONAL ( optional_HAVE_STD_OPTIONAL ? optional_OPTIONAL_STD : optional_OPTIONAL_NONSTD ) |
|
| 31 |
#endif |
|
| 32 | ||
| 33 |
// Control presence of exception handling (try and auto discover): |
|
| 34 | ||
| 35 |
#ifndef optional_CONFIG_NO_EXCEPTIONS |
|
| 36 |
# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) |
|
| 37 |
# define optional_CONFIG_NO_EXCEPTIONS 0 |
|
| 38 |
# else |
|
| 39 |
# define optional_CONFIG_NO_EXCEPTIONS 1 |
|
| 40 |
# endif |
|
| 41 |
#endif |
|
| 42 | ||
| 43 |
// C++ language version detection (C++20 is speculative): |
|
| 44 |
// Note: VC14.0/1900 (VS2015) lacks too much from C++14. |
|
| 45 | ||
| 46 |
#ifndef optional_CPLUSPLUS |
|
| 47 |
# if defined(_MSVC_LANG ) && !defined(__clang__) |
|
| 48 |
# define optional_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) |
|
| 49 |
# else |
|
| 50 |
# define optional_CPLUSPLUS __cplusplus |
|
| 51 |
# endif |
|
| 52 |
#endif |
|
| 53 | ||
| 54 |
#define optional_CPP98_OR_GREATER ( optional_CPLUSPLUS >= 199711L ) |
|
| 55 |
#define optional_CPP11_OR_GREATER ( optional_CPLUSPLUS >= 201103L ) |
|
| 56 |
#define optional_CPP11_OR_GREATER_ ( optional_CPLUSPLUS >= 201103L ) |
|
| 57 |
#define optional_CPP14_OR_GREATER ( optional_CPLUSPLUS >= 201402L ) |
|
| 58 |
#define optional_CPP17_OR_GREATER ( optional_CPLUSPLUS >= 201703L ) |
|
| 59 |
#define optional_CPP20_OR_GREATER ( optional_CPLUSPLUS >= 202000L ) |
|
| 60 | ||
| 61 |
// C++ language version (represent 98 as 3): |
|
| 62 | ||
| 63 |
#define optional_CPLUSPLUS_V ( optional_CPLUSPLUS / 100 - (optional_CPLUSPLUS > 200000 ? 2000 : 1994) ) |
|
| 64 | ||
| 65 |
// Use C++17 std::optional if available and requested: |
|
| 66 | ||
| 67 |
#if optional_CPP17_OR_GREATER && defined(__has_include ) |
|
| 68 |
# if __has_include( <optional> ) |
|
| 69 |
# define optional_HAVE_STD_OPTIONAL 1 |
|
| 70 |
# else |
|
| 71 |
# define optional_HAVE_STD_OPTIONAL 0 |
|
| 72 |
# endif |
|
| 73 |
#else |
|
| 74 |
# define optional_HAVE_STD_OPTIONAL 0 |
|
| 75 |
#endif |
|
| 76 | ||
| 77 |
#define optional_USES_STD_OPTIONAL ( (optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_STD) || ((optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_DEFAULT) && optional_HAVE_STD_OPTIONAL) ) |
|
| 78 | ||
| 79 |
// |
|
| 80 |
// in_place: code duplicated in any-lite, expected-lite, optional-lite, value-ptr-lite, variant-lite: |
|
| 81 |
// |
|
| 82 | ||
| 83 |
#ifndef nonstd_lite_HAVE_IN_PLACE_TYPES |
|
| 84 |
#define nonstd_lite_HAVE_IN_PLACE_TYPES 1 |
|
| 85 | ||
| 86 |
// C++17 std::in_place in <utility>: |
|
| 87 | ||
| 88 |
#if optional_CPP17_OR_GREATER |
|
| 89 | ||
| 90 |
#include <utility> |
|
| 91 | ||
| 92 |
namespace nonstd {
|
|
| 93 | ||
| 94 |
using std::in_place; |
|
| 95 |
using std::in_place_type; |
|
| 96 |
using std::in_place_index; |
|
| 97 |
using std::in_place_t; |
|
| 98 |
using std::in_place_type_t; |
|
| 99 |
using std::in_place_index_t; |
|
| 100 | ||
| 101 |
#define nonstd_lite_in_place_t( T) std::in_place_t |
|
| 102 |
#define nonstd_lite_in_place_type_t( T) std::in_place_type_t<T> |
|
| 103 |
#define nonstd_lite_in_place_index_t(K) std::in_place_index_t<K> |
|
| 104 | ||
| 105 |
#define nonstd_lite_in_place( T) std::in_place_t{}
|
|
| 106 |
#define nonstd_lite_in_place_type( T) std::in_place_type_t<T>{}
|
|
| 107 |
#define nonstd_lite_in_place_index(K) std::in_place_index_t<K>{}
|
|
| 108 | ||
| 109 |
} // namespace nonstd |
|
| 110 | ||
| 111 |
#else // optional_CPP17_OR_GREATER |
|
| 112 | ||
| 113 |
#include <cstddef> |
|
| 114 | ||
| 115 |
namespace nonstd {
|
|
| 116 |
namespace detail {
|
|
| 117 | ||
| 118 |
template< class T > |
|
| 119 |
struct in_place_type_tag {};
|
|
| 120 | ||
| 121 |
template< std::size_t K > |
|
| 122 |
struct in_place_index_tag {};
|
|
| 123 | ||
| 124 |
} // namespace detail |
|
| 125 | ||
| 126 |
struct in_place_t {};
|
|
| 127 | ||
| 128 |
template< class T > |
|
| 129 |
inline in_place_t in_place( detail::in_place_type_tag<T> /*unused*/ = detail::in_place_type_tag<T>() ) |
|
| 130 |
{
|
|
| 131 |
return in_place_t(); |
|
| 132 |
} |
|
| 133 | ||
| 134 |
template< std::size_t K > |
|
| 135 |
inline in_place_t in_place( detail::in_place_index_tag<K> /*unused*/ = detail::in_place_index_tag<K>() ) |
|
| 136 |
{
|
|
| 137 |
return in_place_t(); |
|
| 138 |
} |
|
| 139 | ||
| 140 |
template< class T > |
|
| 141 |
inline in_place_t in_place_type( detail::in_place_type_tag<T> /*unused*/ = detail::in_place_type_tag<T>() ) |
|
| 142 |
{
|
|
| 143 |
return in_place_t(); |
|
| 144 |
} |
|
| 145 | ||
| 146 |
template< std::size_t K > |
|
| 147 |
inline in_place_t in_place_index( detail::in_place_index_tag<K> /*unused*/ = detail::in_place_index_tag<K>() ) |
|
| 148 |
{
|
|
| 149 |
return in_place_t(); |
|
| 150 |
} |
|
| 151 | ||
| 152 |
// mimic templated typedef: |
|
| 153 | ||
| 154 |
#define nonstd_lite_in_place_t( T) nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T> ) |
|
| 155 |
#define nonstd_lite_in_place_type_t( T) nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T> ) |
|
| 156 |
#define nonstd_lite_in_place_index_t(K) nonstd::in_place_t(&)( nonstd::detail::in_place_index_tag<K> ) |
|
| 157 | ||
| 158 |
#define nonstd_lite_in_place( T) nonstd::in_place_type<T> |
|
| 159 |
#define nonstd_lite_in_place_type( T) nonstd::in_place_type<T> |
|
| 160 |
#define nonstd_lite_in_place_index(K) nonstd::in_place_index<K> |
|
| 161 | ||
| 162 |
} // namespace nonstd |
|
| 163 | ||
| 164 |
#endif // optional_CPP17_OR_GREATER |
|
| 165 |
#endif // nonstd_lite_HAVE_IN_PLACE_TYPES |
|
| 166 | ||
| 167 |
// |
|
| 168 |
// Using std::optional: |
|
| 169 |
// |
|
| 170 | ||
| 171 |
#if optional_USES_STD_OPTIONAL |
|
| 172 | ||
| 173 |
#include <optional> |
|
| 174 | ||
| 175 |
namespace nonstd {
|
|
| 176 | ||
| 177 |
using std::optional; |
|
| 178 |
using std::bad_optional_access; |
|
| 179 |
using std::hash; |
|
| 180 | ||
| 181 |
using std::nullopt; |
|
| 182 |
using std::nullopt_t; |
|
| 183 | ||
| 184 |
using std::operator==; |
|
| 185 |
using std::operator!=; |
|
| 186 |
using std::operator<; |
|
| 187 |
using std::operator<=; |
|
| 188 |
using std::operator>; |
|
| 189 |
using std::operator>=; |
|
| 190 |
using std::make_optional; |
|
| 191 |
using std::swap; |
|
| 192 |
} |
|
| 193 | ||
| 194 |
#else // optional_USES_STD_OPTIONAL |
|
| 195 | ||
| 196 |
#include <cassert> |
|
| 197 |
#include <utility> |
|
| 198 | ||
| 199 |
// optional-lite alignment configuration: |
|
| 200 | ||
| 201 |
#ifndef optional_CONFIG_MAX_ALIGN_HACK |
|
| 202 |
# define optional_CONFIG_MAX_ALIGN_HACK 0 |
|
| 203 |
#endif |
|
| 204 | ||
| 205 |
#ifndef optional_CONFIG_ALIGN_AS |
|
| 206 |
// no default, used in #if defined() |
|
| 207 |
#endif |
|
| 208 | ||
| 209 |
#ifndef optional_CONFIG_ALIGN_AS_FALLBACK |
|
| 210 |
# define optional_CONFIG_ALIGN_AS_FALLBACK double |
|
| 211 |
#endif |
|
| 212 | ||
| 213 |
// Compiler warning suppression: |
|
| 214 | ||
| 215 |
#if defined(__clang__) |
|
| 216 |
# pragma clang diagnostic push |
|
| 217 |
# pragma clang diagnostic ignored "-Wundef" |
|
| 218 |
#elif defined(__GNUC__) |
|
| 219 |
# pragma GCC diagnostic push |
|
| 220 |
# pragma GCC diagnostic ignored "-Wundef" |
|
| 221 |
#elif defined(_MSC_VER ) |
|
| 222 |
# pragma warning( push ) |
|
| 223 |
#endif |
|
| 224 | ||
| 225 |
// half-open range [lo..hi): |
|
| 226 |
#define optional_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) |
|
| 227 | ||
| 228 |
// Compiler versions: |
|
| 229 |
// |
|
| 230 |
// MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0) |
|
| 231 |
// MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002) |
|
| 232 |
// MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003) |
|
| 233 |
// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) |
|
| 234 |
// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) |
|
| 235 |
// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) |
|
| 236 |
// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) |
|
| 237 |
// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) |
|
| 238 |
// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) |
|
| 239 |
// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017) |
|
| 240 | ||
| 241 |
#if defined(_MSC_VER ) && !defined(__clang__) |
|
| 242 |
# define optional_COMPILER_MSVC_VER (_MSC_VER ) |
|
| 243 |
# define optional_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) |
|
| 244 |
#else |
|
| 245 |
# define optional_COMPILER_MSVC_VER 0 |
|
| 246 |
# define optional_COMPILER_MSVC_VERSION 0 |
|
| 247 |
#endif |
|
| 248 | ||
| 249 |
#define optional_COMPILER_VERSION( major, minor, patch ) ( 10 * (10 * (major) + (minor) ) + (patch) ) |
|
| 250 | ||
| 251 |
#if defined(__GNUC__) && !defined(__clang__) |
|
| 252 |
# define optional_COMPILER_GNUC_VERSION optional_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) |
|
| 253 |
#else |
|
| 254 |
# define optional_COMPILER_GNUC_VERSION 0 |
|
| 255 |
#endif |
|
| 256 | ||
| 257 |
#if defined(__clang__) |
|
| 258 |
# define optional_COMPILER_CLANG_VERSION optional_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) |
|
| 259 |
#else |
|
| 260 |
# define optional_COMPILER_CLANG_VERSION 0 |
|
| 261 |
#endif |
|
| 262 | ||
| 263 |
#if optional_BETWEEN(optional_COMPILER_MSVC_VERSION, 70, 140 ) |
|
| 264 |
# pragma warning( disable: 4345 ) // initialization behavior changed |
|
| 265 |
#endif |
|
| 266 | ||
| 267 |
#if optional_BETWEEN(optional_COMPILER_MSVC_VERSION, 70, 150 ) |
|
| 268 |
# pragma warning( disable: 4814 ) // in C++14 'constexpr' will not imply 'const' |
|
| 269 |
#endif |
|
| 270 | ||
| 271 |
// Presence of language and library features: |
|
| 272 | ||
| 273 |
#define optional_HAVE(FEATURE) ( optional_HAVE_##FEATURE ) |
|
| 274 | ||
| 275 |
#ifdef _HAS_CPP0X |
|
| 276 |
# define optional_HAS_CPP0X _HAS_CPP0X |
|
| 277 |
#else |
|
| 278 |
# define optional_HAS_CPP0X 0 |
|
| 279 |
#endif |
|
| 280 | ||
| 281 |
// Unless defined otherwise below, consider VC14 as C++11 for optional-lite: |
|
| 282 | ||
| 283 |
#if optional_COMPILER_MSVC_VER >= 1900 |
|
| 284 |
# undef optional_CPP11_OR_GREATER |
|
| 285 |
# define optional_CPP11_OR_GREATER 1 |
|
| 286 |
#endif |
|
| 287 | ||
| 288 |
#define optional_CPP11_90 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1500) |
|
| 289 |
#define optional_CPP11_100 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1600) |
|
| 290 |
#define optional_CPP11_110 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1700) |
|
| 291 |
#define optional_CPP11_120 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1800) |
|
| 292 |
#define optional_CPP11_140 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1900) |
|
| 293 |
#define optional_CPP11_141 (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1910) |
|
| 294 | ||
| 295 |
#define optional_CPP14_000 (optional_CPP14_OR_GREATER) |
|
| 296 |
#define optional_CPP17_000 (optional_CPP17_OR_GREATER) |
|
| 297 | ||
| 298 |
// Presence of C++11 language features: |
|
| 299 | ||
| 300 |
#define optional_HAVE_CONSTEXPR_11 optional_CPP11_140 |
|
| 301 |
#define optional_HAVE_IS_DEFAULT optional_CPP11_140 |
|
| 302 |
#define optional_HAVE_NOEXCEPT optional_CPP11_140 |
|
| 303 |
#define optional_HAVE_NULLPTR optional_CPP11_100 |
|
| 304 |
#define optional_HAVE_REF_QUALIFIER optional_CPP11_140 |
|
| 305 | ||
| 306 |
// Presence of C++14 language features: |
|
| 307 | ||
| 308 |
#define optional_HAVE_CONSTEXPR_14 optional_CPP14_000 |
|
| 309 | ||
| 310 |
// Presence of C++17 language features: |
|
| 311 | ||
| 312 |
#define optional_HAVE_NODISCARD optional_CPP17_000 |
|
| 313 | ||
| 314 |
// Presence of C++ library features: |
|
| 315 | ||
| 316 |
#define optional_HAVE_CONDITIONAL optional_CPP11_120 |
|
| 317 |
#define optional_HAVE_REMOVE_CV optional_CPP11_120 |
|
| 318 |
#define optional_HAVE_TYPE_TRAITS optional_CPP11_90 |
|
| 319 | ||
| 320 |
#define optional_HAVE_TR1_TYPE_TRAITS (!! optional_COMPILER_GNUC_VERSION ) |
|
| 321 |
#define optional_HAVE_TR1_ADD_POINTER (!! optional_COMPILER_GNUC_VERSION ) |
|
| 322 | ||
| 323 |
// C++ feature usage: |
|
| 324 | ||
| 325 |
#if optional_HAVE( CONSTEXPR_11 ) |
|
| 326 |
# define optional_constexpr constexpr |
|
| 327 |
#else |
|
| 328 |
# define optional_constexpr /*constexpr*/ |
|
| 329 |
#endif |
|
| 330 | ||
| 331 |
#if optional_HAVE( IS_DEFAULT ) |
|
| 332 |
# define optional_is_default = default; |
|
| 333 |
#else |
|
| 334 |
# define optional_is_default {}
|
|
| 335 |
#endif |
|
| 336 | ||
| 337 |
#if optional_HAVE( CONSTEXPR_14 ) |
|
| 338 |
# define optional_constexpr14 constexpr |
|
| 339 |
#else |
|
| 340 |
# define optional_constexpr14 /*constexpr*/ |
|
| 341 |
#endif |
|
| 342 | ||
| 343 |
#if optional_HAVE( NODISCARD ) |
|
| 344 |
# define optional_nodiscard [[nodiscard]] |
|
| 345 |
#else |
|
| 346 |
# define optional_nodiscard /*[[nodiscard]]*/ |
|
| 347 |
#endif |
|
| 348 | ||
| 349 |
#if optional_HAVE( NOEXCEPT ) |
|
| 350 |
# define optional_noexcept noexcept |
|
| 351 |
#else |
|
| 352 |
# define optional_noexcept /*noexcept*/ |
|
| 353 |
#endif |
|
| 354 | ||
| 355 |
#if optional_HAVE( NULLPTR ) |
|
| 356 |
# define optional_nullptr nullptr |
|
| 357 |
#else |
|
| 358 |
# define optional_nullptr NULL |
|
| 359 |
#endif |
|
| 360 | ||
| 361 |
#if optional_HAVE( REF_QUALIFIER ) |
|
| 362 |
// NOLINTNEXTLINE( bugprone-macro-parentheses ) |
|
| 363 |
# define optional_ref_qual & |
|
| 364 |
# define optional_refref_qual && |
|
| 365 |
#else |
|
| 366 |
# define optional_ref_qual /*&*/ |
|
| 367 |
# define optional_refref_qual /*&&*/ |
|
| 368 |
#endif |
|
| 369 | ||
| 370 |
// additional includes: |
|
| 371 | ||
| 372 |
#if optional_CONFIG_NO_EXCEPTIONS |
|
| 373 |
// already included: <cassert> |
|
| 374 |
#else |
|
| 375 |
# include <stdexcept> |
|
| 376 |
#endif |
|
| 377 | ||
| 378 |
#if optional_CPP11_OR_GREATER |
|
| 379 |
# include <functional> |
|
| 380 |
#endif |
|
| 381 | ||
| 382 |
#if optional_HAVE( INITIALIZER_LIST ) |
|
| 383 |
# include <initializer_list> |
|
| 384 |
#endif |
|
| 385 | ||
| 386 |
#if optional_HAVE( TYPE_TRAITS ) |
|
| 387 |
# include <type_traits> |
|
| 388 |
#elif optional_HAVE( TR1_TYPE_TRAITS ) |
|
| 389 |
# include <tr1/type_traits> |
|
| 390 |
#endif |
|
| 391 | ||
| 392 |
// Method enabling |
|
| 393 | ||
| 394 |
#if optional_CPP11_OR_GREATER |
|
| 395 | ||
| 396 |
#define optional_REQUIRES_0(...) \ |
|
| 397 |
template< bool B = (__VA_ARGS__), typename std::enable_if<B, int>::type = 0 > |
|
| 398 | ||
| 399 |
#define optional_REQUIRES_T(...) \ |
|
| 400 |
, typename = typename std::enable_if< (__VA_ARGS__), nonstd::optional_lite::detail::enabler >::type |
|
| 401 | ||
| 402 |
#define optional_REQUIRES_R(R, ...) \ |
|
| 403 |
typename std::enable_if< (__VA_ARGS__), R>::type |
|
| 404 | ||
| 405 |
#define optional_REQUIRES_A(...) \ |
|
| 406 |
, typename std::enable_if< (__VA_ARGS__), void*>::type = nullptr |
|
| 407 | ||
| 408 |
#endif |
|
| 409 | ||
| 410 |
// |
|
| 411 |
// optional: |
|
| 412 |
// |
|
| 413 | ||
| 414 |
namespace nonstd { namespace optional_lite {
|
|
| 415 | ||
| 416 |
namespace std11 {
|
|
| 417 | ||
| 418 |
#if optional_CPP11_OR_GREATER |
|
| 419 |
using std::move; |
|
| 420 |
#else |
|
| 421 |
template< typename T > T & move( T & t ) { return t; }
|
|
| 422 |
#endif |
|
| 423 | ||
| 424 |
#if optional_HAVE( CONDITIONAL ) |
|
| 425 |
using std::conditional; |
|
| 426 |
#else |
|
| 427 |
template< bool B, typename T, typename F > struct conditional { typedef T type; };
|
|
| 428 |
template< typename T, typename F > struct conditional<false, T, F> { typedef F type; };
|
|
| 429 |
#endif // optional_HAVE_CONDITIONAL |
|
| 430 | ||
| 431 |
} // namespace std11 |
|
| 432 | ||
| 433 |
#if optional_CPP11_OR_GREATER |
|
| 434 | ||
| 435 |
/// type traits C++17: |
|
| 436 | ||
| 437 |
namespace std17 {
|
|
| 438 | ||
| 439 |
#if optional_CPP17_OR_GREATER |
|
| 440 | ||
| 441 |
using std::is_swappable; |
|
| 442 |
using std::is_nothrow_swappable; |
|
| 443 | ||
| 444 |
#elif optional_CPP11_OR_GREATER |
|
| 445 | ||
| 446 |
namespace detail {
|
|
| 447 | ||
| 448 |
using std::swap; |
|
| 449 | ||
| 450 |
struct is_swappable |
|
| 451 |
{
|
|
| 452 |
template< typename T, typename = decltype( swap( std::declval<T&>(), std::declval<T&>() ) ) > |
|
| 453 |
static std::true_type test( int /*unused*/ ); |
|
| 454 | ||
| 455 |
template< typename > |
|
| 456 |
static std::false_type test(...); |
|
| 457 |
}; |
|
| 458 | ||
| 459 |
struct is_nothrow_swappable |
|
| 460 |
{
|
|
| 461 |
// wrap noexcept(expr) in separate function as work-around for VC140 (VS2015): |
|
| 462 | ||
| 463 |
template< typename T > |
|
| 464 |
static constexpr bool satisfies() |
|
| 465 |
{
|
|
| 466 |
return noexcept( swap( std::declval<T&>(), std::declval<T&>() ) ); |
|
| 467 |
} |
|
| 468 | ||
| 469 |
template< typename T > |
|
| 470 |
static auto test( int /*unused*/ ) -> std::integral_constant<bool, satisfies<T>()>{}
|
|
| 471 | ||
| 472 |
template< typename > |
|
| 473 |
static auto test(...) -> std::false_type; |
|
| 474 |
}; |
|
| 475 | ||
| 476 |
} // namespace detail |
|
| 477 | ||
| 478 |
// is [nothow] swappable: |
|
| 479 | ||
| 480 |
template< typename T > |
|
| 481 |
struct is_swappable : decltype( detail::is_swappable::test<T>(0) ){};
|
|
| 482 | ||
| 483 |
template< typename T > |
|
| 484 |
struct is_nothrow_swappable : decltype( detail::is_nothrow_swappable::test<T>(0) ){};
|
|
| 485 | ||
| 486 |
#endif // optional_CPP17_OR_GREATER |
|
| 487 | ||
| 488 |
} // namespace std17 |
|
| 489 | ||
| 490 |
/// type traits C++20: |
|
| 491 | ||
| 492 |
namespace std20 {
|
|
| 493 | ||
| 494 |
template< typename T > |
|
| 495 |
struct remove_cvref |
|
| 496 |
{
|
|
| 497 |
typedef typename std::remove_cv< typename std::remove_reference<T>::type >::type type; |
|
| 498 |
}; |
|
| 499 | ||
| 500 |
} // namespace std20 |
|
| 501 | ||
| 502 |
#endif // optional_CPP11_OR_GREATER |
|
| 503 | ||
| 504 |
/// class optional |
|
| 505 | ||
| 506 |
template< typename T > |
|
| 507 |
class optional; |
|
| 508 | ||
| 509 |
namespace detail {
|
|
| 510 | ||
| 511 |
// for optional_REQUIRES_T |
|
| 512 | ||
| 513 |
#if optional_CPP11_OR_GREATER |
|
| 514 |
enum class enabler{};
|
|
| 515 |
#endif |
|
| 516 | ||
| 517 |
// C++11 emulation: |
|
| 518 | ||
| 519 |
struct nulltype{};
|
|
| 520 | ||
| 521 |
template< typename Head, typename Tail > |
|
| 522 |
struct typelist |
|
| 523 |
{
|
|
| 524 |
typedef Head head; |
|
| 525 |
typedef Tail tail; |
|
| 526 |
}; |
|
| 527 | ||
| 528 |
#if optional_CONFIG_MAX_ALIGN_HACK |
|
| 529 | ||
| 530 |
// Max align, use most restricted type for alignment: |
|
| 531 | ||
| 532 |
#define optional_UNIQUE( name ) optional_UNIQUE2( name, __LINE__ ) |
|
| 533 |
#define optional_UNIQUE2( name, line ) optional_UNIQUE3( name, line ) |
|
| 534 |
#define optional_UNIQUE3( name, line ) name ## line |
|
| 535 | ||
| 536 |
#define optional_ALIGN_TYPE( type ) \ |
|
| 537 |
type optional_UNIQUE( _t ); struct_t< type > optional_UNIQUE( _st ) |
|
| 538 | ||
| 539 |
template< typename T > |
|
| 540 |
struct struct_t { T _; };
|
|
| 541 | ||
| 542 |
union max_align_t |
|
| 543 |
{
|
|
| 544 |
optional_ALIGN_TYPE( char ); |
|
| 545 |
optional_ALIGN_TYPE( short int ); |
|
| 546 |
optional_ALIGN_TYPE( int ); |
|
| 547 |
optional_ALIGN_TYPE( long int ); |
|
| 548 |
optional_ALIGN_TYPE( float ); |
|
| 549 |
optional_ALIGN_TYPE( double ); |
|
| 550 |
optional_ALIGN_TYPE( long double ); |
|
| 551 |
optional_ALIGN_TYPE( char * ); |
|
| 552 |
optional_ALIGN_TYPE( short int * ); |
|
| 553 |
optional_ALIGN_TYPE( int * ); |
|
| 554 |
optional_ALIGN_TYPE( long int * ); |
|
| 555 |
optional_ALIGN_TYPE( float * ); |
|
| 556 |
optional_ALIGN_TYPE( double * ); |
|
| 557 |
optional_ALIGN_TYPE( long double * ); |
|
| 558 |
optional_ALIGN_TYPE( void * ); |
|
| 559 | ||
| 560 |
#ifdef HAVE_LONG_LONG |
|
| 561 |
optional_ALIGN_TYPE( long long ); |
|
| 562 |
#endif |
|
| 563 | ||
| 564 |
struct Unknown; |
|
| 565 | ||
| 566 |
Unknown ( * optional_UNIQUE(_) )( Unknown ); |
|
| 567 |
Unknown * Unknown::* optional_UNIQUE(_); |
|
| 568 |
Unknown ( Unknown::* optional_UNIQUE(_) )( Unknown ); |
|
| 569 | ||
| 570 |
struct_t< Unknown ( * )( Unknown) > optional_UNIQUE(_); |
|
| 571 |
struct_t< Unknown * Unknown::* > optional_UNIQUE(_); |
|
| 572 |
struct_t< Unknown ( Unknown::* )(Unknown) > optional_UNIQUE(_); |
|
| 573 |
}; |
|
| 574 | ||
| 575 |
#undef optional_UNIQUE |
|
| 576 |
#undef optional_UNIQUE2 |
|
| 577 |
#undef optional_UNIQUE3 |
|
| 578 | ||
| 579 |
#undef optional_ALIGN_TYPE |
|
| 580 | ||
| 581 |
#elif defined( optional_CONFIG_ALIGN_AS ) // optional_CONFIG_MAX_ALIGN_HACK |
|
| 582 | ||
| 583 |
// Use user-specified type for alignment: |
|
| 584 | ||
| 585 |
#define optional_ALIGN_AS( unused ) \ |
|
| 586 |
optional_CONFIG_ALIGN_AS |
|
| 587 | ||
| 588 |
#else // optional_CONFIG_MAX_ALIGN_HACK |
|
| 589 | ||
| 590 |
// Determine POD type to use for alignment: |
|
| 591 | ||
| 592 |
#define optional_ALIGN_AS( to_align ) \ |
|
| 593 |
typename type_of_size< alignment_types, alignment_of< to_align >::value >::type |
|
| 594 | ||
| 595 |
template< typename T > |
|
| 596 |
struct alignment_of; |
|
| 597 | ||
| 598 |
template< typename T > |
|
| 599 |
struct alignment_of_hack |
|
| 600 |
{
|
|
| 601 |
char c; |
|
| 602 |
T t; |
|
| 603 |
alignment_of_hack(); |
|
| 604 |
}; |
|
| 605 | ||
| 606 |
template< size_t A, size_t S > |
|
| 607 |
struct alignment_logic |
|
| 608 |
{
|
|
| 609 |
enum { value = A < S ? A : S };
|
|
| 610 |
}; |
|
| 611 | ||
| 612 |
template< typename T > |
|
| 613 |
struct alignment_of |
|
| 614 |
{
|
|
| 615 |
enum { value = alignment_logic<
|
|
| 616 |
sizeof( alignment_of_hack<T> ) - sizeof(T), sizeof(T) >::value }; |
|
| 617 |
}; |
|
| 618 | ||
| 619 |
template< typename List, size_t N > |
|
| 620 |
struct type_of_size |
|
| 621 |
{
|
|
| 622 |
typedef typename std11::conditional< |
|
| 623 |
N == sizeof( typename List::head ), |
|
| 624 |
typename List::head, |
|
| 625 |
typename type_of_size<typename List::tail, N >::type >::type type; |
|
| 626 |
}; |
|
| 627 | ||
| 628 |
template< size_t N > |
|
| 629 |
struct type_of_size< nulltype, N > |
|
| 630 |
{
|
|
| 631 |
typedef optional_CONFIG_ALIGN_AS_FALLBACK type; |
|
| 632 |
}; |
|
| 633 | ||
| 634 |
template< typename T> |
|
| 635 |
struct struct_t { T _; };
|
|
| 636 | ||
| 637 |
#define optional_ALIGN_TYPE( type ) \ |
|
| 638 |
typelist< type , typelist< struct_t< type > |
|
| 639 | ||
| 640 |
struct Unknown; |
|
| 641 | ||
| 642 |
typedef |
|
| 643 |
optional_ALIGN_TYPE( char ), |
|
| 644 |
optional_ALIGN_TYPE( short ), |
|
| 645 |
optional_ALIGN_TYPE( int ), |
|
| 646 |
optional_ALIGN_TYPE( long ), |
|
| 647 |
optional_ALIGN_TYPE( float ), |
|
| 648 |
optional_ALIGN_TYPE( double ), |
|
| 649 |
optional_ALIGN_TYPE( long double ), |
|
| 650 | ||
| 651 |
optional_ALIGN_TYPE( char *), |
|
| 652 |
optional_ALIGN_TYPE( short * ), |
|
| 653 |
optional_ALIGN_TYPE( int * ), |
|
| 654 |
optional_ALIGN_TYPE( long * ), |
|
| 655 |
optional_ALIGN_TYPE( float * ), |
|
| 656 |
optional_ALIGN_TYPE( double * ), |
|
| 657 |
optional_ALIGN_TYPE( long double * ), |
|
| 658 | ||
| 659 |
optional_ALIGN_TYPE( Unknown ( * )( Unknown ) ), |
|
| 660 |
optional_ALIGN_TYPE( Unknown * Unknown::* ), |
|
| 661 |
optional_ALIGN_TYPE( Unknown ( Unknown::* )( Unknown ) ), |
|
| 662 | ||
| 663 |
nulltype |
|
| 664 |
> > > > > > > > > > > > > > |
|
| 665 |
> > > > > > > > > > > > > > |
|
| 666 |
> > > > > > |
|
| 667 |
alignment_types; |
|
| 668 | ||
| 669 |
#undef optional_ALIGN_TYPE |
|
| 670 | ||
| 671 |
#endif // optional_CONFIG_MAX_ALIGN_HACK |
|
| 672 | ||
| 673 |
/// C++03 constructed union to hold value. |
|
| 674 | ||
| 675 |
template< typename T > |
|
| 676 |
union storage_t |
|
| 677 |
{
|
|
| 678 |
//private: |
|
| 679 |
// template< typename > friend class optional; |
|
| 680 | ||
| 681 |
typedef T value_type; |
|
| 682 | ||
| 683 |
storage_t() optional_is_default |
|
| 684 | ||
| 685 | 19x |
explicit storage_t( value_type const & v ) |
| 686 |
{
|
|
| 687 | 19x |
construct_value( v ); |
| 688 |
} |
|
| 689 | ||
| 690 | 19x |
void construct_value( value_type const & v ) |
| 691 |
{
|
|
| 692 | 19x |
::new( value_ptr() ) value_type( v ); |
| 693 |
} |
|
| 694 | ||
| 695 |
#if optional_CPP11_OR_GREATER |
|
| 696 | ||
| 697 | 10x |
explicit storage_t( value_type && v ) |
| 698 |
{
|
|
| 699 | 10x |
construct_value( std::move( v ) ); |
| 700 |
} |
|
| 701 | ||
| 702 | 10x |
void construct_value( value_type && v ) |
| 703 |
{
|
|
| 704 | 10x |
::new( value_ptr() ) value_type( std::move( v ) ); |
| 705 |
} |
|
| 706 | ||
| 707 |
template< class... Args > |
|
| 708 |
void emplace( Args&&... args ) |
|
| 709 |
{
|
|
| 710 |
::new( value_ptr() ) value_type( std::forward<Args>(args)... ); |
|
| 711 |
} |
|
| 712 | ||
| 713 |
template< class U, class... Args > |
|
| 714 |
void emplace( std::initializer_list<U> il, Args&&... args ) |
|
| 715 |
{
|
|
| 716 |
::new( value_ptr() ) value_type( il, std::forward<Args>(args)... ); |
|
| 717 |
} |
|
| 718 | ||
| 719 |
#endif |
|
| 720 | ||
| 721 | 29x |
void destruct_value() |
| 722 |
{
|
|
| 723 | 29x |
value_ptr()->~T(); |
| 724 |
} |
|
| 725 | ||
| 726 |
optional_nodiscard value_type const * value_ptr() const |
|
| 727 |
{
|
|
| 728 |
return as<value_type>(); |
|
| 729 |
} |
|
| 730 | ||
| 731 | 87x |
value_type * value_ptr() |
| 732 |
{
|
|
| 733 | 87x |
return as<value_type>(); |
| 734 |
} |
|
| 735 | ||
| 736 |
optional_nodiscard value_type const & value() const optional_ref_qual |
|
| 737 |
{
|
|
| 738 |
return * value_ptr(); |
|
| 739 |
} |
|
| 740 | ||
| 741 | 29x |
value_type & value() optional_ref_qual |
| 742 |
{
|
|
| 743 | 29x |
return * value_ptr(); |
| 744 |
} |
|
| 745 | ||
| 746 |
#if optional_CPP11_OR_GREATER |
|
| 747 | ||
| 748 |
optional_nodiscard value_type const && value() const optional_refref_qual |
|
| 749 |
{
|
|
| 750 |
return std::move( value() ); |
|
| 751 |
} |
|
| 752 | ||
| 753 |
value_type && value() optional_refref_qual |
|
| 754 |
{
|
|
| 755 |
return std::move( value() ); |
|
| 756 |
} |
|
| 757 | ||
| 758 |
#endif |
|
| 759 | ||
| 760 |
#if optional_CPP11_OR_GREATER |
|
| 761 | ||
| 762 |
using aligned_storage_t = typename std::aligned_storage< sizeof(value_type), alignof(value_type) >::type; |
|
| 763 |
aligned_storage_t data; |
|
| 764 | ||
| 765 |
#elif optional_CONFIG_MAX_ALIGN_HACK |
|
| 766 | ||
| 767 |
typedef struct { unsigned char data[ sizeof(value_type) ]; } aligned_storage_t;
|
|
| 768 | ||
| 769 |
max_align_t hack; |
|
| 770 |
aligned_storage_t data; |
|
| 771 | ||
| 772 |
#else |
|
| 773 |
typedef optional_ALIGN_AS(value_type) align_as_type; |
|
| 774 | ||
| 775 |
typedef struct { align_as_type data[ 1 + ( sizeof(value_type) - 1 ) / sizeof(align_as_type) ]; } aligned_storage_t;
|
|
| 776 |
aligned_storage_t data; |
|
| 777 | ||
| 778 |
# undef optional_ALIGN_AS |
|
| 779 | ||
| 780 |
#endif // optional_CONFIG_MAX_ALIGN_HACK |
|
| 781 | ||
| 782 | 87x |
optional_nodiscard void * ptr() optional_noexcept |
| 783 |
{
|
|
| 784 | 87x |
return &data; |
| 785 |
} |
|
| 786 | ||
| 787 |
optional_nodiscard void const * ptr() const optional_noexcept |
|
| 788 |
{
|
|
| 789 |
return &data; |
|
| 790 |
} |
|
| 791 | ||
| 792 |
template <typename U> |
|
| 793 | 87x |
optional_nodiscard U * as() |
| 794 |
{
|
|
| 795 | 87x |
return reinterpret_cast<U*>( ptr() ); |
| 796 |
} |
|
| 797 | ||
| 798 |
template <typename U> |
|
| 799 |
optional_nodiscard U const * as() const |
|
| 800 |
{
|
|
| 801 |
return reinterpret_cast<U const *>( ptr() ); |
|
| 802 |
} |
|
| 803 |
}; |
|
| 804 | ||
| 805 |
} // namespace detail |
|
| 806 | ||
| 807 |
/// disengaged state tag |
|
| 808 | ||
| 809 |
struct nullopt_t |
|
| 810 |
{
|
|
| 811 |
struct init{};
|
|
| 812 |
explicit optional_constexpr nullopt_t( init /*unused*/ ) optional_noexcept {}
|
|
| 813 |
}; |
|
| 814 | ||
| 815 |
#if optional_HAVE( CONSTEXPR_11 ) |
|
| 816 |
constexpr nullopt_t nullopt{ nullopt_t::init{} };
|
|
| 817 |
#else |
|
| 818 |
// extra parenthesis to prevent the most vexing parse: |
|
| 819 |
const nullopt_t nullopt(( nullopt_t::init() )); |
|
| 820 |
#endif |
|
| 821 | ||
| 822 |
/// optional access error |
|
| 823 | ||
| 824 |
#if ! optional_CONFIG_NO_EXCEPTIONS |
|
| 825 | ||
| 826 |
class bad_optional_access : public std::logic_error |
|
| 827 |
{
|
|
| 828 |
public: |
|
| 829 |
explicit bad_optional_access() |
|
| 830 |
: logic_error( "bad optional access" ) {}
|
|
| 831 |
}; |
|
| 832 | ||
| 833 |
#endif //optional_CONFIG_NO_EXCEPTIONS |
|
| 834 | ||
| 835 |
/// optional |
|
| 836 | ||
| 837 |
template< typename T> |
|
| 838 |
class optional |
|
| 839 |
{
|
|
| 840 |
private: |
|
| 841 |
template< typename > friend class optional; |
|
| 842 | ||
| 843 |
typedef void (optional::*safe_bool)() const; |
|
| 844 | ||
| 845 |
public: |
|
| 846 |
typedef T value_type; |
|
| 847 | ||
| 848 |
// x.x.3.1, constructors |
|
| 849 | ||
| 850 |
// 1a - default construct |
|
| 851 |
optional_constexpr optional() optional_noexcept |
|
| 852 |
: has_value_( false ) |
|
| 853 |
, contained() |
|
| 854 |
{}
|
|
| 855 | ||
| 856 |
// 1b - construct explicitly empty |
|
| 857 |
// NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions ) |
|
| 858 | 5x |
optional_constexpr optional( nullopt_t /*unused*/ ) optional_noexcept |
| 859 |
: has_value_( false ) |
|
| 860 | 5x |
, contained() |
| 861 |
{}
|
|
| 862 | ||
| 863 |
// 2 - copy-construct |
|
| 864 |
optional_constexpr14 optional( optional const & other |
|
| 865 |
#if optional_CPP11_OR_GREATER |
|
| 866 |
optional_REQUIRES_A( |
|
| 867 |
true || std::is_copy_constructible<T>::value |
|
| 868 |
) |
|
| 869 |
#endif |
|
| 870 |
) |
|
| 871 |
: has_value_( other.has_value() ) |
|
| 872 |
{
|
|
| 873 |
if ( other.has_value() ) |
|
| 874 |
{
|
|
| 875 |
contained.construct_value( other.contained.value() ); |
|
| 876 |
} |
|
| 877 |
} |
|
| 878 | ||
| 879 |
#if optional_CPP11_OR_GREATER |
|
| 880 | ||
| 881 |
// 3 (C++11) - move-construct from optional |
|
| 882 |
optional_constexpr14 optional( optional && other |
|
| 883 |
optional_REQUIRES_A( |
|
| 884 |
true || std::is_move_constructible<T>::value |
|
| 885 |
) |
|
| 886 |
// NOLINTNEXTLINE( performance-noexcept-move-constructor ) |
|
| 887 |
) noexcept( std::is_nothrow_move_constructible<T>::value ) |
|
| 888 |
: has_value_( other.has_value() ) |
|
| 889 |
{
|
|
| 890 |
if ( other.has_value() ) |
|
| 891 |
{
|
|
| 892 |
contained.construct_value( std::move( other.contained.value() ) ); |
|
| 893 |
} |
|
| 894 |
} |
|
| 895 | ||
| 896 |
// 4a (C++11) - explicit converting copy-construct from optional |
|
| 897 |
template< typename U > |
|
| 898 |
explicit optional( optional<U> const & other |
|
| 899 |
optional_REQUIRES_A( |
|
| 900 |
std::is_constructible<T, U const &>::value |
|
| 901 |
&& !std::is_constructible<T, optional<U> & >::value |
|
| 902 |
&& !std::is_constructible<T, optional<U> && >::value |
|
| 903 |
&& !std::is_constructible<T, optional<U> const & >::value |
|
| 904 |
&& !std::is_constructible<T, optional<U> const && >::value |
|
| 905 |
&& !std::is_convertible< optional<U> & , T>::value |
|
| 906 |
&& !std::is_convertible< optional<U> && , T>::value |
|
| 907 |
&& !std::is_convertible< optional<U> const & , T>::value |
|
| 908 |
&& !std::is_convertible< optional<U> const &&, T>::value |
|
| 909 |
&& !std::is_convertible< U const & , T>::value /*=> explicit */ |
|
| 910 |
) |
|
| 911 |
) |
|
| 912 |
: has_value_( other.has_value() ) |
|
| 913 |
{
|
|
| 914 |
if ( other.has_value() ) |
|
| 915 |
{
|
|
| 916 |
contained.construct_value( T{ other.contained.value() } );
|
|
| 917 |
} |
|
| 918 |
} |
|
| 919 |
#endif // optional_CPP11_OR_GREATER |
|
| 920 | ||
| 921 |
// 4b (C++98 and later) - non-explicit converting copy-construct from optional |
|
| 922 |
template< typename U > |
|
| 923 |
// NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions ) |
|
| 924 |
optional( optional<U> const & other |
|
| 925 |
#if optional_CPP11_OR_GREATER |
|
| 926 |
optional_REQUIRES_A( |
|
| 927 |
std::is_constructible<T, U const &>::value |
|
| 928 |
&& !std::is_constructible<T, optional<U> & >::value |
|
| 929 |
&& !std::is_constructible<T, optional<U> && >::value |
|
| 930 |
&& !std::is_constructible<T, optional<U> const & >::value |
|
| 931 |
&& !std::is_constructible<T, optional<U> const && >::value |
|
| 932 |
&& !std::is_convertible< optional<U> & , T>::value |
|
| 933 |
&& !std::is_convertible< optional<U> && , T>::value |
|
| 934 |
&& !std::is_convertible< optional<U> const & , T>::value |
|
| 935 |
&& !std::is_convertible< optional<U> const &&, T>::value |
|
| 936 |
&& std::is_convertible< U const & , T>::value /*=> non-explicit */ |
|
| 937 |
) |
|
| 938 |
#endif // optional_CPP11_OR_GREATER |
|
| 939 |
) |
|
| 940 |
: has_value_( other.has_value() ) |
|
| 941 |
{
|
|
| 942 |
if ( other.has_value() ) |
|
| 943 |
{
|
|
| 944 |
contained.construct_value( other.contained.value() ); |
|
| 945 |
} |
|
| 946 |
} |
|
| 947 | ||
| 948 |
#if optional_CPP11_OR_GREATER |
|
| 949 | ||
| 950 |
// 5a (C++11) - explicit converting move-construct from optional |
|
| 951 |
template< typename U > |
|
| 952 |
explicit optional( optional<U> && other |
|
| 953 |
optional_REQUIRES_A( |
|
| 954 |
std::is_constructible<T, U &&>::value |
|
| 955 |
&& !std::is_constructible<T, optional<U> & >::value |
|
| 956 |
&& !std::is_constructible<T, optional<U> && >::value |
|
| 957 |
&& !std::is_constructible<T, optional<U> const & >::value |
|
| 958 |
&& !std::is_constructible<T, optional<U> const && >::value |
|
| 959 |
&& !std::is_convertible< optional<U> & , T>::value |
|
| 960 |
&& !std::is_convertible< optional<U> && , T>::value |
|
| 961 |
&& !std::is_convertible< optional<U> const & , T>::value |
|
| 962 |
&& !std::is_convertible< optional<U> const &&, T>::value |
|
| 963 |
&& !std::is_convertible< U &&, T>::value /*=> explicit */ |
|
| 964 |
) |
|
| 965 |
) |
|
| 966 |
: has_value_( other.has_value() ) |
|
| 967 |
{
|
|
| 968 |
if ( other.has_value() ) |
|
| 969 |
{
|
|
| 970 |
contained.construct_value( T{ std::move( other.contained.value() ) } );
|
|
| 971 |
} |
|
| 972 |
} |
|
| 973 | ||
| 974 |
// 5a (C++11) - non-explicit converting move-construct from optional |
|
| 975 |
template< typename U > |
|
| 976 |
// NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions ) |
|
| 977 |
optional( optional<U> && other |
|
| 978 |
optional_REQUIRES_A( |
|
| 979 |
std::is_constructible<T, U &&>::value |
|
| 980 |
&& !std::is_constructible<T, optional<U> & >::value |
|
| 981 |
&& !std::is_constructible<T, optional<U> && >::value |
|
| 982 |
&& !std::is_constructible<T, optional<U> const & >::value |
|
| 983 |
&& !std::is_constructible<T, optional<U> const && >::value |
|
| 984 |
&& !std::is_convertible< optional<U> & , T>::value |
|
| 985 |
&& !std::is_convertible< optional<U> && , T>::value |
|
| 986 |
&& !std::is_convertible< optional<U> const & , T>::value |
|
| 987 |
&& !std::is_convertible< optional<U> const &&, T>::value |
|
| 988 |
&& std::is_convertible< U &&, T>::value /*=> non-explicit */ |
|
| 989 |
) |
|
| 990 |
) |
|
| 991 |
: has_value_( other.has_value() ) |
|
| 992 |
{
|
|
| 993 |
if ( other.has_value() ) |
|
| 994 |
{
|
|
| 995 |
contained.construct_value( std::move( other.contained.value() ) ); |
|
| 996 |
} |
|
| 997 |
} |
|
| 998 | ||
| 999 |
// 6 (C++11) - in-place construct |
|
| 1000 |
template< typename... Args |
|
| 1001 |
optional_REQUIRES_T( |
|
| 1002 |
std::is_constructible<T, Args&&...>::value |
|
| 1003 |
) |
|
| 1004 |
> |
|
| 1005 |
optional_constexpr explicit optional( nonstd_lite_in_place_t(T), Args&&... args ) |
|
| 1006 |
: has_value_( true ) |
|
| 1007 |
, contained( T( std::forward<Args>(args)...) ) |
|
| 1008 |
{}
|
|
| 1009 | ||
| 1010 |
// 7 (C++11) - in-place construct, initializer-list |
|
| 1011 |
template< typename U, typename... Args |
|
| 1012 |
optional_REQUIRES_T( |
|
| 1013 |
std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value |
|
| 1014 |
) |
|
| 1015 |
> |
|
| 1016 |
optional_constexpr explicit optional( nonstd_lite_in_place_t(T), std::initializer_list<U> il, Args&&... args ) |
|
| 1017 |
: has_value_( true ) |
|
| 1018 |
, contained( T( il, std::forward<Args>(args)...) ) |
|
| 1019 |
{}
|
|
| 1020 | ||
| 1021 |
// 8a (C++11) - explicit move construct from value |
|
| 1022 |
template< typename U = value_type > |
|
| 1023 |
optional_constexpr explicit optional( U && value |
|
| 1024 |
optional_REQUIRES_A( |
|
| 1025 |
std::is_constructible<T, U&&>::value |
|
| 1026 |
&& !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value |
|
| 1027 |
&& !std::is_same<typename std20::remove_cvref<U>::type, optional<T>>::value |
|
| 1028 |
&& !std::is_convertible<U&&, T>::value /*=> explicit */ |
|
| 1029 |
) |
|
| 1030 |
) |
|
| 1031 |
: has_value_( true ) |
|
| 1032 |
, contained( T{ std::forward<U>( value ) } )
|
|
| 1033 |
{}
|
|
| 1034 | ||
| 1035 |
// 8b (C++11) - non-explicit move construct from value |
|
| 1036 |
template< typename U = value_type > |
|
| 1037 |
// NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions ) |
|
| 1038 | 58x |
optional_constexpr optional( U && value |
| 1039 |
optional_REQUIRES_A( |
|
| 1040 |
std::is_constructible<T, U&&>::value |
|
| 1041 |
&& !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value |
|
| 1042 |
&& !std::is_same<typename std20::remove_cvref<U>::type, optional<T>>::value |
|
| 1043 |
&& std::is_convertible<U&&, T>::value /*=> non-explicit */ |
|
| 1044 |
) |
|
| 1045 |
) |
|
| 1046 |
: has_value_( true ) |
|
| 1047 | 58x |
, contained( std::forward<U>( value ) ) |
| 1048 |
{}
|
|
| 1049 | ||
| 1050 |
#else // optional_CPP11_OR_GREATER |
|
| 1051 | ||
| 1052 |
// 8 (C++98) |
|
| 1053 |
optional( value_type const & value ) |
|
| 1054 |
: has_value_( true ) |
|
| 1055 |
, contained( value ) |
|
| 1056 |
{}
|
|
| 1057 | ||
| 1058 |
#endif // optional_CPP11_OR_GREATER |
|
| 1059 | ||
| 1060 |
// x.x.3.2, destructor |
|
| 1061 | ||
| 1062 | 34x |
~optional() |
| 1063 |
{
|
|
| 1064 | 34x |
if ( has_value() ) |
| 1065 |
{
|
|
| 1066 | 29x |
contained.destruct_value(); |
| 1067 |
} |
|
| 1068 |
} |
|
| 1069 | ||
| 1070 |
// x.x.3.3, assignment |
|
| 1071 | ||
| 1072 |
// 1 (C++98and later) - assign explicitly empty |
|
| 1073 |
optional & operator=( nullopt_t /*unused*/) optional_noexcept |
|
| 1074 |
{
|
|
| 1075 |
reset(); |
|
| 1076 |
return *this; |
|
| 1077 |
} |
|
| 1078 | ||
| 1079 |
// 2 (C++98and later) - copy-assign from optional |
|
| 1080 |
#if optional_CPP11_OR_GREATER |
|
| 1081 |
// NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator ) |
|
| 1082 |
optional_REQUIRES_R( |
|
| 1083 |
optional &, |
|
| 1084 |
true |
|
| 1085 |
// std::is_copy_constructible<T>::value |
|
| 1086 |
// && std::is_copy_assignable<T>::value |
|
| 1087 |
) |
|
| 1088 |
operator=( optional const & other ) |
|
| 1089 |
noexcept( |
|
| 1090 |
std::is_nothrow_move_assignable<T>::value |
|
| 1091 |
&& std::is_nothrow_move_constructible<T>::value |
|
| 1092 |
) |
|
| 1093 |
#else |
|
| 1094 |
optional & operator=( optional const & other ) |
|
| 1095 |
#endif |
|
| 1096 |
{
|
|
| 1097 |
if ( (has_value() == true ) && (other.has_value() == false) ) { reset(); }
|
|
| 1098 |
else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( *other ); }
|
|
| 1099 |
else if ( (has_value() == true ) && (other.has_value() == true ) ) { contained.value() = *other; }
|
|
| 1100 |
return *this; |
|
| 1101 |
} |
|
| 1102 | ||
| 1103 |
#if optional_CPP11_OR_GREATER |
|
| 1104 | ||
| 1105 |
// 3 (C++11) - move-assign from optional |
|
| 1106 |
// NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator ) |
|
| 1107 |
optional_REQUIRES_R( |
|
| 1108 |
optional &, |
|
| 1109 |
true |
|
| 1110 |
// std::is_move_constructible<T>::value |
|
| 1111 |
// && std::is_move_assignable<T>::value |
|
| 1112 |
) |
|
| 1113 |
operator=( optional && other ) noexcept |
|
| 1114 |
{
|
|
| 1115 |
if ( (has_value() == true ) && (other.has_value() == false) ) { reset(); }
|
|
| 1116 |
else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( std::move( *other ) ); }
|
|
| 1117 |
else if ( (has_value() == true ) && (other.has_value() == true ) ) { contained.value() = std::move( *other ); }
|
|
| 1118 |
return *this; |
|
| 1119 |
} |
|
| 1120 | ||
| 1121 |
// 4 (C++11) - move-assign from value |
|
| 1122 |
template< typename U = T > |
|
| 1123 |
// NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator ) |
|
| 1124 |
optional_REQUIRES_R( |
|
| 1125 |
optional &, |
|
| 1126 |
std::is_constructible<T , U>::value |
|
| 1127 |
&& std::is_assignable<T&, U>::value |
|
| 1128 |
&& !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value |
|
| 1129 |
&& !std::is_same<typename std20::remove_cvref<U>::type, optional<T>>::value |
|
| 1130 |
&& !(std::is_scalar<T>::value && std::is_same<T, typename std::decay<U>::type>::value) |
|
| 1131 |
) |
|
| 1132 |
operator=( U && value ) |
|
| 1133 |
{
|
|
| 1134 |
if ( has_value() ) |
|
| 1135 |
{
|
|
| 1136 |
contained.value() = std::forward<U>( value ); |
|
| 1137 |
} |
|
| 1138 |
else |
|
| 1139 |
{
|
|
| 1140 |
initialize( T( std::forward<U>( value ) ) ); |
|
| 1141 |
} |
|
| 1142 |
return *this; |
|
| 1143 |
} |
|
| 1144 | ||
| 1145 |
#else // optional_CPP11_OR_GREATER |
|
| 1146 | ||
| 1147 |
// 4 (C++98) - copy-assign from value |
|
| 1148 |
template< typename U /*= T*/ > |
|
| 1149 |
optional & operator=( U const & value ) |
|
| 1150 |
{
|
|
| 1151 |
if ( has_value() ) contained.value() = value; |
|
| 1152 |
else initialize( T( value ) ); |
|
| 1153 |
return *this; |
|
| 1154 |
} |
|
| 1155 | ||
| 1156 |
#endif // optional_CPP11_OR_GREATER |
|
| 1157 | ||
| 1158 |
// 5 (C++98 and later) - converting copy-assign from optional |
|
| 1159 |
template< typename U > |
|
| 1160 |
#if optional_CPP11_OR_GREATER |
|
| 1161 |
// NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator ) |
|
| 1162 |
optional_REQUIRES_R( |
|
| 1163 |
optional&, |
|
| 1164 |
std::is_constructible< T , U const &>::value |
|
| 1165 |
&& std::is_assignable< T&, U const &>::value |
|
| 1166 |
&& !std::is_constructible<T, optional<U> & >::value |
|
| 1167 |
&& !std::is_constructible<T, optional<U> && >::value |
|
| 1168 |
&& !std::is_constructible<T, optional<U> const & >::value |
|
| 1169 |
&& !std::is_constructible<T, optional<U> const && >::value |
|
| 1170 |
&& !std::is_convertible< optional<U> & , T>::value |
|
| 1171 |
&& !std::is_convertible< optional<U> && , T>::value |
|
| 1172 |
&& !std::is_convertible< optional<U> const & , T>::value |
|
| 1173 |
&& !std::is_convertible< optional<U> const &&, T>::value |
|
| 1174 |
&& !std::is_assignable< T&, optional<U> & >::value |
|
| 1175 |
&& !std::is_assignable< T&, optional<U> && >::value |
|
| 1176 |
&& !std::is_assignable< T&, optional<U> const & >::value |
|
| 1177 |
&& !std::is_assignable< T&, optional<U> const && >::value |
|
| 1178 |
) |
|
| 1179 |
#else |
|
| 1180 |
optional& |
|
| 1181 |
#endif // optional_CPP11_OR_GREATER |
|
| 1182 |
operator=( optional<U> const & other ) |
|
| 1183 |
{
|
|
| 1184 |
return *this = optional( other ); |
|
| 1185 |
} |
|
| 1186 | ||
| 1187 |
#if optional_CPP11_OR_GREATER |
|
| 1188 | ||
| 1189 |
// 6 (C++11) - converting move-assign from optional |
|
| 1190 |
template< typename U > |
|
| 1191 |
// NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, misc-unconventional-assign-operator ) |
|
| 1192 |
optional_REQUIRES_R( |
|
| 1193 |
optional&, |
|
| 1194 |
std::is_constructible< T , U>::value |
|
| 1195 |
&& std::is_assignable< T&, U>::value |
|
| 1196 |
&& !std::is_constructible<T, optional<U> & >::value |
|
| 1197 |
&& !std::is_constructible<T, optional<U> && >::value |
|
| 1198 |
&& !std::is_constructible<T, optional<U> const & >::value |
|
| 1199 |
&& !std::is_constructible<T, optional<U> const && >::value |
|
| 1200 |
&& !std::is_convertible< optional<U> & , T>::value |
|
| 1201 |
&& !std::is_convertible< optional<U> && , T>::value |
|
| 1202 |
&& !std::is_convertible< optional<U> const & , T>::value |
|
| 1203 |
&& !std::is_convertible< optional<U> const &&, T>::value |
|
| 1204 |
&& !std::is_assignable< T&, optional<U> & >::value |
|
| 1205 |
&& !std::is_assignable< T&, optional<U> && >::value |
|
| 1206 |
&& !std::is_assignable< T&, optional<U> const & >::value |
|
| 1207 |
&& !std::is_assignable< T&, optional<U> const && >::value |
|
| 1208 |
) |
|
| 1209 |
operator=( optional<U> && other ) |
|
| 1210 |
{
|
|
| 1211 |
return *this = optional( std::move( other ) ); |
|
| 1212 |
} |
|
| 1213 | ||
| 1214 |
// 7 (C++11) - emplace |
|
| 1215 |
template< typename... Args |
|
| 1216 |
optional_REQUIRES_T( |
|
| 1217 |
std::is_constructible<T, Args&&...>::value |
|
| 1218 |
) |
|
| 1219 |
> |
|
| 1220 |
T& emplace( Args&&... args ) |
|
| 1221 |
{
|
|
| 1222 |
*this = nullopt; |
|
| 1223 |
contained.emplace( std::forward<Args>(args)... ); |
|
| 1224 |
has_value_ = true; |
|
| 1225 |
return contained.value(); |
|
| 1226 |
} |
|
| 1227 | ||
| 1228 |
// 8 (C++11) - emplace, initializer-list |
|
| 1229 |
template< typename U, typename... Args |
|
| 1230 |
optional_REQUIRES_T( |
|
| 1231 |
std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value |
|
| 1232 |
) |
|
| 1233 |
> |
|
| 1234 |
T& emplace( std::initializer_list<U> il, Args&&... args ) |
|
| 1235 |
{
|
|
| 1236 |
*this = nullopt; |
|
| 1237 |
contained.emplace( il, std::forward<Args>(args)... ); |
|
| 1238 |
has_value_ = true; |
|
| 1239 |
return contained.value(); |
|
| 1240 |
} |
|
| 1241 | ||
| 1242 |
#endif // optional_CPP11_OR_GREATER |
|
| 1243 | ||
| 1244 |
// x.x.3.4, swap |
|
| 1245 | ||
| 1246 |
void swap( optional & other ) |
|
| 1247 |
#if optional_CPP11_OR_GREATER |
|
| 1248 |
noexcept( |
|
| 1249 |
std::is_nothrow_move_constructible<T>::value |
|
| 1250 |
&& std17::is_nothrow_swappable<T>::value |
|
| 1251 |
) |
|
| 1252 |
#endif |
|
| 1253 |
{
|
|
| 1254 |
using std::swap; |
|
| 1255 |
if ( (has_value() == true ) && (other.has_value() == true ) ) { swap( **this, *other ); }
|
|
| 1256 |
else if ( (has_value() == false) && (other.has_value() == true ) ) { initialize( std11::move(*other) ); other.reset(); }
|
|
| 1257 |
else if ( (has_value() == true ) && (other.has_value() == false) ) { other.initialize( std11::move(**this) ); reset(); }
|
|
| 1258 |
} |
|
| 1259 | ||
| 1260 |
// x.x.3.5, observers |
|
| 1261 | ||
| 1262 |
optional_constexpr value_type const * operator ->() const |
|
| 1263 |
{
|
|
| 1264 |
return assert( has_value() ), |
|
| 1265 |
contained.value_ptr(); |
|
| 1266 |
} |
|
| 1267 | ||
| 1268 |
optional_constexpr14 value_type * operator ->() |
|
| 1269 |
{
|
|
| 1270 |
return assert( has_value() ), |
|
| 1271 |
contained.value_ptr(); |
|
| 1272 |
} |
|
| 1273 | ||
| 1274 |
optional_constexpr value_type const & operator *() const optional_ref_qual |
|
| 1275 |
{
|
|
| 1276 |
return assert( has_value() ), |
|
| 1277 |
contained.value(); |
|
| 1278 |
} |
|
| 1279 | ||
| 1280 |
optional_constexpr14 value_type & operator *() optional_ref_qual |
|
| 1281 |
{
|
|
| 1282 |
return assert( has_value() ), |
|
| 1283 |
contained.value(); |
|
| 1284 |
} |
|
| 1285 | ||
| 1286 |
#if optional_HAVE( REF_QUALIFIER ) && ( !optional_COMPILER_GNUC_VERSION || optional_COMPILER_GNUC_VERSION >= 490 ) |
|
| 1287 | ||
| 1288 |
optional_constexpr value_type const && operator *() const optional_refref_qual |
|
| 1289 |
{
|
|
| 1290 |
return std::move( **this ); |
|
| 1291 |
} |
|
| 1292 | ||
| 1293 |
optional_constexpr14 value_type && operator *() optional_refref_qual |
|
| 1294 |
{
|
|
| 1295 |
return std::move( **this ); |
|
| 1296 |
} |
|
| 1297 | ||
| 1298 |
#endif |
|
| 1299 | ||
| 1300 |
#if optional_CPP11_OR_GREATER |
|
| 1301 |
optional_constexpr explicit operator bool() const optional_noexcept |
|
| 1302 |
{
|
|
| 1303 |
return has_value(); |
|
| 1304 |
} |
|
| 1305 |
#else |
|
| 1306 |
optional_constexpr operator safe_bool() const optional_noexcept |
|
| 1307 |
{
|
|
| 1308 |
return has_value() ? &optional::this_type_does_not_support_comparisons : 0; |
|
| 1309 |
} |
|
| 1310 |
#endif |
|
| 1311 | ||
| 1312 |
// NOLINTNEXTLINE( modernize-use-nodiscard ) |
|
| 1313 | 68x |
/*optional_nodiscard*/ optional_constexpr bool has_value() const optional_noexcept |
| 1314 |
{
|
|
| 1315 | 68x |
return has_value_; |
| 1316 |
} |
|
| 1317 | ||
| 1318 |
// NOLINTNEXTLINE( modernize-use-nodiscard ) |
|
| 1319 |
/*optional_nodiscard*/ optional_constexpr14 value_type const & value() const optional_ref_qual |
|
| 1320 |
{
|
|
| 1321 |
#if optional_CONFIG_NO_EXCEPTIONS |
|
| 1322 |
assert( has_value() ); |
|
| 1323 |
#else |
|
| 1324 |
if ( ! has_value() ) |
|
| 1325 |
{
|
|
| 1326 |
throw bad_optional_access(); |
|
| 1327 |
} |
|
| 1328 |
#endif |
|
| 1329 |
return contained.value(); |
|
| 1330 |
} |
|
| 1331 | ||
| 1332 |
optional_constexpr14 value_type & value() optional_ref_qual |
|
| 1333 |
{
|
|
| 1334 |
#if optional_CONFIG_NO_EXCEPTIONS |
|
| 1335 |
assert( has_value() ); |
|
| 1336 |
#else |
|
| 1337 |
if ( ! has_value() ) |
|
| 1338 |
{
|
|
| 1339 |
throw bad_optional_access(); |
|
| 1340 |
} |
|
| 1341 |
#endif |
|
| 1342 |
return contained.value(); |
|
| 1343 |
} |
|
| 1344 | ||
| 1345 |
#if optional_HAVE( REF_QUALIFIER ) && ( !optional_COMPILER_GNUC_VERSION || optional_COMPILER_GNUC_VERSION >= 490 ) |
|
| 1346 | ||
| 1347 |
// NOLINTNEXTLINE( modernize-use-nodiscard ) |
|
| 1348 |
/*optional_nodiscard*/ optional_constexpr value_type const && value() const optional_refref_qual |
|
| 1349 |
{
|
|
| 1350 |
return std::move( value() ); |
|
| 1351 |
} |
|
| 1352 | ||
| 1353 |
optional_constexpr14 value_type && value() optional_refref_qual |
|
| 1354 |
{
|
|
| 1355 |
return std::move( value() ); |
|
| 1356 |
} |
|
| 1357 | ||
| 1358 |
#endif |
|
| 1359 | ||
| 1360 |
#if optional_CPP11_OR_GREATER |
|
| 1361 | ||
| 1362 |
template< typename U > |
|
| 1363 |
optional_constexpr value_type value_or( U && v ) const optional_ref_qual |
|
| 1364 |
{
|
|
| 1365 |
return has_value() ? contained.value() : static_cast<T>(std::forward<U>( v ) ); |
|
| 1366 |
} |
|
| 1367 | ||
| 1368 |
template< typename U > |
|
| 1369 | 34x |
optional_constexpr14 value_type value_or( U && v ) optional_refref_qual |
| 1370 |
{
|
|
| 1371 | 34x |
return has_value() ? std::move( contained.value() ) : static_cast<T>(std::forward<U>( v ) ); |
| 1372 |
} |
|
| 1373 | ||
| 1374 |
#else |
|
| 1375 | ||
| 1376 |
template< typename U > |
|
| 1377 |
optional_constexpr value_type value_or( U const & v ) const |
|
| 1378 |
{
|
|
| 1379 |
return has_value() ? contained.value() : static_cast<value_type>( v ); |
|
| 1380 |
} |
|
| 1381 | ||
| 1382 |
#endif // optional_CPP11_OR_GREATER |
|
| 1383 | ||
| 1384 |
// x.x.3.6, modifiers |
|
| 1385 | ||
| 1386 |
void reset() optional_noexcept |
|
| 1387 |
{
|
|
| 1388 |
if ( has_value() ) |
|
| 1389 |
{
|
|
| 1390 |
contained.destruct_value(); |
|
| 1391 |
} |
|
| 1392 | ||
| 1393 |
has_value_ = false; |
|
| 1394 |
} |
|
| 1395 | ||
| 1396 |
private: |
|
| 1397 |
void this_type_does_not_support_comparisons() const {}
|
|
| 1398 | ||
| 1399 |
template< typename V > |
|
| 1400 |
void initialize( V const & value ) |
|
| 1401 |
{
|
|
| 1402 |
assert( ! has_value() ); |
|
| 1403 |
contained.construct_value( value ); |
|
| 1404 |
has_value_ = true; |
|
| 1405 |
} |
|
| 1406 | ||
| 1407 |
#if optional_CPP11_OR_GREATER |
|
| 1408 |
template< typename V > |
|
| 1409 |
void initialize( V && value ) |
|
| 1410 |
{
|
|
| 1411 |
assert( ! has_value() ); |
|
| 1412 |
contained.construct_value( std::move( value ) ); |
|
| 1413 |
has_value_ = true; |
|
| 1414 |
} |
|
| 1415 | ||
| 1416 |
#endif |
|
| 1417 | ||
| 1418 |
private: |
|
| 1419 |
bool has_value_; |
|
| 1420 |
detail::storage_t< value_type > contained; |
|
| 1421 | ||
| 1422 |
}; |
|
| 1423 | ||
| 1424 |
// Relational operators |
|
| 1425 | ||
| 1426 |
template< typename T, typename U > |
|
| 1427 |
inline optional_constexpr bool operator==( optional<T> const & x, optional<U> const & y ) |
|
| 1428 |
{
|
|
| 1429 |
return bool(x) != bool(y) ? false : !bool( x ) ? true : *x == *y; |
|
| 1430 |
} |
|
| 1431 | ||
| 1432 |
template< typename T, typename U > |
|
| 1433 |
inline optional_constexpr bool operator!=( optional<T> const & x, optional<U> const & y ) |
|
| 1434 |
{
|
|
| 1435 |
return !(x == y); |
|
| 1436 |
} |
|
| 1437 | ||
| 1438 |
template< typename T, typename U > |
|
| 1439 |
inline optional_constexpr bool operator<( optional<T> const & x, optional<U> const & y ) |
|
| 1440 |
{
|
|
| 1441 |
return (!y) ? false : (!x) ? true : *x < *y; |
|
| 1442 |
} |
|
| 1443 | ||
| 1444 |
template< typename T, typename U > |
|
| 1445 |
inline optional_constexpr bool operator>( optional<T> const & x, optional<U> const & y ) |
|
| 1446 |
{
|
|
| 1447 |
return (y < x); |
|
| 1448 |
} |
|
| 1449 | ||
| 1450 |
template< typename T, typename U > |
|
| 1451 |
inline optional_constexpr bool operator<=( optional<T> const & x, optional<U> const & y ) |
|
| 1452 |
{
|
|
| 1453 |
return !(y < x); |
|
| 1454 |
} |
|
| 1455 | ||
| 1456 |
template< typename T, typename U > |
|
| 1457 |
inline optional_constexpr bool operator>=( optional<T> const & x, optional<U> const & y ) |
|
| 1458 |
{
|
|
| 1459 |
return !(x < y); |
|
| 1460 |
} |
|
| 1461 | ||
| 1462 |
// Comparison with nullopt |
|
| 1463 | ||
| 1464 |
template< typename T > |
|
| 1465 |
inline optional_constexpr bool operator==( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept |
|
| 1466 |
{
|
|
| 1467 |
return (!x); |
|
| 1468 |
} |
|
| 1469 | ||
| 1470 |
template< typename T > |
|
| 1471 |
inline optional_constexpr bool operator==( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept |
|
| 1472 |
{
|
|
| 1473 |
return (!x); |
|
| 1474 |
} |
|
| 1475 | ||
| 1476 |
template< typename T > |
|
| 1477 |
inline optional_constexpr bool operator!=( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept |
|
| 1478 |
{
|
|
| 1479 |
return bool(x); |
|
| 1480 |
} |
|
| 1481 | ||
| 1482 |
template< typename T > |
|
| 1483 |
inline optional_constexpr bool operator!=( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept |
|
| 1484 |
{
|
|
| 1485 |
return bool(x); |
|
| 1486 |
} |
|
| 1487 | ||
| 1488 |
template< typename T > |
|
| 1489 |
inline optional_constexpr bool operator<( optional<T> const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept |
|
| 1490 |
{
|
|
| 1491 |
return false; |
|
| 1492 |
} |
|
| 1493 | ||
| 1494 |
template< typename T > |
|
| 1495 |
inline optional_constexpr bool operator<( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept |
|
| 1496 |
{
|
|
| 1497 |
return bool(x); |
|
| 1498 |
} |
|
| 1499 | ||
| 1500 |
template< typename T > |
|
| 1501 |
inline optional_constexpr bool operator<=( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept |
|
| 1502 |
{
|
|
| 1503 |
return (!x); |
|
| 1504 |
} |
|
| 1505 | ||
| 1506 |
template< typename T > |
|
| 1507 |
inline optional_constexpr bool operator<=( nullopt_t /*unused*/, optional<T> const & /*unused*/ ) optional_noexcept |
|
| 1508 |
{
|
|
| 1509 |
return true; |
|
| 1510 |
} |
|
| 1511 | ||
| 1512 |
template< typename T > |
|
| 1513 |
inline optional_constexpr bool operator>( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept |
|
| 1514 |
{
|
|
| 1515 |
return bool(x); |
|
| 1516 |
} |
|
| 1517 | ||
| 1518 |
template< typename T > |
|
| 1519 |
inline optional_constexpr bool operator>( nullopt_t /*unused*/, optional<T> const & /*unused*/ ) optional_noexcept |
|
| 1520 |
{
|
|
| 1521 |
return false; |
|
| 1522 |
} |
|
| 1523 | ||
| 1524 |
template< typename T > |
|
| 1525 |
inline optional_constexpr bool operator>=( optional<T> const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept |
|
| 1526 |
{
|
|
| 1527 |
return true; |
|
| 1528 |
} |
|
| 1529 | ||
| 1530 |
template< typename T > |
|
| 1531 |
inline optional_constexpr bool operator>=( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept |
|
| 1532 |
{
|
|
| 1533 |
return (!x); |
|
| 1534 |
} |
|
| 1535 | ||
| 1536 |
// Comparison with T |
|
| 1537 | ||
| 1538 |
template< typename T, typename U > |
|
| 1539 |
inline optional_constexpr bool operator==( optional<T> const & x, U const & v ) |
|
| 1540 |
{
|
|
| 1541 |
return bool(x) ? *x == v : false; |
|
| 1542 |
} |
|
| 1543 | ||
| 1544 |
template< typename T, typename U > |
|
| 1545 |
inline optional_constexpr bool operator==( U const & v, optional<T> const & x ) |
|
| 1546 |
{
|
|
| 1547 |
return bool(x) ? v == *x : false; |
|
| 1548 |
} |
|
| 1549 | ||
| 1550 |
template< typename T, typename U > |
|
| 1551 |
inline optional_constexpr bool operator!=( optional<T> const & x, U const & v ) |
|
| 1552 |
{
|
|
| 1553 |
return bool(x) ? *x != v : true; |
|
| 1554 |
} |
|
| 1555 | ||
| 1556 |
template< typename T, typename U > |
|
| 1557 |
inline optional_constexpr bool operator!=( U const & v, optional<T> const & x ) |
|
| 1558 |
{
|
|
| 1559 |
return bool(x) ? v != *x : true; |
|
| 1560 |
} |
|
| 1561 | ||
| 1562 |
template< typename T, typename U > |
|
| 1563 |
inline optional_constexpr bool operator<( optional<T> const & x, U const & v ) |
|
| 1564 |
{
|
|
| 1565 |
return bool(x) ? *x < v : true; |
|
| 1566 |
} |
|
| 1567 | ||
| 1568 |
template< typename T, typename U > |
|
| 1569 |
inline optional_constexpr bool operator<( U const & v, optional<T> const & x ) |
|
| 1570 |
{
|
|
| 1571 |
return bool(x) ? v < *x : false; |
|
| 1572 |
} |
|
| 1573 | ||
| 1574 |
template< typename T, typename U > |
|
| 1575 |
inline optional_constexpr bool operator<=( optional<T> const & x, U const & v ) |
|
| 1576 |
{
|
|
| 1577 |
return bool(x) ? *x <= v : true; |
|
| 1578 |
} |
|
| 1579 | ||
| 1580 |
template< typename T, typename U > |
|
| 1581 |
inline optional_constexpr bool operator<=( U const & v, optional<T> const & x ) |
|
| 1582 |
{
|
|
| 1583 |
return bool(x) ? v <= *x : false; |
|
| 1584 |
} |
|
| 1585 | ||
| 1586 |
template< typename T, typename U > |
|
| 1587 |
inline optional_constexpr bool operator>( optional<T> const & x, U const & v ) |
|
| 1588 |
{
|
|
| 1589 |
return bool(x) ? *x > v : false; |
|
| 1590 |
} |
|
| 1591 | ||
| 1592 |
template< typename T, typename U > |
|
| 1593 |
inline optional_constexpr bool operator>( U const & v, optional<T> const & x ) |
|
| 1594 |
{
|
|
| 1595 |
return bool(x) ? v > *x : true; |
|
| 1596 |
} |
|
| 1597 | ||
| 1598 |
template< typename T, typename U > |
|
| 1599 |
inline optional_constexpr bool operator>=( optional<T> const & x, U const & v ) |
|
| 1600 |
{
|
|
| 1601 |
return bool(x) ? *x >= v : false; |
|
| 1602 |
} |
|
| 1603 | ||
| 1604 |
template< typename T, typename U > |
|
| 1605 |
inline optional_constexpr bool operator>=( U const & v, optional<T> const & x ) |
|
| 1606 |
{
|
|
| 1607 |
return bool(x) ? v >= *x : true; |
|
| 1608 |
} |
|
| 1609 | ||
| 1610 |
// Specialized algorithms |
|
| 1611 | ||
| 1612 |
template< typename T |
|
| 1613 |
#if optional_CPP11_OR_GREATER |
|
| 1614 |
optional_REQUIRES_T( |
|
| 1615 |
std::is_move_constructible<T>::value |
|
| 1616 |
&& std17::is_swappable<T>::value ) |
|
| 1617 |
#endif |
|
| 1618 |
> |
|
| 1619 |
void swap( optional<T> & x, optional<T> & y ) |
|
| 1620 |
#if optional_CPP11_OR_GREATER |
|
| 1621 |
noexcept( noexcept( x.swap(y) ) ) |
|
| 1622 |
#endif |
|
| 1623 |
{
|
|
| 1624 |
x.swap( y ); |
|
| 1625 |
} |
|
| 1626 | ||
| 1627 |
#if optional_CPP11_OR_GREATER |
|
| 1628 | ||
| 1629 |
template< typename T > |
|
| 1630 |
optional_constexpr optional< typename std::decay<T>::type > make_optional( T && value ) |
|
| 1631 |
{
|
|
| 1632 |
return optional< typename std::decay<T>::type >( std::forward<T>( value ) ); |
|
| 1633 |
} |
|
| 1634 | ||
| 1635 |
template< typename T, typename...Args > |
|
| 1636 |
optional_constexpr optional<T> make_optional( Args&&... args ) |
|
| 1637 |
{
|
|
| 1638 |
return optional<T>( nonstd_lite_in_place(T), std::forward<Args>(args)...); |
|
| 1639 |
} |
|
| 1640 | ||
| 1641 |
template< typename T, typename U, typename... Args > |
|
| 1642 |
optional_constexpr optional<T> make_optional( std::initializer_list<U> il, Args&&... args ) |
|
| 1643 |
{
|
|
| 1644 |
return optional<T>( nonstd_lite_in_place(T), il, std::forward<Args>(args)...); |
|
| 1645 |
} |
|
| 1646 | ||
| 1647 |
#else |
|
| 1648 | ||
| 1649 |
template< typename T > |
|
| 1650 |
optional<T> make_optional( T const & value ) |
|
| 1651 |
{
|
|
| 1652 |
return optional<T>( value ); |
|
| 1653 |
} |
|
| 1654 | ||
| 1655 |
#endif // optional_CPP11_OR_GREATER |
|
| 1656 | ||
| 1657 |
} // namespace optional_lite |
|
| 1658 | ||
| 1659 |
using optional_lite::optional; |
|
| 1660 |
using optional_lite::nullopt_t; |
|
| 1661 |
using optional_lite::nullopt; |
|
| 1662 |
using optional_lite::bad_optional_access; |
|
| 1663 | ||
| 1664 |
using optional_lite::make_optional; |
|
| 1665 | ||
| 1666 |
} // namespace nonstd |
|
| 1667 | ||
| 1668 |
#if optional_CPP11_OR_GREATER |
|
| 1669 | ||
| 1670 |
// specialize the std::hash algorithm: |
|
| 1671 | ||
| 1672 |
namespace std {
|
|
| 1673 | ||
| 1674 |
template< class T > |
|
| 1675 |
struct hash< nonstd::optional<T> > |
|
| 1676 |
{
|
|
| 1677 |
public: |
|
| 1678 |
std::size_t operator()( nonstd::optional<T> const & v ) const optional_noexcept |
|
| 1679 |
{
|
|
| 1680 |
return bool( v ) ? std::hash<T>{}( *v ) : 0;
|
|
| 1681 |
} |
|
| 1682 |
}; |
|
| 1683 | ||
| 1684 |
} //namespace std |
|
| 1685 | ||
| 1686 |
#endif // optional_CPP11_OR_GREATER |
|
| 1687 | ||
| 1688 |
#if defined(__clang__) |
|
| 1689 |
# pragma clang diagnostic pop |
|
| 1690 |
#elif defined(__GNUC__) |
|
| 1691 |
# pragma GCC diagnostic pop |
|
| 1692 |
#elif defined(_MSC_VER ) |
|
| 1693 |
# pragma warning( pop ) |
|
| 1694 |
#endif |
|
| 1695 | ||
| 1696 |
#endif // optional_USES_STD_OPTIONAL |
|
| 1697 | ||
| 1698 |
#endif // NONSTD_OPTIONAL_LITE_HPP |
| 1 |
// Copyright (c) 2018 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#include "area.h" |
|
| 15 | ||
| 16 |
#include <cmath> |
|
| 17 |
#include <cstddef> |
|
| 18 | ||
| 19 |
namespace exactextract {
|
|
| 20 | ||
| 21 | 1656x |
double area_signed(const std::vector<Coordinate> &ring) {
|
| 22 | 1656x |
if (ring.size() < 3) {
|
| 23 | ! |
return 0; |
| 24 |
} |
|
| 25 | ||
| 26 | 1656x |
double sum{0};
|
| 27 | ||
| 28 | 1656x |
double x0{ring[0].x};
|
| 29 | 15008x |
for (size_t i = 1; i < ring.size() - 1; i++) {
|
| 30 | 13352x |
double x = ring[i].x - x0; |
| 31 | 13352x |
double y1 = ring[i + 1].y; |
| 32 | 13352x |
double y2 = ring[i - 1].y; |
| 33 | 13352x |
sum += x * (y2 - y1); |
| 34 |
} |
|
| 35 | ||
| 36 | 1656x |
return sum / 2.0; |
| 37 |
} |
|
| 38 | ||
| 39 | 1656x |
double area(const std::vector<Coordinate> &ring) {
|
| 40 | 1656x |
return std::abs(area_signed(ring)); |
| 41 |
} |
|
| 42 | ||
| 43 |
} |
| 1 |
// Copyright (c) 2018 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#include <cmath> |
|
| 15 |
#include <stdexcept> |
|
| 16 | ||
| 17 |
#include "box.h" |
|
| 18 | ||
| 19 |
namespace exactextract {
|
|
| 20 | ||
| 21 | 1854x |
static inline double clamp(double x, double low, double high) {
|
| 22 | 1854x |
return std::min(std::max(x, low), high); |
| 23 |
} |
|
| 24 | ||
| 25 | ! |
Side Box::side(const Coordinate &c) const {
|
| 26 | ! |
if (c.x == xmin) {
|
| 27 | ! |
return Side::LEFT; |
| 28 | ! |
} else if (c.x == xmax) {
|
| 29 | ! |
return Side::RIGHT; |
| 30 | ! |
} else if (c.y == ymin) {
|
| 31 | ! |
return Side::BOTTOM; |
| 32 | ! |
} else if (c.y == ymax) {
|
| 33 | ! |
return Side::TOP; |
| 34 |
} |
|
| 35 | ||
| 36 | ! |
return Side::NONE; |
| 37 |
} |
|
| 38 | ||
| 39 | 1938x |
Crossing Box::crossing(const Coordinate &c1, const Coordinate &c2) const {
|
| 40 |
// vertical line |
|
| 41 | 1938x |
if (c1.x == c2.x) {
|
| 42 | 16x |
if (c2.y >= ymax) {
|
| 43 | 12x |
return Crossing{Side::TOP, c1.x, ymax};
|
| 44 | 4x |
} else if (c2.y <= ymin) {
|
| 45 | 4x |
return Crossing{Side::BOTTOM, c1.x, ymin};
|
| 46 |
} else {
|
|
| 47 | ! |
throw std::runtime_error("Never get here.");
|
| 48 |
} |
|
| 49 |
} |
|
| 50 | ||
| 51 |
// horizontal line |
|
| 52 | 1922x |
if (c1.y == c2.y) {
|
| 53 | 68x |
if (c2.x >= xmax) {
|
| 54 | 34x |
return Crossing{Side::RIGHT, xmax, c1.y};
|
| 55 | 34x |
} else if (c2.x <= xmin) {
|
| 56 | 34x |
return Crossing{Side::LEFT, xmin, c1.y};
|
| 57 |
} else {
|
|
| 58 | ! |
throw std::runtime_error("Never get here");
|
| 59 |
} |
|
| 60 |
} |
|
| 61 | ||
| 62 | 1854x |
double m = std::abs((c2.y - c1.y) / (c2.x - c1.x)); |
| 63 | ||
| 64 | 1854x |
bool up = c2.y > c1.y; |
| 65 | 1854x |
bool right = c2.x > c1.x; |
| 66 | ||
| 67 | 1854x |
if (up) {
|
| 68 | 979x |
if (right) {
|
| 69 |
// 1st quadrant |
|
| 70 | 452x |
double y2 = c1.y + m * (xmax - c1.x); |
| 71 | ||
| 72 | 452x |
if (y2 < ymax) {
|
| 73 | 276x |
return Crossing{Side::RIGHT, xmax, clamp(y2, ymin, ymax)};
|
| 74 |
} else {
|
|
| 75 | 176x |
double x2 = c1.x + (ymax - c1.y) / m; |
| 76 | 176x |
return Crossing{Side::TOP, clamp(x2, xmin, xmax), ymax};
|
| 77 |
} |
|
| 78 |
} else {
|
|
| 79 |
// 2nd quadrant |
|
| 80 | 527x |
double y2 = c1.y + m * (c1.x - xmin); |
| 81 | ||
| 82 | 527x |
if (y2 < ymax) {
|
| 83 | 229x |
return Crossing{Side::LEFT, xmin, clamp(y2, ymin, ymax)};
|
| 84 |
} else {
|
|
| 85 | 298x |
double x2 = c1.x - (ymax - c1.y) / m; |
| 86 | 298x |
return Crossing{Side::TOP, clamp(x2, xmin, xmax), ymax};
|
| 87 |
} |
|
| 88 |
} |
|
| 89 |
} else {
|
|
| 90 | 875x |
if (right) {
|
| 91 |
// 4th quadrant |
|
| 92 | 435x |
double y2 = c1.y - m * (xmax - c1.x); |
| 93 | ||
| 94 | 435x |
if (y2 > ymin) {
|
| 95 | 170x |
return Crossing{Side::RIGHT, xmax, clamp(y2, ymin, ymax)};
|
| 96 |
} else {
|
|
| 97 | 265x |
double x2 = c1.x + (c1.y - ymin) / m; |
| 98 | 265x |
return Crossing{Side::BOTTOM, clamp(x2, xmin, xmax), ymin};
|
| 99 |
} |
|
| 100 |
} else {
|
|
| 101 |
// 3rd quadrant |
|
| 102 | 440x |
double y2 = c1.y - m * (c1.x - xmin); |
| 103 | ||
| 104 | 440x |
if (y2 > ymin) {
|
| 105 | 217x |
return Crossing{Side::LEFT, xmin, clamp(y2, ymin, ymax)};
|
| 106 |
} else {
|
|
| 107 | 223x |
double x2 = c1.x - (c1.y - ymin) / m; |
| 108 | 223x |
return Crossing{Side::BOTTOM, clamp(x2, xmin, xmax), ymin};
|
| 109 |
} |
|
| 110 |
} |
|
| 111 |
} |
|
| 112 | ||
| 113 |
} |
|
| 114 | ||
| 115 | 39x |
bool Box::contains(const Box& b) const {
|
| 116 |
return b.xmin >= xmin && b.xmax <= xmax && b.ymin >= ymin && b.ymax <= ymax; |
|
| 117 |
} |
|
| 118 | ||
| 119 | 2321x |
bool Box::contains(const Coordinate &c) const {
|
| 120 | 2321x |
return c.x >= xmin && c.x <= xmax && c.y >= ymin && c.y <= ymax; |
| 121 |
} |
|
| 122 | ||
| 123 | 15459x |
bool Box::strictly_contains(const Coordinate &c) const {
|
| 124 | 15459x |
return c.x > xmin && c.x < xmax && c.y > ymin && c.y < ymax; |
| 125 |
} |
|
| 126 | ||
| 127 | ! |
std::ostream &operator<<(std::ostream &os, const Box &b) {
|
| 128 | ! |
os << "POLYGON ((";
|
| 129 | ! |
os << b.xmin << " " << b.ymin << ", "; |
| 130 | ! |
os << b.xmax << " " << b.ymin << ", "; |
| 131 | ! |
os << b.xmax << " " << b.ymax << ", "; |
| 132 | ! |
os << b.xmin << " " << b.ymax << ", "; |
| 133 | ! |
os << b.xmin << " " << b.ymin << "))"; |
| 134 | ! |
return os; |
| 135 |
} |
|
| 136 | ||
| 137 |
} |
| 1 |
// Copyright (c) 2018 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#ifndef EXACTEXTRACT_COORDINATE_H |
|
| 15 |
#define EXACTEXTRACT_COORDINATE_H |
|
| 16 | ||
| 17 |
#include <cmath> |
|
| 18 |
#include <iostream> |
|
| 19 | ||
| 20 |
namespace exactextract {
|
|
| 21 | ||
| 22 |
struct Coordinate {
|
|
| 23 |
double x; |
|
| 24 |
double y; |
|
| 25 | ||
| 26 |
Coordinate() = default; |
|
| 27 | ||
| 28 | 23291x |
Coordinate(double p_x, double p_y) : x{p_x}, y{p_y} {}
|
| 29 | ||
| 30 |
bool equals(const Coordinate &other, double tol) const {
|
|
| 31 |
return std::abs(other.x - x) < tol && std::abs(other.y - y) < tol; |
|
| 32 |
} |
|
| 33 | ||
| 34 | 4314x |
bool operator==(const Coordinate &other) const {
|
| 35 | 4314x |
return x == other.x && y == other.y; |
| 36 |
} |
|
| 37 | ||
| 38 | 3621x |
bool operator!=(const Coordinate &other) const {
|
| 39 | 3621x |
return !(*this == other); |
| 40 |
} |
|
| 41 |
}; |
|
| 42 | ||
| 43 |
std::ostream &operator<<(std::ostream &os, const Coordinate &c); |
|
| 44 | ||
| 45 |
} |
|
| 46 | ||
| 47 |
#endif |
| 1 |
// Copyright (c) 2018 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#ifndef EXACTEXTRACT_CELL_CROSSING_H |
|
| 15 |
#define EXACTEXTRACT_CELL_CROSSING_H |
|
| 16 | ||
| 17 |
#include "coordinate.h" |
|
| 18 |
#include "side.h" |
|
| 19 | ||
| 20 |
namespace exactextract {
|
|
| 21 | ||
| 22 |
class Crossing {
|
|
| 23 |
public: |
|
| 24 | 1938x |
Crossing(Side s, double x, double y) : m_side{s}, m_coord{x, y} {}
|
| 25 | ||
| 26 |
Crossing(Side s, const Coordinate &c) : m_side{s}, m_coord{c} {}
|
|
| 27 | ||
| 28 | 1938x |
const Side &side() const {
|
| 29 | 1938x |
return m_side; |
| 30 |
} |
|
| 31 | ||
| 32 | 1938x |
const Coordinate &coord() const {
|
| 33 | 1938x |
return m_coord; |
| 34 |
} |
|
| 35 | ||
| 36 |
private: |
|
| 37 |
Side m_side; |
|
| 38 |
Coordinate m_coord; |
|
| 39 | ||
| 40 |
}; |
|
| 41 | ||
| 42 |
} |
|
| 43 | ||
| 44 |
#endif |
| 1 |
// Copyright (c) 2018 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#include <stdexcept> |
|
| 15 | ||
| 16 |
#include "area.h" |
|
| 17 |
#include "cell.h" |
|
| 18 |
#include "crossing.h" |
|
| 19 |
#include "traversal_areas.h" |
|
| 20 |
#include "geos_utils.h" |
|
| 21 | ||
| 22 |
namespace exactextract {
|
|
| 23 | ||
| 24 | ! |
double Cell::height() const {
|
| 25 | ! |
return m_box.height(); |
| 26 |
} |
|
| 27 | ||
| 28 | ! |
double Cell::width() const {
|
| 29 | ! |
return m_box.width(); |
| 30 |
} |
|
| 31 | ||
| 32 | 1604x |
double Cell::area() const {
|
| 33 | 1604x |
return m_box.area(); |
| 34 |
} |
|
| 35 | ||
| 36 | 2172x |
Side Cell::side(const Coordinate &c) const {
|
| 37 | 2172x |
if (c.x == m_box.xmin) {
|
| 38 | 556x |
return Side::LEFT; |
| 39 | 1616x |
} else if (c.x == m_box.xmax) {
|
| 40 | 653x |
return Side::RIGHT; |
| 41 | 963x |
} else if (c.y == m_box.ymin) {
|
| 42 | 417x |
return Side::BOTTOM; |
| 43 | 546x |
} else if (c.y == m_box.ymax) {
|
| 44 | 512x |
return Side::TOP; |
| 45 |
} |
|
| 46 | ||
| 47 | 34x |
return Side::NONE; |
| 48 |
} |
|
| 49 | ||
| 50 | 2055x |
void Cell::force_exit() {
|
| 51 | 2055x |
if (last_traversal().exited()) {
|
| 52 | 1938x |
return; |
| 53 |
} |
|
| 54 | ||
| 55 | 117x |
const Coordinate &last = last_traversal().last_coordinate(); |
| 56 | ||
| 57 | 117x |
if (location(last) == Location::BOUNDARY) {
|
| 58 | 117x |
last_traversal().force_exit(side(last)); |
| 59 |
} |
|
| 60 |
} |
|
| 61 | ||
| 62 | 15459x |
Cell::Location Cell::location(const Coordinate &c) const {
|
| 63 | 15459x |
if (m_box.strictly_contains(c)) {
|
| 64 | 13138x |
return Cell::Location::INSIDE; |
| 65 |
} |
|
| 66 | ||
| 67 | 2321x |
if (m_box.contains(c)) {
|
| 68 | 383x |
return Cell::Location::BOUNDARY; |
| 69 |
} |
|
| 70 | ||
| 71 | 1938x |
return Cell::Location::OUTSIDE; |
| 72 |
} |
|
| 73 | ||
| 74 | 17397x |
Traversal &Cell::traversal_in_progress() {
|
| 75 | 17397x |
if (m_traversals.empty() || m_traversals[m_traversals.size() - 1].exited()) {
|
| 76 | 2055x |
m_traversals.emplace_back(); |
| 77 |
} |
|
| 78 | ||
| 79 | 17397x |
return m_traversals[m_traversals.size() - 1]; |
| 80 |
} |
|
| 81 | ||
| 82 | 27823x |
Traversal &Cell::last_traversal() {
|
| 83 | 27823x |
return m_traversals.at(m_traversals.size() - 1); |
| 84 |
} |
|
| 85 | ||
| 86 | 17397x |
bool Cell::take(const Coordinate &c) {
|
| 87 | 17397x |
Traversal &t = traversal_in_progress(); |
| 88 | ||
| 89 | 17397x |
if (t.empty()) {
|
| 90 |
//std::cout << "Entering " << m_box << " from " << side(c) << " at " << c << std::endl; |
|
| 91 | ||
| 92 | 2055x |
t.enter(c, side(c)); |
| 93 | 2055x |
return true; |
| 94 |
} |
|
| 95 | ||
| 96 | 15342x |
if (location(c) != Cell::Location::OUTSIDE) {
|
| 97 |
//std::cout << "Still in " << m_box << " with " << c << std::endl; |
|
| 98 | ||
| 99 | 13404x |
t.add(c); |
| 100 | 13404x |
return true; |
| 101 |
} |
|
| 102 | ||
| 103 | 1938x |
Crossing x = m_box.crossing(t.last_coordinate(), c); |
| 104 | 1938x |
t.exit(x.coord(), x.side()); |
| 105 | ||
| 106 |
//std::cout << "Leaving " << m_box << " from " << x.side() << " at " << x.coord(); |
|
| 107 |
//std::cout << " on the way to " << c << std::endl; |
|
| 108 | ||
| 109 | 1938x |
return false; |
| 110 |
} |
|
| 111 | ||
| 112 | 1604x |
double Cell::covered_fraction() const {
|
| 113 |
// Handle the special case of a ring that is enclosed within a |
|
| 114 |
// single pixel of our raster |
|
| 115 |
if (m_traversals.size() == 1 && m_traversals[0].is_closed_ring()) {
|
|
| 116 | ! |
return exactextract::area(m_traversals[0].coords()) / area(); |
| 117 |
} |
|
| 118 | ||
| 119 |
// TODO consider porting in simplified single-traversal area calculations |
|
| 120 |
// from Java code. Do they really make a performance difference? |
|
| 121 |
//if (m_traversals.size() == 1) {
|
|
| 122 |
// double a = area(); |
|
| 123 | ||
| 124 |
// return (a - area_right_of(m_traversals.at(m_traversals.size() - 1))) / a; |
|
| 125 |
//} |
|
| 126 | ||
| 127 | 1604x |
std::vector<const std::vector<Coordinate> *> coord_lists; |
| 128 | ||
| 129 | 3301x |
for (const auto &t : m_traversals) {
|
| 130 | 1697x |
if (!t.traversed() || !t.multiple_unique_coordinates()) {
|
| 131 | 86x |
continue; |
| 132 |
} |
|
| 133 | ||
| 134 | 1611x |
coord_lists.push_back(&t.coords()); |
| 135 |
} |
|
| 136 | ||
| 137 | 1604x |
return left_hand_area(m_box, coord_lists) / area(); |
| 138 |
} |
|
| 139 | ||
| 140 |
#if 0 |
|
| 141 |
Crossing Cell::crossing(const Coordinate & c1, const Coordinate & c2) const {
|
|
| 142 |
Coordinate result(0, 0); |
|
| 143 | ||
| 144 |
if (c2.y > c1.y && segment_intersection(c1, c2, m_box.upper_left(), m_box.upper_right(), result)) {
|
|
| 145 |
return Crossing(Side::TOP, result); |
|
| 146 |
} |
|
| 147 | ||
| 148 |
if (c2.y < c1.y && segment_intersection(c1, c2, m_box.lower_right(), m_box.lower_left(), result)) {
|
|
| 149 |
return Crossing(Side::BOTTOM, result); |
|
| 150 |
} |
|
| 151 | ||
| 152 |
if (c2.x < c1.x && segment_intersection(c1, c2, m_box.lower_left(), m_box.upper_left(), result)) {
|
|
| 153 |
return Crossing(Side::LEFT, result); |
|
| 154 |
} |
|
| 155 | ||
| 156 |
if (c2.x > c1.x && segment_intersection(c1, c2, m_box.lower_right(), m_box.upper_right(), result)) {
|
|
| 157 |
return Crossing(Side::RIGHT, result); |
|
| 158 |
} |
|
| 159 | ||
| 160 |
throw std::runtime_error("Never get here!");
|
|
| 161 |
} |
|
| 162 |
#endif |
|
| 163 | ||
| 164 | ||
| 165 |
} |
| 1 |
// Copyright (c) 2018 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#ifndef EXACTEXTRACT_TRAVERSAL_H |
|
| 15 |
#define EXACTEXTRACT_TRAVERSAL_H |
|
| 16 | ||
| 17 |
#include <vector> |
|
| 18 | ||
| 19 |
#include "coordinate.h" |
|
| 20 |
#include "side.h" |
|
| 21 | ||
| 22 |
namespace exactextract {
|
|
| 23 | ||
| 24 |
class Traversal {
|
|
| 25 |
public: |
|
| 26 | 2055x |
Traversal() : m_entry{Side::NONE}, m_exit{Side::NONE} {}
|
| 27 | ||
| 28 |
bool is_closed_ring() const; |
|
| 29 | ||
| 30 |
bool empty() const; |
|
| 31 | ||
| 32 |
bool entered() const; |
|
| 33 | ||
| 34 |
bool exited() const; |
|
| 35 | ||
| 36 |
bool traversed() const; |
|
| 37 | ||
| 38 |
bool multiple_unique_coordinates() const; |
|
| 39 | ||
| 40 |
void enter(const Coordinate &c, Side s); |
|
| 41 | ||
| 42 |
void exit(const Coordinate &c, Side s); |
|
| 43 | ||
| 44 |
Side entry_side() const { return m_entry; }
|
|
| 45 | ||
| 46 | 2055x |
Side exit_side() const { return m_exit; }
|
| 47 | ||
| 48 |
const Coordinate &last_coordinate() const; |
|
| 49 | ||
| 50 |
const Coordinate &exit_coordinate() const; |
|
| 51 | ||
| 52 |
void add(const Coordinate &c); |
|
| 53 | ||
| 54 | 117x |
void force_exit(Side s) { m_exit = s; }
|
| 55 | ||
| 56 | 1645x |
const std::vector<Coordinate> &coords() const { return m_coords; }
|
| 57 | ||
| 58 |
private: |
|
| 59 |
std::vector<Coordinate> m_coords; |
|
| 60 |
Side m_entry; |
|
| 61 |
Side m_exit; |
|
| 62 |
}; |
|
| 63 | ||
| 64 |
} |
|
| 65 | ||
| 66 |
#endif |
| 1 |
// Copyright (c) 2018 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#include "coordinate.h" |
|
| 15 | ||
| 16 |
namespace exactextract {
|
|
| 17 | ||
| 18 | ! |
std::ostream &operator<<(std::ostream &os, const Coordinate &c) {
|
| 19 | ! |
os << "POINT (" << c.x << " " << c.y << ")";
|
| 20 | ! |
return os; |
| 21 |
} |
|
| 22 | ||
| 23 |
} |
| 1 |
// Copyright (c) 2018-2020 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#include <geos_c.h> |
|
| 15 | ||
| 16 |
#include "grid.h" |
|
| 17 |
#include "floodfill.h" |
|
| 18 |
#include "geos_utils.h" |
|
| 19 | ||
| 20 |
namespace exactextract {
|
|
| 21 | ||
| 22 | 117x |
FloodFill::FloodFill(GEOSContextHandle_t context, const GEOSGeometry *g, const Grid<bounded_extent> &extent) : |
| 23 |
m_extent{extent},
|
|
| 24 |
m_geos_context{context},
|
|
| 25 |
m_g{nullptr},
|
|
| 26 | 117x |
m_pg{nullptr} {
|
| 27 | 117x |
geom_ptr_r ring_copy = geos_ptr(context, GEOSGeom_clone_r(context, g)); |
| 28 | 117x |
m_g = geos_ptr(context, GEOSGeom_createPolygon_r(context, ring_copy.release(), nullptr, 0)); |
| 29 | 117x |
m_pg = GEOSPrepare_ptr(context, m_g.get()); |
| 30 |
} |
|
| 31 | ||
| 32 | 76x |
bool FloodFill::cell_is_inside(size_t i, size_t j) const {
|
| 33 | 76x |
double x = m_extent.x_for_col(j); |
| 34 | 76x |
double y = m_extent.y_for_row(i); |
| 35 | ||
| 36 | 76x |
auto point = GEOSGeom_createPoint_ptr(m_geos_context, x, y); |
| 37 | ||
| 38 | 152x |
return GEOSPreparedContains_r(m_geos_context, m_pg.get(), point.get()); |
| 39 |
} |
|
| 40 | ||
| 41 |
} |
| 1 |
// Copyright (c) 2018-2020 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#ifndef EXACTEXTRACT_GEOS_UTILS_H |
|
| 15 |
#define EXACTEXTRACT_GEOS_UTILS_H |
|
| 16 | ||
| 17 |
#include <algorithm> |
|
| 18 |
#include <functional> |
|
| 19 |
#include <limits> |
|
| 20 |
#include <memory> |
|
| 21 |
#include <stdexcept> |
|
| 22 |
#include <vector> |
|
| 23 | ||
| 24 |
#include <geos_c.h> |
|
| 25 | ||
| 26 |
#define HAVE_370 (GEOS_VERSION_MAJOR >= 3 && GEOS_VERSION_MINOR >= 7) |
|
| 27 |
#define HAVE_380 (GEOS_VERSION_MAJOR >= 3 && GEOS_VERSION_MINOR >= 8) |
|
| 28 | ||
| 29 |
#include "box.h" |
|
| 30 |
#include "coordinate.h" |
|
| 31 | ||
| 32 |
namespace exactextract {
|
|
| 33 | ||
| 34 |
using geom_ptr_r = std::unique_ptr<GEOSGeometry, std::function<void(GEOSGeometry*)>>; |
|
| 35 |
using tree_ptr_r = std::unique_ptr<GEOSSTRtree, std::function<void(GEOSSTRtree*)>>; |
|
| 36 |
using seq_ptr_r = std::unique_ptr<GEOSCoordSequence, std::function<void(GEOSCoordSequence*)>>; |
|
| 37 |
using prep_geom_ptr_r = std::unique_ptr<const GEOSPreparedGeometry, std::function<void(const GEOSPreparedGeometry*)>>; |
|
| 38 | ||
| 39 | 310x |
inline geom_ptr_r geos_ptr(GEOSContextHandle_t context, GEOSGeometry* geom) {
|
| 40 | 193x |
auto deleter = [context](GEOSGeometry* g){ GEOSGeom_destroy_r(context, g); };
|
| 41 | 310x |
return geom_ptr_r{geom, deleter};
|
| 42 |
} |
|
| 43 | ||
| 44 |
inline tree_ptr_r geos_ptr(GEOSContextHandle_t context, GEOSSTRtree* tree) {
|
|
| 45 |
auto deleter = [context](GEOSSTRtree_t* t){ GEOSSTRtree_destroy_r(context, t); };
|
|
| 46 |
return tree_ptr_r{tree, deleter};
|
|
| 47 |
} |
|
| 48 | ||
| 49 | ! |
inline seq_ptr_r geos_ptr(GEOSContextHandle_t context, GEOSCoordSequence* seq) {
|
| 50 | ! |
auto deleter = [context](GEOSCoordSequence* s){ GEOSCoordSeq_destroy_r(context, s); };
|
| 51 | ! |
return seq_ptr_r{seq, deleter};
|
| 52 |
} |
|
| 53 | ||
| 54 |
inline prep_geom_ptr_r |
|
| 55 | 117x |
GEOSPrepare_ptr(GEOSContextHandle_t context, const GEOSGeometry *g) {
|
| 56 | 117x |
auto deleter = [context](const GEOSPreparedGeometry* pg) { GEOSPreparedGeom_destroy_r(context, pg); };
|
| 57 | 117x |
return prep_geom_ptr_r{GEOSPrepare_r(context, g), deleter};
|
| 58 |
} |
|
| 59 | ||
| 60 |
inline seq_ptr_r |
|
| 61 |
GEOSCoordSeq_create_ptr(GEOSContextHandle_t context, unsigned int size, unsigned int dims) {
|
|
| 62 |
return geos_ptr(context, GEOSCoordSeq_create_r(context, size, dims)); |
|
| 63 |
} |
|
| 64 | ||
| 65 |
inline geom_ptr_r |
|
| 66 | 76x |
GEOSGeom_createPoint_ptr(GEOSContextHandle_t context, double x, double y) {
|
| 67 |
#if HAVE_380 |
|
| 68 | 76x |
return geos_ptr(context, GEOSGeom_createPointFromXY_r(context, x, y)); |
| 69 |
#else |
|
| 70 |
auto seq = GEOSCoordSeq_create_ptr(context, 1, 2); |
|
| 71 |
GEOSCoordSeq_setX_r(context, seq.get(), 0, x); |
|
| 72 |
GEOSCoordSeq_setY_r(context, seq.get(), 0, y); |
|
| 73 |
return geos_ptr(context, GEOSGeom_createPoint_r(context, seq.release())); |
|
| 74 |
#endif |
|
| 75 |
} |
|
| 76 | ||
| 77 |
inline geom_ptr_r |
|
| 78 |
GEOSGeom_createLineString_ptr(GEOSContextHandle_t context, GEOSCoordSequence *seq) {
|
|
| 79 |
return geos_ptr(context, GEOSGeom_createLineString_r(context, seq)); |
|
| 80 |
} |
|
| 81 | ||
| 82 | 164x |
inline unsigned int geos_get_num_points(GEOSContextHandle_t context, const GEOSCoordSequence *s) {
|
| 83 |
unsigned int result; |
|
| 84 | 164x |
if (!GEOSCoordSeq_getSize_r(context, s, &result)) {
|
| 85 | ! |
throw std::runtime_error("Error calling GEOSCoordSeq_getSize_r.");
|
| 86 |
} |
|
| 87 | 164x |
return result; |
| 88 |
} |
|
| 89 | ||
| 90 |
inline geom_ptr_r |
|
| 91 |
GEOSGeom_read_r(GEOSContextHandle_t context, const std::string &s) {
|
|
| 92 |
return geos_ptr(context, GEOSGeomFromWKT_r(context, s.c_str())); |
|
| 93 |
} |
|
| 94 |
geom_ptr_r geos_make_box_polygon(GEOSContextHandle_t context, const Box & b); |
|
| 95 | ||
| 96 |
Box geos_get_box(GEOSContextHandle_t context, const GEOSGeometry* g); |
|
| 97 | ||
| 98 |
std::vector<Box> geos_get_component_boxes(GEOSContextHandle_t context, const GEOSGeometry* g); |
|
| 99 | ||
| 100 |
bool segment_intersection(GEOSContextHandle_t context, const Coordinate &a0, const Coordinate &a1, const Coordinate &b0, const Coordinate &b1, |
|
| 101 |
Coordinate &result); |
|
| 102 | ||
| 103 |
bool geos_is_ccw(GEOSContextHandle_t context, const GEOSCoordSequence *s); |
|
| 104 | ||
| 105 |
std::vector<Coordinate> read(GEOSContextHandle_t context, const GEOSCoordSequence *s); |
|
| 106 | ||
| 107 |
} |
|
| 108 | ||
| 109 |
#endif //RASTER_OVERLAY_CPP_GEOS_UTILS_H |
| 1 |
// Copyright (c) 2018-2020 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#include "geos_utils.h" |
|
| 15 | ||
| 16 |
#if !HAVE_380 |
|
| 17 |
static inline int GEOSCoordSeq_setXY_r(GEOSContextHandle_t context, |
|
| 18 |
GEOSCoordSequence* seq, |
|
| 19 |
unsigned int idx, |
|
| 20 |
double x, |
|
| 21 |
double y) {
|
|
| 22 |
return GEOSCoordSeq_setX_r(context, seq, idx, x) && GEOSCoordSeq_setY_r(context, seq, idx, y); |
|
| 23 |
} |
|
| 24 |
#endif |
|
| 25 | ||
| 26 |
namespace exactextract {
|
|
| 27 | ||
| 28 | ! |
geom_ptr_r geos_make_box_polygon(GEOSContextHandle_t context, const Box & b) {
|
| 29 | ! |
auto seq = geos_ptr(context, GEOSCoordSeq_create_r(context, 5, 2)); |
| 30 | ||
| 31 | ! |
GEOSCoordSeq_setXY_r(context, seq.get(), 0, b.xmin, b.ymin); |
| 32 | ! |
GEOSCoordSeq_setXY_r(context, seq.get(), 1, b.xmax, b.ymin); |
| 33 | ! |
GEOSCoordSeq_setXY_r(context, seq.get(), 2, b.xmax, b.ymax); |
| 34 | ! |
GEOSCoordSeq_setXY_r(context, seq.get(), 3, b.xmin, b.ymax); |
| 35 | ! |
GEOSCoordSeq_setXY_r(context, seq.get(), 4, b.xmin, b.ymin); |
| 36 | ||
| 37 | ! |
auto shell = geos_ptr(context, GEOSGeom_createLinearRing_r(context, seq.release())); |
| 38 | ||
| 39 | ! |
return geos_ptr(context, GEOSGeom_createPolygon_r(context, shell.release(), nullptr, 0)); |
| 40 |
} |
|
| 41 | ||
| 42 | ! |
bool segment_intersection( |
| 43 |
GEOSContextHandle_t context, |
|
| 44 |
const Coordinate &a0, |
|
| 45 |
const Coordinate &a1, |
|
| 46 |
const Coordinate &b0, |
|
| 47 |
const Coordinate &b1, |
|
| 48 |
Coordinate &result) {
|
|
| 49 |
#if HAVE_370 |
|
| 50 | ! |
int code = GEOSSegmentIntersection_r(context, |
| 51 | ! |
a0.x, a0.y, |
| 52 | ! |
a1.x, a1.y, |
| 53 | ! |
b0.x, b0.y, |
| 54 | ! |
b1.x, b1.y, |
| 55 |
&result.x, &result.y); |
|
| 56 | ! |
if (!code) {
|
| 57 | ! |
throw std::runtime_error("Error in GEOSSegmentIntersection_r");
|
| 58 |
} |
|
| 59 | ||
| 60 | ! |
return code == 1; |
| 61 |
#else |
|
| 62 |
auto seqa = GEOSCoordSeq_create_ptr(context, 2, 2); |
|
| 63 |
auto seqb = GEOSCoordSeq_create_ptr(context, 2, 2); |
|
| 64 | ||
| 65 |
GEOSCoordSeq_setX_r(context, seqa.get(), 0, a0.x); |
|
| 66 |
GEOSCoordSeq_setY_r(context, seqa.get(), 0, a0.y); |
|
| 67 |
GEOSCoordSeq_setX_r(context, seqa.get(), 1, a1.x); |
|
| 68 |
GEOSCoordSeq_setY_r(context, seqa.get(), 1, a1.y); |
|
| 69 | ||
| 70 |
GEOSCoordSeq_setX_r(context, seqb.get(), 0, b0.x); |
|
| 71 |
GEOSCoordSeq_setY_r(context, seqb.get(), 0, b0.y); |
|
| 72 |
GEOSCoordSeq_setX_r(context, seqb.get(), 1, b1.x); |
|
| 73 |
GEOSCoordSeq_setY_r(context, seqb.get(), 1, b1.y); |
|
| 74 | ||
| 75 |
auto geom_a = GEOSGeom_createLineString_ptr(context, seqa.release()); |
|
| 76 |
auto geom_b = GEOSGeom_createLineString_ptr(context, seqb.release()); |
|
| 77 | ||
| 78 |
geom_ptr_r intersection = geos_ptr(context, GEOSIntersection_r(context, geom_a.get(), geom_b.get())); |
|
| 79 | ||
| 80 |
if (GEOSisEmpty_r(context, intersection.get())) {
|
|
| 81 |
return false; |
|
| 82 |
} |
|
| 83 | ||
| 84 |
if (GEOSGeomTypeId_r(context, intersection.get()) != GEOS_POINT) {
|
|
| 85 |
return false; |
|
| 86 |
} |
|
| 87 | ||
| 88 |
GEOSGeomGetX_r(context, intersection.get(), &result.x); |
|
| 89 |
GEOSGeomGetY_r(context, intersection.get(), &result.y); |
|
| 90 | ||
| 91 |
return true; |
|
| 92 |
#endif |
|
| 93 |
} |
|
| 94 | ||
| 95 | 536x |
Box geos_get_box(GEOSContextHandle_t context, const GEOSGeometry* g) {
|
| 96 |
double xmin, ymin, xmax, ymax; |
|
| 97 | ||
| 98 |
#if HAVE_370 |
|
| 99 |
if (!(GEOSGeom_getXMin_r(context, g, &xmin) && |
|
| 100 | 536x |
GEOSGeom_getYMin_r(context, g, &ymin) && |
| 101 | 536x |
GEOSGeom_getXMax_r(context, g, &xmax) && |
| 102 | 536x |
GEOSGeom_getYMax_r(context, g, &ymax))) {
|
| 103 | ! |
throw std::runtime_error("Error getting geometry extent.");
|
| 104 |
} |
|
| 105 |
#else |
|
| 106 |
geom_ptr_r env = geos_ptr(context, GEOSEnvelope_r(context, g)); |
|
| 107 | ||
| 108 |
const GEOSGeometry* ring = GEOSGetExteriorRing_r(context, env.get()); |
|
| 109 |
const GEOSCoordSequence* seq = GEOSGeom_getCoordSeq_r(context, ring); |
|
| 110 | ||
| 111 |
xmin = std::numeric_limits<double>::max(); |
|
| 112 |
ymin = std::numeric_limits<double>::max(); |
|
| 113 |
xmax = std::numeric_limits<double>::lowest(); |
|
| 114 |
ymax = std::numeric_limits<double>::lowest(); |
|
| 115 | ||
| 116 |
for (unsigned int i = 0; i < 4; i++) {
|
|
| 117 |
double x, y; |
|
| 118 | ||
| 119 |
if (!GEOSCoordSeq_getX_r(context, seq, i, &x) || !GEOSCoordSeq_getY_r(context, seq, i, &y)) {
|
|
| 120 |
throw std::runtime_error("Error reading coordinates.");
|
|
| 121 |
} |
|
| 122 | ||
| 123 |
xmin = std::min(xmin, x); |
|
| 124 |
ymin = std::min(ymin, y); |
|
| 125 |
xmax = std::max(xmax, x); |
|
| 126 |
ymax = std::max(ymax, y); |
|
| 127 |
} |
|
| 128 |
#endif |
|
| 129 | 1072x |
return {xmin, ymin, xmax, ymax};
|
| 130 |
} |
|
| 131 | ||
| 132 | 167x |
std::vector<Box> geos_get_component_boxes(GEOSContextHandle_t context, const GEOSGeometry* g) {
|
| 133 | 167x |
size_t n = static_cast<size_t>(GEOSGetNumGeometries_r(context, g)); |
| 134 | 167x |
std::vector<Box> boxes; |
| 135 | 167x |
boxes.reserve(n); |
| 136 | ||
| 137 | 337x |
for (size_t i = 0; i < n; i++) {
|
| 138 | 170x |
boxes.push_back(geos_get_box(context, GEOSGetGeometryN_r(context, g, i))); |
| 139 |
} |
|
| 140 | ||
| 141 | 167x |
return boxes; |
| 142 |
} |
|
| 143 | ||
| 144 | 117x |
bool geos_is_ccw(GEOSContextHandle_t context, const GEOSCoordSequence *s) {
|
| 145 |
#if HAVE_370 |
|
| 146 |
char result; |
|
| 147 | 117x |
if (!GEOSCoordSeq_isCCW_r(context, s, &result)) {
|
| 148 | ! |
throw std::runtime_error("Error calling GEOSCoordSeq_isCCW_r.");
|
| 149 |
} |
|
| 150 | 117x |
return result; |
| 151 |
#else |
|
| 152 |
std::vector<Coordinate> coords = read(context, s); |
|
| 153 | ||
| 154 |
if (coords.size() < 4) {
|
|
| 155 |
throw std::runtime_error("Ring has fewer than 4 points, so orientation cannot be determined.");
|
|
| 156 |
} |
|
| 157 | ||
| 158 |
// find highest point |
|
| 159 |
size_t hi_index = (size_t) std::distance( |
|
| 160 |
coords.begin(), |
|
| 161 |
std::max_element(coords.begin(), coords.end(), [](const auto& a, const auto&b) {
|
|
| 162 |
return a.y < b.y; |
|
| 163 |
}) |
|
| 164 |
); |
|
| 165 | ||
| 166 |
// find distinct point before highest point |
|
| 167 |
size_t i_prev = hi_index; |
|
| 168 |
do {
|
|
| 169 |
if (i_prev == 0) {
|
|
| 170 |
i_prev = coords.size() - 1; |
|
| 171 |
} else {
|
|
| 172 |
i_prev--; |
|
| 173 |
} |
|
| 174 |
} while (i_prev != hi_index && coords[i_prev] == coords[hi_index]); |
|
| 175 | ||
| 176 |
// find distinct point after highest point |
|
| 177 |
size_t i_next = hi_index; |
|
| 178 |
do {
|
|
| 179 |
i_next = (i_next + 1) % coords.size(); |
|
| 180 |
} while (i_next != hi_index && coords[i_next] == coords[hi_index]); |
|
| 181 | ||
| 182 |
Coordinate& a = coords[i_prev]; |
|
| 183 |
Coordinate& b = coords[hi_index]; |
|
| 184 |
Coordinate& c = coords[i_next]; |
|
| 185 | ||
| 186 |
if (a == b || b == c || a == c) {
|
|
| 187 |
return false; |
|
| 188 |
} |
|
| 189 | ||
| 190 |
int disc = GEOSOrientationIndex_r(context, a.x, a.y, b.x, b.y, c.x, c.y); |
|
| 191 | ||
| 192 |
if (disc == 0) {
|
|
| 193 |
// poly is CCW if prev x is right of next x |
|
| 194 |
return (a.x > b.x); |
|
| 195 |
} else {
|
|
| 196 |
// if area is positive, points are ordered CCW |
|
| 197 |
return disc > 0; |
|
| 198 |
} |
|
| 199 |
#endif |
|
| 200 |
} |
|
| 201 | ||
| 202 | 51x |
std::vector<Coordinate> read(GEOSContextHandle_t context, const GEOSCoordSequence *s) {
|
| 203 |
unsigned int size; |
|
| 204 | ||
| 205 | 51x |
if (!GEOSCoordSeq_getSize_r(context, s, &size)) {
|
| 206 | ! |
throw std::runtime_error("Error calling GEOSCoordSeq_getSize.");
|
| 207 |
} |
|
| 208 | ||
| 209 | 51x |
std::vector<Coordinate> coords{size};
|
| 210 | ||
| 211 | 306x |
for (unsigned int i = 0; i < size; i++) {
|
| 212 |
if (!GEOSCoordSeq_getX_r(context, s, i, &(coords[i].x)) || !GEOSCoordSeq_getY_r(context, s, i, &(coords[i].y))) {
|
|
| 213 | ! |
throw std::runtime_error("Error reading coordinates.");
|
| 214 |
} |
|
| 215 |
} |
|
| 216 | ||
| 217 | 102x |
return coords; |
| 218 |
} |
|
| 219 | ||
| 220 |
} |
| 1 |
// Copyright (c) 2018 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#include "grid.h" |
|
| 15 | ||
| 16 |
namespace exactextract {
|
|
| 17 | 500250x |
Box grid_cell(const Grid<bounded_extent> & grid, size_t row, size_t col) {
|
| 18 |
// The ternary clauses below are used to make sure that the cells along |
|
| 19 |
// the right and bottom edges of our grid are slightly larger than m_dx,dy |
|
| 20 |
// if needed to make sure that we capture our whole extent. This is necessary |
|
| 21 |
// because xmin + nx*m_dx may be less than xmax because of floating point |
|
| 22 |
// errors. |
|
| 23 |
return {
|
|
| 24 | 500250x |
grid.xmin() + col * grid.dx(), |
| 25 | 500250x |
row == (grid.rows() - 1) ? grid.ymin() : (grid.ymax() - (row + 1) * grid.dy()), |
| 26 | 1000500x |
col == (grid.cols() - 1) ? grid.xmax() : (grid.xmin() + (col + 1) * grid.dx()), |
| 27 | 500250x |
grid.ymax() - row * grid.dy() |
| 28 | ||
| 29 |
}; |
|
| 30 |
} |
|
| 31 | ||
| 32 | 3055885x |
Box grid_cell(const Grid<infinite_extent> & grid, size_t row, size_t col) {
|
| 33 |
double xmin, xmax, ymin, ymax; |
|
| 34 | ||
| 35 | 3055885x |
if (col == 0) {
|
| 36 | 84x |
xmin = std::numeric_limits<double>::lowest(); |
| 37 | 3055801x |
} else if (col == grid.cols() - 1) {
|
| 38 | 678618x |
xmin = grid.xmax(); // because rightmost col of regular grid may have different width from others |
| 39 |
} else {
|
|
| 40 | 2377183x |
xmin = grid.xmin() + (col - 1) * grid.dx(); |
| 41 |
} |
|
| 42 | ||
| 43 | 3055885x |
switch(grid.cols() - col) {
|
| 44 | 678618x |
case 1: xmax = std::numeric_limits<double>::max(); break; |
| 45 | 2249065x |
case 2: xmax = grid.xmax(); break; |
| 46 | 128202x |
default: xmax = grid.xmin() + col*grid.dx(); |
| 47 |
} |
|
| 48 | ||
| 49 | 3055885x |
if (row == 0) {
|
| 50 | 132x |
ymax = std::numeric_limits<double>::max(); |
| 51 | 3055753x |
} else if (row == grid.rows() - 1) {
|
| 52 | 584023x |
ymax = grid.ymin(); // because bottom row of regular grid may have different height from others |
| 53 |
} else {
|
|
| 54 | 2471730x |
ymax = grid.ymax() - (row - 1) * grid.dy(); |
| 55 |
} |
|
| 56 | ||
| 57 | 3055885x |
switch(grid.rows() - row) {
|
| 58 | 584023x |
case 1: ymin = std::numeric_limits<double>::lowest(); break; |
| 59 | 2201765x |
case 2: ymin = grid.ymin(); break; |
| 60 | 270097x |
default: ymin = grid.ymax() - row*grid.dy(); |
| 61 |
} |
|
| 62 | ||
| 63 | 3055885x |
return { xmin, ymin, xmax, ymax };
|
| 64 |
} |
|
| 65 | ||
| 66 | 382859x |
Grid<infinite_extent> make_infinite(const Grid<bounded_extent> & grid) {
|
| 67 | 382859x |
return { grid.extent(), grid.dx(), grid.dy() };
|
| 68 |
} |
|
| 69 | ||
| 70 | 500534x |
Grid<bounded_extent> make_finite(const Grid<infinite_extent> & grid) {
|
| 71 | 500534x |
return { grid.extent(), grid.dx(), grid.dy() };
|
| 72 |
} |
|
| 73 | ||
| 74 | 75x |
std::vector<Grid<bounded_extent>> subdivide(const Grid<bounded_extent> & grid, size_t max_size) {
|
| 75 | 75x |
if (grid.size() < max_size) {
|
| 76 | 74x |
return { grid };
|
| 77 |
} |
|
| 78 | ||
| 79 | 1x |
size_t cols_per_block = std::min(max_size, grid.cols()); |
| 80 | 1x |
size_t rows_per_block = max_size / cols_per_block; |
| 81 | ||
| 82 | 1x |
size_t col_blocks = (grid.cols() - 1) / cols_per_block + 1; |
| 83 | 1x |
size_t row_blocks = (grid.rows() - 1) / rows_per_block + 1; |
| 84 | ||
| 85 | 2x |
std::vector<Grid<bounded_extent>> subgrids; |
| 86 | 7x |
for (size_t i = 0; i < row_blocks; i++) {
|
| 87 | 42x |
for (size_t j = 0; j < col_blocks; j++) {
|
| 88 | 36x |
double xmin = grid.xmin() + grid.dx()*cols_per_block*j; |
| 89 | 36x |
double xmax = j == (col_blocks - 1) ? grid.xmax() : (grid.xmin() + grid.dx()*cols_per_block*(j+1)); |
| 90 | 36x |
double ymax = grid.ymax() - grid.dy()*rows_per_block*i; |
| 91 | 36x |
double ymin = i == (row_blocks - 1) ? grid.ymin() : (grid.ymax() - grid.dy()*rows_per_block*(i+1)); |
| 92 | ||
| 93 | 36x |
Box reduced = {xmin, ymin, xmax, ymax};
|
| 94 | 36x |
subgrids.emplace_back(reduced, grid.dx(), grid.dy()); |
| 95 |
} |
|
| 96 |
} |
|
| 97 | ||
| 98 | 1x |
return subgrids; |
| 99 |
} |
|
| 100 |
} |
| 1 |
// Copyright (c) 2018 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#include <stdexcept> |
|
| 15 | ||
| 16 |
#include "perimeter_distance.h" |
|
| 17 | ||
| 18 |
namespace exactextract {
|
|
| 19 | ||
| 20 | 3222x |
double perimeter_distance(double xmin, double ymin, double xmax, double ymax, double x, double y) {
|
| 21 | 3222x |
if (x == xmin) {
|
| 22 |
// Left |
|
| 23 | 865x |
return y - ymin; |
| 24 |
} |
|
| 25 | ||
| 26 | 2357x |
if (y == ymax) {
|
| 27 |
// Top |
|
| 28 | 775x |
return (ymax - ymin) + x - xmin; |
| 29 |
} |
|
| 30 | ||
| 31 | 1582x |
if (x == xmax) {
|
| 32 |
// Right |
|
| 33 | 863x |
return (xmax - xmin) + (ymax - ymin) + ymax - y; |
| 34 |
} |
|
| 35 | ||
| 36 | 719x |
if (y == ymin) {
|
| 37 |
// Bottom |
|
| 38 | 719x |
return (xmax - xmin) + 2 * (ymax - ymin) + (xmax - x); |
| 39 |
} |
|
| 40 | ||
| 41 | ! |
throw std::runtime_error("Never get here");
|
| 42 |
} |
|
| 43 | ||
| 44 | ! |
double perimeter_distance(double xmin, double ymin, double xmax, double ymax, const Coordinate &c) {
|
| 45 | ! |
return perimeter_distance(xmin, ymin, xmax, ymax, c.x, c.y); |
| 46 |
} |
|
| 47 | ||
| 48 | 3222x |
double perimeter_distance(const Box &b, const Coordinate &c) {
|
| 49 | 3222x |
return perimeter_distance(b.xmin, b.ymin, b.xmax, b.ymax, c.x, c.y); |
| 50 |
} |
|
| 51 | ||
| 52 | 18493x |
double perimeter_distance_ccw(double measure1, double measure2, double perimeter) {
|
| 53 | 18493x |
if (measure2 <= measure1) {
|
| 54 | 10790x |
return measure1 - measure2; |
| 55 |
} |
|
| 56 | 7703x |
return perimeter + measure1 - measure2; |
| 57 |
} |
|
| 58 | ||
| 59 |
} |
| 1 |
// Copyright (c) 2018 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#ifndef EXACTEXTRACT_CELL_H |
|
| 15 |
#define EXACTEXTRACT_CELL_H |
|
| 16 | ||
| 17 |
#include <memory> |
|
| 18 | ||
| 19 |
#include "box.h" |
|
| 20 |
#include "crossing.h" |
|
| 21 |
#include "coordinate.h" |
|
| 22 |
#include "side.h" |
|
| 23 |
#include "traversal.h" |
|
| 24 | ||
| 25 |
namespace exactextract {
|
|
| 26 | ||
| 27 |
class Cell {
|
|
| 28 | ||
| 29 |
public: |
|
| 30 | ||
| 31 |
Cell(double xmin, double ymin, double xmax, double ymax) : |
|
| 32 |
m_box{xmin, ymin, xmax, ymax} {}
|
|
| 33 | ||
| 34 | 1933x |
explicit Cell(const Box & b) : m_box{b} {}
|
| 35 | ||
| 36 |
void force_exit(); |
|
| 37 | ||
| 38 |
double width() const; |
|
| 39 | ||
| 40 |
double height() const; |
|
| 41 | ||
| 42 |
double area() const; |
|
| 43 | ||
| 44 |
double covered_fraction() const; |
|
| 45 | ||
| 46 |
Traversal &last_traversal(); |
|
| 47 | ||
| 48 |
const Box &box() const { return m_box; }
|
|
| 49 | ||
| 50 |
bool take(const Coordinate &c); |
|
| 51 | ||
| 52 |
private: |
|
| 53 |
enum class Location {
|
|
| 54 |
INSIDE, OUTSIDE, BOUNDARY |
|
| 55 |
}; |
|
| 56 | ||
| 57 |
Box m_box; |
|
| 58 | ||
| 59 |
std::vector<Traversal> m_traversals; |
|
| 60 | ||
| 61 |
Side side(const Coordinate &c) const; |
|
| 62 | ||
| 63 |
Location location(const Coordinate &c) const; |
|
| 64 | ||
| 65 |
Traversal &traversal_in_progress(); |
|
| 66 |
}; |
|
| 67 | ||
| 68 |
} |
|
| 69 | ||
| 70 |
#endif |
| 1 |
// Copyright (c) 2018-2020 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#ifndef EXACTEXTRACT_FLOODFILL_H |
|
| 15 |
#define EXACTEXTRACT_FLOODFILL_H |
|
| 16 | ||
| 17 |
#include <queue> |
|
| 18 | ||
| 19 |
#include <geos_c.h> |
|
| 20 | ||
| 21 |
#include "grid.h" |
|
| 22 |
#include "geos_utils.h" |
|
| 23 |
#include "matrix.h" |
|
| 24 | ||
| 25 |
namespace exactextract {
|
|
| 26 | ||
| 27 |
template<typename T> |
|
| 28 |
struct fill_values {
|
|
| 29 |
static T EXTERIOR; |
|
| 30 |
}; |
|
| 31 | ||
| 32 |
template<> |
|
| 33 |
struct fill_values<float> {
|
|
| 34 |
static constexpr float EXTERIOR{0.0f}; // Cell is known to be entirely outside the polygon
|
|
| 35 |
static constexpr float INTERIOR{1.0f}; // Cell is known to be entirely within the polygon
|
|
| 36 |
static constexpr float FILLABLE{-1.0f}; // Cell location relative to polygon unknown, but
|
|
| 37 |
// can be determined by fill. |
|
| 38 |
static constexpr float UNKNOWN{-2.0f}; // Cell location relative to polygon unknown
|
|
| 39 |
// and cannot be determined from a flood fill |
|
| 40 |
// (must be explicitly tested) |
|
| 41 | ||
| 42 |
}; |
|
| 43 | ||
| 44 |
class FloodFill {
|
|
| 45 | ||
| 46 |
public: |
|
| 47 |
FloodFill(GEOSContextHandle_t context, const GEOSGeometry *g, const Grid<bounded_extent> &extent); |
|
| 48 | ||
| 49 |
template<typename T> |
|
| 50 |
void flood(Matrix<T> &arr) const; |
|
| 51 | ||
| 52 |
bool cell_is_inside(size_t i, size_t j) const; |
|
| 53 | ||
| 54 |
private: |
|
| 55 |
Grid<bounded_extent> m_extent; |
|
| 56 |
GEOSContextHandle_t m_geos_context; |
|
| 57 |
geom_ptr_r m_g; |
|
| 58 |
prep_geom_ptr_r m_pg; |
|
| 59 |
}; |
|
| 60 | ||
| 61 |
template<typename T> |
|
| 62 | 76x |
void flood_from_pixel(Matrix<T> &arr, size_t i, size_t j, T fill_value) {
|
| 63 | 152x |
std::queue<std::pair<size_t, size_t> > locations; |
| 64 | ||
| 65 | 76x |
locations.emplace(i, j); |
| 66 | ||
| 67 | 29541x |
while (!locations.empty()) {
|
| 68 | 29465x |
i = locations.front().first; |
| 69 | 29465x |
j = locations.front().second; |
| 70 | 29465x |
locations.pop(); |
| 71 | ||
| 72 | 29465x |
if (arr(i, j) == fill_value) {
|
| 73 | 18102x |
continue; |
| 74 |
} |
|
| 75 | ||
| 76 |
// Left |
|
| 77 | 11363x |
if (j > 0 && arr(i, j - 1) == fill_values<T>::FILLABLE) {
|
| 78 | 10623x |
locations.emplace(i, j - 1); |
| 79 |
} |
|
| 80 | ||
| 81 | 11363x |
auto j0 = j; |
| 82 | ||
| 83 |
// Fill along this row until we hit something |
|
| 84 | 30932x |
for (; j < arr.cols() && arr(i, j) == fill_values<T>::FILLABLE; j++) {
|
| 85 | 19569x |
arr(i, j) = fill_value; |
| 86 |
} |
|
| 87 | ||
| 88 | 11363x |
auto j1 = j; |
| 89 | ||
| 90 |
// Initiate scanlines above our current row |
|
| 91 | 11363x |
if (i > 0) {
|
| 92 | 30639x |
for (j = j0; j < j1; j++) {
|
| 93 |
// Up |
|
| 94 | 19313x |
if (arr(i - 1, j) == fill_values<T>::FILLABLE) {
|
| 95 | ! |
locations.emplace(i - 1, j); |
| 96 |
} |
|
| 97 |
} |
|
| 98 |
} |
|
| 99 | ||
| 100 |
// Initiate scanlines below our current row |
|
| 101 | 11363x |
if (i < arr.rows() - 1) {
|
| 102 | 30486x |
for (j = j0; j < j1; j++) {
|
| 103 |
// Down |
|
| 104 | 19328x |
if (arr(i + 1, j) == fill_values<T>::FILLABLE) {
|
| 105 | 18766x |
locations.emplace(i + 1, j); |
| 106 |
} |
|
| 107 |
} |
|
| 108 |
} |
|
| 109 | ||
| 110 |
} |
|
| 111 |
} |
|
| 112 | ||
| 113 |
template<typename T> |
|
| 114 | 117x |
void FloodFill::flood(Matrix<T> &arr) const {
|
| 115 | ||
| 116 | 613x |
for (size_t i = 0; i < arr.rows(); i++) {
|
| 117 | 21669x |
for (size_t j = 0; j < arr.cols(); j++) {
|
| 118 | 21173x |
if (arr(i, j) == fill_values<T>::UNKNOWN) {
|
| 119 | ! |
throw std::runtime_error("Cell with unknown position encountered.");
|
| 120 | 21173x |
} else if (arr(i, j) == fill_values<T>::FILLABLE) {
|
| 121 |
// Cell position relative to polygon is unknown but can |
|
| 122 |
// be determined from adjacent cells. |
|
| 123 | 76x |
if (cell_is_inside(i, j)) {
|
| 124 | 46x |
flood_from_pixel(arr, i, j, fill_values<T>::INTERIOR); |
| 125 |
} else {
|
|
| 126 | 30x |
flood_from_pixel(arr, i, j, fill_values<T>::EXTERIOR); |
| 127 |
} |
|
| 128 |
} |
|
| 129 |
} |
|
| 130 |
} |
|
| 131 |
} |
|
| 132 | ||
| 133 |
} |
|
| 134 | ||
| 135 |
#endif |
| 1 |
// Copyright (c) 2018-2020 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#include <stdexcept> |
|
| 15 | ||
| 16 |
#include <geos_c.h> |
|
| 17 | ||
| 18 |
#include "area.h" |
|
| 19 |
#include "cell.h" |
|
| 20 |
#include "floodfill.h" |
|
| 21 |
#include "geos_utils.h" |
|
| 22 |
#include "raster_cell_intersection.h" |
|
| 23 | ||
| 24 |
namespace exactextract {
|
|
| 25 | ||
| 26 | 167x |
Raster<float> raster_cell_intersection(const Grid<bounded_extent> & raster_grid, GEOSContextHandle_t context, const GEOSGeometry* g) {
|
| 27 | 167x |
RasterCellIntersection rci(raster_grid, context, g); |
| 28 | ||
| 29 | 167x |
return { std::move(const_cast<Matrix<float>&>(rci.overlap_areas())),
|
| 30 | 501x |
make_finite(rci.m_geometry_grid) }; |
| 31 |
} |
|
| 32 | ||
| 33 | 500250x |
Raster<float> raster_cell_intersection(const Grid<bounded_extent> & raster_grid, const Box & box) {
|
| 34 | 500250x |
RasterCellIntersection rci(raster_grid, box); |
| 35 | ||
| 36 | 500250x |
return { std::move(const_cast<Matrix<float>&>(rci.overlap_areas())),
|
| 37 | 1500750x |
make_finite(rci.m_geometry_grid) }; |
| 38 |
} |
|
| 39 | ||
| 40 | 2055x |
static Cell *get_cell(Matrix<std::unique_ptr<Cell>> &cells, const Grid<infinite_extent> &ex, size_t row, size_t col) {
|
| 41 |
//std::cout << " getting cell " << row << ", " << col << std::endl; |
|
| 42 | ||
| 43 | 2055x |
if (cells(row, col) == nullptr) {
|
| 44 | 1933x |
cells(row, col) = std::make_unique<Cell>(grid_cell(ex, row, col)); |
| 45 |
} |
|
| 46 | ||
| 47 | 2055x |
return cells(row, col).get(); |
| 48 |
} |
|
| 49 | ||
| 50 | 167x |
Box processing_region(const Box & raster_extent, const std::vector<Box> & component_boxes) {
|
| 51 | 167x |
Box ret = Box::make_empty(); |
| 52 | 337x |
for (const auto& box : component_boxes) {
|
| 53 | 170x |
if (ret == raster_extent) {
|
| 54 |
// No more expansion is possible |
|
| 55 | ! |
return ret; |
| 56 |
} |
|
| 57 | ||
| 58 | 170x |
if (!box.intersects(raster_extent)) {
|
| 59 | 6x |
continue; |
| 60 |
} |
|
| 61 | ||
| 62 | 164x |
Box isect = raster_extent.intersection(box); |
| 63 | 164x |
if (ret.empty()) {
|
| 64 | 161x |
ret = isect; |
| 65 | 3x |
} else if (!ret.contains(isect)) {
|
| 66 | 3x |
ret = ret.expand_to_include(isect); |
| 67 |
} |
|
| 68 |
} |
|
| 69 | ||
| 70 | 167x |
return ret; |
| 71 |
} |
|
| 72 | ||
| 73 | 167x |
static Grid<infinite_extent> get_geometry_grid(const Grid<bounded_extent> &raster_grid, GEOSContextHandle_t context, const GEOSGeometry* g) {
|
| 74 | 167x |
if (GEOSisEmpty_r(context, g)) {
|
| 75 | ! |
throw std::invalid_argument("Can't get statistics for empty geometry");
|
| 76 |
} |
|
| 77 | ||
| 78 | 167x |
Box region = processing_region(raster_grid.extent(), geos_get_component_boxes(context, g)); |
| 79 | ||
| 80 | 167x |
if (!region.empty()) {
|
| 81 | 322x |
return make_infinite(raster_grid.shrink_to_fit(region)); |
| 82 |
} else {
|
|
| 83 | 6x |
return Grid<infinite_extent>::make_empty(); |
| 84 |
} |
|
| 85 |
} |
|
| 86 | ||
| 87 | 500250x |
static Grid<infinite_extent> get_geometry_grid(const Grid<bounded_extent> & raster_grid, const Box & box) {
|
| 88 | 500250x |
auto region = box.intersection(raster_grid.extent()); |
| 89 | ||
| 90 | 500250x |
if (!region.empty()) {
|
| 91 | 765396x |
return make_infinite(raster_grid.shrink_to_fit(region)); |
| 92 |
} else {
|
|
| 93 | 117552x |
return Grid<infinite_extent>::make_empty(); |
| 94 |
} |
|
| 95 |
} |
|
| 96 | ||
| 97 | ||
| 98 | 167x |
RasterCellIntersection::RasterCellIntersection(const Grid<bounded_extent> &raster_grid, GEOSContextHandle_t context, const GEOSGeometry *g) |
| 99 | 167x |
: m_geometry_grid{get_geometry_grid(raster_grid, context, g)},
|
| 100 | 167x |
m_overlap_areas{std::make_unique<Matrix<float>>(m_geometry_grid.rows() - 2, m_geometry_grid.cols() - 2)}
|
| 101 |
{
|
|
| 102 | 167x |
if (!m_geometry_grid.empty()) |
| 103 | 161x |
process(context, g); |
| 104 |
} |
|
| 105 | ||
| 106 | 500250x |
RasterCellIntersection::RasterCellIntersection(const Grid<bounded_extent> & raster_grid, const Box & box) |
| 107 | 500250x |
: m_geometry_grid{get_geometry_grid(raster_grid, box)},
|
| 108 | 500250x |
m_overlap_areas{std::make_unique<Matrix<float>>(m_geometry_grid.rows() - 2, m_geometry_grid.cols() - 2)} {
|
| 109 | 500250x |
if (!m_geometry_grid.empty()) {
|
| 110 | 382698x |
process_rectangular_ring(box, true); |
| 111 |
} |
|
| 112 |
} |
|
| 113 | ||
| 114 | 168x |
void RasterCellIntersection::process(GEOSContextHandle_t context, const GEOSGeometry *g) {
|
| 115 | 168x |
if (GEOSGeomTypeId_r(context, g) == GEOS_POLYGON) {
|
| 116 | 164x |
process_ring(context, GEOSGetExteriorRing_r(context, g), true); |
| 117 | ||
| 118 |
for (int i = 0; i < GEOSGetNumInteriorRings_r(context, g); i++) {
|
|
| 119 | ! |
process_ring(context, GEOSGetInteriorRingN_r(context, g, i), false); |
| 120 |
} |
|
| 121 | 4x |
} else if (GEOSGeomTypeId_r(context, g) == GEOS_MULTIPOLYGON) {
|
| 122 | 11x |
for (int i = 0; i < GEOSGetNumGeometries_r(context, g); i++) {
|
| 123 | 7x |
process(context, GEOSGetGeometryN_r(context, g, i)); |
| 124 |
} |
|
| 125 |
} else {
|
|
| 126 | ! |
throw std::invalid_argument("Unsupported geometry type.");
|
| 127 |
} |
|
| 128 |
} |
|
| 129 | ||
| 130 | 382862x |
static Grid<infinite_extent> get_box_grid(const Box & box, const Grid<infinite_extent> & geometry_grid) {
|
| 131 | 382862x |
Box cropped_ring_extent = geometry_grid.extent().intersection(box); |
| 132 | 765724x |
return geometry_grid.shrink_to_fit(cropped_ring_extent); |
| 133 |
} |
|
| 134 | ||
| 135 | 117x |
static Grid<infinite_extent> get_ring_grid(GEOSContextHandle_t context, const GEOSGeometry* ls, const Grid<infinite_extent> & geometry_grid) {
|
| 136 | 234x |
return get_box_grid(geos_get_box(context, ls), geometry_grid); |
| 137 |
} |
|
| 138 | ||
| 139 | 382745x |
void RasterCellIntersection::process_rectangular_ring(const Box& box, bool exterior_ring) {
|
| 140 | 382745x |
if (!box.intersects(m_geometry_grid.extent())) {
|
| 141 | ! |
return; |
| 142 |
} |
|
| 143 | ||
| 144 | 382745x |
auto ring_grid = get_box_grid(box, m_geometry_grid); |
| 145 | ||
| 146 | 382745x |
auto row_min = ring_grid.get_row(box.ymax); |
| 147 | 382745x |
auto row_max = ring_grid.get_row(box.ymin); |
| 148 | 382745x |
auto col_min = ring_grid.get_column(box.xmin); |
| 149 | 382745x |
auto col_max = ring_grid.get_column(box.xmax); |
| 150 | ||
| 151 | 765490x |
Matrix<float> areas(ring_grid.rows() - 2, ring_grid.cols() - 2); |
| 152 | ||
| 153 |
// upper-left |
|
| 154 | 382745x |
if (row_min > 0 && col_min > 0) {
|
| 155 | 381406x |
auto ul = grid_cell(ring_grid, row_min, col_min); |
| 156 | 381406x |
areas(row_min - 1, col_min - 1) = ul.intersection(box).area() / ul.area(); |
| 157 |
} |
|
| 158 | ||
| 159 |
// upper-right |
|
| 160 | 382745x |
if (row_min > 0 && col_max < ring_grid.cols() - 1) {
|
| 161 | 381404x |
auto ur = grid_cell(ring_grid, row_min, col_max); |
| 162 | 381404x |
auto frac = ur.intersection(box).area() / ur.area(); |
| 163 | 381404x |
areas(row_min - 1, col_max - 1) = frac; |
| 164 |
} |
|
| 165 | ||
| 166 |
// lower-left |
|
| 167 | 382745x |
if (row_max < ring_grid.rows() - 1 && col_min > 0) {
|
| 168 | 381406x |
auto ll = grid_cell(ring_grid, row_max, col_min); |
| 169 | 381406x |
areas(row_max - 1, col_min - 1) = ll.intersection(box).area() / ll.area(); |
| 170 |
} |
|
| 171 | ||
| 172 |
// lower-right |
|
| 173 | 382745x |
if (row_max < ring_grid.rows() - 1 && col_max < ring_grid.cols() - 1) {
|
| 174 | 381404x |
auto lr = grid_cell(ring_grid, row_max, col_max); |
| 175 | 381404x |
areas(row_max - 1, col_max - 1) = lr.intersection(box).area() / lr.area(); |
| 176 |
} |
|
| 177 | ||
| 178 |
// left |
|
| 179 | 382745x |
if (col_min > 0) {
|
| 180 | 382316x |
auto left = grid_cell(ring_grid, row_min + 1, col_min); |
| 181 | 382316x |
auto frac = left.intersection(box).area() / left.area(); |
| 182 | 383219x |
for (size_t row = row_min + 1; row < row_max; row++) {
|
| 183 | 903x |
areas(row - 1, col_min - 1) = frac; |
| 184 |
} |
|
| 185 |
} |
|
| 186 | ||
| 187 |
// right |
|
| 188 | 382745x |
if (col_max < ring_grid.cols() - 1) {
|
| 189 | 382314x |
auto right = grid_cell(ring_grid, row_min + 1, col_max); |
| 190 | 382314x |
auto frac = right.intersection(box).area() / right.area(); |
| 191 | 383217x |
for (size_t row = row_min + 1; row < row_max; row++) {
|
| 192 | 903x |
areas(row - 1, col_max - 1) = frac; |
| 193 |
} |
|
| 194 |
} |
|
| 195 | ||
| 196 |
// top |
|
| 197 | 382745x |
if (row_min > 0) {
|
| 198 | 381833x |
auto top = grid_cell(ring_grid, row_min, col_min + 1); |
| 199 | 381833x |
auto frac = top.intersection(box).area() / top.area(); |
| 200 | 382236x |
for (size_t col = col_min + 1; col < col_max; col++) {
|
| 201 | 403x |
areas(row_min - 1, col - 1) = frac; |
| 202 |
} |
|
| 203 |
} |
|
| 204 | ||
| 205 |
// bottom |
|
| 206 | 382745x |
if (row_max < ring_grid.rows() - 1) {
|
| 207 | 381833x |
auto bottom = grid_cell(ring_grid, row_max, col_min + 1); |
| 208 | 381833x |
auto frac = bottom.intersection(box).area() / bottom.area(); |
| 209 | 382236x |
for (size_t col = col_min + 1; col < col_max; col++) {
|
| 210 | 403x |
areas(row_max - 1, col - 1) = frac; |
| 211 |
} |
|
| 212 |
} |
|
| 213 | ||
| 214 |
// interior |
|
| 215 | 383744x |
for (size_t row = row_min + 1; row < row_max; row++) {
|
| 216 | 9776x |
for(size_t col = col_min + 1; col < col_max; col++) {
|
| 217 | 8777x |
areas(row - 1, col - 1) = 1.0f; |
| 218 |
} |
|
| 219 |
} |
|
| 220 | ||
| 221 |
// Transfer these areas to our global set |
|
| 222 | 382745x |
size_t i0 = ring_grid.row_offset(m_geometry_grid); |
| 223 | 382745x |
size_t j0 = ring_grid.col_offset(m_geometry_grid); |
| 224 | ||
| 225 | 382745x |
add_ring_areas(i0, j0, areas, exterior_ring); |
| 226 |
} |
|
| 227 | ||
| 228 | 164x |
void RasterCellIntersection::process_ring(GEOSContextHandle_t context, const GEOSGeometry *ls, bool exterior_ring) {
|
| 229 | 164x |
auto geom_box = geos_get_box(context, ls); |
| 230 | ||
| 231 | 164x |
if (!geom_box.intersects(m_geometry_grid.extent())) {
|
| 232 |
return; |
|
| 233 |
} |
|
| 234 | ||
| 235 | 164x |
const GEOSCoordSequence *seq = GEOSGeom_getCoordSeq_r(context, ls); |
| 236 | 164x |
unsigned int npoints = geos_get_num_points(context, seq); |
| 237 | ||
| 238 | 164x |
if (npoints == 5) {
|
| 239 | 51x |
auto coords = read(context, seq); |
| 240 | 51x |
if (area(coords) == geom_box.area()) {
|
| 241 | 47x |
process_rectangular_ring(geom_box, exterior_ring); |
| 242 | 47x |
return; |
| 243 |
} |
|
| 244 |
} |
|
| 245 | ||
| 246 | 117x |
Grid<infinite_extent> ring_grid = get_ring_grid(context, ls, m_geometry_grid); |
| 247 | ||
| 248 | 117x |
size_t rows = ring_grid.rows(); |
| 249 | 117x |
size_t cols = ring_grid.cols(); |
| 250 | ||
| 251 |
// Short circuit for small rings that are entirely contained |
|
| 252 |
// within a single grid cell. |
|
| 253 | 37x |
if (rows == (1 + 2*infinite_extent::padding) && |
| 254 |
cols == (1 + 2*infinite_extent::padding) && |
|
| 255 | 153x |
grid_cell(ring_grid, 1, 1).contains(geom_box)) {
|
| 256 | ||
| 257 | ! |
auto coords = read(context, seq); |
| 258 | ! |
auto ring_area = area(coords) / grid_cell(ring_grid, 1, 1).area(); |
| 259 | ||
| 260 | ! |
size_t i0 = ring_grid.row_offset(m_geometry_grid); |
| 261 | ! |
size_t j0 = ring_grid.col_offset(m_geometry_grid); |
| 262 | ||
| 263 | ! |
if (exterior_ring) {
|
| 264 | ! |
m_overlap_areas->increment(i0, j0, ring_area); |
| 265 |
} else {
|
|
| 266 | ! |
m_overlap_areas->increment(i0, j0, -1 * ring_area); |
| 267 |
} |
|
| 268 | ||
| 269 | ! |
return; |
| 270 |
} |
|
| 271 | ||
| 272 | 117x |
bool is_ccw = geos_is_ccw(context, seq); |
| 273 | 234x |
Matrix<std::unique_ptr<Cell>> cells(rows, cols); |
| 274 | ||
| 275 | 234x |
std::deque<Coordinate> stk; |
| 276 |
{
|
|
| 277 | 13116x |
for (unsigned int i = 0; i < npoints; i++) {
|
| 278 |
double x, y; |
|
| 279 |
#if HAVE_380 |
|
| 280 | 12999x |
if (!GEOSCoordSeq_getXY_r(context, seq, i, &x, &y)) {
|
| 281 | ! |
throw std::runtime_error("Error reading coordinates.");
|
| 282 |
} |
|
| 283 |
#else |
|
| 284 |
if (!GEOSCoordSeq_getX_r(context, seq, i, &x) || !GEOSCoordSeq_getY_r(context, seq, i, &y)) {
|
|
| 285 |
throw std::runtime_error("Error reading coordinates.");
|
|
| 286 |
} |
|
| 287 |
#endif |
|
| 288 | ||
| 289 | 12999x |
if (is_ccw) {
|
| 290 | 26x |
stk.emplace_back(x, y); |
| 291 |
} else {
|
|
| 292 | 12973x |
stk.emplace_front(x, y); |
| 293 |
} |
|
| 294 |
} |
|
| 295 |
} |
|
| 296 | ||
| 297 | 117x |
size_t row = ring_grid.get_row(stk.front().y); |
| 298 | 117x |
size_t col = ring_grid.get_column(stk.front().x); |
| 299 | ||
| 300 | 2172x |
while (!stk.empty()) {
|
| 301 | 2055x |
Cell &cell = *get_cell(cells, ring_grid, row, col); |
| 302 | ||
| 303 | 17514x |
while (!stk.empty()) {
|
| 304 | 17397x |
cell.take(stk.front()); |
| 305 | ||
| 306 | 17397x |
if (cell.last_traversal().exited()) {
|
| 307 |
// Only push our exit coordinate if it's not same as the |
|
| 308 |
// coordinate we just took. This covers the case where |
|
| 309 |
// the next coordinate in the stack falls exactly on |
|
| 310 |
// the cell boundary. |
|
| 311 | 1938x |
const Coordinate &exc = cell.last_traversal().exit_coordinate(); |
| 312 | 1938x |
if (exc != stk.front()) {
|
| 313 | 1938x |
stk.emplace_front(exc.x, exc.y); |
| 314 |
} |
|
| 315 | 1938x |
break; |
| 316 |
} else {
|
|
| 317 | 15459x |
stk.pop_front(); |
| 318 |
} |
|
| 319 |
} |
|
| 320 | ||
| 321 | 2055x |
cell.force_exit(); |
| 322 | ||
| 323 | 2055x |
if (cell.last_traversal().exited()) {
|
| 324 |
// When we start in the middle of a cell, we need to save the coordinates |
|
| 325 |
// from our incomplete traversal and reprocess them at the end of the line. |
|
| 326 |
// The effect is just to restructure the polygon so that the start/end |
|
| 327 |
// coordinate falls on a cell boundary. |
|
| 328 | 2055x |
if (!cell.last_traversal().traversed()) {
|
| 329 | 556x |
for (const auto &coord : cell.last_traversal().coords()) {
|
| 330 | 522x |
stk.push_back(coord); |
| 331 |
} |
|
| 332 |
} |
|
| 333 | ||
| 334 | 2055x |
switch (cell.last_traversal().exit_side()) {
|
| 335 | 530x |
case Side::TOP: |
| 336 | 530x |
row--; |
| 337 | 530x |
break; |
| 338 | 494x |
case Side::BOTTOM: |
| 339 | 494x |
row++; |
| 340 | 494x |
break; |
| 341 | 501x |
case Side::LEFT: |
| 342 | 501x |
col--; |
| 343 | 501x |
break; |
| 344 | 530x |
case Side::RIGHT: |
| 345 | 530x |
col++; |
| 346 | 530x |
break; |
| 347 | ! |
default: |
| 348 | ! |
throw std::runtime_error("Invalid traversal");
|
| 349 |
} |
|
| 350 |
} |
|
| 351 |
} |
|
| 352 | ||
| 353 |
// Compute the fraction covered for all cells and assign it to |
|
| 354 |
// the area matrix |
|
| 355 |
// TODO avoid copying matrix when geometry has only one polygon, and polygon has only one ring |
|
| 356 | 234x |
Matrix<float> areas(rows - 2, cols - 2, fill_values<float>::FILLABLE); |
| 357 | ||
| 358 | 234x |
FloodFill ff(context, ls, make_finite(ring_grid)); |
| 359 | ||
| 360 | 613x |
for (size_t i = 1; i <= areas.rows(); i++) {
|
| 361 | 21669x |
for (size_t j = 1; j <= areas.cols(); j++) {
|
| 362 | 21173x |
if (cells(i, j) != nullptr) {
|
| 363 |
// When we encounter a cell that has been processed (ie, it is not nullptr) |
|
| 364 |
// but has zero covered fraction, we have no way to know if that cell is on |
|
| 365 |
// the inside of the polygon. So we perform point-in-polygon test and set |
|
| 366 |
// the covered fraction to 1.0 if needed. |
|
| 367 | ||
| 368 | 1604x |
auto frac = static_cast<float>(cells(i, j)->covered_fraction()); |
| 369 | 1604x |
if (frac == 0) {
|
| 370 | ! |
areas(i-1, j-1) = ff.cell_is_inside(i-1, j-1) ? fill_values<float>::INTERIOR : fill_values<float>::EXTERIOR; |
| 371 |
} else {
|
|
| 372 | 1604x |
areas(i-1, j-1) = frac; |
| 373 |
} |
|
| 374 |
} |
|
| 375 |
} |
|
| 376 |
} |
|
| 377 | ||
| 378 | 117x |
ff.flood(areas); |
| 379 | ||
| 380 |
// Transfer these areas to our global set |
|
| 381 | 117x |
size_t i0 = ring_grid.row_offset(m_geometry_grid); |
| 382 | 117x |
size_t j0 = ring_grid.col_offset(m_geometry_grid); |
| 383 | ||
| 384 | 117x |
add_ring_areas(i0, j0, areas, exterior_ring); |
| 385 |
} |
|
| 386 | ||
| 387 | 382862x |
void RasterCellIntersection::add_ring_areas(size_t i0, size_t j0, const Matrix<float> &areas, bool exterior_ring) {
|
| 388 |
int factor = exterior_ring ? 1 : -1; |
|
| 389 | ||
| 390 | 856657x |
for (size_t i = 0; i < areas.rows(); i++) {
|
| 391 | 1030930x |
for (size_t j = 0; j < areas.cols(); j++) {
|
| 392 | 557135x |
m_overlap_areas->increment(i0 + i, j0 + j, factor * areas(i, j)); |
| 393 |
} |
|
| 394 |
} |
|
| 395 |
} |
|
| 396 | ||
| 397 |
} |
| 1 |
// Copyright (c) 2018-2019 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#ifndef EXACTEXTRACT_RASTER_CELL_INTERSECTION_H |
|
| 15 |
#define EXACTEXTRACT_RASTER_CELL_INTERSECTION_H |
|
| 16 | ||
| 17 |
#include <memory> |
|
| 18 | ||
| 19 |
#include <geos_c.h> |
|
| 20 | ||
| 21 |
#include "grid.h" |
|
| 22 |
#include "matrix.h" |
|
| 23 |
#include "raster.h" |
|
| 24 | ||
| 25 |
namespace exactextract {
|
|
| 26 | ||
| 27 |
class RasterCellIntersection {
|
|
| 28 | ||
| 29 |
public: |
|
| 30 |
RasterCellIntersection(const Grid<bounded_extent> &raster_grid, GEOSContextHandle_t context, const GEOSGeometry *g); |
|
| 31 | ||
| 32 |
RasterCellIntersection(const Grid<bounded_extent> &raster_grid, const Box & box); |
|
| 33 | ||
| 34 |
size_t rows() const { return m_overlap_areas->rows(); }
|
|
| 35 | ||
| 36 |
size_t cols() const { return m_overlap_areas->cols(); }
|
|
| 37 | ||
| 38 | 500417x |
const Matrix<float> &overlap_areas() const { return *m_overlap_areas; }
|
| 39 | ||
| 40 |
Grid<infinite_extent> m_geometry_grid; |
|
| 41 |
private: |
|
| 42 |
void process(GEOSContextHandle_t context, const GEOSGeometry *g); |
|
| 43 | ||
| 44 |
void process_ring(GEOSContextHandle_t context, const GEOSGeometry *ls, bool exterior_ring); |
|
| 45 | ||
| 46 |
void process_rectangular_ring(const Box & box, bool exterior_ring); |
|
| 47 | ||
| 48 |
void add_ring_areas(size_t i0, size_t j0, const Matrix<float> &areas, bool exterior_ring); |
|
| 49 | ||
| 50 |
std::unique_ptr<Matrix<float>> m_overlap_areas; |
|
| 51 | ||
| 52 |
}; |
|
| 53 | ||
| 54 |
Raster<float> raster_cell_intersection(const Grid<bounded_extent> & raster_grid, GEOSContextHandle_t context, const GEOSGeometry* g); |
|
| 55 |
Raster<float> raster_cell_intersection(const Grid<bounded_extent> & raster_grid, const Box & box); |
|
| 56 |
Box processing_region(const Box & raster_extent, const std::vector<Box> & component_boxes); |
|
| 57 |
} |
|
| 58 | ||
| 59 |
#endif |
| 1 |
// Copyright (c) 2018 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#include <ostream> |
|
| 15 | ||
| 16 |
#include "side.h" |
|
| 17 | ||
| 18 |
namespace exactextract {
|
|
| 19 | ||
| 20 | ! |
std::ostream &operator<<(std::ostream &os, const Side &s) {
|
| 21 | ! |
switch (s) {
|
| 22 | ! |
case Side::NONE: |
| 23 | ! |
os << "none"; |
| 24 | ! |
return os; |
| 25 | ! |
case Side::LEFT: |
| 26 | ! |
os << "left"; |
| 27 | ! |
return os; |
| 28 | ! |
case Side::RIGHT: |
| 29 | ! |
os << "right"; |
| 30 | ! |
return os; |
| 31 | ! |
case Side::TOP: |
| 32 | ! |
os << "top"; |
| 33 | ! |
return os; |
| 34 | ! |
case Side::BOTTOM: |
| 35 | ! |
os << "bottom"; |
| 36 | ! |
return os; |
| 37 |
} |
|
| 38 | ||
| 39 | ! |
return os; // unreachable statement needed for -Werror=return-type |
| 40 |
} |
|
| 41 | ||
| 42 |
} |
| 1 |
// Copyright (c) 2018 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#include <iostream> |
|
| 15 |
#include <limits> |
|
| 16 |
#include <vector> |
|
| 17 | ||
| 18 |
#include "area.h" |
|
| 19 |
#include "box.h" |
|
| 20 |
#include "coordinate.h" |
|
| 21 |
#include "perimeter_distance.h" |
|
| 22 | ||
| 23 |
namespace exactextract {
|
|
| 24 | ||
| 25 |
struct CoordinateChain {
|
|
| 26 |
double start; |
|
| 27 |
double stop; |
|
| 28 |
const std::vector<Coordinate> *coordinates; |
|
| 29 |
bool visited; |
|
| 30 | ||
| 31 | 8027x |
CoordinateChain(double p_start, double p_stop, const std::vector<Coordinate>* p_coords) : |
| 32 |
start{p_start},
|
|
| 33 |
stop{p_stop},
|
|
| 34 |
coordinates{p_coords},
|
|
| 35 | 8027x |
visited{false} {}
|
| 36 |
}; |
|
| 37 | ||
| 38 |
static double |
|
| 39 | 18493x |
exit_to_entry_perimeter_distance_ccw(const CoordinateChain &c1, const CoordinateChain &c2, double perimeter) {
|
| 40 | 18493x |
return perimeter_distance_ccw(c1.stop, c2.start, perimeter); |
| 41 |
} |
|
| 42 | ||
| 43 | 4684x |
static CoordinateChain *next_chain(std::vector<CoordinateChain> &chains, |
| 44 |
const CoordinateChain *chain, |
|
| 45 |
const CoordinateChain *kill, |
|
| 46 |
double perimeter) {
|
|
| 47 | ||
| 48 | 4684x |
CoordinateChain *min = nullptr; |
| 49 | 4684x |
double min_distance = std::numeric_limits<double>::max(); |
| 50 | ||
| 51 | 28127x |
for (CoordinateChain &candidate : chains) {
|
| 52 | 23443x |
if (candidate.visited && std::addressof(candidate) != kill) {
|
| 53 | 4950x |
continue; |
| 54 |
} |
|
| 55 | ||
| 56 | 18493x |
double distance = exit_to_entry_perimeter_distance_ccw(*chain, candidate, perimeter); |
| 57 | 18493x |
if (distance < min_distance) {
|
| 58 | 9079x |
min_distance = distance; |
| 59 | 9079x |
min = std::addressof(candidate); |
| 60 |
} |
|
| 61 |
} |
|
| 62 | ||
| 63 | 4684x |
return min; |
| 64 |
} |
|
| 65 | ||
| 66 | 1604x |
double left_hand_area(const Box &box, const std::vector<const std::vector<Coordinate> *> &coord_lists) {
|
| 67 | 3208x |
std::vector<CoordinateChain> chains; |
| 68 | ||
| 69 | 3215x |
for (const auto &coords : coord_lists) {
|
| 70 | 1611x |
double start = perimeter_distance(box, (*coords)[0]); |
| 71 | 1611x |
double stop = perimeter_distance(box, (*coords)[coords->size() - 1]); |
| 72 | ||
| 73 | 1611x |
chains.emplace_back(start, stop, coords); |
| 74 |
} |
|
| 75 | ||
| 76 | 1604x |
double height{box.height()};
|
| 77 | 1604x |
double width{box.width()};
|
| 78 | 1604x |
double perimeter{box.perimeter()};
|
| 79 | ||
| 80 |
// create coordinate lists for corners |
|
| 81 | 3208x |
std::vector<Coordinate> bottom_left = {Coordinate(box.xmin, box.ymin)};
|
| 82 | 3208x |
std::vector<Coordinate> top_left = {Coordinate(box.xmin, box.ymax)};
|
| 83 | 3208x |
std::vector<Coordinate> top_right = {Coordinate(box.xmax, box.ymax)};
|
| 84 | 1604x |
std::vector<Coordinate> bottom_right = {Coordinate(box.xmax, box.ymin)};
|
| 85 | ||
| 86 |
// Add chains for corners |
|
| 87 | 1604x |
chains.emplace_back(0.0, 0.0, &bottom_left); |
| 88 | 1604x |
chains.emplace_back(height, height, &top_left); |
| 89 | 1604x |
chains.emplace_back(height + width, height + width, &top_right); |
| 90 | 1604x |
chains.emplace_back(2 * height + width, 2 * height + width, &bottom_right); |
| 91 | ||
| 92 | 1604x |
double sum{0.0};
|
| 93 | 9631x |
for (auto &chain_ref : chains) {
|
| 94 | 8027x |
if (chain_ref.visited || chain_ref.coordinates->size() == 1) {
|
| 95 | 6422x |
continue; |
| 96 |
} |
|
| 97 | ||
| 98 | 1605x |
std::vector<Coordinate> coords; |
| 99 | 1605x |
CoordinateChain *chain = std::addressof(chain_ref); |
| 100 | 1605x |
CoordinateChain *first_chain = chain; |
| 101 | 3079x |
do {
|
| 102 | 4684x |
chain->visited = true; |
| 103 | 4684x |
coords.insert(coords.end(), chain->coordinates->cbegin(), chain->coordinates->cend()); |
| 104 | 4684x |
chain = next_chain(chains, chain, first_chain, perimeter); |
| 105 | 4684x |
} while (chain != first_chain); |
| 106 | ||
| 107 | 1605x |
coords.push_back(coords[0]); |
| 108 | ||
| 109 | 1605x |
sum += area(coords); |
| 110 |
} |
|
| 111 | ||
| 112 | 3208x |
return sum; |
| 113 |
} |
|
| 114 | ||
| 115 |
} |
| 1 |
// Copyright (c) 2018 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#include <cstddef> |
|
| 15 |
#include <stdexcept> |
|
| 16 | ||
| 17 |
#include "traversal.h" |
|
| 18 | ||
| 19 |
namespace exactextract {
|
|
| 20 | ||
| 21 | 17397x |
void Traversal::add(const Coordinate &c) {
|
| 22 | 17397x |
m_coords.push_back(c); |
| 23 |
} |
|
| 24 | ||
| 25 | 17397x |
bool Traversal::empty() const {
|
| 26 | 17397x |
return m_coords.empty(); |
| 27 |
} |
|
| 28 | ||
| 29 | 2055x |
void Traversal::enter(const Coordinate &c, Side s) {
|
| 30 | 2055x |
if (!m_coords.empty()) {
|
| 31 | ! |
throw std::runtime_error("Traversal already started");
|
| 32 |
} |
|
| 33 | ||
| 34 | 2055x |
add(c); |
| 35 | 2055x |
m_entry = s; |
| 36 |
} |
|
| 37 | ||
| 38 | 1938x |
void Traversal::exit(const Coordinate &c, Side s) {
|
| 39 | 1938x |
add(c); |
| 40 | 1938x |
m_exit = s; |
| 41 |
} |
|
| 42 | ||
| 43 | 1513x |
bool Traversal::is_closed_ring() const {
|
| 44 |
return m_coords.size() >= 3 && m_coords[0] == m_coords[m_coords.size() - 1]; |
|
| 45 |
} |
|
| 46 | ||
| 47 | 3752x |
bool Traversal::entered() const {
|
| 48 | 3752x |
return m_entry != Side::NONE; |
| 49 |
} |
|
| 50 | ||
| 51 | 42613x |
bool Traversal::exited() const {
|
| 52 | 42613x |
return m_exit != Side::NONE; |
| 53 |
} |
|
| 54 | ||
| 55 | 1683x |
bool Traversal::multiple_unique_coordinates() const {
|
| 56 | 1755x |
for (size_t i = 1; i < m_coords.size(); i++) {
|
| 57 | 1683x |
if (m_coords[0] != m_coords[i]) {
|
| 58 | 1611x |
return true; |
| 59 |
} |
|
| 60 |
} |
|
| 61 | ||
| 62 | 72x |
return false; |
| 63 |
} |
|
| 64 | ||
| 65 | 3752x |
bool Traversal::traversed() const {
|
| 66 | 3752x |
return entered() && exited(); |
| 67 |
} |
|
| 68 | ||
| 69 | 3993x |
const Coordinate &Traversal::last_coordinate() const {
|
| 70 | 3993x |
return m_coords.at(m_coords.size() - 1); |
| 71 |
} |
|
| 72 | ||
| 73 | 1938x |
const Coordinate &Traversal::exit_coordinate() const {
|
| 74 | 1938x |
if (!exited()) {
|
| 75 | ! |
throw std::runtime_error("Can't get exit coordinate from incomplete traversal.");
|
| 76 |
} |
|
| 77 | ||
| 78 | 1938x |
return last_coordinate(); |
| 79 |
} |
|
| 80 | ||
| 81 |
} |
| 1 |
// Copyright (c) 2019-2020 ISciences, LLC. |
|
| 2 |
// All rights reserved. |
|
| 3 |
// |
|
| 4 |
// This software is licensed under the Apache License, Version 2.0 (the "License"). |
|
| 5 |
// You may not use this file except in compliance with the License. You may |
|
| 6 |
// obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. |
|
| 7 |
// |
|
| 8 |
// Unless required by applicable law or agreed to in writing, software |
|
| 9 |
// distributed under the License is distributed on an "AS IS" BASIS, |
|
| 10 |
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
| 11 |
// See the License for the specific language governing permissions and |
|
| 12 |
// limitations under the License. |
|
| 13 | ||
| 14 |
#include "weighted_quantiles.h" |
|
| 15 | ||
| 16 |
namespace exactextract {
|
|
| 17 | ||
| 18 | 6x |
void WeightedQuantiles::prepare() const {
|
| 19 | 6x |
std::sort(m_elems.begin(), m_elems.end(), [](const elem_t &a, const elem_t &b) {
|
| 20 | 117x |
return a.x < b.x; |
| 21 |
}); |
|
| 22 | ||
| 23 | 6x |
m_sum_w = 0; |
| 24 |
// relies on map being sorted which it is no |
|
| 25 | 57x |
for (size_t i = 0; i < m_elems.size(); i++) {
|
| 26 | 51x |
m_sum_w += m_elems[i].w; |
| 27 | ||
| 28 | 51x |
if (i == 0) {
|
| 29 | 6x |
m_elems[i].s = 0; |
| 30 | 6x |
m_elems[i].cumsum = m_elems[i].w; |
| 31 |
} else {
|
|
| 32 | 45x |
m_elems[i].cumsum = m_elems[i - 1].cumsum + m_elems[i].w; |
| 33 | 45x |
m_elems[i].s = i * m_elems[i].w + (static_cast<double>(m_elems.size()) - 1) * m_elems[i - 1].cumsum; |
| 34 |
} |
|
| 35 |
} |
|
| 36 | ||
| 37 | 6x |
m_ready_to_query = true; |
| 38 |
} |
|
| 39 | ||
| 40 | 12x |
double WeightedQuantiles::quantile(double q) const {
|
| 41 | 12x |
if (!std::isfinite(q) || q < 0 || q > 1) {
|
| 42 | 2x |
throw std::runtime_error("Quantile must be between 0 and 1.");
|
| 43 |
} |
|
| 44 | ||
| 45 | 10x |
if (!m_ready_to_query) {
|
| 46 | 6x |
prepare(); |
| 47 |
} |
|
| 48 | ||
| 49 | 10x |
auto sn = m_sum_w * (static_cast<double>(m_elems.size()) - 1); |
| 50 | ||
| 51 | 10x |
elem_t lookup(0, 0); // create a dummy element to use with std::upper_bound |
| 52 | 10x |
lookup.s = q*sn; |
| 53 | ||
| 54 |
// get first element that is greater than the lookup value (q * sn) |
|
| 55 | 32x |
auto right = std::upper_bound(m_elems.cbegin(), m_elems.cend(), lookup, [](const elem_t & a, const elem_t & b) {
|
| 56 | 32x |
return a.s < b.s; |
| 57 |
}); |
|
| 58 | ||
| 59 |
// since the minimum value of "lookup" is zero, and the first value of "s" is zero, |
|
| 60 |
// we are guaranteed to have at least one element to the left of "right" |
|
| 61 | 10x |
auto left = std::prev(right, 1); |
| 62 | ||
| 63 | 10x |
if (right == m_elems.cend()) {
|
| 64 | ! |
return left->x; |
| 65 |
} |
|
| 66 | ||
| 67 | 10x |
return left->x + (q*sn - left->s)*(right->x - left->x)/(right->s - left->s); |
| 68 |
} |
|
| 69 | ||
| 70 |
} |