exactextractr/0000755000176200001440000000000015113343512013131 5ustar liggesusersexactextractr/tests/0000755000176200001440000000000014776002150014277 5ustar liggesusersexactextractr/tests/testthat/0000755000176200001440000000000015113343512016133 5ustar liggesusersexactextractr/tests/testthat/test_exact_resample_terra.R0000644000176200001440000001043014776002150023510 0ustar liggesusers# Copyright (c) 2020-2022 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. context('exact_resample (terra)') test_that("exact_resample supports SpatRaster arguments", { set.seed(123) # generate a random raster with a strange extent and resolution src <- raster::raster(matrix(runif(10000), nrow=100), xmn=runif(1), xmx=runif(1) + 9, ymn=runif(1), ymx=runif(1) + 9) # resample it to a raster with a larger grid with different resolution dst <- raster::raster(xmn=0, xmx=10, ymn=0, ymx=10, res=c(1, 2), crs=raster::crs(src)) dst <- exact_resample(src, dst, 'sum') dst_terra <- exact_resample(terra::rast(src), terra::rast(dst), 'sum') expect_equal(terra::rast(dst), dst_terra) }) test_that("resampling can be weighted with coverage areas instead of coverage fractions", { r <- terra::rast(nrows = 10, ncols = 10, xmin = 0, xmax = 10, ymin = 60, ymax = 70, crs = 'EPSG:4326') terra::values(r) <- seq(1, terra::ncell(r)) r2 <- terra::rast(nrows = 1, ncols = 1, xmin = 0, xmax = 10, ymin = 60, ymax = 70, crs = 'EPSG:4326') unweighted <- exact_resample(r, r2, 'mean') area_weighted <- exact_resample(r, r2, 'mean', coverage_area = TRUE) expect_true(area_weighted[1] > unweighted[1]) }) test_that("an R function can be used for resampling", { r1 <- make_square_rast(1:100) r2 <- terra::rast(nrows = 4, ncols = 4, xmin = 0, xmax = 10, ymin = 0, ymax = 10, crs = terra::crs(r1)) r2_rfun <- exact_resample(r1, r2, function(value, cov_frac) { sum(value * cov_frac) }) r2_stat <- exact_resample(r1, r2, 'sum') expect_equal(terra::values(r2_rfun), terra::values(r2_stat)) }) test_that("a multi-layer SpatRaster can be provided to an R summary function", { r1 <- make_square_rast(1:100, crs = 'EPSG:4326') r2 <- terra::rast(nrows = 4, ncols = 4, xmin = 0, xmax = 10, ymin = 0, ymax = 10, crs = terra::crs(r1)) # calculate an area-weighed mean by putting areas in a second layer r1_area <- terra::cellSize(r1) r1_stk <- terra::rast(list(r1, r1_area)) result_a <- exact_resample(r1_stk, r2, function(values, coverage_fraction) { weighted.mean(values[,1], values[,2] * coverage_fraction) }) # compare this to the more straightforward method of setting coverage_area = TRUE result_b <- exact_resample(r1, r2, 'mean', coverage_area = TRUE) expect_equal( terra::values(result_a), terra::values(result_b), tolerance = 1e-3 ) expect_error( exact_resample(r1_stk, r2, 'mean'), 'must have a single layer' ) }) test_that("error thrown if R function returns non-scalar value", { r1 <- make_square_rast(1:100) r2 <- terra::rast(nrows = 4, ncols = 4, xmin = 0, xmax = 10, ymin = 0, ymax = 10, crs = terra::crs(r1)) expect_error( exact_resample(r1, r2, function(value, cov_frac) { return(1:2) }), 'must return a single value' ) expect_error( exact_resample(r1, r2, function(value, cov_frac) { return(numeric()) }), 'must return a single value' ) expect_error( exact_resample(r1, r2, function(value, cov_frac) { return(NULL) }), 'Not compatible' ) expect_error( exact_resample(r1, r2, function(value, cov_frac) { 'abc' }), 'Not compatible' ) }) test_that("error thrown if R function has wrong signature", { r1 <- make_square_rast(1:100) r2 <- terra::rast(nrows = 4, ncols = 4, xmin = 0, xmax = 10, ymin = 0, ymax = 10, crs = terra::crs(r1)) expect_error( exact_resample(r1, r2, sum), 'does not appear to be of the form' ) }) exactextractr/tests/testthat/test_exact_extract_errors.R0000644000176200001440000004103015100141763023545 0ustar liggesusers# Copyright (c) 2018-2021 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License ta http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. library(testthat) library(exactextractr) context('exact_extract input validation') test_that('Error thrown if weighted stat requested but weights not provided', { rast <- make_square_raster(1:9) square <- make_circle(2, 2, 0.5, sf::st_crs(rast)) for (stat in c('weighted_mean', 'weighted_sum')) { expect_error(exact_extract(rast, square, stat), 'no weights provided') } }) test_that('Warning raised if weights provided but weighted stat not requested', { rast <- make_square_raster(1:9) square <- make_circle(2, 2, 0.5, sf::st_crs(rast)) for (stat in c('count', 'sum', 'mean', 'min', 'max', 'minority', 'majority', 'mode', 'variety')) { expect_warning(exact_extract(rast, square, stat, weights=rast), 'Weights provided but no.*operations use them') } }) test_that('Generic sfc_GEOMETRY fails if a feature is not polygonal', { rast <- make_square_raster(1:100) features <- st_as_sfc(c('POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))', 'POINT (2 7)'), crs=sf::st_crs(rast)) expect_error(exact_extract(rast, features, 'sum', progress = FALSE), 'must be polygonal') }) test_that('Incorrect argument types are handled gracefully', { data <- matrix(1:9, nrow=3, byrow=TRUE) rast <- raster::raster(data, xmn=0, xmx=3, ymn=0, ymx=3, crs='+proj=longlat +datum=WGS84') point <- sf::st_sfc(sf::st_point(1:2), crs=sf::st_crs(rast)) linestring <- sf::st_sfc(sf::st_linestring(matrix(1:4, nrow=2)), crs=sf::st_crs(rast)) multipoint <- sf::st_sfc(sf::st_multipoint(matrix(1:4, nrow=2)), crs=sf::st_crs(rast)) multilinestring <- sf::st_sfc( sf::st_multilinestring(list( matrix(1:4, nrow=2), matrix(5:8, nrow=2) )), crs=sf::st_crs(rast)) geometrycollection <- sf::st_sfc( sf::st_geometrycollection(list( sf::st_geometry(point)[[1]], sf::st_geometry(linestring)[[1]])), crs=sf::st_crs(rast)) expect_error(exact_extract(rast, point), 'unable to find.* method') expect_error(exact_extract(rast, linestring, 'unable to find.* method')) expect_error(exact_extract(rast, multipoint, 'unable to find.* method')) expect_error(exact_extract(rast, multilinesetring, 'unable to find.* method')) expect_error(exact_extract(rast, geometrycollection, 'unable to find.* method')) }) test_that('Warning is raised on CRS mismatch', { rast <- raster::raster(matrix(1:100, nrow=10), xmn=-180, xmx=180, ymn=-90, ymx=90, crs='+proj=longlat +datum=WGS84') poly <- sf::st_buffer( sf::st_as_sfc('POINT(442944.5 217528.7)', crs=32145), 150000) expect_warning(exact_extract(rast, poly, weighted.mean, na.rm=TRUE), 'transformed to raster') }) test_that('Warning is raised on undefined CRS', { rast <- raster::raster(matrix(1:100, nrow=10), xmn=0, xmx=10, ymn=0, ymx=10) weights <- raster::raster(matrix(runif(100), nrow=10), xmn=0, xmx=10, ymn=0, ymx=10) poly <- make_circle(8, 4, 0.4, crs=NA_integer_) # neither has a defined CRS expect_silent(exact_extract(rast, poly, 'sum')) # only raster has defined CRS raster::crs(rast) <- '+proj=longlat +datum=WGS84' expect_warning(exact_extract(rast, poly, 'sum'), 'assuming .* same CRS .* raster') # weights have no defined CRS expect_warning(exact_extract(rast, poly, 'weighted_mean', weights=weights), 'No CRS .* weighting raster.* assuming .* same CRS') # both have defined crs sf::st_crs(poly) <- sf::st_crs(rast) expect_silent(exact_extract(rast, poly, 'sum')) # only polygons have defined crs raster::crs(rast) <- NULL expect_warning(exact_extract(rast, poly, 'sum'), 'assuming .* same CRS .* polygon') }) test_that('Error thrown if value raster and weighting raster have different crs', { values <- make_square_raster(runif(100), crs=NA) weights <- make_square_raster(runif(100), crs=NA) poly <- make_circle(8, 4, 1.5, crs=NA_real_) # no CRS for values or weights exact_extract(values, poly, 'weighted_mean', weights=weights) # values have defined CRS, weights do not raster::crs(values) <- '+proj=longlat +datum=WGS84' raster::crs(weights) <- '+proj=longlat +datum=NAD83' expect_error( exact_extract(values, poly, 'weighted_mean', weights=weights), 'Weighting raster does not have .* same CRS as value raster') }) test_that('Error thrown if value raster and weighting raster have incompatible grids', { poly <- make_circle(5, 4, 2, NA_integer_) values <- raster::raster(matrix(runif(10*10), nrow=10), xmn=0, xmx=10, ymn=0, ymx=10) # weights have same extent as values, higher resolution weights <- raster::raster(matrix(runif(100*100), nrow=100), xmn=0, xmx=10, ymn=0, ymx=10) exact_extract(values, poly, 'weighted_mean', weights=weights) # weights have same extent as values, lower resolution weights <- raster::raster(matrix(1:4, nrow=2), xmn=0, xmx=10, ymn=0, ymx=10) exact_extract(values, poly, 'weighted_mean', weights=weights) # weights have offset extent from values, same resolution, compatible origin weights <- raster::raster(matrix(runif(10*10), nrow=2), xmn=1, xmx=11, ymn=2, ymx=12) exact_extract(values, poly, 'weighted_mean', weights=weights) # weights have offset extent from values, same resolution, incompatible origin weights <- raster::raster(matrix(runif(10*10), nrow=2), xmn=0.5, xmx=10.5, ymn=2, ymx=12) expect_error(exact_extract(values, poly, 'weighted_mean', weights=weights), 'Incompatible extents') }) test_that('Error is raised if function has unexpected signature', { rast <- make_square_raster(1:100) poly <- make_circle(5, 5, 3, sf::st_crs(rast)) # unweighted, standard form for (fun in c(length, sum, median, mean, sd)) { expect_error( exact_extract(rast, poly, fun), 'function .* not .* of the form') } expect_silent(exact_extract(rast, poly, weighted.mean)) # unweighted, summarize_df expect_error( exact_extract(rast, poly, function() {}, summarize_df = TRUE), 'function .* not .* of the form' ) # weighted, standard form expect_error( exact_extract(rast, poly, weights = rast, fun = function(x, frac) {}), 'function .* not .* of the form') expect_error( exact_extract(rast, poly, weights = rast, fun = function(x) {}), 'function .* not .* of the form') expect_error( exact_extract(rast, poly, weights = rast, fun = function() {}), 'function .* not .* of the form') # weighted, summarize_df expect_error( exact_extract(rast, poly, weights = rast, fun = function() {}, summarize_df = TRUE), 'function .* not .* of the form' ) }) test_that('Error is raised for unknown summary operation', { rast <- make_square_raster(1:100) poly <- make_circle(5, 5, 3, sf::st_crs(rast)) expect_error(exact_extract(rast, poly, 'whatimean'), 'Unknown stat') }) test_that('Error is raised if arguments passed without R summary function', { rast <- make_square_raster(1:100) poly <- make_circle(5, 5, 3, sf::st_crs(rast)) expect_error(exact_extract(rast, poly, 'sum', na.rm=TRUE), 'does not accept additional arguments') expect_error( exact_extract(rast, poly, cookie = FALSE), 'Unexpected arguments' ) }) test_that('Error is raised for invalid max_cells_in_memory', { rast <- make_square_raster(1:100) poly <- make_circle(5, 5, 3, sf::st_crs(rast)) expect_error(exact_extract(rast, poly, 'mean', max_cells_in_memory=-123), 'Invalid.*max_cells') expect_error( exact_extract(rast, poly, 'mean', max_cells_in_memory = NA), 'must be a single numeric') expect_error( exact_extract(rast, poly, 'mean', max_cells_in_memory = numeric()), 'must be a single numeric') expect_error( exact_extract(rast, poly, 'mean', max_cells_in_memory = integer()), 'must be a single numeric') expect_error( exact_extract(rast, poly, 'mean', max_cells_in_memory = NULL), 'must be a single numeric') }) test_that('Error is thrown when using include_* with named summary operation', { rast <- make_square_raster(1:100) circles <- st_sf( fid = c(2, 9), size = c('large', 'small'), geometry = c( make_circle(5, 4, 2, sf::st_crs(rast)), make_circle(3, 1, 1, sf::st_crs(rast)))) expect_error(exact_extract(rast, circles, 'sum', include_xy = TRUE), 'include_xy must be FALSE') expect_error(exact_extract(rast, circles, 'sum', include_area = TRUE), 'include_area must be FALSE') expect_error(exact_extract(rast, circles, 'sum', include_cell = TRUE), 'include_cell must be FALSE') expect_error(exact_extract(rast, circles, 'sum', include_cols = 'fid'), 'include_cols not supported') }) test_that('Error is thrown when using include_cols or append_cols with nonexisting columns', { rast <- make_square_raster(1:100) circles <- st_sf( fid = c(2, 9), size = c('large', 'small'), geometry = c( make_circle(5, 4, 2, sf::st_crs(rast)), make_circle(3, 1, 1, sf::st_crs(rast)))) # append_cols specified but sfc has no attribute columns expect_error( exact_extract(rast, st_geometry(circles), 'mean', append_cols = 'fid', progress = FALSE), 'only supported for sf') expect_error( exact_extract(rast, st_geometry(circles), weighted.mean, append_cols = 'fid', progress = FALSE), 'only supported for sf') # append_cols specified for misspelled column expect_error( exact_extract(rast, circles, 'mean', append_cols = 'fidd', progress = FALSE), 'undefined columns' ) expect_error( exact_extract(rast, circles, 'mean', append_cols = TRUE, progress = FALSE), 'must be a list of column names' ) expect_error( exact_extract(rast, circles, weighted.mean, append_cols = 'fidd', progress = FALSE), 'undefined columns' ) # include_cols specified for sfc expect_error( exact_extract(rast, st_geometry(circles), include_cols = 'fidd', progress = FALSE), 'only supported for sf' ) # include_cols specified for misspelled column expect_error( exact_extract(rast, circles, include_cols = 'fidd', progress = FALSE), 'undefined columns' ) # include_cols has wrong type expect_error( exact_extract(rast, circles, include_cols = TRUE, progress = FALSE), 'must be a list of column names' ) }) test_that('Error is thrown if quantiles not specified or not valid', { rast <- make_square_raster(1:100) square <- make_rect(2, 2, 4, 4, crs=sf::st_crs(rast)) expect_error(exact_extract(rast, square, 'quantile'), 'Quantiles not specified') expect_error(exact_extract(rast, square, 'quantile', quantiles=NA), 'must be between 0 and 1') expect_error(exact_extract(rast, square, 'quantile', quantiles=c(0.5, 1.1)), 'must be between 0 and 1') expect_error(exact_extract(rast, square, 'quantile', quantiles=numeric()), 'Quantiles not specified') }) test_that('Warning emitted when value raster is disaggregated', { r1 <- make_square_raster(1:100) r2 <- make_square_raster(runif(100)) r1d <- raster::disaggregate(r1, 2) r2d <- raster::disaggregate(r2, 2) circle <- make_circle(2, 7, 3, sf::st_crs(r1)) # no warning, values and weights have same resolution expect_silent(exact_extract(r1, circle, weights=r2)) # no warning, values have higher resolution than weights expect_silent(exact_extract(r1d, circle, weights=r2)) # warning, weights have higher resolution than values expect_warning(exact_extract(r1, circle, weights=r2d), 'value .* disaggregated') }) test_that('Error raised when value raster is disaggregated and unweighted sum/count requested', { r1 <- make_square_raster(1:100) r1d <- raster::disaggregate(r1, 2) circle <- make_circle(2, 7, 3, sf::st_crs(r1)) # no error, requested operations either expect disaggregation # or are not impacted by it expect_silent(exact_extract(r1, circle, c('weighted_sum', 'weighted_mean', 'mean'), weights=r1d)) # on the other hand, "count" would be messed up by the disaggregation expect_error(exact_extract(r1, circle, c('weighted_sum', 'count'), weights=r1d), 'raster is disaggregated') # as would "sum" expect_error(exact_extract(r1, circle, c('weighted_sum', 'count'), weights=r1d), 'raster is disaggregated') # no problem if the weights are disaggregated, though expect_silent(exact_extract(r1d, circle, c('weighted_sum', 'count'), weights=r1)) }) test_that('We get an error if using stack_apply with incompatible stacks', { vals <- stack(replicate(3, make_square_raster(runif(100)))) names(vals) <- c('a', 'b', 'c') weights <- stack(replicate(2, make_square_raster(runif(100)))) names(weights) <- c('d', 'e') circle <- make_circle(2, 7, 3, sf::st_crs(vals)) expect_error( exact_extract(vals, circle, function(v, c, w) 1, weights=weights, stack_apply=TRUE), "Can't apply") }) test_that('Error thrown if summarize_df set where not applicable', { rast <- make_square_raster(1:100) circle <- make_circle(7.5, 5.5, 4, sf::st_crs(rast)) expect_error( exact_extract(rast, circle, 'mean', summarize_df = TRUE), 'can only be used when .* function') expect_error( exact_extract(rast, circle, summarize_df = TRUE), 'can only be used when .* function') }) test_that('Error thrown if stack_apply set where not applicable', { rast <- make_square_raster(1:100) circle <- make_circle(7.5, 5.5, 4, sf::st_crs(rast)) expect_error( exact_extract(rast, circle, stack_apply = TRUE), 'can only be used when .* is a summary operation or function' ) }) test_that('Error thrown if append_cols set where not applicable', { rast <- make_square_raster(1:100) circle <- st_sf(make_circle(7.5, 5.5, 4, sf::st_crs(rast))) expect_error( exact_extract(rast, circle, append_cols = 'name'), 'can only be used when .* is a summary operation or function' ) }) test_that('Error thrown if scalar args have length != 1', { rast <- make_square_raster(1:100) circle <- make_circle(7.5, 5.5, 4, sf::st_crs(rast)) flags <- c( 'coverage_area', 'force_df', 'full_colnames', 'include_area', 'include_cell', 'include_xy', 'progress', 'stack_apply', 'summarize_df') for (flag in flags) { base_args <- list(rast, circle) for (bad_value in list(logical(), c(TRUE, TRUE), NA)) { args <- base_args args[[flag]] <- bad_value expect_error( do.call(exact_extract, args), 'must be TRUE or FALSE' ) } } }) test_that('Error thrown if fun is empty', { rast <- make_square_raster(1:100) circle <- make_circle(7.5, 5.5, 4, sf::st_crs(rast)) expect_error( exact_extract(rast, circle, character()), 'No summary operations' ) }) test_that('Error thrown if fun is incorrect type', { rast <- make_square_raster(1:100) circle <- make_circle(7.5, 5.5, 4, sf::st_crs(rast)) expect_error( exact_extract(rast, circle, 44), 'must be a character vector, function') expect_error( exact_extract(rast, circle, list(function() {}, function() {})), 'must be a character vector, function') }) test_that('Error thrown if default values have incorrect type/length', { rast <- make_square_raster(1:100) circle <- make_circle(7.5, 5.5, 4, sf::st_crs(rast)) expect_error( exact_extract(rast, circle, 'mean', default_value = numeric()), 'must be a single numeric value' ) expect_error( exact_extract(rast, circle, 'mean', default_value = c(3, 8)), 'must be a single numeric value' ) expect_error( exact_extract(rast, circle, 'mean', default_value = NULL), 'must be a single numeric value' ) expect_error( exact_extract(rast, circle, 'mean', default_value = FALSE), 'must be a single numeric value' ) }) exactextractr/tests/testthat/test_exact_resample.R0000644000176200001440000000633014776002150022317 0ustar liggesusers# Copyright (c) 2020-2022 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. context('exact_resample') test_that("exact_resample preserves values", { set.seed(123) # generate a random raster with a strange extent and resolution src <- raster::raster(matrix(runif(10000), nrow=100), xmn=runif(1), xmx=runif(1) + 9, ymn=runif(1), ymx=runif(1) + 9) # resample it to a raster with a larger grid with different resolution dst <- raster::raster(xmn=0, xmx=10, ymn=0, ymx=10, res=c(1, 2), crs=raster::crs(src)) dst <- exact_resample(src, dst, 'sum') # total values should be preserved expect_equal(cellStats(src, 'sum'), cellStats(dst, 'sum')) # resample it to a raster with a larger grid and a smaller resolution dst <- raster::raster(xmn=0, xmx=10, ymn=0, ymx=10, res=c(0.01, 0.02), crs=raster::crs(src)) dst <- exact_resample(src, dst, 'sum') # total values should be preserved expect_equal(cellStats(src, 'sum'), cellStats(dst, 'sum')) }) test_that("error thrown if multiple or no stats provided", { src <- make_square_raster(1:100) dst <- make_square_raster(1:4) expect_error( exact_resample(src, dst, c('sum', 'mean')), 'Only a single') expect_error( exact_resample(src, dst, character()), 'Only a single') }) test_that("error thrown if weighted stat provided", { r <- raster::raster(resolution = 2) target <- raster::shift(r, 2.5, 1) expect_error( exact_resample(r, target, fun = "weighted_mean"), 'cannot be used for resampling' ) }) test_that("error thrown if rasters have different CRS", { src <- make_square_raster(1:100, crs='+init=epsg:4326') dst <- make_square_raster(1:100, crs='+init=epsg:4269') expect_error( exact_resample(src, dst, 'sum'), 'same CRS') }) test_that("warning raised if one CRS undefined", { a <- make_square_raster(1:100, crs='+init=epsg:4326') b <- make_square_raster(1:100, crs=NA) expect_warning( exact_resample(a, b, 'sum'), 'No CRS specified for destination' ) expect_warning( exact_resample(b, a, 'sum'), 'No CRS specified for source' ) }) test_that("stats requiring stored values can be used", { # https://github.com/isciences/exactextractr/issues/47 r <- raster::raster(resolution = 2) target <- raster::shift(r, 2.5, 1) set.seed(1111) raster::values(r) = as.integer(round(rnorm(raster::ncell(r), 0, 1))) vals <- unique(raster::getValues(r)) mode_vals <- sort(unique(raster::getValues(exactextractr::exact_resample(r, target, fun = "mode")))) expect_true(length(mode_vals) > 1) expect_true(all(mode_vals %in% vals)) }) exactextractr/tests/testthat/helper_functions.R0000644000176200001440000000324214776002150021632 0ustar liggesusers# Copyright (c) 2018-2022 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License ta http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. default_proj <- '+init=epsg:26918' # UTM 18N; avoid wgs84 to keep cartesian calcs in sf make_rect <- function(xmin, ymin, xmax, ymax, crs) { sf::st_sfc( sf::st_polygon( list( matrix( c(xmin, ymin, xmax, ymin, xmax, ymax, xmin, ymax, xmin, ymin), ncol=2, byrow=TRUE))), crs=crs) } make_circle <- function(x, y, r, crs) { suppressWarnings(sf::st_buffer( sf::st_sfc( sf::st_point(c(x, y)), crs=crs), r)) } make_square_raster <- function(vals, crs=default_proj) { n <- sqrt(length(vals)) stopifnot(as.integer(n) == n) raster::raster(matrix(vals, nrow=n, byrow=TRUE), xmn=0, xmx=n, ymn=0, ymx=n, crs=crs) } make_square_rast <- function(vals, crs=default_proj) { n <- sqrt(length(vals)) stopifnot(as.integer(n) == n) x <- terra::rast(nrows = n, ncols = n, xmin=0, xmax=n, ymin=0, ymax=n, crs = gsub("+init=", "", crs, fixed = TRUE)) terra::values(x) <- vals x } exactextractr/tests/testthat/test_exact_extract_eager_load.R0000644000176200001440000001051014776002150024316 0ustar liggesusers# Copyright (c) 2018-2022 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. context('exact_extract eager loading') test_that("message emitted when working area doesn't fit in memory", { rast_fname <- system.file(file.path('sao_miguel', 'clc2018_v2020_20u1.tif'), package = 'exactextractr') poly_fname <- system.file(file.path('sao_miguel', 'concelhos.gpkg'), package = 'exactextractr') r <- terra::rast(rast_fname) polys <- st_read(poly_fname, quiet = TRUE) # no output when everything fits in memory capture.output({ msg <- capture_messages({ exact_extract(r, polys, 'mode', progress = TRUE, max_cells_in_memory = 1e7) }) }) expect_equal(msg, character()) # message emitted when it doesn't fit capture.output({ expect_message( exact_extract(r, polys, 'mode', progress = TRUE, max_cells_in_memory = 1e6), 'Cannot preload' ) }) # if progress is disabled, so are hints expect_silent( exact_extract(r, polys, 'mode', progress = FALSE, max_cells_in_memory = 1e6) ) # get additional warning by blowing out the GDAL block cache prevCacheSize <- terra::gdalCache() terra::gdalCache(1) capture.output({ expect_message( exact_extract(r, polys, 'mode', max_cells_in_memory = 1e6), 'GDAL block size cache is only 1 MB' ) }) # get additional warning if we are using a RasterStack capture.output({ expect_message( exact_extract(stack(r), polys, 'mode', max_cells_in_memory = 1e6), 'It is recommended to use a SpatRaster' ) }) terra::gdalCache(prevCacheSize) }) test_that('cropping does not introduce grid incompatibility', { rast_fname <- system.file(file.path('sao_miguel', 'clc2018_v2020_20u1.tif'), package = 'exactextractr') poly_fname <- system.file(file.path('sao_miguel', 'concelhos.gpkg'), package = 'exactextractr') weight_fname <- system.file(file.path('sao_miguel', 'gpw_v411_2020_density_2020.tif'), package = 'exactextractr') r <- terra::rast(rast_fname) p <- st_read(poly_fname, quiet = TRUE) w <- terra::rast(weight_fname) expect_silent({ exact_extract(r, p, weights = w, grid_compat_tol = 1e-3, progress = FALSE) }) }) test_that("eager loading does not change values", { # this will fail if terra::crop is not called with snap = 'out' rast_fname <- system.file(file.path('sao_miguel', 'clc2018_v2020_20u1.tif'), package = 'exactextractr') poly_fname <- system.file(file.path('sao_miguel', 'concelhos.gpkg'), package = 'exactextractr') weight_fname <- system.file(file.path('sao_miguel', 'gpw_v411_2020_density_2020.tif'), package = 'exactextractr') r <- terra::rast(rast_fname) p <- st_read(poly_fname, quiet = TRUE) w <- terra::rast(weight_fname) no_eager_load <- exact_extract(r, p, weights = w, include_xy = TRUE, include_cell = TRUE, max_cells_in_memory = 2000, progress = FALSE) eager_load <- exact_extract(r, p, weights = w, include_xy = TRUE, include_cell = TRUE, progress = FALSE) expect_equal(eager_load, no_eager_load, tol = 2e-7) }) test_that('eager loading does not error when geometry is outside extent of raster', { ras <- terra::rast(matrix(1:100, nrow=10)) touches_corner <- make_rect(xmin = 10, xmax = 20, ymin = 10, ymax = 20, crs = sf::st_crs(ras)) loaded <- .eagerLoad(ras, touches_corner, Inf, '') expect_equal( nrow(exact_extract(loaded, touches_corner)[[1]]), 0) }) exactextractr/tests/testthat/test_num_expected_args.R0000644000176200001440000000332114776003326023022 0ustar liggesuserscontext(".num_expected_args") test_that( desc = 'Number of arguments that a `fun` expects a user to supply matches expectations. That is, the number of non-... arguments that a function expects that don\'t have a default value.', code = { # Function has two arguments and no defaults. weighted_mean_no_defaults <- function(df, weighted) { if(!weighted) { out <- mean(df$value) } else { out <- weighted.mean(x = df$value, w = df$value) } out } # Function has two arguments, but a non-NULL default is supplied # for one of them. weighted_mean_non_null_defaults <- function(df, weighted = TRUE) { if(!weighted) { out <- mean(df$value) } else { out <- weighted.mean(x = df$value, w = df$value) } out } # Function has two arguments, but a NULL default is supplied for # one of them. weighted_mean_null_defaults <- function(df, weighted = NULL) { if(is.null(weighted)) { out <- mean(df$value) } else { out <- weighted.mean(x = df$value, w = df$value) } out } # Function has two arguments and no defaults. Should count both # arguments without defaults. expect_equal(.num_expected_args(weighted_mean_no_defaults), 2) # Function has two arguments, but a non-NULL default is supplied # for one of them. Should only count the one argument without a # default. expect_equal(.num_expected_args(weighted_mean_non_null_defaults), 1) # Function has two arguments, but a NULL default is supplied for # one of them. Should only count the one argument without a default. expect_equal(.num_expected_args(weighted_mean_null_defaults), 1) } ) exactextractr/tests/testthat/test_exact_extract_terra.R0000644000176200001440000000665214776002150023365 0ustar liggesusers# Copyright (c) 2021-2022 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License ta http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. library(testthat) library(exactextractr) context('exact_extract (terra)') test_that('terra inputs supported (single layer)', { ras <- make_square_raster(1:100) terra_ras <- terra::rast(ras) circ <- make_circle(3, 2, 4, sf::st_crs(ras)) expect_equal( exact_extract(ras, circ), exact_extract(terra_ras, circ) ) expect_equal( exact_extract(ras, circ, 'mean'), exact_extract(terra_ras, circ, 'mean') ) expect_equal( exact_extract(ras, circ, weighted.mean), exact_extract(terra_ras, circ, weighted.mean) ) }) test_that('terra inputs supported (single layer, weighted)', { ras <- make_square_raster(1:100) ras_w <- sqrt(ras) terra_ras <- terra::rast(ras) terra_ras_w <- terra::rast(ras_w) circ <- make_circle(3, 2, 4, sf::st_crs(ras)) expect_equal( exact_extract(ras, circ, weights = ras_w), exact_extract(terra_ras, circ, weights = terra_ras_w) ) expect_equal( exact_extract(ras, circ, 'weighted_mean', weights = ras_w), exact_extract(terra_ras, circ, 'weighted_mean', weights = terra_ras_w) ) # mixed inputs supported: terra values, raster weights expect_equal( exact_extract(ras, circ, 'weighted_mean', weights = ras_w), exact_extract(terra_ras, circ, 'weighted_mean', weights = ras_w) ) # mixed inputs supported: raster values, terra weights expect_equal( exact_extract(ras, circ, 'weighted_mean', weights = ras_w), exact_extract(ras, circ, 'weighted_mean', weights = terra_ras_w) ) expect_equal( exact_extract(ras, circ, weighted.mean), exact_extract(terra_ras, circ, weighted.mean) ) }) test_that('terra inputs supported (multi-layer)', { stk <- raster::stack(list(a = make_square_raster(1:100), b = make_square_raster(101:200))) terra_stk <- terra::rast(stk) circ <- make_circle(3, 2, 4, sf::st_crs(stk)) expect_equal( exact_extract(stk, circ, 'mean'), exact_extract(terra_stk, circ, 'mean') ) expect_equal( exact_extract(stk, circ), exact_extract(terra_stk, circ) ) }) test_that('terra inputs supported (weighted, multi-layer)', { stk <- raster::stack(list(a = make_square_raster(1:100), a = make_square_raster(101:200))) stk <- terra::rast(stk) names(stk) <- c('a', 'a') ras <- terra::rast(make_square_raster(runif(100))) ras <- terra::disagg(ras, 2) circ <- make_circle(3, 2, 4, sf::st_crs(ras)) expect_error( exact_extract(stk, circ, 'mean'), 'names.*must be unique' ) }) test_that('include_* arguments supported for terra inputs', { ras <- make_square_raster(1:100) terra_ras <- terra::rast(ras) circ <- make_circle(3, 2, 4, sf::st_crs(ras)) expect_equal( exact_extract(terra_ras, circ, include_cell = TRUE, include_xy = TRUE), exact_extract(ras, circ, include_cell = TRUE, include_xy = TRUE) ) }) exactextractr/tests/testthat/test_exact_extract_include_args.R0000644000176200001440000001632114776002150024701 0ustar liggesusers# Copyright (c) 2018-2022 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License ta http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. library(testthat) library(exactextractr) context('exact_extract include* arguments') test_that('when include_xy = TRUE, center coordinates area included in the output', { rast <- raster::raster(matrix(1:100, nrow=10), xmn=0, xmx=10, ymn=0, ymx=10, crs='+proj=longlat +datum=WGS84') poly <- sf::st_sfc(sf::st_polygon( list( matrix( c(3.5, 4.4, 7.5, 4.5, 7.5, 6.5, 3.5, 6.5, 3.5, 4.4), ncol=2, byrow=TRUE ) ) ), crs=sf::st_crs(rast)) results <- exact_extract(rast, poly, include_xy=TRUE, include_cell=TRUE)[[1]] # check that correct ranges of X,Y values are output expect_equal( c(3.5, 4.5, 5.5, 6.5, 7.5), sort(unique(results[, 'x']))) expect_equal( c(4.5, 5.5, 6.5), sort(unique(results[, 'y']))) expect_equal(results[, 'cell'], raster::cellFromXY(rast, results[, c('x', 'y')])) # check the XY values of an individual cell with a known coverage fraction expect_equal( results[results[, 'x']==3.5 & results[,'y']==4.5, 'coverage_fraction'], 0.2968749999999998, tolerance=1e-8, check.attributes=FALSE) # we can also send the weights to a callback exact_extract(rast, sf::st_sf(data.frame(id=1), geom=poly), include_xy=TRUE, fun=function(values, weights) { expect_equal(3, ncol(values)) }, progress=FALSE) }) test_that('We can use the stack_apply argument with include_xy and include_cols', { set.seed(123) stk <- raster::stack(list(a = make_square_raster(runif(100)), b = make_square_raster(runif(100)))) circles <- c( make_circle(5, 4, 2, sf::st_crs(stk)), make_circle(3, 1, 1, sf::st_crs(stk))) result <- exact_extract(stk, circles, include_xy = TRUE, stack_apply = TRUE, progress = FALSE, function(df, frac) { weighted.mean(df$value[df$y > 1], frac[df$y > 1]) }) expect_named(result, c('fun.a', 'fun.b')) }) test_that('when include_area = TRUE, cell areas are included in output (geographic) and are accurate to 1%', { rast <- raster::raster(matrix(1:54000, ncol=360), xmn=-180, xmx=180, ymn=-65, ymx=85, crs='+proj=longlat +datum=WGS84') accuracy_pct_tol <- 0.01 suppressMessages({ circle <- make_circle(0, 45, 15, crs=st_crs(rast)) }) results <- exact_extract(rast, circle, include_cell = TRUE, include_area = TRUE)[[1]] expected_areas <- raster::area(rast)[results$cell] actual_areas <- results$area / 1e6 expect_true(all(abs(actual_areas - expected_areas) / expected_areas < accuracy_pct_tol)) }) test_that('when include_area = TRUE, cell areas are included in output (projected)', { rast_utm <- make_square_raster(1:100) circle <- make_circle(5, 5, 5, crs=st_crs(rast_utm)) areas <- exact_extract(rast_utm, circle, include_area = TRUE)[[1]]$area expect_true(all(areas == 1)) }) test_that('include_cols copies columns from the source data frame to the returned data frames', { rast <- make_square_raster(1:100) circles <- st_sf( fid = c(2, 9), size = c('large', 'small'), geometry = c( make_circle(5, 4, 2, sf::st_crs(rast)), make_circle(3, 1, 1, sf::st_crs(rast)))) combined_result <- do.call(rbind, exact_extract(rast, circles, include_cols = 'fid', progress = FALSE)) expect_named(combined_result, c('fid', 'value', 'coverage_fraction')) }) test_that('When disaggregating values, xy coordinates refer to disaggregated grid', { rast <- make_square_raster(1:100) rast2 <- raster::disaggregate(rast, 4) circle <- make_circle(7.5, 5.5, 0.4, sf::st_crs(rast)) xy_disaggregated <- exact_extract(rast2, circle, include_xy = TRUE)[[1]][, c('x', 'y')] suppressWarnings({ xy_weighted <- exact_extract(rast, circle, include_xy = TRUE, weights = rast2)[[1]][, c('x', 'y')] xy_weighted2 <- exact_extract(rast2, circle, include_xy = TRUE, weights = rast)[[1]][, c('x', 'y')] }) expect_equal(xy_weighted, xy_disaggregated) expect_equal(xy_weighted2, xy_disaggregated) }) test_that('When value and weighting rasters have different grids, cell numbers refer to value raster', { anom <- raster(xmn=-180, xmx=180, ymn=-90, ymx=90, res=10) values(anom) <- rnorm(length(anom)) pop <- raster(xmn=-180, xmx=180, ymn=-65, ymx=85, res=5) values(pop) <- rlnorm(length(pop)) circle <- make_circle(17, 21, 18, sf::st_crs(anom)) suppressWarnings({ extracted <- exact_extract(anom, circle, weights=pop, include_cell=TRUE)[[1]] }) expect_equal(extracted$value, anom[extracted$cell]) }) test_that('include_ arguments supported with weighted summary function', { rast1 <- 5 + make_square_raster(1:100) rast2 <- make_square_raster(runif(100)) circle <- st_sf( id = 77, make_circle(7.5, 5.5, 4, sf::st_crs(rast1))) x <- exact_extract(rast1, circle, function(v, c, w) { expect_is(v, 'data.frame') expect_named(v, c('value', 'id')) expect_true(all(v$id == 77)) expect_is(c, 'numeric') expect_is(w, 'numeric') }, weights=rast2, include_cols = 'id') x <- exact_extract(rast1, circle, function(v, c, w) { expect_is(v, 'data.frame') expect_named(v, c('value', 'id', 'x', 'y', 'cell')) expect_true(all(v$id == 77)) expect_equal(v$value, rast1[v$cell]) expect_equal(w, rast2[v$cell]) expect_equal(v$x, raster::xFromCell(rast1, v$cell)) expect_equal(v$y, raster::yFromCell(rast1, v$cell)) expect_is(c, 'numeric') expect_is(w, 'numeric') }, weights=rast2, include_cols = 'id', include_cell = TRUE, include_xy = TRUE) }) test_that('we get a zero-row data frame for a polygon not intersecting a raster', { # https://github.com/isciences/exactextractr/issues/68 rast <- raster(matrix(0, nrow = 100, ncol = 100)) nonoverlap_poly <- st_sf(st_sfc(st_polygon(list(matrix(c(0, 0, 1, 0, 1, -0.25, 0, -0.25, 0, 0), ncol = 2, byrow = TRUE))))) df <- exact_extract(rast, nonoverlap_poly)[[1]] expect_named(df, c('value', 'coverage_fraction')) expect_equal(nrow(df), 0) df <- exact_extract(rast, nonoverlap_poly, include_xy = TRUE)[[1]] expect_named(df, c('value', 'x', 'y', 'coverage_fraction')) expect_equal(nrow(df), 0) df <- exact_extract(rast, nonoverlap_poly, include_cell = TRUE)[[1]] expect_named(df, c('value', 'cell', 'coverage_fraction')) expect_equal(nrow(df), 0) df <- exact_extract(rast, nonoverlap_poly, include_area = TRUE)[[1]] expect_named(df, c('value', 'area', 'coverage_fraction')) expect_equal(nrow(df), 0) }) exactextractr/tests/testthat/test_helper_blocksize.R0000644000176200001440000000312614776002150022647 0ustar liggesusers# Copyright (c) 2021-2023 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License ta http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. context('block size detection') test_that('blockSize reports block size in row-col order', { landcov_fname <- system.file(file.path('sao_miguel', 'clc2018_v2020_20u1.tif'), package='exactextractr') expect_equal( .blockSize(raster::raster(landcov_fname)), c(2, 3840) ) expect_equal( .blockSize(terra::rast(landcov_fname)), c(2, 3840) ) # netCDF uses a different code path, so copy our test input to netCDF format # and repeat. GDAL doesn't let us control the block size, so hopefully it # is stable. if ('netCDF' %in% terra::gdal(drivers=TRUE)$name) { nc_fname <- tempfile(fileext = '.nc') suppressWarnings({ terra::writeRaster(terra::rast(landcov_fname), nc_fname, gdal=c('FORMAT=NC4', 'COMPRESS=DEFLATE')) }) expect_equal( .blockSize(raster::raster(nc_fname)), c(1, 3840) ) expect_equal( .blockSize(terra::rast(nc_fname)), c(1, 3840) ) file.remove(nc_fname) } }) exactextractr/tests/testthat/test_rasterize.R0000644000176200001440000000564314776002150021341 0ustar liggesusers# Copyright (c) 2022 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. context('rasterize_polygons') test_that('value is assigned to polygon with greatest coverage area', { polys <- st_as_sf( data.frame(id = 1:3, geom = c( 'POLYGON ((10 0, 10 5, 5 5, 10 0))', 'POLYGON ((0 0, 10 0, 5 5, 1 10, 0 10, 0 0))', 'POLYGON ((5 5, 10 5, 10 10, 1 10, 5 5))' )), wkt = 'geom' ) rt <- terra::rast(xmin = 0, xmax = 10, ymin = 0, ymax = 10, res = 2) r <- rasterize_polygons(polys, rt) # lower-right is a tie, so it goes to the first feature encountered expect_equal( extract(r, cbind(9, 1))[[1]], 1) # center tell is touched by all three, goes to polygon that covers the greatest area expect_equal( extract(r, cbind(5, 5))[[1]], 2) }) test_that('min_coverage excludes cells with small coverage area', { rt <- terra::rast(xmin = 0, xmax = 10, ymin = 0, ymax = 10, res = 1) circ <- make_circle(5, 5, 3.5, crs=st_crs(rt)) circ_pieces <- st_sf(st_intersection(circ, st_make_grid(circ, 1))) cfrac <- coverage_fraction(rt, circ, crop = FALSE)[[1]] # by default, all touched cells are included in output r <- rasterize_polygons(circ_pieces, rt) expect_equal( values(cfrac) > 0, !is.na(values(r)) ) # min_coverage excludes cells with small coverage area r <- rasterize_polygons(circ_pieces, rt, min_coverage = 0.5) expect_equal( values(cfrac) > 0.5, !is.na(values(r)) ) }) test_that('input type is preserved', { rt <- terra::rast(xmin = 0, xmax = 10, ymin = 0, ymax = 10, res = 2) rr <- raster::raster(rt) circ <- st_sf(make_circle(5, 5, 3.5, crs=st_crs(rt))) r <- rasterize_polygons(circ, rt) expect_s4_class(r, 'SpatRaster') r <- rasterize_polygons(circ, rr) expect_s4_class(r, 'RasterLayer') }) test_that('no error when polygon does not intersect raster', { rt <- terra::rast(xmin = 0, xmax = 10, ymin = 0, ymax = 10, res = 2, crs=NA) circ <- st_sf(make_circle(500, 500, 3.5, crs=st_crs(rt))) r <- rasterize_polygons(circ, rt) expect_true(all(is.na(values(r)))) }) test_that('no error when polygon partially intersects raster', { rt <- terra::rast(xmin = 0, xmax = 10, ymin = 0, ymax = 10, res = 2, crs=NA) circ <- st_sf(make_circle(10, 5, 3.5, crs=st_crs(rt))) expect_invisible( r <- rasterize_polygons(circ, rt) ) }) exactextractr/tests/testthat/test_coverage_fraction.R0000644000176200001440000001364614776002150023013 0ustar liggesusers# Copyright (c) 2018-2020 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. context('coverage_fraction') test_that("Coverage fraction function works", { # This test just verifies a successful journey from R # to C++ and back. The correctness of the algorithm # is tested at the C++ level. square <- sf::st_sfc(sf::st_polygon( list( matrix( c(0.5, 0.5, 2.5, 0.5, 2.5, 2.5, 0.5, 2.5, 0.5, 0.5), ncol=2, byrow=TRUE)))) rast <- raster::raster(xmn=0, xmx=3, ymn=0, ymx=3, nrows=3, ncols=3, crs=NA) weights <- coverage_fraction(rast, square)[[1]] expect_s4_class(weights, 'RasterLayer') expect_equal(as.matrix(weights), rbind( c(0.25, 0.5, 0.25), c(0.50, 1.0, 0.50), c(0.25, 0.5, 0.25) ), check.attributes=FALSE) }) test_that("Output can be cropped to the extent of the input feature", { square <- sf::st_sfc(sf::st_polygon( list( matrix( c(0.5, 0.5, 2.5, 0.5, 2.5, 2.5, 0.5, 2.5, 0.5, 0.5), ncol=2, byrow=TRUE)))) rast <- raster::raster(xmn=0, xmx=10, ymn=0, ymx=10, nrows=10, ncols=10, crs=NA) weights <- coverage_fraction(rast, square, crop=TRUE)[[1]] expect_equal(raster::res(weights), raster::res(rast)) expect_equal(raster::crs(weights), raster::crs(rast)) expect_equal(raster::extent(weights), raster::extent(0, 3, 0, 3)) }) test_that("When output is not cropped, cells outside of the processed area are 0, not NA", { square <- sf::st_sfc(sf::st_polygon( list( matrix( c(0.5, 0.5, 2.5, 0.5, 2.5, 2.5, 0.5, 2.5, 0.5, 0.5), ncol=2, byrow=TRUE)))) rast <- raster::raster(xmn=0, xmx=10, ymn=0, ymx=10, nrows=10, ncols=10, crs=NA) weights <- coverage_fraction(rast, square, crop=TRUE)[[1]] expect_false(any(is.na(as.matrix(weights)))) }) test_that('Raster returned by coverage_fraction has same properties as the input', { r <- raster::raster(xmn=391030, xmx=419780, ymn=5520000, ymx=5547400, crs=NA) raster::res(r) = c(100, 100) raster::values(r) <- 1:ncell(r) p <- sf::st_as_sfc('POLYGON((397199.680921053 5541748.05921053,402813.496710526 5543125.03289474,407103.299342105 5537246.41447368,398470.733552632 5533962.86184211,397199.680921053 5541748.05921053))') w <- coverage_fraction(r, p) expect_length(w, 1) expect_is(w[[1]], 'RasterLayer') expect_equal(raster::res(r), raster::res(w[[1]])) expect_equal(raster::extent(r), raster::extent(w[[1]])) expect_equal(raster::crs(r), raster::crs(w[[1]])) }) test_that('Raster returned by coverage_fraction has same properties as the input (terra)', { r <- terra::rast(xmin=391030, xmax=419780, ymin=5520000, ymax=5547400, crs='EPSG:32618') terra::res(r) = c(100, 100) terra::values(r) <- 1:ncell(r) p <- sf::st_as_sfc('POLYGON((397199.680921053 5541748.05921053,402813.496710526 5543125.03289474,407103.299342105 5537246.41447368,398470.733552632 5533962.86184211,397199.680921053 5541748.05921053))', crs = sf::st_crs(r)) w <- coverage_fraction(r, p) expect_length(w, 1) expect_is(w[[1]], 'SpatRaster') expect_equal(terra::res(r), terra::res(w[[1]])) expect_equal(terra::ext(r), terra::ext(w[[1]])) expect_equal(terra::crs(r), terra::crs(w[[1]])) }) test_that('Coverage fractions are exact', { r <- raster::raster(xmn=391030, xmx=419780, ymn=5520000, ymx=5547400, crs=NA) raster::res(r) = c(100, 100) raster::values(r) <- 1:ncell(r) p <- sf::st_as_sfc('POLYGON((397199.680921053 5541748.05921053,402813.496710526 5543125.03289474,407103.299342105 5537246.41447368,398470.733552632 5533962.86184211,397199.680921053 5541748.05921053))') w <- coverage_fraction(r, p) cell_area <- prod(raster::res(w[[1]])) ncells <- raster::cellStats(w[[1]], 'sum') expect_equal(sf::st_area(sf::st_geometry(p)), ncells*cell_area) }) test_that('Warning is raised on CRS mismatch', { rast <- raster::raster(matrix(1:100, nrow=10), xmn=-75, xmx=-70, ymn=41, ymx=46, crs='+proj=longlat +datum=WGS84') poly <- sf::st_buffer( sf::st_as_sfc('POINT(442944.5 217528.7)', crs=32145), 150000) expect_warning(coverage_fraction(rast, poly), 'transformed to raster') }) test_that('Warning is raised on undefined CRS', { rast <- raster::raster(matrix(1:100, nrow=10), xmn=0, xmx=10, ymn=0, ymx=10) poly <- sf::st_buffer(sf::st_as_sfc('POINT(8 4)'), 0.4) # neither has a defined CRS expect_silent(coverage_fraction(rast, poly)) # only raster has defined CRS raster::crs(rast) <- '+proj=longlat +datum=WGS84' expect_warning(coverage_fraction(rast, poly), 'assuming .* same CRS .* raster') # both have defined crs sf::st_crs(poly) <- sf::st_crs(rast) expect_silent(coverage_fraction(rast, poly)) # only polygons have defined crs raster::crs(rast) <- NULL expect_warning(coverage_fraction(rast, poly), 'assuming .* same CRS .* polygon') }) test_that('Z dimension is ignored, if present', { # see https://github.com/isciences/exactextractr/issues/26 polyz <- st_as_sfc('POLYGON Z ((1 1 0, 4 1 0, 4 4 0, 1 1 0))') poly <- st_as_sfc('POLYGON ((1 1, 4 1, 4 4, 1 1))') values <- raster(matrix(1:25, nrow=5, ncol=5, byrow=TRUE), xmn=0, xmx=5, ymn=0, ymx=5) expect_equal(coverage_fraction(values, poly), coverage_fraction(values, polyz)) }) exactextractr/tests/testthat/test_exact_extract.R0000644000176200001440000013736314776002150022174 0ustar liggesusers# Copyright (c) 2018-2021 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License ta http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. library(testthat) library(exactextractr) context('exact_extract') test_that("Basic stat functions work", { # This test just verifies a successful journey from R # to C++ and back. The correctness of the algorithm # is tested at the C++ level. data <- matrix(1:9, nrow=3, byrow=TRUE) rast <- raster::raster(data, xmn=0, xmx=3, ymn=0, ymx=3, crs='+proj=longlat +datum=WGS84') square <- make_rect(0.5, 0.5, 2.5, 2.5, sf::st_crs(rast)) dat <- exact_extract(rast, square) # Calling without a function returns a data frame with values and coverage fractions expect_equal(dat[[1]], data.frame(value=1:9, coverage_fraction=c(0.25, 0.5, 0.25, 0.5, 1, 0.5, 0.25, 0.5, 0.25)) ) # Calling with a function(w, v) returns the result of the function expect_equal(exact_extract(rast, square, fun=weighted.mean), 5) # Calling with a string computes a named operation from the C++ library expect_equal(exact_extract(rast, square, fun='count'), 4) expect_equal(exact_extract(rast, square, fun='mean'), 5) expect_equal(exact_extract(rast, square, fun='median'), 5) expect_equal(exact_extract(rast, square, fun='quantile', quantiles=0.25), 3.5) expect_equal(exact_extract(rast, square, fun='quantile', quantiles=0.75), 6.5) expect_equal(exact_extract(rast, square, fun='min'), 1) expect_equal(exact_extract(rast, square, fun='max'), 9) expect_equal(exact_extract(rast, square, fun='mode'), 5) expect_equal(exact_extract(rast, square, fun='majority'), 5) expect_equal(exact_extract(rast, square, fun='minority'), 1) expect_equal(exact_extract(rast, square, fun='variety'), 9) expect_equal(exact_extract(rast, square, fun='variance'), 5) expect_equal(exact_extract(rast, square, fun='stdev'), sqrt(5)) expect_equal(exact_extract(rast, square, fun='coefficient_of_variation'), sqrt(5)/5) # Can also do multiple stats at once expect_equal(exact_extract(rast, square, fun=c('min', 'max', 'mode')), data.frame(min=1, max=9, mode=5)) expect_equal(exact_extract(rast, c(square, square), fun=c('min', 'max', 'mode'), progress = FALSE), data.frame(min=c(1, 1), max=c(9, 9), mode=c(5, 5))) }) test_that('Weighted stat functions work', { data <- matrix(1:9, nrow=3, byrow=TRUE) rast <- raster::raster(data, xmn=0, xmx=3, ymn=0, ymx=3, crs='+proj=longlat +datum=WGS84') equal_weights <- raster::raster(matrix(1, nrow=3, ncol=3), xmn=0, xmx=3, ymn=0, ymx=3, crs='+proj=longlat +datum=WGS84') bottom_row_only <- raster::raster(rbind(c(0, 0, 0), c(0, 0, 0), c(1, 1, 1)), xmn=0, xmx=3, ymn=0, ymx=3, crs='+proj=longlat +datum=WGS84') square <- make_rect(0.5, 0.5, 2.5, 2.5, sf::st_crs(rast)) # equal weights expect_equal(exact_extract(rast, square, 'weighted_mean', weights=equal_weights), exact_extract(rast, square, 'mean')) expect_equal(exact_extract(rast, square, 'weighted_sum', weights=equal_weights), exact_extract(rast, square, 'sum')) expect_equal(exact_extract(rast, square, 'weighted_stdev', weights=equal_weights), exact_extract(rast, square, 'stdev')) expect_equal(exact_extract(rast, square, 'weighted_variance', weights=equal_weights), exact_extract(rast, square, 'variance')) # unequal weights expect_equal(exact_extract(rast, square, 'weighted_mean', weights=bottom_row_only), (0.25*7 + 0.5*8 + 0.25*9)/(0.25 + 0.5 + 0.25)) expect_equal(exact_extract(rast, square, 'weighted_sum', weights=bottom_row_only), (0.25*7 + 0.5*8 + 0.25*9)) expect_equal(exact_extract(rast, square, 'weighted_stdev', weights=bottom_row_only), 0.7071068, tolerance = 1e-7) # Weighted.Desc.Stat::w.sd(x = c(7, 8, 9), mu = c(0.25, 0.5, 0.25))) expect_equal(exact_extract(rast, square, 'weighted_variance', weights=bottom_row_only), 0.5) # Weighted.Desc.Stat::w.var(x = c(7, 8, 9), mu = c(0.25, 0.5, 0.25))) }) test_that('Grouped stat functions work', { rast <- raster::raster(matrix(rep(1:3, each = 3), nrow=3, byrow=TRUE), xmn=0, xmx=3, ymn=0, ymx=3, crs='+proj=longlat +datum=WGS84') weights <- raster::raster(matrix(rep(3:1, each = 3), nrow = 3, byrow=TRUE), xmn=0, xmx=3, ymn=0, ymx=3, crs='+proj=longlat +datum=WGS84') square1 <- make_rect(0.5, 0.5, 1.0, 1.0, sf::st_crs(rast)) square2 <- make_rect(0.5, 0.5, 2.5, 2.5, sf::st_crs(rast)) squares <- c(square1, square2) expect_equal( exact_extract(rast, squares, c('count', 'frac'), progress = FALSE), rbind( data.frame(count = 0.25, frac_1 = 0, frac_2 = 0, frac_3 = 1.00), data.frame(count = 4.00, frac_1 = 0.25, frac_2 = 0.5, frac_3 = 0.25))) expect_equal( exact_extract(rast, squares, c('weighted_frac', 'sum'), weights = weights, progress = FALSE), rbind( data.frame(weighted_frac_1 = 0, weighted_frac_2 = 0, weighted_frac_3 = 1, sum = 0.75), data.frame(weighted_frac_1 = 0.375, weighted_frac_2 = 0.5, weighted_frac_3 = 0.125, sum = 8) )) }) test_that('Grouped stat functions work (multilayer)', { rast <- raster::raster(matrix(rep(1:3, each = 3), nrow=3, byrow=TRUE), xmn=0, xmx=3, ymn=0, ymx=3, crs='+proj=longlat +datum=WGS84') rast <- raster::stack(list(a = rast, b = rast + 1)) weights <- raster::raster(matrix(rep(3:1, each = 3), nrow = 3, byrow=TRUE), xmn=0, xmx=3, ymn=0, ymx=3, crs='+proj=longlat +datum=WGS84') square1 <- make_rect(0.5, 0.5, 2.5, 2.5, sf::st_crs(rast)) square2 <- make_rect(0.5, 0.5, 1.0, 1.0, sf::st_crs(rast)) squares <- c(square1, square2) stats <- c('count', 'frac', 'quantile') quantiles <- c(0.25, 0.75) single_layer_a <- exact_extract(rast[['a']], squares, stats, quantiles = quantiles, progress = FALSE) single_layer_b <- exact_extract(rast[['b']], squares, stats, quantiles = quantiles, progress = FALSE) multi_layer <- exact_extract(rast, squares, stats, quantiles = quantiles, progress = FALSE) # for each layer (a, b) we get a column with the coverage fraction of each # value that occurs in a OR b. If a value does not occur for a given layer, # the values in its associated column will be zero. for (col in unique(c(names(single_layer_a), names(single_layer_b)))) { if (col %in% names(single_layer_a)) { expect_equal(single_layer_a[[col]], multi_layer[[paste(col, 'a', sep='.')]]) } else { expect_equal(c(0, 0), multi_layer[[paste(col, 'a', sep='.')]]) } if (col %in% names(single_layer_b)) { expect_equal(single_layer_b[[col]], multi_layer[[paste(col, 'b', sep='.')]]) } else { expect_equal(c(0, 0), multi_layer[[paste(col, 'b', sep='.')]]) } } }) test_that('Raster NA values are correctly handled', { data <- matrix(1:100, nrow=10, byrow=TRUE) data[7:10, 1:4] <- NA # cut out lower-left corner rast <- raster::raster(data, xmn=0, xmx=10, ymn=0, ymx=10, crs='+proj=longlat +datum=WGS84') # check polygon entirely within NA region circ <- sf::st_sfc(sf::st_buffer(sf::st_point(c(2,2)), 0.9), crs=sf::st_crs(rast)) expect_equal(0, exact_extract(rast, circ, 'count')) expect_equal(NA_real_, exact_extract(rast, circ, 'mean')) expect_equal(NA_real_, exact_extract(rast, circ, weighted.mean)) # check polygon partially within NA region square <- make_rect(3.5, 3.5, 4.5, 4.5, sf::st_crs(rast)) expect_equal(43.5, exact_extract(rast, square, 'sum')) expect_equal(NA_real_, exact_extract(rast, square, weighted.mean)) expect_equal(58, exact_extract(rast, square, weighted.mean, na.rm=TRUE)) }) test_that('MultiPolygons also work', { data <- matrix(1:100, nrow=10, byrow=TRUE) rast <- raster::raster(data, xmn=0, xmx=10, ymn=0, ymx=10, crs='+proj=longlat +datum=WGS84') multipoly <- sf::st_sfc( sf::st_multipolygon(list( sf::st_polygon( list( matrix( c(0.5, 0.5, 2.5, 0.5, 2.5, 2.5, 0.5, 2.5, 0.5, 0.5), ncol=2, byrow=TRUE))), sf::st_polygon( list( matrix( 4 + c(0.5, 0.5, 2.5, 0.5, 2.5, 2.5, 0.5, 2.5, 0.5, 0.5), ncol=2, byrow=TRUE))))), crs=sf::st_crs(rast)) expect_equal(exact_extract(rast, multipoly, fun='variety'), 18) }) test_that('sp inputs supported', { rast <- make_square_raster(1:100) circles <- c( make_circle(3, 2, 4, sf::st_crs(rast)), make_circle(7, 7, 2, sf::st_crs(rast)) ) circles_sf <- sf::st_sf(id = 1:2, geometry = circles) result <- exact_extract(rast, circles, 'mean', progress = FALSE) # SpatialPolygons circles_sp <- sf::as_Spatial(circles) result_sp <- exact_extract(rast, circles_sp, 'mean', progress = FALSE) expect_equal(result, result_sp) # SpatialPolygonsDataFrame circles_spdf <- sf::as_Spatial(circles_sf) result_spdf <- exact_extract(rast, circles_spdf, 'mean', progress = FALSE) expect_equal(result, result_spdf) }) test_that('Generic sfc_GEOMETRY works if the features are polygonal', { rast <- make_square_raster(1:100) polys <- st_as_sfc(c('POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0))', 'MULTIPOLYGON (((2 2, 4 2, 4 4, 2 4, 2 2)), ((4 4, 8 4, 8 8, 4 8, 4 4)))'), crs=sf::st_crs(rast)) expect_equal(exact_extract(rast, polys, 'count', progress = FALSE), c(4, 4+16)) }) test_that('GeometryCollections are supported if they are polygonal', { rast <- make_square_raster(1:100) gc <- st_as_sfc('GEOMETRYCOLLECTION( POLYGON ((0 0, 2 0, 2 2, 0 2, 0 0)), POLYGON ((2 2, 4 2, 4 4, 2 4, 2 2)))', crs = st_crs(rast)) mp <- st_as_sfc('MULTIPOLYGON (((0 0, 2 0, 2 2, 0 2, 0 0)), ((2 2, 4 2, 4 4, 2 4, 2 2)))', crs = st_crs(rast)) expect_equal( exact_extract(rast, gc), exact_extract(rast, mp) ) }) test_that('We ignore portions of the polygon that extend outside the raster', { rast <- raster::raster(matrix(1:(360*720), nrow=360), xmn=-180, xmx=180, ymn=-90, ymx=90, crs='+proj=longlat +datum=WGS84') rect <- make_rect(179.5, 0, 180.5, 1, sf::st_crs(rast)) cells_included <- exact_extract(rast, rect, include_xy=TRUE)[[1]][, c('x', 'y')] expect_equal(cells_included, data.frame(x=179.75, y=c(0.75, 0.25)), check.attributes=FALSE) index_included <- exact_extract(rast, rect, include_xy=TRUE, include_cell = TRUE)[[1]][, c('x', 'y', 'cell')] expect_equivalent(as.matrix(cells_included[c("x", "y")]), raster::xyFromCell(rast, index_included$cell)) expect_equal(index_included$cell, raster::cellFromXY(rast, cbind(cells_included$x, cells_included$y))) }) test_that('Additional arguments can be passed to fun', { data <- matrix(1:9, nrow=3, byrow=TRUE) rast <- raster::raster(data, xmn=0, xmx=3, ymn=0, ymx=3, crs='+proj=longlat +datum=WGS84') square <- make_rect(0.5, 0.5, 2.5, 2.5, sf::st_crs(rast)) exact_extract(rast, square, function(x, w, custom) { expect_equal(custom, 6) }, progress=FALSE, 6) }) test_that('We can extract values from a RasterStack', { rast <- raster::raster(matrix(1:16, nrow=4, byrow=TRUE), xmn=0, xmx=4, ymn=0, ymx=4, crs='+proj=longlat +datum=WGS84') stk <- raster::stack(rast, sqrt(rast)) square <- make_rect(0.5, 0.5, 2.5, 2.5, sf::st_crs(rast)) extracted <- exact_extract(stk, square)[[1]] expect_equal(names(extracted), c('layer.1', 'layer.2', 'coverage_fraction')) expect_equal(extracted[, 'layer.2'], sqrt(extracted[, 'layer.1'])) expect_equal(extracted[extracted$coverage_fraction==0.25, 'layer.1'], c(5, 7, 13, 15)) expect_equal(extracted[extracted$coverage_fraction==0.50, 'layer.1'], c(6, 9, 11, 14)) expect_equal(extracted[extracted$coverage_fraction==1.00, 'layer.1'], 10) }) test_that('We can pass extracted RasterStack values to an R function', { population <- raster::raster(matrix(1:16, nrow=4, byrow=TRUE), xmn=0, xmx=4, ymn=0, ymx=4, crs='+proj=longlat +datum=WGS84') income <- sqrt(population) square <- make_rect(0.5, 0.5, 2.5, 2.5, sf::st_crs(population)) mean_income <- exact_extract(raster::stack(list(population=population, income=income)), square, function(vals, weights) { weighted.mean(vals[, 'population']*vals[, 'income'], weights) }) expect_equal(mean_income, 32.64279, tolerance=1e-5) }) test_that('We can pass extracted RasterStack values to a C++ function', { rast <- raster::raster(matrix(runif(16), nrow=4), xmn=0, xmx=4, ymn=0, ymx=4, crs='+proj=longlat +datum=WGS84') square <- make_rect(0.5, 0.5, 2.5, 2.5, sf::st_crs(rast)) stk <- raster::stack(list(a=rast, b=sqrt(rast))) brk <- raster::brick(stk) for (input in c(stk, brk)) { expect_equal( exact_extract(input, square, 'variety'), data.frame(variety.a=9, variety.b=9) ) twostats <- exact_extract(input, square, c('variety', 'mean')) expect_equal(nrow(twostats), 1) expect_named(twostats, c('variety.a', 'variety.b', 'mean.a', 'mean.b')) } }) test_that('We can apply the same function to each layer of a RasterStack', { set.seed(123) stk <- raster::stack(list(a = make_square_raster(runif(100)), b = make_square_raster(runif(100)))) circles <- c( make_circle(5, 4, 2, sf::st_crs(stk)), make_circle(3, 1, 1, sf::st_crs(stk))) # by default layers are processed together expect_error( exact_extract(stk, circles, weighted.mean, progress=FALSE), 'must have the same length' ) # but we can process them independently with stack_apply means <- exact_extract(stk, circles, weighted.mean, progress=FALSE, stack_apply=TRUE) expect_named(means, c('weighted.mean.a', 'weighted.mean.b')) # results are same as we would get by processing layers independently for (i in 1:raster::nlayers(stk)) { expect_equal(means[, i], exact_extract(stk[[i]], circles, weighted.mean, progress=FALSE)) } }) test_that('Layers of a RasterBrick can be processed independently with stack_apply', { # https://github.com/isciences/exactextractr/issues/54 data <- matrix(1:100, nrow=10, byrow=TRUE) data[7:10, 1:4] <- NA # cut out lower-left corner rast <- raster::raster( data, xmn=0, xmx=10, ymn=0, ymx=10, crs='+proj=longlat +datum=WGS84' ) rast_brick <- brick(rast, rast) square <- make_rect(3.5, 3.5, 4.5, 4.5, sf::st_crs(rast)) expect_equal( exact_extract(rast_brick, square, weighted.mean, stack_apply = T), data.frame(weighted.mean.layer.1 = NA_real_, weighted.mean.layer.2 = NA_real_)) }) test_that('We can summarize a RasterStack / RasterBrick using weights from a RasterLayer', { set.seed(123) stk <- raster::stack(list(a = make_square_raster(1:100), b = make_square_raster(101:200))) weights <- make_square_raster(runif(100)) circle <- make_circle(5, 4, 2, sf::st_crs(stk)) # same weights get used for both expect_equal(exact_extract(stk, circle, 'weighted_mean', weights=weights), data.frame(weighted_mean.a = 63.0014, weighted_mean.b = 163.0014), tolerance=1e-6) # error when trying to use a non-raster as weights expect_error(exact_extract(stk, circle, 'weighted_mean', weights='stk'), "Weights must be a Raster") }) test_that('We get acceptable default values when processing a polygon that does not intersect the raster', { rast <- raster::raster(matrix(runif(100), nrow=5), xmn=-180, xmx=180, ymn=-65, ymx=85, crs='+proj=longlat +datum=WGS84') # extent of GPW poly <- make_rect(-180, -90, 180, -65.5, sf::st_crs(rast)) # extent of Antarctica in Natural Earth # RasterLayer expect_equal(list(data.frame(value=numeric(), coverage_fraction=numeric())), exact_extract(rast, poly)) expect_equal(list(data.frame(value=numeric(), x=numeric(), y=numeric(), cell=numeric(), coverage_fraction=numeric())), exact_extract(rast, poly, include_xy=TRUE, include_cell=TRUE)) expect_equal(0, exact_extract(rast, poly, function(x, c) sum(x))) expect_equal(0, exact_extract(rast, poly, 'count')) expect_equal(0, exact_extract(rast, poly, 'sum')) expect_equal(0, exact_extract(rast, poly, 'variety')) expect_equal(NA_real_, exact_extract(rast, poly, 'majority')) expect_equal(NA_real_, exact_extract(rast, poly, 'minority')) expect_equal(NA_real_, exact_extract(rast, poly, 'minority')) expect_equal(NA_real_, exact_extract(rast, poly, 'mean')) expect_equal(NA_real_, exact_extract(rast, poly, 'min')) expect_equal(NA_real_, exact_extract(rast, poly, 'max')) # RasterStack rast2 <- as.integer(rast) raster::dataType(rast2) <- 'INT4S' stk <- raster::stack(list(q=rast, xi=rast2, area=raster::area(rast))) expect_equal(list(data.frame(q=numeric(), xi=integer(), area=numeric(), coverage_fraction=numeric())), exact_extract(stk, poly)) expect_equal(list(data.frame(q=numeric(), xi=integer(), area=numeric(), x=numeric(), y=numeric(), cell=numeric(), coverage_fraction=numeric())), exact_extract(stk, poly, include_xy=TRUE, include_cell=TRUE)) exact_extract(stk, poly, function(values, cov) { expect_equal(values, data.frame(q=numeric(), xi=integer(), area=numeric())) expect_equal(cov, numeric()) }) }) test_that('Coverage area can be output instead of coverage fraction (projected)', { rast_utm <- disaggregate(make_square_raster(1:100), c(2, 3)) circle <- make_circle(5, 5, 5, crs=st_crs(rast_utm)) df_frac <- exact_extract(rast_utm, circle, include_area = TRUE)[[1]] df_area <- exact_extract(rast_utm, circle, coverage_area = TRUE)[[1]] expect_named(df_area, c('value', 'coverage_area')) expect_equal(df_frac$coverage_fraction * df_frac$area, df_area$coverage_area) }) test_that('Coverage area can be output instead of coverage fraction (geographic)', { rast <- raster::raster(matrix(1:54000, ncol=360), xmn=-180, xmx=180, ymn=-65, ymx=85, crs='+proj=longlat +datum=WGS84') suppressMessages({ circle <- make_circle(0, 45, 15, crs=st_crs(rast)) }) df_frac <- exact_extract(rast, circle, include_area = TRUE)[[1]] df_area <- exact_extract(rast, circle, coverage_area = TRUE)[[1]] expect_equal(df_frac$coverage_fraction * df_frac$area, df_area$coverage_area) }) test_that('coverage_area argument can be used with named summary operations', { rast1 <- raster(matrix(1:54000, ncol=360), xmn=-180, xmx=180, ymn=-65, ymx=85, crs='+proj=longlat +datum=WGS84') rast2 <- sqrt(rast1) suppressMessages({ circle <- make_circle(0, 45, 15, crs=st_crs(rast1)) }) # using only area as weighting expect_equal(exact_extract(rast1, circle, 'weighted_mean', weights = 'area'), exact_extract(rast1, circle, 'mean', coverage_area = TRUE)) # using area x weight as weighting expect_equal( exact_extract(rast1, circle, 'weighted_mean', weights = rast2, coverage_area = TRUE), exact_extract(rast1, circle, fun = function(x, cov, w) { weighted.mean(x, cov * w$rast2 * w$area) }, weights = stack(list(rast2 = rast2, area = area(rast2)))), tol = 1e-2 ) }) test_that('We can weight with cell areas (projected coordinates)', { rast_utm <- raster(matrix(1:100, ncol=10), xmn=0, xmx=5, ymn=0, ymx=5, crs='+init=epsg:26918') circle1 <- make_circle(5, 5, 5, crs=st_crs(rast_utm)) # for projected (Cartesian coordinates), means with cell area and # coverage fraction are the same expect_equal(exact_extract(rast_utm, circle1, 'mean'), exact_extract(rast_utm, circle1, 'weighted_mean', weights='area')) # same result with R summary function expect_equal( exact_extract(rast_utm, circle1, 'weighted_mean', weights='area'), exact_extract(rast_utm, circle1, function(x,c,w) { weighted.mean(x, c*w) }, weights='area'), 1e-5 ) # name doesn't pop out in data frame columns expect_named( exact_extract(rast_utm, circle1, c('sum', 'weighted_mean'), weights='area', force_df = TRUE), c('sum', 'weighted_mean')) # sums differ by the cell area expect_equal(prod(res(rast_utm)) * exact_extract(rast_utm, circle1, 'sum'), exact_extract(rast_utm, circle1, 'weighted_sum', weights='area')) # when using area weighting, disaggregating does not affect the sum expect_equal(exact_extract(rast_utm, circle1, 'weighted_sum', weights='area'), exact_extract(disaggregate(rast_utm, 8), circle1, 'weighted_sum', weights='area')) }) test_that('We can weight with cell areas (geographic coordinates)', { rast <- raster::raster(matrix(1:54000, ncol=360), xmn=-180, xmx=180, ymn=-65, ymx=85, crs='+proj=longlat +datum=WGS84') accuracy_pct_tol <- 0.01 suppressMessages({ circle <- make_circle(0, 45, 15, crs=st_crs(rast)) }) # result is reasonably close to what we get with raster::area, which uses # a geodesic calculation expected <- exact_extract(rast, circle, 'weighted_sum', weights = area(rast) * 1e6) actual <- exact_extract(rast, circle, 'weighted_sum', weights = 'area') expect_true(abs(actual - expected) / expected < accuracy_pct_tol) }) test_that('Correct results obtained when max_cells_in_memory is limited', { rast <- make_square_raster(1:100) poly <- make_circle(5, 5, 3, sf::st_crs(rast)) expect_equal(exact_extract(rast, poly, 'mean'), exact_extract(rast, poly, 'mean', max_cells_in_memory=1)) }) test_that('Weighted stats work when polygon is contained in weight raster but only partially contained in value raster', { values <- raster(matrix(1:15, nrow=3, ncol=5, byrow=TRUE), xmn=0, xmx=5, ymn=2, ymx=5) weights <- raster(sqrt(matrix(1:25, nrow=5, ncol=5, byrow=TRUE)), xmn=0, xmx=5, ymn=0, ymx=5) poly <- make_circle(2.1, 2.1, 1, NA_real_) value_tbl <- exact_extract(values, poly, include_xy=TRUE)[[1]] weight_tbl <- exact_extract(weights, poly, include_xy=TRUE)[[1]] tbl <- merge(value_tbl, weight_tbl, by=c('x', 'y')) expect_equal( exact_extract(values, poly, 'weighted_mean', weights=weights), weighted.mean(tbl$value.x, tbl$coverage_fraction.x * tbl$value.y), tol=1e-6 ) }) test_that('When part of a polygon is within the value raster but not the weighting raster, values for unweighted stats requested at the same time as weighted stats are correct', { values <- raster(matrix(1:25, nrow=5, ncol=5, byrow=TRUE), xmn=0, xmx=5, ymn=0, ymx=5) weights <- raster(sqrt(matrix(1:15, nrow=3, ncol=5, byrow=TRUE)), xmn=0, xmx=5, ymn=2, ymx=5) poly <- make_circle(2.1, 2.1, 1, NA_real_) expect_equal( exact_extract(values, poly, 'sum'), exact_extract(values, poly, c('sum', 'weighted_mean'), weights=weights)$sum ) }) test_that('When polygon is entirely outside the value raster and entirely within the weighting raster, we get NA instead of an exception', { values <- raster(matrix(1:25, nrow=5, ncol=5, byrow=TRUE), xmn=5, xmx=10, ymn=5, ymx=10) weights <- raster(matrix(1:10, nrow=10, ncol=10, byrow=TRUE), xmn=0, xmx=10, ymn=0, ymx=10) poly <- make_circle(2.1, 2.1, 1, NA_real_) expect_equal(NA_real_, exact_extract(values, poly, 'weighted_mean', weights=weights)) }) test_that('Z dimension is ignored, if present', { # see https://github.com/isciences/exactextractr/issues/26 poly <- st_as_sfc('POLYGON Z ((1 1 0, 4 1 0, 4 4 0, 1 1 0))') values <- raster(matrix(1:25, nrow=5, ncol=5, byrow=TRUE), xmn=0, xmx=5, ymn=0, ymx=5) expect_equal(exact_extract(values, poly, 'sum'), 70.5) # CPP code path expect_equal(exact_extract(values, poly, function(x,f) sum(x*f)), 70.5) # R code path }) test_that('No error thrown when weighting with different resolution grid (regression)', { poly <- st_as_sfc(structure(list( '01060000000200000001030000000100000008000000065bb0055b7866401c222222223233c0454444242e776640338ee338842d33c0abaaaacac0776640338ee338962733c0676666469f776640a4aaaaaa362033c03a8ee3784f7866404f555555a41c33c0a64ffa840b7966406c1cc771522133c0454444645a796640f4a44ffa9c2b33c0065bb0055b7866401c222222223233c0010300000001000000080000004b9ff4499f7c6640a3aaaaaaaaaa32c0bdbbbb3b747a6640f8ffff7f549632c0ea933e09aa7b664004b6608b399132c0b1055bb0637e6640dc388e63278f32c0d9822d58827e6640dc388ee3109432c09a999979837c6640590bb660159c32c0676666867c7d664070777777039c32c04b9ff4499f7c6640a3aaaaaaaaaa32c0'), class='WKB'), EWKB=TRUE) v <- raster(matrix(1:360*720, nrow=360, ncol=720), xmn=-180, xmx=180, ymn=-90, ymx=90) w <- raster(matrix(1:360*720*36, nrow=360*6, ncol=720*6), xmn=-180, xmx=180, ymn=-90, ymx=90) exact_extract(v, poly, 'weighted_sum', weights=w) succeed() }) test_that('when force_df = TRUE, exact_extract always returns a data frame', { rast <- make_square_raster(1:100) names(rast) <- 'z' poly <- c(make_circle(5, 5, 3, sf::st_crs(rast)), make_circle(3, 1, 1, sf::st_crs(rast))) vals <- exact_extract(rast, poly, 'mean', progress=FALSE) # named summary operation vals_df <- exact_extract(rast, poly, 'mean', force_df=TRUE, progress=FALSE) expect_s3_class(vals_df, 'data.frame') expect_equal(vals, vals_df[['mean']]) # R function vals2_df <- exact_extract(rast, poly, weighted.mean, force_df=TRUE, progress=FALSE) expect_s3_class(vals2_df, 'data.frame') expect_equal(vals, vals2_df[['result']], tol=1e-6) }) test_that('We can have include the input raster name in column names even if the input raster has only one layer', { rast <- make_square_raster(1:100) names(rast) <- 'z' poly <- c(make_circle(5, 5, 3, sf::st_crs(rast)), make_circle(3, 1, 1, sf::st_crs(rast))) vals <- exact_extract(rast, poly, c('mean', 'sum'), progress=FALSE) expect_named(vals, c('mean', 'sum')) # named summary operations vals_named <- exact_extract(rast, poly, c('mean', 'sum'), full_colnames=TRUE, progress=FALSE) expect_named(vals_named, c('mean.z', 'sum.z')) }) test_that('We can summarize a categorical raster by returning a data frame from a custom function', { set.seed(456) # smaller circle does not have class 5 classes <- c(1, 2, 3, 5) rast <- raster::raster(xmn = 0, xmx = 10, ymn = 0, ymx = 10, res = 1) values(rast) <- sample(classes, length(rast), replace = TRUE) circles <- c( make_circle(5, 4, 2, sf::st_crs(rast)), make_circle(3, 1, 1, sf::st_crs(rast))) # approach 1: classes known in advance result <- exact_extract(rast, circles, function(x, c) { row <- lapply(classes, function(cls) sum(c[x == cls])) names(row) <- paste('sum', classes, sep='_') do.call(data.frame, row) }, progress = FALSE) expect_named(result, c('sum_1', 'sum_2', 'sum_3', 'sum_5')) # check a single value expect_equal(result[2, 'sum_3'], exact_extract(rast, circles[2], function(x, c) { sum(c[x == 3]) })) if (requireNamespace('dplyr', quietly = TRUE)) { # approach 2: classes not known in advance (requires dplyr::bind_rows) result2 <- exact_extract(rast, circles, function(x, c) { found_classes <- unique(x) row <- lapply(found_classes, function(cls) sum(c[x == cls])) names(row) <- paste('sum', found_classes, sep='_') do.call(data.frame, row) }, progress = FALSE) for (colname in names(result)) { expect_equal(result[[colname]], dplyr::coalesce(result2[[colname]], 0)) } } }) test_that('We can append columns from the source data frame in the results', { rast <- make_square_raster(1:100) circles <- st_sf( fid = c(2, 9), size = c('large', 'small'), geometry = c( make_circle(5, 4, 2, sf::st_crs(rast)), make_circle(3, 1, 1, sf::st_crs(rast)))) result_1 <- exact_extract(rast, circles, 'mean', append_cols = c('size', 'fid'), progress = FALSE) expect_named(result_1, c('size', 'fid', 'mean')) result_2 <- exact_extract(rast, circles, weighted.mean, append_cols = c('size', 'fid'), progress = FALSE) # result_2 won't be identical to result_2 because the column names are different # instead, check that the naming is consistent with what we get from the force_df argument expect_identical(result_2, cbind(sf::st_drop_geometry(circles[, c('size', 'fid')]), exact_extract(rast, circles, weighted.mean, force_df = TRUE, progress = FALSE))) }) test_that('We can get multiple quantiles with the "quantiles" argument', { rast <- make_square_raster(1:100) circles <- st_sf( fid = c(2, 9), size = c('large', 'small'), geometry = c( make_circle(5, 4, 2, sf::st_crs(rast)), make_circle(3, 1, 1, sf::st_crs(rast)))) result <- exact_extract(rast, circles, 'quantile', quantiles=c(0.25, 0.50, 0.75), progress=FALSE) expect_true(inherits(result, 'data.frame')) expect_named(result, c('q25', 'q50', 'q75')) }) test_that('Both value and weighting rasters can be a stack', { vals <- stack(replicate(3, make_square_raster(runif(100)))) names(vals) <- c('a', 'b', 'c') weights <- stack(replicate(2, make_square_raster(rbinom(100, 2, 0.5)))) names(weights) <- c('w1', 'w2') circle <- make_circle(2, 7, 3, sf::st_crs(vals)) extracted <- exact_extract(vals, circle, weights=weights)[[1]] expect_named(extracted, c('a', 'b', 'c', 'w1', 'w2', 'coverage_fraction')) # stack of values, stack of weights: both passed as data frames exact_extract(vals, circle, function(v, c, w) { expect_true(is.data.frame(v)) expect_true(is.data.frame(w)) expect_named(v, names(vals)) expect_named(w, names(weights)) }, weights = weights) # stack of values, single layer of weights: weights passed as vector exact_extract(vals, circle, function(v, c, w) { expect_true(is.data.frame(v)) expect_true(is.vector(w)) }, weights = weights[[1]]) # single layer of values, stack of weights: values passed as vector exact_extract(vals[[1]], circle, function(v, c, w) { expect_true(is.vector(v)) expect_true(is.data.frame(w)) }, weights = weights) # single layer of values, single layer of weights: both passed as vector exact_extract(vals[[1]], circle, function(v, c, w) { expect_true(is.vector(v)) expect_true(is.vector(w)) }, weights = weights[[1]]) }) test_that('Named summary operations support both stacks of values and weights', { vals <- stack(replicate(3, make_square_raster(runif(100)))) names(vals) <- c('v1', 'v2', 'v3') weights <- stack(replicate(3, make_square_raster(rbinom(100, 2, 0.5)))) names(weights) <- c('w1', 'w2', 'w3') circle <- make_circle(2, 7, 3, sf::st_crs(vals)) stats <- c('sum', 'weighted_mean') # stack of values, stack of weights: values and weights are applied pairwise result <- exact_extract(vals, circle, stats, weights=weights) expect_named(result, c( 'sum.v1', 'sum.v2', 'sum.v3', 'weighted_mean.v1.w1', 'weighted_mean.v2.w2', 'weighted_mean.v3.w3')) expect_equal(result$sum.v1, exact_extract(vals[[1]], circle, 'sum')) expect_equal(result$sum.v2, exact_extract(vals[[2]], circle, 'sum')) expect_equal(result$sum.v3, exact_extract(vals[[3]], circle, 'sum')) expect_equal(result$weighted_mean.v1, exact_extract(vals[[1]], circle, 'weighted_mean', weights=weights[[1]])) expect_equal(result$weighted_mean.v2, exact_extract(vals[[2]], circle, 'weighted_mean', weights=weights[[2]])) expect_equal(result$weighted_mean.v3, exact_extract(vals[[3]], circle, 'weighted_mean', weights=weights[[3]])) # stack of values, layer of weights: weights are recycled result <- exact_extract(vals, circle, stats, weights=weights[[1]]) expect_named(result, c( 'sum.v1', 'sum.v2', 'sum.v3', 'weighted_mean.v1', 'weighted_mean.v2', 'weighted_mean.v3')) expect_equal(result$sum.v1, exact_extract(vals[[1]], circle, 'sum')) expect_equal(result$sum.v2, exact_extract(vals[[2]], circle, 'sum')) expect_equal(result$sum.v3, exact_extract(vals[[3]], circle, 'sum')) expect_equal(result$weighted_mean.v1, exact_extract(vals[[1]], circle, 'weighted_mean', weights=weights[[1]])) expect_equal(result$weighted_mean.v2, exact_extract(vals[[2]], circle, 'weighted_mean', weights=weights[[1]])) expect_equal(result$weighted_mean.v3, exact_extract(vals[[3]], circle, 'weighted_mean', weights=weights[[1]])) # layer of values, stack of weights: values are recycled result <- exact_extract(vals[[3]], circle, stats, weights=weights) expect_named(result, c('sum', 'weighted_mean.w1', 'weighted_mean.w2', 'weighted_mean.w3')) expect_equal(result$sum, exact_extract(vals[[3]], circle, 'sum')) expect_equal(result$weighted_mean.w1, exact_extract(vals[[3]], circle, 'weighted_mean', weights=weights[[1]])) expect_equal(result$weighted_mean.w2, exact_extract(vals[[3]], circle, 'weighted_mean', weights=weights[[2]])) expect_equal(result$weighted_mean.w3, exact_extract(vals[[3]], circle, 'weighted_mean', weights=weights[[3]])) }) test_that('We can use stack_apply with both values and weights', { vals <- stack(replicate(3, make_square_raster(runif(100)))) names(vals) <- c('v1', 'v2', 'v3') weights <- stack(replicate(3, make_square_raster(rbinom(100, 2, 0.5)))) names(weights) <- c('w1', 'w2', 'w3') circle <- make_circle(2, 7, 3, sf::st_crs(vals)) weighted_mean <- function(v, c, w) { expect_equal(length(v), length(c)) expect_equal(length(v), length(w)) weighted.mean(v, c*w) } # stack of values, stack of weights: values and weights are applied pairwise result <- exact_extract(vals, circle, weighted_mean, weights = weights, stack_apply = TRUE) expect_named(result, c('fun.v1.w1', 'fun.v2.w2', 'fun.v3.w3')) expect_equal(result$fun.v2.w2, exact_extract(vals[[2]], circle, 'weighted_mean', weights=weights[[2]]), tol = 1e-6) # stack of values, layer of weights: weights are recycled result <- exact_extract(vals, circle, weighted_mean, weights = weights[[2]], stack_apply = TRUE, full_colnames = TRUE) expect_named(result, c('fun.v1.w2', 'fun.v2.w2', 'fun.v3.w2')) expect_equal(result$fun.v1.w2, exact_extract(vals[[1]], circle, 'weighted_mean', weights=weights[[2]]), tol = 1e-6) # layer of values, stack of weights: values are recycled result <- exact_extract(vals[[3]], circle, weighted_mean, weights = weights, stack_apply = TRUE, full_colnames = TRUE) expect_named(result, c('fun.v3.w1', 'fun.v3.w2', 'fun.v3.w3')) expect_equal(result$fun.v3.w1, exact_extract(vals[[3]], circle, 'weighted_mean', weights=weights[[1]]), tol = 1e-6) }) test_that('Layers are implicity renamed if value layers have same name as weight layers', { # this happens when a stack is created and no names are provided # raster package assigns layer.1, layer.2 # here we assign our own identical names to avoid relying on raster package # implementation detail vals <- stack(replicate(2, make_square_raster(runif(100)))) names(vals) <- c('a', 'b') weights <- stack(replicate(2, make_square_raster(runif(100)))) names(weights) <- c('a', 'b') circle <- make_circle(2, 7, 3, sf::st_crs(vals)) result <- exact_extract(vals, circle, weights=weights)[[1]] expect_named(result, c('a', 'b', 'a.1', 'b.1', 'coverage_fraction')) }) test_that('Progress bar updates incrementally', { rast <- make_square_raster(1:100) npolys <- 13 polys <- st_sf(fid = seq_len(npolys), geometry = st_sfc(replicate(npolys, { x <- runif(1, min=0, max=10) y <- runif(1, min=0, max=10) r <- runif(1, min=0, max=2) make_circle(x, y, r, crs=sf::st_crs(rast)) }), crs=sf::st_crs(rast))) for (fun in list('sum', weighted.mean)) { for (input in list(polys, sf::st_geometry(polys))) { output <- capture.output(q <- exact_extract(rast, input, fun)) lines <- strsplit(output, '\r', fixed=TRUE)[[1]] numlines <- lines[endsWith(lines, '%')] len <- nchar(numlines[1]) pcts <- as.integer(substr(numlines, len - 3, len - 1)) expect_length(pcts, 1 + npolys) expect_equal(pcts[1], 0) expect_equal(pcts[length(pcts)], 100) expect_false(is.unsorted(pcts)) } } }) test_that('generated column names follow expected pattern', { values <- c('v1', 'v2', 'v3') weights <- c('w1', 'w2', 'w3') stats <- c('mean', 'weighted_mean') test_mean <- function(x, c) { weighted.mean(x, c) } # layer of values, no weights # named summary operations expect_equal(.resultColNames(values[[2]], NULL, c('mean', 'sum'), TRUE), c('mean.v2', 'sum.v2')) expect_equal(.resultColNames(values[[2]], NULL, c('mean', 'sum'), FALSE), c('mean', 'sum')) # generic method (we can recover its name) expect_equal(.resultColNames(values[[2]], NULL, weighted.mean, TRUE), 'weighted.mean.v2') expect_equal(.resultColNames(values[[2]], NULL, weighted.mean, FALSE), 'weighted.mean') # regular function (we can't recover its name) expect_equal(.resultColNames(values[[2]], NULL, test_mean, TRUE), 'fun.v2') expect_equal(.resultColNames(values[[2]], NULL, test_mean, FALSE), 'fun') # stack of values, no weights for (full_colnames in c(TRUE, FALSE)) { expect_equal(.resultColNames(values, NULL, c('mean', 'sum'), full_colnames), c('mean.v1', 'mean.v2', 'mean.v3', 'sum.v1', 'sum.v2', 'sum.v3')) expect_equal(.resultColNames(values, NULL, test_mean, full_colnames), c('fun.v1', 'fun.v2', 'fun.v3')) } # values, weights processed in parallel for (full_colnames in c(TRUE, FALSE)) { expect_equal(.resultColNames(values, weights, stats, full_colnames), c('mean.v1', 'mean.v2', 'mean.v3', 'weighted_mean.v1.w1', 'weighted_mean.v2.w2', 'weighted_mean.v3.w3')) expect_equal(.resultColNames(values, weights, test_mean, full_colnames), c('fun.v1.w1', 'fun.v2.w2', 'fun.v3.w3')) } # values recycled (full names) expect_equal(.resultColNames(values[1], weights, stats, TRUE), c('mean.v1', 'mean.v1', 'mean.v1', 'weighted_mean.v1.w1', 'weighted_mean.v1.w2', 'weighted_mean.v1.w3')) expect_equal(.resultColNames(values[1], weights, test_mean, TRUE), c('fun.v1.w1', 'fun.v1.w2', 'fun.v1.w3')) expect_equal(.resultColNames(values[1], weights, 'weighted_frac', full_colnames = TRUE, unique_values = c(4, 8)), c('weighted_frac_4.v1.w1', 'weighted_frac_4.v1.w2', 'weighted_frac_4.v1.w3', 'weighted_frac_8.v1.w1', 'weighted_frac_8.v1.w2', 'weighted_frac_8.v1.w3')) # here the values are always the same so we don't bother adding them to the names expect_equal(.resultColNames(values[1], weights, stats, FALSE), c('mean', 'mean', 'mean', 'weighted_mean.w1', 'weighted_mean.w2', 'weighted_mean.w3')) expect_equal(.resultColNames(values[1], weights, test_mean, FALSE), c('fun.w1', 'fun.w2', 'fun.w3')) # weights recycled (full names) expect_equal(.resultColNames(values, weights[1], stats, TRUE), c('mean.v1', 'mean.v2', 'mean.v3', 'weighted_mean.v1.w1', 'weighted_mean.v2.w1', 'weighted_mean.v3.w1')) expect_equal(.resultColNames(values, weights[1], test_mean, TRUE), c('fun.v1.w1', 'fun.v2.w1', 'fun.v3.w1')) # here the weights are always the same so we don't bother adding them to the name expect_equal(.resultColNames(values, weights[1], stats, FALSE), c('mean.v1', 'mean.v2', 'mean.v3', 'weighted_mean.v1', 'weighted_mean.v2', 'weighted_mean.v3')) expect_equal(.resultColNames(values, weights[1], test_mean, FALSE), c('fun.v1', 'fun.v2', 'fun.v3')) # custom colnames_fun expect_equal( .resultColNames(values, weights[1], stats, full_colnames = FALSE, colname_fun = function(fun_name, values, weights, ...) { paste(weights, values, fun_name, sep = '-') }), c('NA-v1-mean', 'NA-v2-mean', 'NA-v3-mean', 'w1-v1-weighted_mean', 'w1-v2-weighted_mean', 'w1-v3-weighted_mean') ) }) test_that('We can replace NA values in the value and weighting rasters with constants', { set.seed(05401) x <- runif(100) x[sample(length(x), 0.5*length(x))] <- NA y <- runif(100) y[sample(length(y), 0.5*length(y))] <- NA rx <- make_square_raster(x) ry <- make_square_raster(y) poly <- make_circle(4.5, 4.8, 4, crs=st_crs(rx)) # manually fill the missing values with 0.5 and missing weights with 0.3 rx_filled <- make_square_raster(ifelse(is.na(x), 0.5, x)) ry_filled <- make_square_raster(ifelse(is.na(y), 0.3, y)) expected <- exact_extract(rx_filled, poly, 'weighted_mean', weights = ry_filled) # fill values on the fly and verify that we get the same result expect_equal( exact_extract(rx, poly, 'weighted_mean', weights = ry, default_value = 0.5, default_weight = 0.3), expected) # check same calculation but using R summary function expect_equal( exact_extract(rx, poly, weights = ry, default_value = 0.5, default_weight = 0.3, fun = function(value, cov_frac, weight) { weighted.mean(value, cov_frac*weight) }), expected, 1e-6) # check substitution in raw returned values expect_equal( which(is.na(exact_extract(rx, poly)[[1]]$value)), which(44 == exact_extract(rx, poly, default_value = 44)[[1]]$value) ) }) test_that('All summary function arguments combined when summarize_df = TRUE', { rast <- make_square_raster(1:100) values <- stack(list(a = rast - 1, b = rast, c = rast + 1)) weights <- sqrt(values) names(weights) <- c('d', 'e', 'f') circle <- st_sf( id = 77, make_circle(7.5, 5.5, 4, sf::st_crs(rast))) # in the tests below, we check names inside the R summary function # to verify that our checks were actually hit, we have the summary # function return NULL and check for it with `expect_null`. # values only expect_null( exact_extract(values, circle, summarize_df = TRUE, fun = function(df) { expect_named(df, c('a', 'b', 'c', 'coverage_fraction')) NULL })[[1]]) expect_null( exact_extract(rast, circle, coverage_area = TRUE, summarize_df = TRUE, fun = function(df) { expect_named(df, c('value', 'coverage_area')) NULL })[[1]]) expect_null( exact_extract(values[[1]], circle, coverage_area = TRUE, summarize_df = TRUE, fun = function(df) { expect_named(df, c('value', 'coverage_area')) NULL })[[1]]) # values and weights expect_null( exact_extract(values, circle, summarize_df = TRUE, fun = function(df) { expect_named(df, c('a', 'b', 'c', 'd', 'e', 'f', 'coverage_fraction')) NULL }, weights = weights)[[1]]) expect_null( exact_extract(values, circle, include_cell = TRUE, include_xy = TRUE, include_area = TRUE, include_cols = 'id', summarize_df = TRUE, fun = function(df, extra_arg) { expect_named(df, c('id', 'a', 'b', 'c', 'd', 'e', 'f', 'x', 'y', 'cell', 'area', 'coverage_fraction')) expect_equal(extra_arg, 600) NULL }, weights = weights, extra_arg = 600)[[1]]) # values and weights, stack_apply = TRUE expect_equal( exact_extract(values, circle, weights = weights, summarize_df = TRUE, stack_apply = TRUE, fun = function(df, extra_arg) { expect_named(df, c('value', 'weight', 'coverage_fraction')) extra_arg }, extra_arg = 30809), data.frame(fun.a.d = 30809, fun.b.e = 30809, fun.c.f = 30809)) }) test_that('floating point errors do not cause an error that "logical subsetting requires vectors of identical size"', { rast <- raster(matrix(1:100, nrow=10), xm=0, xmx=1, ymn=0, ymx=1) poly <- make_rect(0.4, 0.7, 0.5, 0.8, crs = st_crs(rast)) val <- exact_extract(rast, poly, weights = rast, fun = NULL, include_cell = TRUE)[[1]] expect_equal(val$value, rast[val$cell]) expect_equal(val$weight, rast[val$cell]) }) test_that("append_cols works correctly when summary function returns multi-row data frame", { rast <- make_square_raster(1:100) circles <- st_sf( id = c('a', 'b'), geom = c( make_circle(3, 2, 4, sf::st_crs(rast)), make_circle(7, 7, 2, sf::st_crs(rast)) )) expect_silent({ result <- exact_extract(rast, circles, function(x, cov) data.frame(x = 1:3, x2 = 4:6), append_cols = 'id', progress = FALSE) }) expect_named(result, c('id', 'x', 'x2')) expect_equal(result$id, c('a', 'a', 'a', 'b', 'b', 'b')) expect_equal(result$x, c(1:3, 1:3)) expect_equal(result$x2, c(4:6, 4:6)) }) test_that("append_cols works correctly when summary function returns vector with length > 1", { rast <- make_square_raster(1:100) circles <- st_sf( id = c('a', 'b'), geom = c( make_circle(3, 2, 4, sf::st_crs(rast)), make_circle(7, 7, 2, sf::st_crs(rast)) )) expect_silent({ result <- exact_extract(rast, circles, function(x, cov) 1:3, append_cols = 'id', progress = FALSE) }) expect_named(result, c('id', 'result')) expect_equal(result$id, c('a', 'a', 'a', 'b', 'b', 'b')) expect_equal(result$result, c(1:3, 1:3)) }) test_that("append_cols works correctly when summary function returns data frame with length 0", { rast <- make_square_raster(1:100) circles <- st_sf( id = c('a', 'b'), geom = c( make_circle(3, 2, 4, sf::st_crs(rast)), make_circle(7, 7, 2, sf::st_crs(rast)) )) expect_silent({ result <- exact_extract(rast, circles, function(x, cov) data.frame(x = character(0), x2 = numeric(0)), append_cols = 'id', progress = FALSE) }) expect_named(result, c('id', 'x', 'x2')) expect_equal(nrow(result), 0) expect_equal(class(result$id), class(circles$id)) expect_equal(class(result$x), 'character') expect_equal(class(result$x2), 'numeric') }) exactextractr/tests/testthat.R0000644000176200001440000000010614776002150016257 0ustar liggesuserslibrary(testthat) library(exactextractr) test_check("exactextractr") exactextractr/MD50000644000176200001440000002276715113343512013457 0ustar liggesusers61662d4d1c0cddff68e9e33a946cfe39 *DESCRIPTION ce24cc4bda72139c93a1bb521cc5ad1b *NAMESPACE 6073e4735bc46dbe7e1b35f547a47bbe *NEWS.md 6a7d05cc54f35f2560f090b843db23ae *R/RcppExports.R cd28dc4fdcff8eec0a89701a4a5d7960 *R/coverage_fraction.R b2ca028a3c4d682a883ee15655c5f567 *R/exact_extract.R f01d12cf5c959fc446869a6318562441 *R/exact_extract_helpers.R 98dfcc036ae3b80becbf6e81fd6bf990 *R/exact_resample.R 9de4a1528af564c8c7ff757053af5e50 *R/exactextractr-package.R ffe59880900afce3a51d7c02da1c5100 *R/rasterize.R 9a73b51e792a0e5d86e401f9f1fce00c *README.md 2687093a53ff0b6093aae3eaa153b276 *build/vignette.rds 66e06ac2d2dfba498696933eb79ac2b6 *cleanup 407fec3ab098ff5d0b209a3e76e185c9 *configure c0fc5e6fed092459c06a0e22dedaa03f *configure.ac af40f157583be471758687a7b514d9df *inst/doc/vig1_population.R 6fb1af8d7efb3e246ab5d5b58bb76204 *inst/doc/vig1_population.Rmd adec56f1873af18e8166123a4a2e100f *inst/doc/vig1_population.html 4abf3b4b373370484a2cadf57430c870 *inst/doc/vig2_categorical.R e0aeee24ecfb2160af13fe77d075d1d2 *inst/doc/vig2_categorical.Rmd ff1ed991958d775a6e72e288e917403f *inst/doc/vig2_categorical.html b3b57ce3f9e1d4f82afcc7aa7a55713a *inst/sao_miguel/clc2018_v2020_20u1.tif 33edb90ed6e32378bbab268685d56197 *inst/sao_miguel/clc2018_v2020_20u1.tif.vat.dbf ff3c5d6f4fde6c0e59d4e330ac7182af *inst/sao_miguel/concelhos.gpkg 336b7a88d6641a274faf79f25b3748e4 *inst/sao_miguel/eu_dem_v11.tif 8eff768fa1a98eee3be9babe01357c02 *inst/sao_miguel/gpw_v411_2020_count_2020.tif 934a9260f67b210193aa932f6074e677 *inst/sao_miguel/gpw_v411_2020_density_2020.tif b9d97130ff837a8fd46730d8e7e4cb9f *inst/sao_miguel/gpw_v411_2020_land_area_2020.tif 7778bec7daba912b5efbff86b25286b0 *man/coverage_fraction.Rd 3176b42f7bafa458d053252edca2f542 *man/dot-resultColumns.Rd 7031e15de0eb32d9ceb94f5225d09f94 *man/dot-valueWeightIndexes.Rd 4aff9aa5f042ccaa706b91c371f90aec *man/exact_extract.Rd a56933832fccd3761e2448484fce43bd *man/exact_resample.Rd 49e5ef64ab57ca13cb5f85577cb54ce4 *man/exactextractr-package.Rd 72c2fdf5b8c21a380d69f6684ad58fab *man/rasterize_polygons.Rd 89a8db01a1424d72d5ef3697b6d97394 *src/Makevars.in a4a12377c5097ad912ff8b497959e08d *src/Makevars.ucrt 9f7890fcbed07f559417967026ba4f4e *src/Makevars.win 25b720720aab82ece69dd634f9845998 *src/RcppExports.cpp 9b917507ad6b8f15e544f6dfb0ef065a *src/coverage_fraction.cpp 20f53765bbb95a196bdacf5e4d717a3c *src/exact_extract.cpp 81e5a9d81c58cd84405ee8de2b7f3c08 *src/exactextract/CMakeLists.txt ef618452b0b9d0e219555d1841e4c0e8 *src/exactextract/Dockerfile 3b83ef96387f14655fc854ddc3c6bd57 *src/exactextract/LICENSE b24931b9133c01b365c666ce0e112d76 *src/exactextract/README.md 53831fd6af4faa513935703a724dc03c *src/exactextract/cmake/FindGEOS.cmake 588db968a352d967258340bdcb3346ad *src/exactextract/cmake/VersionSource.cmake d896ec1015d8018e66a44b88c469b295 *src/exactextract/doc/exactextract.svg 2ed2681158846d24097d44d8627bd63b *src/exactextract/doc/readme_example_values.svg bf6f345d7e237dcd637408d288351b1d *src/exactextract/doc/readme_example_weights.svg d9918aa67a82e4088d993fefc848fd13 *src/exactextract/docs/Doxyfile.in 35e1371fa6fd9cca16b282760279f25e *src/exactextract/src/box.cpp a8519f2726c0136b9291c3d99ce6097b *src/exactextract/src/box.h 5bc93c4a8abb68217a105b063b5d3896 *src/exactextract/src/cell.cpp c28cca7836df206d19ac31928a11372e *src/exactextract/src/cell.h 8c69f45439684d81333036995b0e91c3 *src/exactextract/src/coordinate.cpp 37c5303d64a3c1ca70835ec71d332ad9 *src/exactextract/src/coordinate.h d5a207768b074f38ae0cd1da116f3f85 *src/exactextract/src/crossing.h 907beb40f15b40bdd259973878edbac0 *src/exactextract/src/exactextract.cpp 5c173516652ff412803482954fbbf40a *src/exactextract/src/feature_sequential_processor.cpp 793297c17abba9d6914004394aa3edb3 *src/exactextract/src/feature_sequential_processor.h f0c9b018b593050d6b30e5299b7e9a5a *src/exactextract/src/floodfill.cpp 9eb2019c76c2423e926802d166ebb799 *src/exactextract/src/floodfill.h 3c9cc5e15b52553be0401d34e2c359a5 *src/exactextract/src/gdal_dataset_wrapper.cpp c1b9acd0614fb6714d29dbc442d44ab0 *src/exactextract/src/gdal_dataset_wrapper.h 922e7850c5fee5fdf22ca428da837155 *src/exactextract/src/gdal_raster_wrapper.cpp 43113b4b2858be6a4549ce2df3dd269c *src/exactextract/src/gdal_raster_wrapper.h 235af7461b6e99be6a853cc7695d7b8f *src/exactextract/src/gdal_writer.cpp 0855c83556a738c8f18c54920aa11fee *src/exactextract/src/gdal_writer.h 1adc494eb7be6a17917a72b51df183c9 *src/exactextract/src/geos_utils.cpp 6800c8c8e5647b832a154851695310cf *src/exactextract/src/geos_utils.h fdea24e511761f46b5fafc9ccb621643 *src/exactextract/src/grid.cpp d04eb647e1940db081a29c6b65c9f638 *src/exactextract/src/grid.h 6b62bd28676d240c82c53d121422534f *src/exactextract/src/matrix.h a8cd3f61f282b0add3fd3b31fe9ea062 *src/exactextract/src/measures.cpp 76777ac764dcd8ca447c8af53d8c30d7 *src/exactextract/src/measures.h 341cf8164a10d3bcefb665ab634ddbdb *src/exactextract/src/operation.h dd7b70c52037085ca9427ab5cf8ab489 *src/exactextract/src/output_writer.cpp 608cb07c38edb628a8853b43a5263bf8 *src/exactextract/src/output_writer.h e9ff4e6830967a6787611133f71f69ef *src/exactextract/src/perimeter_distance.cpp c4a35a666eb6b183017c7f09473bbf63 *src/exactextract/src/perimeter_distance.h 5a75f6bd4a3a7257cd1da3f076fcec0d *src/exactextract/src/processor.cpp 885157c42f38d3c4700615c0bbcf4f65 *src/exactextract/src/processor.h 6784fb6536e39bf5b2969e037fb06c6d *src/exactextract/src/raster.h afb86890c3e20df53541575177058ca7 *src/exactextract/src/raster_area.h 9a28a7973bbed3a7cd1994f206554d5d *src/exactextract/src/raster_cell_intersection.cpp 8011f91c825fc16a8d8cacdead2493c6 *src/exactextract/src/raster_cell_intersection.h c23c6728c455850887a95b31365a31f5 *src/exactextract/src/raster_sequential_processor.cpp 2993fc1e95e7f4cac4c33e6bc00e386f *src/exactextract/src/raster_sequential_processor.h 07eecee54f3baf5bf1766164eb7141ab *src/exactextract/src/raster_source.h 908e92bba05618fc0993ffab8c408396 *src/exactextract/src/raster_stats.h a6e49403289b62c3292661cd1d58d773 *src/exactextract/src/side.cpp 502c1dfa24e5e79cd225facac168b2d7 *src/exactextract/src/side.h 2b7a0082f8c305900ada2d18e080d785 *src/exactextract/src/stats_registry.h 07f6133c86553f6489d7801c194b7b24 *src/exactextract/src/traversal.cpp 37f9a01c6e42be9aceb17e377748442a *src/exactextract/src/traversal.h 30e6cb2d8036938915ea3b0761870409 *src/exactextract/src/traversal_areas.cpp 8f5efede0ff489fa925bc772f228708c *src/exactextract/src/traversal_areas.h 55e093794b2ffaf587e4ae17b3cf1e3a *src/exactextract/src/utils.cpp f932c8ba1190a98b4ffa6e26f60b4569 *src/exactextract/src/utils.h 8589c9c7a53b8072bd7b7cce2df33c6b *src/exactextract/src/variance.h ef619a92d82a926e4b67014b7fa39fc6 *src/exactextract/src/version.h.in e7d7753f611c49aba5876dc387db4e32 *src/exactextract/src/weighted_quantiles.cpp d458ba62cce53717389086c8c77a567f *src/exactextract/src/weighted_quantiles.h 0ce317c549591ed524336356ebec3c18 *src/exactextract/test/resources/antarctica.wkt f65ae01deb431fa35e69d96f4e0ecc4d *src/exactextract/test/resources/regression4.wkt 48a9adbd5f8edbbc8ee53e6c9b2907a5 *src/exactextract/test/resources/regression6.wkt fabe1634161a872001556197afcd5632 *src/exactextract/test/resources/russia.wkt 96407df24029f227f0ebfc5a2572e13f *src/exactextract/test/test_box.cpp f369772e26dfc293462c58cab798a870 *src/exactextract/test/test_cell.cpp 1f8221d63512b2e1f1933d063e073ada *src/exactextract/test/test_geos_utils.cpp 782c1d4f7f7d28a35811aaab4527789d *src/exactextract/test/test_grid.cpp 0a8ee344dc5530d9c0ff8a634a45f031 *src/exactextract/test/test_main.cpp 29609ba9cd248edd35b71af44565d0da *src/exactextract/test/test_perimeter_distance.cpp acc3ccd049ac67f30fe752a6873e583f *src/exactextract/test/test_raster.cpp bb273509b190667e57db31db052910a5 *src/exactextract/test/test_raster_area.cpp c0aa6a3be4a3fe4a2d4878463f516794 *src/exactextract/test/test_raster_cell_intersection.cpp d3b9a629d02c02e28863b6b03b8f9192 *src/exactextract/test/test_raster_iterator.cpp cf494594ed445f09ff130adcf9c41710 *src/exactextract/test/test_stats.cpp 8f5de3267c15a5ac544f5891a69b6cb5 *src/exactextract/test/test_traversal_areas.cpp 4799d1b46e13615bd8aa565224b3c1a0 *src/exactextract/test/test_utils.cpp badab9ddff4328d716ad596903b8ed53 *src/exactextract/vend/optional.hpp 42586970f4076f9fa510d6633c2c764f *src/geos_r.h 722ce987b9b8cca044281b78edbebcfb *src/numeric_vector_raster.h e570dee7ed710dfc23810f2b0228a75e *src/raster_utils.cpp 2806851ff78ddd77044e98e5c76011d6 *src/raster_utils.h c9db9f3a5b890319cb3d1266baa3a83f *src/rasterize.cpp 4b5060a5205cd9fe8fb9afb28131cb7a *src/resample.cpp fac39e474bf854bfca7d5839d0fa02ac *src/s4_raster_source.h 08f0623c5f51e2f31a92678c555aac19 *tests/testthat.R 50b6b1060f806788c287134f1428408a *tests/testthat/helper_functions.R 8f7b042ec90716c6caa5dadd40b8073d *tests/testthat/test_coverage_fraction.R 3070fb71acdc8b4921ee4d400231022c *tests/testthat/test_exact_extract.R 846fc111d97e2c5adaf4f46227ea726a *tests/testthat/test_exact_extract_eager_load.R d6324a2bd5e1f73b5882b3b3c71b6265 *tests/testthat/test_exact_extract_errors.R 93ec611c41d3918814c460ec3a0f0114 *tests/testthat/test_exact_extract_include_args.R dde8322a01c68f606745965ee2212e60 *tests/testthat/test_exact_extract_terra.R d5eb059f7adae75d62bf56dc282a8a87 *tests/testthat/test_exact_resample.R 7b90ca8e25c38bd7097b33eaee707b7d *tests/testthat/test_exact_resample_terra.R 4c11aaf0e53c26700dfc409f0e988497 *tests/testthat/test_helper_blocksize.R 0558736e8f7beb9316586b39937619c6 *tests/testthat/test_num_expected_args.R ca4db2ae9445b18980c621a74e19d1c7 *tests/testthat/test_rasterize.R 1d3815a55908c78e8a3604902b269965 *tools/winlibs.R 6fb1af8d7efb3e246ab5d5b58bb76204 *vignettes/vig1_population.Rmd e0aeee24ecfb2160af13fe77d075d1d2 *vignettes/vig2_categorical.Rmd exactextractr/R/0000755000176200001440000000000015113334453013336 5ustar liggesusersexactextractr/R/rasterize.R0000644000176200001440000000574314776002150015502 0ustar liggesusers# Copyright (c) 2022 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. setGeneric("rasterize_polygons", function(x, y, ...) standardGeneric("rasterize_polygons")) #' Create a raster approximation of a polygon coverage #' #' Returns a raster whose values indicate the index of the polygon covering #' each cell. Where multiple polygons cover the same cell, the index of the #' polygon covering the greatest area will be used, with the lowest index #' returned in the case of ties. Cells that are not covered by any polygon, #' or whose total covered fraction is less than \code{min_coverage}, will be #' set to \code{NA}. #' #' @param x a \code{sf} or \code{sfc} object with polygonal geometries #' #' @param y a (possibly empty) \code{RasterLayer} whose resolution and #' extent will be used for the generated \code{RasterLayer}. #' @param min_coverage minimum fraction of a cell that must be covered by #' polygons to be included in the output #' @return a \code{RasterLayer} or \code{SpatRaster}, consistent with the type of \code{y} #' @name rasterize_polygons NULL .rasterize_polygons <- function(x, y, min_coverage = 0) { num_rows <- terra::nrow(y) num_cols <- terra::ncol(y) if (min_coverage == 1.0) { # account for error in sum of single-precision plots min_coverage <- (min_coverage - 1e-6) } max_coverage_values <- matrix(0.0, nrow = num_rows, ncol = num_cols) max_coverage_indexes <- matrix(NA_integer_, nrow = num_rows, ncol = num_cols) tot_coverage <- matrix(0.0, nrow = num_rows, ncol = num_cols) dest_extent <- .extent(y) dest_res <- .res(y) geoms <- sf::st_geometry(x) for (i in seq_along(geoms)) { wkb <- sf::st_as_binary(geoms[[i]], EWKB=TRUE) CPP_update_max_coverage(dest_extent, dest_res, max_coverage_values, max_coverage_indexes, tot_coverage, wkb, i) } max_coverage_indexes[tot_coverage < min_coverage] <- NA if (inherits(y, 'SpatRaster')) { ret <- terra::rast(y) terra::values(ret) <- max_coverage_indexes } else { ret <- raster::raster(y) raster::values(ret) <- max_coverage_indexes } return(ret) } #' @import sf #' @import raster #' @useDynLib exactextractr #' @rdname rasterize_polygons #' @export setMethod('rasterize_polygons', signature(x='sf', y='RasterLayer'), .rasterize_polygons) #' @useDynLib exactextractr #' @rdname rasterize_polygons #' @export setMethod('rasterize_polygons', signature(x='sf', y='SpatRaster'), .rasterize_polygons) exactextractr/R/RcppExports.R0000644000176200001440000000342415113331212015743 0ustar liggesusers# Generated by using Rcpp::compileAttributes() -> do not edit by hand # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 CPP_coverage_fraction <- function(rast, wkb, crop) { .Call('_exactextractr_CPP_coverage_fraction', PACKAGE = 'exactextractr', rast, wkb, crop) } CPP_exact_extract <- function(rast, rast_ext, rast_res, rast_uncropped, weights, wkb, default_value, default_weight, include_xy, include_cell_number, include_area, area_weights, coverage_areas, p_area_method, include_cols, src_names, p_weights_names, warn_on_disaggregate, grid_compat_tol) { .Call('_exactextractr_CPP_exact_extract', PACKAGE = 'exactextractr', rast, rast_ext, rast_res, rast_uncropped, weights, wkb, default_value, default_weight, include_xy, include_cell_number, include_area, area_weights, coverage_areas, p_area_method, include_cols, src_names, p_weights_names, warn_on_disaggregate, grid_compat_tol) } CPP_stats <- function(rast, rast_ext, rast_res, weights, wkb, default_value, default_weight, coverage_areas, p_area_method, stats, max_cells_in_memory, grid_compat_tol, quantiles) { .Call('_exactextractr_CPP_stats', PACKAGE = 'exactextractr', rast, rast_ext, rast_res, weights, wkb, default_value, default_weight, coverage_areas, p_area_method, stats, max_cells_in_memory, grid_compat_tol, quantiles) } CPP_update_max_coverage <- function(extent, res, max_coverage, max_coverage_index, tot_coverage, wkb, index) { invisible(.Call('_exactextractr_CPP_update_max_coverage', PACKAGE = 'exactextractr', extent, res, max_coverage, max_coverage_index, tot_coverage, wkb, index)) } CPP_resample <- function(rast_in, rast_out, p_stat, p_fun, coverage_area, area_method) { .Call('_exactextractr_CPP_resample', PACKAGE = 'exactextractr', rast_in, rast_out, p_stat, p_fun, coverage_area, area_method) } exactextractr/R/exact_extract.R0000644000176200001440000011322015113334453016316 0ustar liggesusers# Copyright (c) 2018-2022 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. setGeneric("exact_extract", function(x, y, ...) standardGeneric("exact_extract")) #' Extract or summarize values from rasters #' #' Extracts the values of cells in a raster (`RasterLayer`, `RasterStack` #' `RasterBrick`, or `SpatRaster`) that are covered by polygons in a #' simple feature collection (`sf` or `sfc`) or `SpatialPolygonsDataFrame`. #' Returns either a summary of the extracted values or the extracted values #' themselves. #' #' @details #' `exact_extract` extracts the values of cells in a raster that are covered #' by polygonal features in a simple feature collection (`sf` or `sfc`) or #' `SpatialPolygonDataFrame`, as well as the fraction or area of each cell that #' is covered by the feature. Pixels covered by all parts of the polygon are #' considered. If an (invalid) multipart polygon covers the same pixels more #' than once, the pixel may have a coverage fraction greater than one. #' #' The function can either return pixel values directly to the caller, or can #' return the result of a predefined summary operation or user-defined R #' function applied to the values. These three approaches are described in the #' subsections below. #' #' ## Returning extracted values directly #' #' If `fun` is not specified, `exact_extract` will return a list with #' one data frame for each feature in the input feature collection. The data #' frame will contain a column with cell values from each layer in the input #' raster (and optional weighting raster) and a column indicating #' the fraction or area of the cell that is covered by the polygon. #' #' If the input rasters have only one layer, the value and weight columns in the #' data frame will be named `values` or `weights`. When the input rasters have #' more than one layer, the columns will be named according to `names(x)` and #' `names(weights)`. The column containing pixel coverage will be called #' `coverage_fraction` when `coverage_area = FALSE`, or `coverage_area` when #' `coverage_area = TRUE`. Additional columns can be added to the returned data #' frames with the `include_area`, `include_cell`, and `include_xy` arguments. #' #' If the output data frames for multiple features are to be combined (e.g., #' with `rbind`), it may be useful to include identifying column(s) from the #' input features in the returned data frames using `include_cols`. #' #' ## Predefined summary operations #' #' Often the individual pixel values are not needed; only one or more summary #' statistics (e.g., mean, sum) is required for each feature. Common summary #' statistics can be calculated by `exact_extract` directly using a predefined #' summary operation. Where possible, this approach is advantageous because it #' allows the package to calculate the statistics incrementally, avoiding the #' need to store all pixel values in memory at the same time. This allows the #' package to process arbitrarily large data with a small amount of memory. (The #' `max_pixels_in_memory` argument can be used to fine-tune the amount of memory #' made available to `exact_extract`.) #' #' To summarize pixel values using a predefined summary option, `fun` should be #' set to a character vector of one or more operation names. If the input raster #' has a single layer and a single summary operation is specified, #' `exact_extract` will return a vector with the result of the summary operation #' for each feature in the input. If the input raster has multiple layers, or if #' multiple summary operations are specified, `exact_extract` will return a data #' frame with a row for each feature and a column for each summary operation / #' layer combination. (The `force_df` option can be used to always return a data #' frame instead of a vector.) #' #' The following summary operations are supported: #' #' * `min` - the minimum non-`NA` value in any raster cell wholly or #' partially covered by the polygon #' * `max` - the maximum non-`NA` value in any raster cell wholly or #' partially covered by the polygon #' * `count` - the sum of fractions of raster cells with non-`NA` #' values covered by the polygon #' * `sum` - the sum of non-`NA` raster cell values, multiplied by #' the fraction of the cell that is covered by the polygon #' * `mean` - the mean cell value, weighted by the fraction of each cell #' that is covered by the polygon #' * `median` - the median cell value, weighted by the fraction of each cell #' that is covered by the polygon #' * `quantile` - arbitrary quantile(s) of cell values, specified in #' `quantiles`, weighted by the fraction of each cell that is #' covered by the polygon #' * `mode` - the most common cell value, weighted by the fraction of #' each cell that is covered by the polygon. Where multiple #' values occupy the same maximum number of weighted cells, #' the largest value will be returned. #' * `majority` - synonym for `mode` #' * `minority` - the least common cell value, weighted by the fraction #' of each cell that is covered by the polygon. Where #' multiple values occupy the same minimum number of #' weighted cells, the smallest value will be returned. #' * `variety` - the number of distinct values in cells that are wholly or #' partially covered by the polygon. #' * `variance` - the population variance of cell values, weighted by the #' fraction of each cell that is covered by the polygon. #' * `stdev` - the population standard deviation of cell values, weighted by #' the fraction of each cell that is covered by the polygon. #' * `coefficient_of_variation` - the population coefficient of variation of #' cell values, weighted by the fraction of each #' cell that is covered by the polygon. #' * `weighted_mean` - the mean cell value, weighted by the product of #' the fraction of each cell covered by the polygon #' and the value of a second weighting raster provided #' as `weights` #' * `weighted_sum` - the sum of defined raster cell values, multiplied by #' the fraction of each cell that is covered by the polygon #' and the value of a second weighting raster provided #' as `weights` #' * `weighted_stdev` - the population standard deviation of cell values, #' weighted by the product of the fraction of each cell #' covered by the polygon and the value of a second #' weighting raster provided as `weights` #' * `weighted_variance` - the population variance of cell values, weighted by #' the product of the fraction of each cell covered by #' the polygon and the value of a second weighting #' raster provided as `weights` #' * `frac` - returns one column for each possible value of `x`, with the #' the fraction of defined raster cells that are equal to that #' value. #' * `weighted_frac` - returns one column for each possible value of `x`, #' with the fraction of defined cells that are equal #' to that value, weighted by `weights.` #' #' In all of the summary operations, `NA` values in the the primary raster (`x`) #' raster are ignored (i.e., `na.rm = TRUE`.) If `NA` values occur in the #' weighting raster, the result of the weighted operation will be `NA`. `NA` #' values in both `x` and `weights` can be replaced on-the-fly using the #' `default_value` and `default_weight` arguments. #' #' ## User-defined summary functions #' #' If no predefined summary operation is suitable, a user-defined R function may #' be provided as `fun`. The function will be called once for each feature and #' must return either a single value or a data frame. The results of the #' function for each feature will be combined and returned by `exact_extract`. #' #' The simplest way to write a summary function is to set #' argument `summarize_df = TRUE`. (For backwards compatibility, this is not the #' default.) In this mode, the summary function takes the signature #' `function(df, ...)` where `df` is the same data frame that would be returned #' by `exact_extract` with `fun = NULL`. #' #' With `summarize_df = FALSE`, the function must have the signature #' `function(values, coverage_fractions, ...)` when weights are not used, and #' `function(values, coverage_fractions, weights, ...)` when weights are used. #' If the value and weight rasters each have a single layer, the function arguments #' will be vectors; if either has multiple layers, the function arguments will #' be data frames, with column names taken from the names of the value/weight #' rasters. Values brought in through the `include_xy`, `include_area`, #' `include_cell`, and `include_cols` arguments will be added to the `values` #' data frame. For most applications, it is simpler to set `summarize_df = TRUE` #' and work with all inputs in a single data frame. #' #' @param x a `RasterLayer`, `RasterStack`, `RasterBrick`, or `SpatRaster` #' @param y a `sf`, `sfc`, `SpatialPolygonsDataFrame`, or `SpatialPolygons` #' object with polygonal geometries #' @param fun an optional function or character vector, as described below #' @param weights a weighting raster to be used with the `weighted_mean` #' and `weighted_sum` summary operations or a user-defined #' summary function. When `weights` is set to `'area'`, the #' cell areas of `x` will be calculated and used as weights. #' @param coverage_area if `TRUE`, output pixel `coverage_area` #' instead of `coverage_fraction` #' @param default_value an optional value to use instead of `NA` in `x` #' @param default_weight an optional value to use instead of `NA` in `weights` #' @param quantiles quantiles to be computed when `fun = 'quantile'` #' @param append_cols when `fun` is not `NULL`, an optional character vector #' of columns from `y` to be included in returned data frame. #' @param force_df always return a data frame instead of a vector, even if #' `x` has only one layer and `fun` has length 1 #' @param summarize_df pass values, coverage fraction/area, and weights to #' `fun` as a single data frame instead of #' separate arguments. #' @param full_colnames include the names of `x` and `weights` in #' the names of the data frame for each feature, even if #' `x` or `weights` has only one layer. #' This is useful when the results of multiple #' calls to `exact_extract` are combined with #' `cbind`. #' @param colname_fun an optional function used to construct column names. #' Should accept arguments `values` (name of value layer), #' `weights` (name of weight layer), `fun_name` (value of #' `fun`), `fun_value` (value associated with `fun`, for #' `fun %in% c('quantile', 'frac', 'weighted_frac)` #' `nvalues` (number of value layers), `weights` #' (number of weight layers) #' @param include_area if `TRUE`, and `fun` is `NULL`, augment #' the data frame for each feature with a column #' for the cell area. If the units of the raster CRS are #' degrees, the area in square meters will be calculated #' based on a spherical approximation of Earth. Otherwise, #' a Cartesian area will be calculated (and will be the #' same for all pixels.) If `TRUE` and `fun` is #' not `NULL`, add `area` to the data frame passed #' to `fun` for each feature. #' @param include_cell if `TRUE`, and `fun` is `NULL`, augment #' the data frame for each feature with a column #' for the cell index (`cell`). If `TRUE` and #' `fun` is not `NULL`, add `cell` to the #' data frame passed to `fun` for each feature. #' @param include_cols an optional character vector of column names in #' `y` to be added to the data frame for each #' feature that is either returned (when `fun` is #' `NULL`) or passed to `fun`. #' @param include_xy if `TRUE`, and `fun` is `NULL`, augment #' the returned data frame for each feature with columns #' for cell center coordinates (`x` and `y`). If #' `TRUE` and `fun` is not `NULL`, add #' `x` and `y` to the data frame passed to `fun` #' for each feature. #' @param stack_apply if `TRUE`, apply `fun` independently to #' each layer or `x` (and its corresponding layer #' of `weights`, if provided.) The number of #' layers in `x` and `weights` must equal #' each other or `1`, in which case the #' single layer raster will be recycled. #' If `FALSE`, apply `fun` to all layers of #' `x` (and `weights`) simultaneously. #' @param max_cells_in_memory the maximum number of raster cells to load at #' a given time when using a named summary operation #' for `fun` (as opposed to a function defined using #' R code). If a polygon covers more than `max_cells_in_memory` #' raster cells, it will be processed in multiple chunks. #' @param grid_compat_tol require value and weight grids to align within #' `grid_compat_tol` times the smaller of the two #' grid resolutions. #' @param progress if `TRUE`, display a progress bar during processing #' @param ... additional arguments to pass to `fun` #' @return a vector, data frame, or list of data frames, depending on the type #' of `x` and the value of `fun` (see Details) #' @examples #' rast <- raster::raster(matrix(1:100, ncol=10), xmn=0, ymn=0, xmx=10, ymx=10) #' poly <- sf::st_as_sfc('POLYGON ((2 2, 7 6, 4 9, 2 2))') #' #' # named summary operation on RasterLayer, returns vector #' exact_extract(rast, poly, 'mean') #' #' # two named summary operations on RasterLayer, returns data frame #' exact_extract(rast, poly, c('min', 'max')) #' #' # named summary operation on RasterStack, returns data frame #' stk <- raster::stack(list(a=rast, b=sqrt(rast))) #' exact_extract(stk, poly, 'mean') #' #' # named weighted summary operation, returns vector #' weights <- raster::raster(matrix(runif(100), ncol=10), xmn=0, ymn=0, xmx=10, ymx=10) #' exact_extract(rast, poly, 'weighted_mean', weights=weights) #' #' # custom summary function, returns vector #' exact_extract(rast, poly, function(value, cov_frac) length(value[cov_frac > 0.9])) #' #' @name exact_extract #' @md NULL .exact_extract <- function(x, y, fun=NULL, ..., weights=NULL, append_cols=NULL, coverage_area=FALSE, default_value=NA_real_, default_weight=NA_real_, include_area=FALSE, include_cell=FALSE, include_cols=NULL, include_xy=FALSE, force_df=FALSE, full_colnames=FALSE, stack_apply=FALSE, summarize_df=FALSE, quantiles=NULL, progress=TRUE, max_cells_in_memory=30000000, grid_compat_tol=1e-3, colname_fun=NULL ) { area_weights <- is.character(weights) && length(weights) == 1 && weights == 'area' if (area_weights) { weights <- NULL } .validateFlag(coverage_area, 'coverage_area') .validateFlag(force_df, 'force_df') .validateFlag(full_colnames, 'full_colnames') .validateFlag(include_area, 'include_area') .validateFlag(include_cell, 'include_cell') .validateFlag(include_xy, 'include_xy') .validateFlag(progress, 'progress') .validateFlag(stack_apply, 'stack_apply') .validateFlag(summarize_df, 'summarize_df') .validateNumericScalar(max_cells_in_memory, 'max_cells_in_memory') .validateNumericScalar(grid_compat_tol, 'grid_compat_tol') .validateNumericScalarOrNA(default_value, 'default_value') .validateNumericScalarOrNA(default_weight, 'default_weight') .validateUniqueNames(x) .validateUniqueNames(weights) if(!is.null(append_cols)) { if (!inherits(y, 'sf')) { stop(sprintf('append_cols only supported for sf arguments (received %s)', paste(class(y), collapse = ' '))) } if (!is.character(append_cols)) { stop('append_cols must be a list of column names') } force_df <- TRUE } if(!is.null(include_cols)) { if (!inherits(y, 'sf')) { stop(sprintf('include_cols only supported for sf arguments (received %s)', paste(class(y), collapse = ' '))) } if (!is.character(include_cols)) { stop('include_cols must be a list of column names') } } if(sf::st_geometry_type(y, by_geometry = FALSE) == 'GEOMETRY') { if (!all(sf::st_dimension(y) == 2)) { stop("Features in sfc_GEOMETRY must be polygonal") } } if(!is.null(weights)) { if (!(.isRaster(weights) || weights == 'area')) { stop("Weights must be a Raster object or \"area\".") } if (is.character(fun) && !any(startsWith(fun, "weighted"))) { warning("Weights provided but no requested operations use them.") } if (inherits(weights, 'BasicRaster') && !is.na(sf::st_crs(x))) { if (is.na(sf::st_crs(weights))) { warning("No CRS specified for weighting raster; assuming it has the same CRS as the value raster.") } else if (sf::st_crs(x) != sf::st_crs(weights)) { stop("Weighting raster does not have the same CRS as value raster.") } } } analysis_crs <- sf::st_crs(x) if(is.na(sf::st_crs(x)) && !is.na(sf::st_crs(y))) { warning("No CRS specified for raster; assuming it has the same CRS as the polygons.") analysis_crs <- sf::st_crs(y) } else if(is.na(sf::st_crs(y)) && !is.na(sf::st_crs(x))) { warning("No CRS specified for polygons; assuming they have the same CRS as the raster.") } else if(sf::st_crs(x) != sf::st_crs(y)) { y <- sf::st_transform(y, sf::st_crs(x)) msg <- "Polygons transformed to raster CRS" if (!is.na(sf::st_crs(x)$epsg)) { msg <- paste0(msg, " (EPSG: ", sf::st_crs(x)$epsg, ") ") } msg <- paste0(msg, ". ", "To avoid on-the-fly coordinate transformation, ensure that ", "st_crs() returns the same result for both raster and polygon ", "inputs.") warning(msg) } if(area_weights || include_area || coverage_area) { area_method <- .areaMethod(analysis_crs) } else { area_method <- NULL } if (is.character(fun)) { if (length(fun) == 0) { stop("No summary operations provided.") } if (length(list(...)) > 0) { stop("exact_extract was called with a named summary operation that ", "does not accept additional arguments ...") } if (include_xy) { stop("include_xy must be FALSE for named summary operations") } if (include_cell) { stop("include_cell must be FALSE for named summary operations") } if (include_area) { stop("include_area must be FALSE for named summary operations") } if (!is.null(include_cols)) { stop("include_cols not supported for named_summary operations (see argument append_cols)") } if (summarize_df) { stop("summarize_df can only be used when `fun` is an R function") } } else if (is.function(fun)) { if (summarize_df) { if (.num_expected_args(fun) < 1) { stop("exact_extract was called with a function that does not appear to ", "be of the form `function(df, ...`).") } } else if (is.null(weights)) { if (.num_expected_args(fun) < 2) { stop("exact_extract was called with a function that does not appear to ", "be of the form `function(values, coverage_fractions, ...)`. If ", "the summary function should accept a single data frame argument, ", "set `summarize_df = TRUE`.") } } else if (.num_expected_args(fun) < 3) { stop("exact_extract was called with a function that does not appear to ", "be of the form `function(values, coverage_fractions, weights, ...)`.", "If the summary function should accept a single data frame argument, ", "set `summarize_df = TRUE`.") } } else if (is.null(fun)) { if (length(list(...)) > 0) { stop("Unexpected arguments: ", paste(names(list(...)), collapse = ',')) } if (summarize_df) { stop("summarize_df can only be used when `fun` is an R function") } if (stack_apply) { stop("stack_apply can only be used when `fun` is a summary operation or function") } if (!is.null(append_cols)) { stop("append_cols can only be used when `fun` is a summary operation or function. See `include_cols`.") } } else { stop("fun must be a character vector, function, or NULL") } geoms <- sf::st_geometry(y) if (requireNamespace('terra', quietly = TRUE)) { strategies <- c('check_block_size', 'eager_load') } else { strategies = c() } x_orig <- NULL if ('eager_load' %in% strategies && length(geoms) > 1 && (!.isInMemory(x))) { # Eagerly load the entire area to be processed into memory. If the raster # block sizes are large, this potentially allows us to cache data in memory # that would not fit within the GDAL block cache (because we can cache an # area smaller than a single block). We only do this when the terra package # is available, because raster::crop throws an error when we set snap = # 'out'. if (include_cell) { # retain a reference to the original so that we can use it for include_cell x_orig <- x } x <- .eagerLoad(x, geoms, max_cells_in_memory, message_on_fail = progress) weights <- .eagerLoad(weights, geoms, max_cells_in_memory, message_on_fail = progress) } # Calculate extent and resolution outside of loop for performance (yes, really) x_ext <- .extent(x) x_res <- .res(x) # At this point, if the data have not been preloaded into memory, either: # - we don't have the terra package available # - we can't fit the whole processing area in memory # Whether this is a problem depends on how the raster is chunked. If the # raster is poorly chunked, we can't do anything to improve the perormance, # but we try to detect the situation so we can alert the user. if (!.isInMemory(x)) { # - Do we have a RasterStack? Suggest using terra instead. if (inherits(x, 'RasterStack')) { message('exact_extract may perform poorly when extracting from a RasterStack. ', 'It is recommended to use a SpatRaster from the terra package instead. ', 'Convert using terra::rast(x)') } if (('check_block_size' %in% strategies) && inherits(x, 'SpatRaster')) { # Can we fit at least one block from every layer in GDAL's block cache? # If not, warn the user of expected poor performance. block_dim <- .blockSize(x) bytes_per_pixel <- 8L # terra has no datatype function? block_sz_mb <- prod(block_dim[1:2])* bytes_per_pixel / 1024 / 1024 cache_sz_mb <- terra::gdalCache() min_cache_needed_mb <- ceiling(block_sz_mb) * .numLayers(x) if (min_cache_needed_mb >= cache_sz_mb) { message('Loading one raster block of data per layer requires approximately ', min_cache_needed_mb, ' MB but the GDAL block size cache is only ', cache_sz_mb, ' MB. This is likely to result in very slow performance. ', 'It is recommended to increase the cache size using ', 'terra::gdalCache(new_size), reduce the number of layers in the ', 'input raster, or rewrite the input raster using a smaller chunk size.') } } } update_progress <- .createProgress(progress, length(geoms)) tryCatch({ x <- .startReading(x) weights <- .startReading(weights) if (is.character(fun)) { # Compute all stats in C++. # CPP_stats returns a matrix, which gets turned into a column by sapply # Results has one column per feature and one row per stat/raster layer if((is.null(weights) && !area_weights) && any(.isWeighted(fun))) { stop("Weighted stat requested but no weights provided.") } results <- lapply(sf::st_as_binary(geoms, EWKB=TRUE), function(wkb) { ret <- CPP_stats(x, x_ext, x_res, weights, wkb, default_value, default_weight, coverage_area, area_method, fun, max_cells_in_memory, grid_compat_tol, quantiles) update_progress() return(ret) }) if ('frac' %in% fun || 'weighted_frac' %in% fun) { unique_values <- sort(unique(do.call(c, lapply(results, function(r) attr(r, 'unique_values'))))) } else { unique_values <- numeric() } result_cols <- .resultColumns(names(x), names(weights), fun, full_colnames, quantiles, unique_values, colname_fun = colname_fun) consistent_cols <- !(result_cols$stat_name %in% c('frac', 'weighted_frac')) # Construct an empty matrix that we can populate with stats returned # for each geometry, with 0 as a default where a geometry does not # return a stat (e.g., 'frac' for a specific value). Then convert the # filled matrix to a data frame. (This is faster than populating the # data frame directly, because the data frame assignment operator is # slow.) result_mat <- matrix(0.0, nrow = length(geoms), ncol = nrow(result_cols)) for (i in seq_along(results)) { cols <- consistent_cols | result_cols$base_value %in% attr(results[[i]], 'unique_values') result_mat[i, cols] <- results[[i]] } colnames(result_mat) <- result_cols$colname result_df <- as.data.frame(result_mat) if (ncol(result_df) == 1 && !force_df) { return(result_df[[1]]) } # drop duplicated columns (occurs when an unweighted stat is # requested alongside a weighted stat, with a stack of weights) result_df <- result_df[, unique(names(result_df)), drop = FALSE] if (!is.null(append_cols)) { result_df <- cbind(sf::st_drop_geometry(y[, append_cols]), result_df) } return(result_df) } else { num_values <- .numLayers(x) value_names <- names(x) if (.isRaster(weights)) { num_weights <- .numLayers(weights) weight_names <- names(weights) } else if (area_weights) { num_weights <- 1 weight_names <- 'area' weights <- NULL } else { num_weights <- 0 weight_names <- NULL } if (stack_apply || (num_values == 1 && num_weights <= 1)) { apply_layerwise <- TRUE if (num_values > 1 && num_weights > 1 && num_values != num_weights) { stop(sprintf("Can't apply function layerwise with stacks of %d value layers and %d layers", num_values, num_weights)) } result_names <- .resultColNames(value_names, weight_names, fun, full_colnames, colname_fun = colname_fun) num_results <- max(num_weights, num_values) ind <- .valueWeightIndexes(num_values, num_weights) } else { apply_layerwise <- FALSE } # For R summary functions and data frame output, we avoid using # input layer names if there is only one value/weight layer if (length(value_names) == 1) { value_names <- 'value' } if (length(weight_names) == 1) { weight_names <- 'weight' } ret <- lapply(seq_along(geoms), function(feature_num) { wkb <- sf::st_as_binary(geoms[[feature_num]], EWKB=TRUE) if (!is.null(include_cols)) { include_col_values <- sf::st_drop_geometry(y[feature_num, include_cols]) } else { include_col_values <- NULL } # only raise a disaggregation warning for the first feature warn_on_disaggregate <- feature_num == 1 col_list <- CPP_exact_extract(x, x_ext, x_res, x_orig, weights, wkb, default_value, default_weight, include_xy, include_cell, include_area, area_weights, coverage_area, area_method, include_col_values, value_names, weight_names, warn_on_disaggregate, grid_compat_tol) if (coverage_area) { coverage_col <- 'coverage_area' } else { coverage_col <- 'coverage_fraction' } if (!is.null(include_cols)) { # Replicate the include_cols vectors to be as long as the other columns, # so we can use quickDf nrow <- length(col_list[[coverage_col]]) col_list[include_cols] <- lapply(col_list[include_cols], rep, nrow) } df <- .quickDf(col_list) update_progress() # No summary function? Return the whole data frame. if (is.null(fun)) { return(df) } # Summary function accepts a data frame? Pass it along. if (summarize_df && !apply_layerwise) { return(fun(df, ...)) } # Break the data frame into components that we can pass # separately to the summary functions. included_cols_df <- df[, !(names(df) %in% c(value_names, weight_names, coverage_col)), drop = FALSE] vals_df <- df[, value_names, drop = FALSE] weights_df <- df[, weight_names, drop = FALSE] cov_fracs <- df[[coverage_col]] if (apply_layerwise) { result <- lapply(seq_len(num_results), function(i) { if (summarize_df) { # Pack everything into a single data frame and pass it to `fun` arg_df <- cbind(value = vals_df[[ind$values[i]]], included_cols_df) if (num_weights > 0) { arg_df$weight <- weights_df[[ind$weights[i]]] } arg_df[[coverage_col]] <- df[[coverage_col]] return(fun(arg_df, ...)) } else { # Pull values and weights out into vectors, unless we have # included columns (x/y/cell, etc.) in which case values # remain a data frame. Retained for backwards compat. vx <- vals_df[, ind$values[i]] if (ncol(included_cols_df) > 0) { vx <- cbind(data.frame(value = vx), included_cols_df) } if (num_weights == 0) { return(fun(vx, cov_fracs, ...)) } else { return(fun(vx, cov_fracs, weights_df[, ind$weights[i]], ...)) } } }) if (num_results == 1) { return(result[[1]]) } names(result) <- result_names return(.quickDf(result)) } else { # Pass all layers to callback, to be handled together # Included columns (x/y/cell) are passed with the values. # Pass single-column data frames as vectors. vals_df <- .singleColumnToVector(cbind(vals_df, included_cols_df)) weights_df <- .singleColumnToVector(weights_df) if (num_weights == 0) { return(fun(vals_df, cov_fracs, ...)) } else { return(fun(vals_df, cov_fracs, weights_df, ...)) } } }) # if we have columns to append, iterate over the results and # append the columns to each, coercing into a data frame if # necessary. We need to iterate over the results before binding # them together because we don't know the legnth of each result # or even if their lengths are constant. if (!is.null(append_cols)) { for (i in seq_along(ret)) { if (!is.data.frame(ret[[i]])) { ret[[i]] = data.frame(result = ret[[i]]) } if (nrow(ret[[i]]) >= 1) { append_row <- i } else { # cbinding row 0 cleanly brings in zero-length columns # of the correct names and types, in the correct positions append_row <- 0 } ret[[i]] <- cbind(sf::st_drop_geometry(y[append_row, append_cols]), ret[[i]], row.names = NULL) } } if (!is.null(fun)) { if (all(sapply(ret, is.data.frame))) { # function returned a data frame for each polygon? rbind them if (requireNamespace('dplyr', quietly = TRUE)) { ret <- dplyr::bind_rows(ret) # handle column name mismatches } else { ret <- do.call(rbind, ret) } } else { # function returned something else; combine the somethings into # an array ret <- simplify2array(ret) if (force_df) { ret <- data.frame(result = ret) } } } return(ret) } }, finally={ .stopReading(x) .stopReading(weights) }) } # faster replacement for as.data.frame when input is a named list # with equal-length columns # from Advanced R, sec. 24.4.2 .quickDf <- function(lst) { class(lst) <- 'data.frame' attr(lst, 'row.names') <- .set_row_names(length(lst[[1]])) lst } .singleColumnToVector <- function(df) { if (ncol(df) == 1) { df[, 1] } else { df } } #' @import sf #' @import raster #' @useDynLib exactextractr #' @rdname exact_extract #' @export setMethod('exact_extract', signature(x='Raster', y='sf'), .exact_extract) #' @useDynLib exactextractr #' @rdname exact_extract #' @export setMethod('exact_extract', signature(x='Raster', y='SpatialPolygonsDataFrame'), function(x, y, ...) .exact_extract(x, sf::st_as_sf(y), ...)) #' @useDynLib exactextractr #' @rdname exact_extract #' @export setMethod('exact_extract', signature(x='Raster', y='SpatialPolygons'), function(x, y, ...) .exact_extract(x, sf::st_as_sf(y), ...)) #' @useDynLib exactextractr #' @rdname exact_extract #' @export setMethod('exact_extract', signature(x='Raster', y='sfc_MULTIPOLYGON'), .exact_extract) #' @useDynLib exactextractr #' @rdname exact_extract #' @export setMethod('exact_extract', signature(x='Raster', y='sfc_POLYGON'), .exact_extract) #' @useDynLib exactextractr #' @rdname exact_extract #' @export setMethod('exact_extract', signature(x='Raster', y='sfc_GEOMETRY'), .exact_extract) # CRAN version of sf does not explicitly declare this class. If we do not do it # ourselves, documentation is not generated for the `sfc_GEOMETRYCOLLECTION` # overload. setOldClass('sfc_GEOMETRYCOLLECTION') #' @useDynLib exactextractr #' @rdname exact_extract #' @export setMethod('exact_extract', signature(x='Raster', y='sfc_GEOMETRYCOLLECTION'), .exact_extract) #' @import sf #' @useDynLib exactextractr #' @rdname exact_extract #' @export setMethod('exact_extract', signature(x='SpatRaster', y='sf'), .exact_extract) #' @useDynLib exactextractr #' @rdname exact_extract #' @export setMethod('exact_extract', signature(x='SpatRaster', y='SpatialPolygonsDataFrame'), function(x, y, ...) .exact_extract(x, sf::st_as_sf(y), ...)) #' @useDynLib exactextractr #' @rdname exact_extract #' @export setMethod('exact_extract', signature(x='SpatRaster', y='SpatialPolygons'), function(x, y, ...) .exact_extract(x, sf::st_as_sf(y), ...)) #' @useDynLib exactextractr #' @rdname exact_extract #' @export setMethod('exact_extract', signature(x='SpatRaster', y='sfc_MULTIPOLYGON'), .exact_extract) #' @useDynLib exactextractr #' @rdname exact_extract #' @export setMethod('exact_extract', signature(x='SpatRaster', y='sfc_POLYGON'), .exact_extract) #' @useDynLib exactextractr #' @rdname exact_extract #' @export setMethod('exact_extract', signature(x='SpatRaster', y='sfc_GEOMETRY'), .exact_extract) #' @useDynLib exactextractr #' @rdname exact_extract #' @export setMethod('exact_extract', signature(x='SpatRaster', y='sfc_GEOMETRYCOLLECTION'), .exact_extract) exactextractr/R/coverage_fraction.R0000644000176200001440000000620114776002150017140 0ustar liggesusers# Copyright (c) 2018-2021 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. setGeneric('coverage_fraction', function(x, y, crop=FALSE, ...) standardGeneric('coverage_fraction')) .coverage_fraction <- function(x, y, crop) { if(is.na(sf::st_crs(x)) && !is.na(sf::st_crs(y))) { warning("No CRS specified for raster; assuming it has the same CRS as the polygons.") } else if(is.na(sf::st_crs(y)) && !is.na(sf::st_crs(x))) { warning("No CRS specified for polygons; assuming they have the same CRS as the raster.") } else if(sf::st_crs(x) != sf::st_crs(y)) { y <- sf::st_transform(y, sf::st_crs(x)) warning("Polygons transformed to raster CRS (EPSG:", sf::st_crs(x)$epsg, ")") } lapply(sf::st_as_binary(y, EWKB=TRUE), function(wkb) { CPP_coverage_fraction(x, wkb, crop) }) } #' Compute the fraction of raster cells covered by a polygon #' #' @param x a (possibly empty) \code{RasterLayer} whose resolution and #' extent will be used for the generated \code{RasterLayer}. #' @param y a \code{sf} object with polygonal geometries #' @param crop if \code{TRUE}, each generated \code{RasterLayer} will be #' cropped to the extent of its associated feature. #' @return a list with a \code{RasterLayer} for each feature in \code{y}. #' Values of the raster represent the fraction of each #' cell in \code{x} that is covered by \code{y}. #' @examples #' rast <- raster::raster(matrix(1:100, ncol=10), xmn=0, ymn=0, xmx=10, ymx=10) #' poly <- sf::st_as_sfc('POLYGON ((2 2, 7 6, 4 9, 2 2))') #' #' cov_frac <- coverage_fraction(rast, poly)[[1]] #' @name coverage_fraction NULL #' @import sf #' @import raster #' @useDynLib exactextractr #' @rdname coverage_fraction #' @export setMethod('coverage_fraction', signature(x='RasterLayer', y='sf'), function(x, y, crop=FALSE) { coverage_fraction(x, sf::st_geometry(y), crop) }) #' @rdname coverage_fraction #' @export setMethod('coverage_fraction', signature(x='RasterLayer', y='sfc_MULTIPOLYGON'), .coverage_fraction) #' @rdname coverage_fraction #' @export setMethod('coverage_fraction', signature(x='RasterLayer', y='sfc_POLYGON'), .coverage_fraction) #' @rdname coverage_fraction #' @export setMethod('coverage_fraction', signature(x='SpatRaster', y='sf'), function(x, y, crop=FALSE) { coverage_fraction(x, sf::st_geometry(y), crop) }) #' @rdname coverage_fraction #' @export setMethod('coverage_fraction', signature(x='SpatRaster', y='sfc_MULTIPOLYGON'), .coverage_fraction) #' @rdname coverage_fraction #' @export setMethod('coverage_fraction', signature(x='SpatRaster', y='sfc_POLYGON'), .coverage_fraction) exactextractr/R/exact_extract_helpers.R0000644000176200001440000003236014776003326020053 0ustar liggesusers# Copyright (c) 2018-2022 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .resultColNames <- function(...) { .resultColumns(...)$colname } #' Return column names to be used for summary operations #' #' @param value_names names of value raster layers #' @param weight_names names of weighting raster layers #' @param fun functions or names of summary operations #' @param full_colnames return a complete column name even when there is no #' ambiguity? #' @param quantiles quantiles to use when \code{stat_names} contains \code{quantile} #' @return character vector of column names #' @keywords internal .resultColumns <- function(value_names, weight_names, fun, full_colnames, quantiles=numeric(), unique_values=numeric(), colname_fun = NULL) { if (inherits(fun, 'standardGeneric')) { stat_names <- fun@generic[1] } else if (is.function(fun)) { stat_names <- 'fun' } else if (is.null(fun)) { stat_names <- '' } else { stat_names <- fun } quantile_index = which(stat_names == 'quantile') if (length(quantile_index) != 0) { stat_names <- splice(stat_names, quantile_index, rep('quantile', length(quantiles))) } for (stat in c('frac', 'weighted_frac')) { frac_index = which(stat_names == stat) if (length(frac_index) != 0) { stat_names <- splice(stat_names, frac_index, rep(stat, length(unique_values))) } } ind <- .valueWeightIndexes(length(value_names), length(weight_names)) vn <- value_names[ind$values] wn <- weight_names[ind$weights] # determine all combinations of index and stat z <- expand.grid(index=seq_along(vn), stat_name=stat_names, stringsAsFactors=FALSE) z$values <- vn[z$index] z$base_value <- NA_real_ for (stat in c('frac', 'weighted_frac')) { ifrac <- which(z$stat_name == stat) z$base_value[ifrac] <- rep(unique_values, each = length(ifrac) / length(unique_values)) } iquantile <- which(z$stat_name == 'quantile') z$base_value[iquantile] <- rep(quantiles, each = length(iquantile) / length(quantiles)) if (is.null(wn)) { z$weights <- NA } else { z$weights <- wn[z$index] } z$weights[!.includeWeightInColName(z$stat_name)] <- NA if (is.null(colname_fun)) { colname_fun <- function(...) { .makeColname(full_colnames = full_colnames, ...) } } z$colname <- mapply(colname_fun, fun_name = z$stat_name, values = z$values, weights = z$weights, fun_value = z$base_value, MoreArgs = list(nvalues = length(value_names), nweights = length(weight_names)), USE.NAMES = FALSE) return(z) } .makeColname <- function(fun_name, values, weights, fun_value, full_colnames, nvalues, nweights) { # construct column names for each index, stat # add weight layer name only if layer is ambiguously weighted if (fun_name == 'quantile') { fun_component <- sprintf('q%02d', as.integer(100 * fun_value)) } else if (fun_name %in% c('frac', 'weighted_frac')) { fun_component <- sprintf('%s_%s', fun_name, fun_value) } else { fun_component <- fun_name } ret <- fun_component if (full_colnames || nvalues > 1) { ret <- paste(ret, values, sep='.') } if ((!is.na(weights)) && ((full_colnames & nweights > 0) || nweights > 1)) { ret <- paste(ret, weights, sep='.') } return(ret) } .includeWeightInColName <- function(fun) { .isWeighted(fun) | fun == 'fun' } .isWeighted <- function(stat_name) { stat_name %in% c('weighted_mean', 'weighted_sum', 'weighted_frac', 'weighted_stdev', 'weighted_variance') } #' Compute indexes for the value and weight layers that should be #' processed together #' #' @param num_values number of layers in value raster #' @param num_weights number of layers in weighting raster #' @return list with \code{values} and \code{weights} elements #' providing layer indexes #' @keywords internal .valueWeightIndexes <- function(num_values, num_weights) { if (num_weights == 0) { vi <- seq_len(num_values) wi <- NA } else if (num_values == num_weights) { # process in parallel vi <- seq_len(num_values) wi <- seq_len(num_weights) } else if (num_values == 1 && num_weights > 1) { # recycle values vi <- rep.int(1, num_weights) wi <- seq_len(num_weights) } else if (num_values > 1 && num_weights == 1) { # recycle weights vi <- seq_len(num_values) wi <- rep.int(1, num_values) } list(values = vi, weights = wi) } .areaMethod <- function(crs_obj) { if (!(is.na(crs_obj)) && crs_obj$units_gdal == 'degree') { return('spherical') } else { return('cartesian') } } .validateFlag <- function(value, name) { if(!(is.logical(value) && length(value) == 1 && !is.na(value))) { stop(name, ' must be TRUE or FALSE') } } .validateNumericScalar <- function(value, name) { if (!(is.numeric(value) && length(value) == 1 && !is.na(value))) { stop(name, ' must be a single numeric value') } } .validateNumericScalarOrNA <- function(value, name) { if (!(is.numeric(value) && length(value) == 1)) { stop(name, ' must be a single numeric value') } } .validateUniqueNames <- function(x) { nm <- names(x) if (!is.null(nm)) { if (length(nm) != length(unique(nm))) { stop('names of input rasters must be unique') } } } # faster replacement for as.data.frame when input is a named list # with equal-length columns # from Advanced R, sec. 24.4.2 .quickDf <- function(lst) { class(lst) <- 'data.frame' attr(lst, 'row.names') <- .set_row_names(length(lst[[1]])) lst } .singleColumnToVector <- function(df) { if (ncol(df) == 1) { df[, 1] } else { df } } # Return the number of standard (non-...) arguments in a supplied function that # do not have a default value. This is used to fail if the summary function # provided by the user cannot accept arguments of values and weights. .num_expected_args <- function(fun) { a <- formals(args(fun)) a <- a[names(a) != '...'] sum(sapply(a, nchar) == 0, na.rm = TRUE) } .startReading <- function(r) { if(inherits(r, 'BasicRaster')) { return(raster::readStart(r)) } else if (inherits(r, 'SpatRaster')) { terra::readStart(r) } return(r) } .stopReading <- function(r) { if(inherits(r, 'BasicRaster')) { return(raster::readStop(r)) } else if (inherits(r, 'SpatRaster')) { terra::readStop(r) } return(r) } .crs <- function(r) { if(inherits(r, 'BasicRaster')) { if (utils::packageVersion('raster') < numeric_version('3.5')) { return(raster::crs(r)) } else { return(terra::crs(r)) } } else if (inherits(r, 'SpatRaster')) { return(terra::crs(r)) } else { stop('Unknown type: ', class(r)) } } .setValues <- function(r, x) { if(inherits(r, 'BasicRaster')) { if (utils::packageVersion('raster') < numeric_version('3.5')) { raster::values(r) <- x } else { terra::values(r) <- x } } else if (inherits(r, 'SpatRaster')) { raster::values(r) <- x } else { stop('Unknown type: ', class(r)) } return(r) } .numLayers <- function(r) { if(inherits(r, 'BasicRaster')) { return(raster::nlayers(r)) } else if (inherits(r, 'SpatRaster')) { return(terra::nlyr(r)) } else { stop('Unknown type: ', class(r)) } } .isRaster <- function(r) { inherits(r, 'BasicRaster') | inherits(r, 'SpatRaster') } .xFromCol <- function(r, col) { if (inherits(r, 'BasicRaster')) { raster::xFromCol(r, col) } else if (inherits(r, 'SpatRaster')) { terra::xFromCol(r, col) } else { stop('Unknown type: ', class(r)) } } .colFromX <- function(r, x) { if (inherits(r, 'BasicRaster')) { raster::colFromX(r, x) } else if (inherits(r, 'SpatRaster')) { terra::colFromX(r, x) } else { stop('Unknown type: ', class(r)) } } .yFromRow <- function(r, row) { if (inherits(r, 'BasicRaster')) { raster::yFromRow(r, row) } else if (inherits(r, 'SpatRaster')) { terra::yFromRow(r, row) } else { stop('Unknown type: ', class(r)) } } .rowFromY <- function(r, y) { if (inherits(r, 'BasicRaster')) { raster::rowFromY(r, y) } else if (inherits(r, 'SpatRaster')) { terra::rowFromY(r, y) } else { stop('Unknown type: ', class(r)) } } .cellFromRowCol <- function(r, row, col) { if (inherits(r, 'BasicRaster')) { raster::cellFromRowCol(r, row, col) } else if (inherits(r, 'SpatRaster')) { terra::cellFromRowCol(r, row, col) } else { stop('Unknown type: ', class(r)) } } .extent <- function(r) { if (inherits(r, 'BasicRaster')) { ex <- r@extent c(ex@xmin, ex@ymin, ex@xmax, ex@ymax) } else if (inherits(r, 'SpatRaster')) { ex <- terra::ext(r) c(ex$xmin, ex$ymin, ex$xmax, ex$ymax) } else { stop('Unknown type: ', class(r)) } } .res <- function(r) { if (inherits(r, 'BasicRaster')) { raster::res(r) } else if (inherits(r, 'SpatRaster')) { terra::res(r) } else { stop('Unknown type: ', class(r)) } } .getValuesBlock <- function(r, row, nrows, col, ncols) { if (inherits(r, 'BasicRaster')) { raster::getValuesBlock(r, row, nrows, col, ncols, format = 'm') } else if (inherits(r, 'SpatRaster')) { terra::readValues(r, row, nrows, col, ncols, mat = TRUE) } else { stop('Unknown type: ', class(r)) } } .createProgress <- function(progress, n) { if (progress && n > 1) { pb <- utils::txtProgressBar(min = 0, max = n, initial=0, style=3) update_progress <- function() { i <- 1 + utils::getTxtProgressBar(pb) utils::setTxtProgressBar(pb, i) if (i == n) { close(pb) } } } else { update_progress <- function() {} } return(update_progress) } .isInMemory <- function(r) { if (inherits(r, 'BasicRaster')) { return(raster::inMemory(r)) } else if (inherits(r, 'SpatRaster')) { return(terra::inMemory(r)[1]) } else { stop('Unknown type: ', class(r)) } } .netCDFBlockSize <- function(fname, varname) { nc <- NULL sz <- NA tryCatch({ nc <- ncdf4::nc_open(fname) sz <- nc$var[[varname]]$chunksizes dim_index <- nc$var[[varname]]$dimids + 1L dim_names <- sapply(dim_index, function(i) nc$dim[[i]]$name) if (all(is.na(sz))) { # file is not compressed sz <- rep.int(1, length(dim_names)) } names(sz) <- dim_names }, finally = { if (!is.null(nc)) { ncdf4::nc_close(nc) } }) # flip dimensions 1 and 2 so we return row/col return(sz[c(2, 1, seq_along(sz)[-(1:2)])]) } .blockSize <- function(r) { # set default return value in case file is uncompressed and has # has no block size, or we simply can't figure it out ret <- c(1, 1) if (inherits(r, 'BasicRaster')) { if (r[[1]]@file@driver == 'netcdf') { ret <- .netCDFBlockSize(r[[1]]@file@name, attr(r[[1]]@data, 'zvar')) } else if (r@file@driver == 'gdal') { ret <- c(r@file@blockrows, r@file@blockcols) } } else if (inherits(r, 'SpatRaster')) { ret <- terra::fileBlocksize(r)[1, ] } unname(ret) } .eagerLoad <- function(r, geoms, max_cells_in_memory, message_on_fail) { if (is.null(r)) { return(NULL) } cells_required <- .numCells(r, geoms) if (cells_required <= max_cells_in_memory) { box <- sf::st_bbox(geoms) geom_ext <- terra::ext(box[c('xmin', 'xmax', 'ymin', 'ymax')]) if (!inherits(r, 'SpatRaster')) { # current CRAN version of terra (1.4-22) does not preserve # names on conversion (https://github.com/rspatial/terra/issues/430) nm <- names(r) r <- terra::rast(r) names(r) <- nm } overlap_ext <- terra::intersect(terra::ext(r), geom_ext) if (is.null(overlap_ext)) { # Extents do not overlap, and terra::crop will throw an error # if we try to crop. Return the input raster as-is; nothing will be # read from it anyway. return(r) } r <- terra::crop(r, geom_ext, snap = 'out') } else if (message_on_fail) { message('Cannot preload entire working area of ', cells_required, ' cells with max_cells_in_memory = ', max_cells_in_memory, '.', ' Raster values will be read for each feature individually.') } return(r) } .numCells <- function(r, g) { if (is.null(r)) { return(0) } box <- sf::st_bbox(g) top <- .rowFromY(r, box['ymax']) bottom <- .rowFromY(r, box['ymin']) left <- .colFromX(r, box['xmin']) right <- .colFromX(r, box['xmax']) if (is.na(top) && is.na(bottom)) { return(0L) } if (is.na(left) && is.na(right)) { return(0L) } if (is.na(top)) { top <- 1 } if (is.na(bottom)) { bottom <- nrow(r) } if (is.na(left)) { left <- 1 } if (is.na(right)) { right <- ncol(r) } return( (bottom - top + 1) * (right - left + 1) * .numLayers(r) ) } splice <- function(x, i, replacement) { c(x[seq_along(x) < i], replacement, x[seq_along(x) > i]) } exactextractr/R/exactextractr-package.R0000644000176200001440000000023214776002150017730 0ustar liggesusers#' @importFrom Rcpp evalCpp #' @importFrom methods setMethod #' @keywords internal "_PACKAGE" ## usethis namespace: start ## usethis namespace: end NULL exactextractr/R/exact_resample.R0000644000176200001440000000655014776002150016463 0ustar liggesusers# Copyright (c) 2020-2022 ISciences, LLC. # All rights reserved. # # This software is licensed under the Apache License, Version 2.0 (the "License"). # You may not use this file except in compliance with the License. You may # obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. setGeneric("exact_resample", function(x, y, ...) standardGeneric("exact_resample")) #' Resample a raster to a new grid #' #' @param x a \code{RasterLayer} or \code{SpatRaster} to be resampled #' @param y a raster of the same class as \code{x} with a grid definition to #' which \code{x} should be resampled #' @param fun a named summary operation or R function to be used for the resampling #' @param coverage_area use cell coverage areas instead of coverage fractions #' in \code{fun} #' @return a resampled version of \code{x}, returned as a \code{RasterLayer} or #' \code{SpatRaster}, depending on the values of \code{x} and \code{y} #' #' @name exact_resample NULL .exact_resample <- function(x, y, fun, coverage_area = FALSE) { analysis_crs <- sf::st_crs(x) if (sf::st_crs(x) != sf::st_crs(y)) { if (is.na(sf::st_crs(x))) { warning("No CRS specified for source raster; assuming it has the same CRS as destination raster.") analysis_crs <- sf::st_crs(y) } else if (is.na(sf::st_crs(y))) { warning("No CRS specified for destination raster; assuming it has the same CRS as source raster.") } else { stop('Destination raster must have same CRS as source.') } } if (is.character(fun)) { if (length(fun) != 1) { stop("Only a single operation may be used for resampling.") } if (startsWith(fun, 'weighted')) { stop("Weighted operations cannot be used for resampling.") } if (.numLayers(x) != 1) { stop("Raster to be resampled must have a single layer.") } summary_stat <- fun summary_fun <- NULL } else { if (!is.function(fun)) { stop("fun must be a named summary operation or an R function") } if (.num_expected_args(fun) != 2) { stop("exact_extract was called with a function that does not appear to ", "be of the form `function(values, coverage_fractions)`.") } summary_stat <- NULL summary_fun <- fun } .validateFlag(coverage_area, 'coverage_area') area_method <- .areaMethod(analysis_crs) x <- .startReading(x) tryCatch({ ret <- CPP_resample(x, y, summary_stat, summary_fun, coverage_area, area_method) if (inherits(x, 'SpatRaster')) { terra::rast(ret) } else { ret } }, finally={ .stopReading(x) }) } #' @import sf #' @import raster #' @useDynLib exactextractr #' @rdname exact_resample #' @export setMethod('exact_resample', signature(x='RasterLayer', y='RasterLayer'), .exact_resample) #' @useDynLib exactextractr #' @rdname exact_resample #' @export setMethod('exact_resample', signature(x='SpatRaster', y='SpatRaster'), .exact_resample) exactextractr/cleanup0000755000176200001440000000004715113334656014520 0ustar liggesusers#!/bin/sh rm -r config.* src/Makevars exactextractr/vignettes/0000755000176200001440000000000015113334656015152 5ustar liggesusersexactextractr/vignettes/vig1_population.Rmd0000644000176200001440000002730715113334453020742 0ustar liggesusers--- title: "1. Summarizing gridded population data" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{1. Summarizing gridded population data} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7 ) knitr::opts_knit$set( global.par = TRUE ) ``` ```{r, include = FALSE} par(mar = c(1, 1, 1, 1)) ``` ## Introduction This vignette describes the use of `exactextractr` to summarize population and elevation data from the [Gridded Population of the World](https://www.earthdata.nasa.gov/data/projects/gpw) and [EU-DEM](https://www.eea.europa.eu/en/datahub/dataset-redirect/DAT-193-en) datasets. The `exactextractr` package includes samples of both of these datasets, cropped to the extent of São Miguel, the largest and most populous island of the Azores archipelago. This example uses the following packages: ```{r setup, message = FALSE} library(dplyr) library(exactextractr) library(raster) library(sf) ``` ## Loading the sample data To begin, we load the population count file from GPW. This raster provides the total population in each pixel for the calendar year 2020. On top of the population grid, we plot boundaries for the six municipalities, or _concelhos_, into which the island is divided. We can see that the population is concentrated along the coastlines, with smaller communities located in volcanic calderas inland. ```{r load-data-1} pop_count <- raster(system.file('sao_miguel/gpw_v411_2020_count_2020.tif', package = 'exactextractr')) concelhos <- st_read(system.file('sao_miguel/concelhos.gpkg', package = 'exactextractr'), quiet = TRUE) plot(pop_count, axes = FALSE) plot(st_geometry(concelhos), add = TRUE) ``` ## Calculating total population Because the population count raster has been cropped and contains no land area outside of São Miguel, we can calculate the total population of the island using the `cellStats` function from the `raster` package. ```{r total-pop-cellstats} cellStats(pop_count, 'sum') ``` ```{r, echo = FALSE} sao_miguel_pop <- cellStats(pop_count, 'sum') ``` ### Calculating population from the GPW population count file We might also attempt to use `exact_extract` with the population count raster to see how the population is divided among _concelhos_: ```{r extract-example} exact_extract(pop_count, concelhos, 'sum', progress = FALSE) ``` The result is a vector with one entry for each feature in `concelhos`. The order of the result is consistent with the input features, so we can assign the result of `exact_extract` to a new column in `concelhos` if desired. To calculate the populations, we used `fun = 'sum'`, where `'sum'` is a named summary operation recognized by `exactextractr`. A full list of supported operations can be found in the function documentation for `exact_extract`. If none of the named operations is suitable, we can set `fun` equal to an R function such as `function(pixel_value, coverage_fraction) sum(pixel_value * coverage_fraction)`. However, the named operations are generally faster than R equivalents and use less memory when rasters or polygons are large. To review the results more easily, we can use the `append_cols` argument to copy columns from the input `sf` object into the result of `exact_extract`. We also use some `dplyr` operations to add a column for the total population of all _concelhos_: ```{r extract-pop-by-sum, cache = TRUE} concelho_pop <- exact_extract(pop_count, concelhos, 'sum', append_cols = 'name', progress = FALSE) %>% rename(pop = sum) %>% arrange(desc(pop)) %>% bind_rows(summarize(., name = 'Total', pop = sum(pop))) ``` This produces the following table: ```{r, echo = FALSE} concelho_pop %>% mutate(pop = prettyNum(round(pop), big.mark = ',')) %>% knitr::kable() ``` ```{r calc-missing-pop, echo=FALSE, results='hide'} total_pop <- filter(concelho_pop, name == 'Total') %>% pull(pop) missing_pop_pct <- (sao_miguel_pop - total_pop) / sao_miguel_pop missing_pop_pct ``` We might reasonably expect the total population to equal the value of `r prettyNum(sao_miguel_pop, big.mark=',')` we previously obtained using `cellStats`, but it doesn't. In fact, `r as.integer(100*missing_pop_pct)`% of the population is unaccounted for in the _concelho_ totals. The cause of the discrepancy can be seen by looking closely at the densely populated Ponta Delgada region on the southern coast. Many of the cells containing population are only partially covered by the _concelho_ boundaries, so some of the total population calculated by `cellStats` is missing from the totals. ```{r plot-pop-ponta-delgada} plot.new() plot.window(xlim = c(-25.75, -25.55), ylim = c(37.70, 37.77)) rect(par("usr")[1], par("usr")[3], par("usr")[2], par("usr")[4], col = "#9cc0f9") plot(pop_count, add = TRUE, axes = FALSE, alpha = 0.8, horizontal = TRUE) plot(st_geometry(concelhos), add = TRUE) ``` ### Calculating population from the GPW population density file It turns out that we need a somewhat more complex solution to get accurate population counts when our polygons follow coastlines. Instead of using the population count raster, we bring in the population density raster, which provides the number of persons per square kilometer of land area in each pixel. ```{r load-data-2} pop_density <- raster(system.file('sao_miguel/gpw_v411_2020_density_2020.tif', package = 'exactextractr')) ``` To get a population count, we can multiply the population density by the area of each cell that is covered by the polygon. One way to do this is by providing the cell areas as a weighting raster and using a custom summary function. Weighted summary functions have the signature `function(values, coverage_fractions, weights)`. We can write one as follows: ```{r pop-from-density, cache = TRUE} concelho_pop2 <- exact_extract(pop_density, concelhos, function(density, frac, area) { sum(density * frac * area) }, weights = raster::area(pop_density), append_cols = 'name', progress = FALSE) ``` This produces the following table: ```{r, echo = FALSE} concelho_pop2 %>% rename(pop = result) %>% arrange(desc(pop)) %>% bind_rows(summarize(., name = 'Total', pop = sum(pop))) %>% mutate(pop = prettyNum(round(pop), big.mark = ',')) %>% knitr::kable() ``` ```{r pop-missing-from-density, echo=FALSE, results='hide'} missing_pop_pct <- 100 * (sao_miguel_pop - sum(concelho_pop2$result)) / sao_miguel_pop stopifnot(missing_pop_pct < 1) ``` The total population obtained using this method is remarkably close (within `r sprintf('%.02f%%', abs(missing_pop_pct))`) to the expected value from `cellStats`. While this solution works well for the sample data, it has a couple of disadvantages for larger data sets: - calling `raster::area(x)` generates an in-memory raster of the same size as `x`. For a raster like GPW at 30 arc-second resolution, this would consume several gigabytes of memory. - passing extracted raster values to a summary function written in R requires that `exactextractr` load all values associated with a given polygon into memory at once. This presents no problem when working with the _concelho_ boundaries, but could cause excessive memory usage when working with large national boundaries. An alternative formulation that resolves both of these problems uses the `weighted_sum` summary operation instead of an R function, and uses `weights = 'area'`, which instructs `exact_extract` to compute its own cell areas based on the projection of `pop_density`. ```{r pop-from-density-2, results = 'hide'} exact_extract(pop_density, concelhos, 'weighted_sum', weights = 'area') ``` ## Population-weighted statistics Suppose that we are interested in calculating the average elevation of a residence in each of the six _concelhos_. Loading the EU-DEM elevation data for the island, we can see that each _concelho_ is at least partly occupied by interior mountains, indicating that the results of a simple mean would be unrepresentative of the primarily coastal population. ```{r plot-elevation} elev <- raster(system.file('sao_miguel/eu_dem_v11.tif', package = 'exactextractr')) plot(elev, axes = FALSE, box = FALSE) plot(st_geometry(concelhos), add = TRUE) ``` As in the previous section, we avoid working with the population count raster to avoid losing population along the coastline. We can formulate the population-weighted average elevation as in terms of population density and pixel areas as: $$ \bar{x}_\mathrm{pop} = \frac{ \Sigma_{i=0}^n {x_ic_id_ia_i}}{\Sigma_{i=0}^n{c_id_ia_i}} $$ where $x_i$ is the elevation of pixel $i$, $c_i$ is the fraction of pixel $i$ that is covered by a polygon, $d_i$ is the population density of pixel $i$, and $a_i$ is the area of pixel $i$. If we are working with projected data, or geographic data over a small area such as São Miguel, we can assume all pixel areas to be equivalent, in which case the $a_i$ components cancel each other out and we are left with the direct usage of population density as a weighting raster: ```{r pop-weighted-mean-elev, results = 'hide'} exact_extract(elev, concelhos, 'weighted_mean', weights = pop_density) ``` What if pixel areas do vary across the region of our analysis? One option is to create a scaled population count raster by multiplying the population density and the pixel area. For pixels that are partly covered by water, this inflates the pixel population such that we obtain the correct population when only the land area is covered by a polygon. This requires that we create and maintain a separate raster data set. Another option is to create a `RasterStack` of `pop_density` and `area(pop_density)`, and then write a summary function to handle the necessary processing. We use the `summarize_df = TRUE` argument to combine the elevation, population density, pixel area, and pixel coverage fraction into a single data frame that is passed to the summary function. ```{r pop-weighted-mean-elev-2, results = 'hide'} exact_extract(elev, concelhos, function(df) { weighted.mean(x = df$value, w = df$coverage_fraction * df$pop_density * df$area, na.rm = TRUE)}, weights = stack(list(pop_density = pop_density, area = area(pop_density))), summarize_df = TRUE, progress = FALSE) ``` This solution shares the same limitations with the previous example using an R summary function with `raster::area()`: we must precompute an area raster and store it in memory, and we must load all raster values intersecting a given polygon into memory at a single time. A better solution is to use the `coverage_area` argument to `exact_extract`, which specifies that all calculations use the area of each cell that is covered by the polygon instead of the fraction of each cell that is covered by the polygon. ```{r pop-weighted-mean-elev-3} concelho_mean_elev <- exact_extract(elev, concelhos, c('mean', 'weighted_mean'), weights = pop_density, coverage_area = TRUE, append_cols = 'name', progress = FALSE) ``` Here we also calculate the unweighted mean for comparison. We can see that the population-weighted mean elevation is substantially lower than the mean elevation in all _concelhos_. ```{r, echo = FALSE} concelho_mean_elev %>% rename(mean_elev = mean, pop_weighted_mean_elev = weighted_mean) %>% arrange(name) %>% knitr::kable() ``` exactextractr/vignettes/vig2_categorical.Rmd0000644000176200001440000002126415113334453021022 0ustar liggesusers--- title: "2. Summarizing categorical data" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{2. Summarizing categorical data} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7 ) knitr::opts_knit$set( global.par = TRUE ) ``` ```{r, include = FALSE} par(mar = c(1, 1, 1, 1)) ``` ## Introduction This vignette builds upon the sample data for São Miguel (introduced in [the previous vignette](vig1_population.html)) to demonstrate the use of `exactextractr` with categorical land cover data. A sample of the [CORINE 2018](https://land.copernicus.eu/en/products/corine-land-cover/clc2018) raster dataset is included in `exactextractr`. As in the previous vignette, the following packages are used: ```{r setup, message = FALSE} library(exactextractr) library(dplyr) library(sf) library(raster) ``` ## Loading the sample data First, we load the CORINE land cover data and the _concelho_ boundaries. ```{r} clc <- raster(system.file('sao_miguel/clc2018_v2020_20u1.tif', package = 'exactextractr')) concelhos <- st_read(system.file('sao_miguel/concelhos.gpkg', package = 'exactextractr'), quiet = TRUE) ``` ```{r, echo = FALSE} corine_palette <- c( "#e6004d", "#ff0000", "#cc4df2", "#cc0000", "#e6cccc", "#e6cce6", "#a600cc", "#a64d00", "#ff4dff", "#ffa6ff", "#ffe6ff", "#ffffa8", "#ffff00", "#e6e600", "#e68000", "#f2a64d", "#e6a600", "#e6e64d", "#ffe6a6", "#ffe64d", "#e6cc4d", "#f2cca6", "#80ff00", "#00a600", "#4dff00", "#ccf24d", "#a6ff80", "#a6e64d", "#a6f200", "#e6e6e6", "#cccccc", "#ccffcc", "#000000", "#a6e6cc", "#a6a6ff", "#4d4dff", "#ccccff", "#e6e6ff", "#a6a6e6", "#00ccf2", "#80f2e6", "#00ffa6", "#a6ffe6", "#e6f2ff", "#ffffff") plot(clc, col = corine_palette, axes = FALSE, legend = FALSE) plot(st_geometry(concelhos), add = TRUE) ``` The land cover class descriptions are provided in a separate DBF file. We read this in to a data frame, then use `levels()` to associate the class descriptions with the raster. ```{r} clc_classes <- foreign::read.dbf(system.file('sao_miguel/clc2018_v2020_20u1.tif.vat.dbf', package = 'exactextractr'), as.is = TRUE) %>% dplyr::select(value = Value, landcov = LABEL3) levels(clc) <- list(data.frame(ID = clc_classes$value, landcov = clc_classes$landcov)) ``` This association provides us with a way to look up the description for a given ID. Alternatively, we can relate the values using `merge` or a ``dplyr` join. ```{r} factorValues(clc, c(2, 18, 24)) ``` ## Summarizing land cover classifications One of the most basic questions we might ask is which land cover classification is predominant in each _concelho_. We can do this with the built-in `mode` summary operation. The `minority` and `variety` operations are also applicable to categorical data and provide the least-common classification and number of distinct classifications, respectively. ```{r landcov-mode} landcov_mode <- exact_extract(clc, concelhos, 'mode', append_cols = 'name', progress = FALSE) %>% inner_join(clc_classes, by=c(mode = 'value')) ``` ```{r landcov-mode-table, echo = FALSE} landcov_mode %>% dplyr::select(-mode) %>% knitr::kable() ``` ### Summary functions While `mode` provides a handy way to see the most common land cover category, we need to write a custom summary function if we want to see the frequency of different land cover types in an area. Summary functions are called once per feature from the input `sf` object. They can return either: * a scalar, in which case the return value of `exact_extract` will be a vector whose entries correspond with the rows of the input `sf` object, or * a data frame, in which case `exact_extract` will return a rowwise combination of the data frames for each feature. If the data frame returned by the summary function will have than a single row, it is useful for some identifying information to be included in the returned data frame. If we are going to perform typical data frame operations on the raster values and coverage fractions, it can be more convenient for the summary function to accept a single data frame argument, instead of separate arguments for the cell values and coverage fractions. This behavior can be enabled with the `summarize_df` argument. Using this method, we can calculate the fraction of each _concelho_ that is covered by each land cover category: ```{r landcov-fracs, message = FALSE} landcov_fracs <- exact_extract(clc, concelhos, function(df) { df %>% mutate(frac_total = coverage_fraction / sum(coverage_fraction)) %>% group_by(name, value) %>% summarize(freq = sum(frac_total)) }, summarize_df = TRUE, include_cols = 'name', progress = FALSE) ``` Here we use the `include_cols` argument here to include the `name` column from `concelhos` in the data frame passed to the summary function. (Although the value of `name` will be the same for all rows in `df`, we include `name` in the `group_by` expression so that it is not removed by `summarize`.) Other similar arguments include `include_xy` to get the cell center coordinates, `include_area` to get the cell area, and `include_cell` to get the cell index used by the `raster` package. This provides us with a correspondence between each numeric land cover category and its frequency in each _concelho_: ```{r} head(landcov_fracs) ``` Joining this table to `clc_classes`, we can associate the descriptions with the numeric types and view the three most common land cover classes in each _concelho_: ```{r landcov-fracs-table} landcov_fracs %>% inner_join(clc_classes, by = 'value') %>% group_by(name) %>% arrange(desc(freq)) %>% slice_head(n = 3) %>% mutate(freq = sprintf('%0.1f%%', 100*freq)) %>% knitr::kable() ``` Similarly, we can find the top land covers by area: ```{r landcov-areas, message = FALSE} landcov_areas <- exact_extract(clc, concelhos, function(df) { df %>% group_by(name, value) %>% summarize(area_km2 = sum(coverage_area) / 1e6) }, summarize_df = TRUE, coverage_area = TRUE, include_cols = 'name', progress = FALSE) ``` ```{r landcov-areas-table, echo = FALSE} landcov_areas %>% inner_join(clc_classes, by = 'value') %>% dplyr::select(-value) %>% group_by(name) %>% arrange(desc(area_km2)) %>% slice_head(n = 3) %>% knitr::kable() ``` ## Summarizing population land cover One extension of the analysis above is to see which land covers are associated with human population in a given _concelho_. Is the population primary urban or rural? As described in the previous vignette, the population density raster provides the most robust results in the presence of partially-covered pixels. ```{r load-pop-density} pop_density <- raster(system.file('sao_miguel/gpw_v411_2020_density_2020.tif', package = 'exactextractr')) ``` We are able to perform this analysis because the CORINE sample distributed with `exactextractr` has been reprojected from its native Lambert Equal Area projection into geographic coordinates consistent with GPW. Otherwise, working with multiple rasters in different projections requires transformation to a common grid using tools such as `raster::projectRaster` or the `gdalwarp` command-line utility. Very little about the call to `exact_extract` requires changing to incorporate population. We set `weights = pop_density` and, since we are using the `summarize_df` option, a column called `weight` will be added to the data frame passed to the summary function. We also set `coverage_area = TRUE` so that we can multiply the density by the covered pixel area to get a population count. ```{r landcov-pop-areas, message = FALSE, results = FALSE} landcov_pop_areas <- exact_extract(clc, concelhos, function(df) { df %>% group_by(name, value) %>% summarize(pop = sum(coverage_area * weight / 1e6)) %>% mutate(pop_frac = pop / sum(pop)) }, weights = pop_density, coverage_area = TRUE, summarize_df = TRUE, include_cols = 'name') ``` Looking at the highest-population land cover type in each _concelho_, we can can see that the western/central _concelhos_ of Lagoa, Ponta Delgada, Ribeira Grande, and Vila Franca do Campo have a more urban population than Nordeste or Povoação to the east. ```{r landcov-pop-areas-table, echo = FALSE} landcov_pop_areas %>% inner_join(clc_classes, by = 'value') %>% group_by(name) %>% arrange(desc(pop_frac)) %>% slice_head(n = 1) %>% dplyr::select(name, landcov, pop, pop_frac) %>% mutate(pop = round(pop), pop_frac = round(pop_frac, 3)) %>% knitr::kable() ``` exactextractr/src/0000755000176200001440000000000015113334656013731 5ustar liggesusersexactextractr/src/exact_extract.cpp0000644000176200001440000004461514776002150017300 0ustar liggesusers// Copyright (c) 2018-2022 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // [[Rcpp::plugins("cpp14")]] #include #include #include "geos_r.h" #include "raster_utils.h" #include "s4_raster_source.h" #include "exactextract/src/geos_utils.h" #include "exactextract/src/grid.h" #include "exactextract/src/matrix.h" #include "exactextract/src/raster_cell_intersection.h" #include "exactextract/src/raster_source.h" #include "exactextract/src/raster_stats.h" using exactextract::Box; using exactextract::Matrix; using exactextract::Grid; using exactextract::subdivide; using exactextract::bounded_extent; using exactextract::Raster; using exactextract::RasterView; using exactextract::raster_cell_intersection; using exactextract::RasterStats; using exactextract::RasterSource; #include #include // [[Rcpp::export]] Rcpp::List CPP_exact_extract(Rcpp::S4 & rast, const Rcpp::NumericVector & rast_ext, const Rcpp::NumericVector & rast_res, Rcpp::Nullable & rast_uncropped, Rcpp::Nullable & weights, const Rcpp::RawVector & wkb, double default_value, double default_weight, bool include_xy, bool include_cell_number, bool include_area, bool area_weights, bool coverage_areas, Rcpp::Nullable & p_area_method, Rcpp::Nullable & include_cols, Rcpp::CharacterVector & src_names, Rcpp::Nullable & p_weights_names, bool warn_on_disaggregate, double grid_compat_tol) { try { GEOSAutoHandle geos; Rcpp::Function names("names"); auto grid = make_grid(rast); auto weights_grid = exactextract::Grid::make_empty(); auto common_grid = grid; S4RasterSource rsrc(rast, rast_ext, rast_res, default_value); int src_nlayers = get_nlayers(rast); std::unique_ptr rweights; int weights_nlayers = 0; Rcpp::CharacterVector weights_names; std::string area_method; if (p_area_method.isNotNull()) { Rcpp::CharacterVector amethod = p_area_method.get(); area_method = amethod[0]; } if (weights.isNotNull()) { Rcpp::S4 weights_s4 = weights.get(); weights_nlayers = get_nlayers(weights_s4); weights_grid = make_grid(weights_s4); common_grid = grid.common_grid(weights_grid, grid_compat_tol); rweights = std::make_unique(weights_s4, default_weight); weights_names = p_weights_names.get(); if (warn_on_disaggregate && (common_grid.dx() < grid.dx() || common_grid.dy() < grid.dy())) { Rcpp::warning("value raster implicitly disaggregated to match higher resolution of weights"); } } else if (area_weights) { weights_nlayers = 1; weights_names = p_weights_names.get(); } auto geom = read_wkb(geos.handle, wkb); auto bbox = exactextract::geos_get_box(geos.handle, geom.get()); common_grid = common_grid.crop(bbox); auto coverage_fractions = raster_cell_intersection(common_grid, geos.handle, geom.get()); auto& cov_grid = coverage_fractions.grid(); // We can't construct an Rcpp::DataFrame with a variable number of columns // because of a bug in Rcpp (see https://github.com/RcppCore/Rcpp/pull/1099) // Instead, we build up a list, and then create a data frame from the list. // Profiling shows that this ends up in as.data.frame, which is slow. // Once Rcpp 1.0.6 is released, this can be reworked to be simpler and faster. Rcpp::List cols; Rcpp::NumericVector coverage_vec = as_vector(coverage_fractions); if (coverage_areas) { auto areas = get_area_raster(area_method, cov_grid); Rcpp::NumericVector area_vec = as_vector(*areas); coverage_vec = coverage_vec * area_vec; } Rcpp::LogicalVector covered = coverage_vec > 0; if (include_cols.isNotNull()) { Rcpp::List include_cols_list = include_cols.get(); Rcpp::CharacterVector include_names = include_cols_list.attr("names"); for (int i = 0; i < include_names.size(); i++) { std::string name(include_names[i]); cols[name] = include_cols_list[name]; } } for (int i = 0; i < src_nlayers; i++) { auto values = rsrc.read_box(cov_grid.extent(), i); const NumericVectorRaster* r = static_cast(values.get()); // TODO Perhaps extend this to preserve types (integer, logical.) // A bit challenging, since we don't know the type of the raster // until we call getValuesBlock, and even then we can't be sure // that the returned type is correct (as in a RasterStack with mixed types.) // Since R integers are only 32-bit, we are not going to lose data by // converting everything to numeric, although we pay a storage penalty. Rcpp::NumericVector value_vec = r->vec(); if (grid.dx() != cov_grid.dx() || grid.dy() != cov_grid.dy() || value_vec.size() != covered.size()) { // Transform values to same grid as coverage fractions RasterView rt(*r, cov_grid); value_vec = as_vector(rt); } value_vec = value_vec[covered]; cols[std::string(src_names[i])] = value_vec; } for (int i = 0; i < weights_nlayers; i++) { Rcpp::NumericVector weight_vec; if (area_weights) { auto weights = get_area_raster(area_method, cov_grid); weight_vec = as_vector(*weights); } else { auto values = rweights->read_box(cov_grid.extent(), i); const NumericVectorRaster* r = static_cast(values.get()); weight_vec = r->vec(); if (weights_grid.dx() != cov_grid.dx() || weights_grid.dy() != cov_grid.dy() || weight_vec.size() != covered.size()) { // Transform weights to same grid as coverage fractions RasterView rt (*r, cov_grid); weight_vec = as_vector(rt); } } weight_vec = weight_vec[covered]; std::string colname(weights_names[i]); if (cols.containsElementNamed(colname.c_str())) { // append ".1" to the column name, to match the behavior of // data.frame(). We're safe to just add ".1" (instead of incrementing // .1 to .2, for example) because duplicated names within the values // or weight stack will already have been made unique by raster::stack() colname = colname + ".1"; // append .1 } cols[colname] = weight_vec; } if (include_xy) { // Include xy values from whichever input raster has the higher resolution if (weights_nlayers > 0 && (weights_grid.dx() < grid.dx() || weights_grid.dy() < grid.dy())) { Rcpp::S4 weights_s4 = weights.get(); cols["x"] = get_x_values(weights_s4, cov_grid)[covered]; cols["y"] = get_y_values(weights_s4, cov_grid)[covered]; } else { cols["x"] = get_x_values(rast, cov_grid)[covered]; cols["y"] = get_y_values(rast, cov_grid)[covered]; } } if (include_cell_number) { if (rast_uncropped.isNull()) { cols["cell"] = get_cell_numbers(rast, cov_grid)[covered]; } else { Rcpp::S4 tmp = rast_uncropped.get(); cols["cell"] = get_cell_numbers(tmp, cov_grid)[covered]; } } if (include_area) { auto area_rast = get_area_raster(area_method, cov_grid); Rcpp::NumericVector area_vec = as_vector(*area_rast); cols["area"] = area_vec[covered]; } if (coverage_areas) { cols["coverage_area"] = coverage_vec[covered]; } else { cols["coverage_fraction"] = coverage_vec[covered]; } return cols; } catch (std::exception & e) { // throw predictable exception class #ifdef __SUNPRO_CC // Rcpp::stop crashes CRAN Solaris build // https://github.com/RcppCore/Rcpp/issues/1159 Rf_error(e.what()); return R_NilValue; #else Rcpp::stop(e.what()); #endif } } enum class WeightingMethod { NONE, RASTER, AREA }; static int get_num_stats(const Rcpp::StringVector & stats, const std::size_t num_quantiles, const std::size_t num_unique_values) { int num_stats = 0; for (const auto & stat : stats) { if (stat == std::string("frac") || stat == std::string("weighted_frac")) { num_stats += static_cast(num_unique_values); } else if (stat == std::string("quantile")) { num_stats += static_cast(num_quantiles); } else { num_stats += 1; } } return num_stats; } // Return a matrix with one row per stat and one row per raster layer // [[Rcpp::export]] Rcpp::NumericMatrix CPP_stats(Rcpp::S4 & rast, const Rcpp::NumericVector & rast_ext, const Rcpp::NumericVector & rast_res, Rcpp::Nullable weights, const Rcpp::RawVector & wkb, double default_value, double default_weight, bool coverage_areas, Rcpp::Nullable & p_area_method, const Rcpp::StringVector & stats, int max_cells_in_memory, double grid_compat_tol, const Rcpp::Nullable & quantiles) { try { GEOSAutoHandle geos; if (max_cells_in_memory < 1) { Rcpp::stop("Invalid value for max_cells_in_memory: %d", max_cells_in_memory); } int nlayers = get_nlayers(rast); S4RasterSource rsrc(rast, rast_ext, rast_res, default_value); std::unique_ptr rweights; std::string area_method; if (p_area_method.isNotNull()) { area_method = ((Rcpp::CharacterVector) p_area_method.get())[0]; } WeightingMethod weighting = WeightingMethod::NONE; int nweights = 0; if (weights.isNotNull()) { weighting = WeightingMethod::RASTER; Rcpp::S4 weights_s4 = weights.get(); nweights = get_nlayers(weights_s4); if (nlayers > 1 && nweights > 1 && nlayers != nweights) { Rcpp::stop("Incompatible number of layers in value and weighting rasters"); } rweights = std::make_unique(weights_s4, default_weight); } else if (p_area_method.isNotNull()) { weighting = WeightingMethod::AREA; nweights = 1; } auto geom = read_wkb(geos.handle, wkb); auto bbox = exactextract::geos_get_box(geos.handle, geom.get()); auto grid = weighting == WeightingMethod::RASTER ? rsrc.grid().common_grid(rweights->grid(), grid_compat_tol) : rsrc.grid(); bool disaggregated = (grid.dx() < rsrc.grid().dx() || grid.dy() < rsrc.grid().dy()); bool store_values = false; bool calc_value_set = false; std::size_t num_unique_values = 0; std::size_t num_quantiles = 0; for (const auto & stat : stats) { store_values = store_values || requires_stored_values(stat); if (disaggregated && (stat == std::string("count") || stat == std::string("sum"))) { Rcpp::stop("Cannot compute 'count' or 'sum' when value raster is disaggregated to resolution of weights."); } if (stat == std::string("frac") || stat == std::string("weighted_frac")) { calc_value_set = true; } if (stat == std::string("quantile")) { if (quantiles.isNotNull()) { Rcpp::NumericVector qvec = quantiles.get(); num_quantiles = qvec.size(); } if (num_quantiles == 0) { Rcpp::stop("Quantiles not specified."); } } } int nresults = std::max(nlayers, nweights); std::vector> raster_stats; raster_stats.reserve(nresults); for (int i = 0; i < nresults; i++) { raster_stats.emplace_back(store_values); } if (bbox.intersects(grid.extent())) { auto cropped_grid = grid.crop(bbox); for (const auto &subgrid : subdivide(cropped_grid, max_cells_in_memory)) { auto coverage_fraction = raster_cell_intersection(subgrid, geos.handle, geom.get()); if (coverage_areas) { auto areas = get_area_raster(area_method, subgrid); for (size_t i = 0; i < coverage_fraction.rows(); i++) { for (size_t j = 0; j < coverage_fraction.cols(); j++) { coverage_fraction(i, j) = coverage_fraction(i, j) * (*areas)(i, j); } } } auto& cov_grid = coverage_fraction.grid(); if (!cov_grid.empty()) { if (weighting == WeightingMethod::NONE) { for (int i = 0; i < nlayers; i++) { auto values = rsrc.read_box(cov_grid.extent(), i); raster_stats[i].process(coverage_fraction, *values); } } else if (weighting == WeightingMethod::AREA) { auto weights = get_area_raster(area_method, cov_grid); for (int i = 0; i < nlayers; i++) { auto values = rsrc.read_box(cov_grid.extent(), i); raster_stats[i].process(coverage_fraction, *values, *weights); } } else { if (nlayers > nweights) { // recycle weights auto weights = rweights->read_box(cov_grid.extent(), 0); for (int i = 0; i < nlayers; i++) { auto values = rsrc.read_box(cov_grid.extent(), i); raster_stats[i].process(coverage_fraction, *values, *weights); } } else if (nweights > nlayers) { // recycle values auto values = rsrc.read_box(cov_grid.extent(), 0); for (int i = 0; i < nweights; i++) { auto weights = rweights->read_box(cov_grid.extent(), i); raster_stats[i].process(coverage_fraction, *values, *weights); } } else { // process values and weights in parallel for (int i = 0; i < nlayers; i++) { auto values = rsrc.read_box(cov_grid.extent(), i); auto weights = rweights->read_box(cov_grid.extent(), i); raster_stats[i].process(coverage_fraction, *values, *weights); } } } } } } std::set value_set; if (calc_value_set) { for (const auto& rs : raster_stats) { for (const auto& value : rs) { value_set.insert(value); } } num_unique_values = value_set.size(); } auto stat_result_size = get_num_stats(stats, num_quantiles, num_unique_values); Rcpp::NumericMatrix stat_results = Rcpp::no_init(nresults, stat_result_size); if (calc_value_set) { Rcpp::NumericVector unique_values(value_set.begin(), value_set.end()); stat_results.attr("unique_values") = unique_values; } // rows (j) represent layers // cols (i) represent stats for (int j = 0; j < nresults; j++) { const auto& rs = raster_stats[j]; int i = 0; for(const auto& stat : stats) { if (stat == std::string("mean")) stat_results(j, i++) = rs.mean(); else if (stat == std::string("sum")) stat_results(j, i++) = rs.sum(); else if (stat == std::string("count")) stat_results(j, i++) = rs.count(); else if (stat == std::string("min")) stat_results(j, i++) = rs.min().value_or(NA_REAL); else if (stat == std::string("max")) stat_results(j, i++) = rs.max().value_or(NA_REAL); else if (stat == std::string("median")) stat_results(j, i++) = rs.quantile(0.5).value_or(NA_REAL); else if (stat == std::string("mode")) stat_results(j, i++) = rs.mode().value_or(NA_REAL); else if (stat == std::string("majority")) stat_results(j, i++) = rs.mode().value_or(NA_REAL); else if (stat == std::string("minority")) stat_results(j, i++) = rs.minority().value_or(NA_REAL); else if (stat == std::string("variety")) stat_results(j, i++) = rs.variety(); else if (stat == std::string("weighted_mean")) stat_results(j, i++) = rs.weighted_mean(); else if (stat == std::string("weighted_sum")) stat_results(j, i++) = rs.weighted_sum(); else if (stat == std::string("variance")) stat_results(j, i++) = rs.variance(); else if (stat == std::string("stdev")) stat_results(j, i++) = rs.stdev(); else if (stat == std::string("coefficient_of_variation")) stat_results(j, i++) = rs.coefficient_of_variation(); else if (stat == std::string("weighted_variance")) stat_results(j, i++) = rs.weighted_variance(); else if (stat == std::string("weighted_stdev")) stat_results(j, i++) = rs.weighted_stdev(); else if (stat == std::string("quantile")) { Rcpp::NumericVector qvec = quantiles.get(); for (double q : qvec) { stat_results(j, i++) = rs.quantile(q).value_or(NA_REAL); } } else if (stat == std::string("frac")) { for (double v : value_set) { stat_results(j, i++) = rs.frac(v).value_or(0); } } else if (stat == std::string("weighted_frac")) { for (double v : value_set) { stat_results(j, i++) = rs.weighted_frac(v).value_or(0); } } else Rcpp::stop("Unknown stat: " + stat); } } // FIXME set dimnames here return stat_results; } catch (std::exception & e) { // throw predictable exception class #ifdef __SUNPRO_CC // Rcpp::stop crashes CRAN Solaris build // https://github.com/RcppCore/Rcpp/issues/1159 Rf_error(e.what()); return R_NilValue; #else Rcpp::stop(e.what()); #endif } } exactextractr/src/RcppExports.cpp0000644000176200001440000002250015113331212016706 0ustar liggesusers// Generated by using Rcpp::compileAttributes() -> do not edit by hand // Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 #include using namespace Rcpp; #ifdef RCPP_USE_GLOBAL_ROSTREAM Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); #endif // CPP_coverage_fraction Rcpp::S4 CPP_coverage_fraction(Rcpp::S4& rast, const Rcpp::RawVector& wkb, bool crop); RcppExport SEXP _exactextractr_CPP_coverage_fraction(SEXP rastSEXP, SEXP wkbSEXP, SEXP cropSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::S4& >::type rast(rastSEXP); Rcpp::traits::input_parameter< const Rcpp::RawVector& >::type wkb(wkbSEXP); Rcpp::traits::input_parameter< bool >::type crop(cropSEXP); rcpp_result_gen = Rcpp::wrap(CPP_coverage_fraction(rast, wkb, crop)); return rcpp_result_gen; END_RCPP } // CPP_exact_extract Rcpp::List CPP_exact_extract(Rcpp::S4& rast, const Rcpp::NumericVector& rast_ext, const Rcpp::NumericVector& rast_res, Rcpp::Nullable& rast_uncropped, Rcpp::Nullable& weights, const Rcpp::RawVector& wkb, double default_value, double default_weight, bool include_xy, bool include_cell_number, bool include_area, bool area_weights, bool coverage_areas, Rcpp::Nullable& p_area_method, Rcpp::Nullable& include_cols, Rcpp::CharacterVector& src_names, Rcpp::Nullable& p_weights_names, bool warn_on_disaggregate, double grid_compat_tol); RcppExport SEXP _exactextractr_CPP_exact_extract(SEXP rastSEXP, SEXP rast_extSEXP, SEXP rast_resSEXP, SEXP rast_uncroppedSEXP, SEXP weightsSEXP, SEXP wkbSEXP, SEXP default_valueSEXP, SEXP default_weightSEXP, SEXP include_xySEXP, SEXP include_cell_numberSEXP, SEXP include_areaSEXP, SEXP area_weightsSEXP, SEXP coverage_areasSEXP, SEXP p_area_methodSEXP, SEXP include_colsSEXP, SEXP src_namesSEXP, SEXP p_weights_namesSEXP, SEXP warn_on_disaggregateSEXP, SEXP grid_compat_tolSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::S4& >::type rast(rastSEXP); Rcpp::traits::input_parameter< const Rcpp::NumericVector& >::type rast_ext(rast_extSEXP); Rcpp::traits::input_parameter< const Rcpp::NumericVector& >::type rast_res(rast_resSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable& >::type rast_uncropped(rast_uncroppedSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable& >::type weights(weightsSEXP); Rcpp::traits::input_parameter< const Rcpp::RawVector& >::type wkb(wkbSEXP); Rcpp::traits::input_parameter< double >::type default_value(default_valueSEXP); Rcpp::traits::input_parameter< double >::type default_weight(default_weightSEXP); Rcpp::traits::input_parameter< bool >::type include_xy(include_xySEXP); Rcpp::traits::input_parameter< bool >::type include_cell_number(include_cell_numberSEXP); Rcpp::traits::input_parameter< bool >::type include_area(include_areaSEXP); Rcpp::traits::input_parameter< bool >::type area_weights(area_weightsSEXP); Rcpp::traits::input_parameter< bool >::type coverage_areas(coverage_areasSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable& >::type p_area_method(p_area_methodSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable& >::type include_cols(include_colsSEXP); Rcpp::traits::input_parameter< Rcpp::CharacterVector& >::type src_names(src_namesSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable& >::type p_weights_names(p_weights_namesSEXP); Rcpp::traits::input_parameter< bool >::type warn_on_disaggregate(warn_on_disaggregateSEXP); Rcpp::traits::input_parameter< double >::type grid_compat_tol(grid_compat_tolSEXP); rcpp_result_gen = Rcpp::wrap(CPP_exact_extract(rast, rast_ext, rast_res, rast_uncropped, weights, wkb, default_value, default_weight, include_xy, include_cell_number, include_area, area_weights, coverage_areas, p_area_method, include_cols, src_names, p_weights_names, warn_on_disaggregate, grid_compat_tol)); return rcpp_result_gen; END_RCPP } // CPP_stats Rcpp::NumericMatrix CPP_stats(Rcpp::S4& rast, const Rcpp::NumericVector& rast_ext, const Rcpp::NumericVector& rast_res, Rcpp::Nullable weights, const Rcpp::RawVector& wkb, double default_value, double default_weight, bool coverage_areas, Rcpp::Nullable& p_area_method, const Rcpp::StringVector& stats, int max_cells_in_memory, double grid_compat_tol, const Rcpp::Nullable& quantiles); RcppExport SEXP _exactextractr_CPP_stats(SEXP rastSEXP, SEXP rast_extSEXP, SEXP rast_resSEXP, SEXP weightsSEXP, SEXP wkbSEXP, SEXP default_valueSEXP, SEXP default_weightSEXP, SEXP coverage_areasSEXP, SEXP p_area_methodSEXP, SEXP statsSEXP, SEXP max_cells_in_memorySEXP, SEXP grid_compat_tolSEXP, SEXP quantilesSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::S4& >::type rast(rastSEXP); Rcpp::traits::input_parameter< const Rcpp::NumericVector& >::type rast_ext(rast_extSEXP); Rcpp::traits::input_parameter< const Rcpp::NumericVector& >::type rast_res(rast_resSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable >::type weights(weightsSEXP); Rcpp::traits::input_parameter< const Rcpp::RawVector& >::type wkb(wkbSEXP); Rcpp::traits::input_parameter< double >::type default_value(default_valueSEXP); Rcpp::traits::input_parameter< double >::type default_weight(default_weightSEXP); Rcpp::traits::input_parameter< bool >::type coverage_areas(coverage_areasSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable& >::type p_area_method(p_area_methodSEXP); Rcpp::traits::input_parameter< const Rcpp::StringVector& >::type stats(statsSEXP); Rcpp::traits::input_parameter< int >::type max_cells_in_memory(max_cells_in_memorySEXP); Rcpp::traits::input_parameter< double >::type grid_compat_tol(grid_compat_tolSEXP); Rcpp::traits::input_parameter< const Rcpp::Nullable& >::type quantiles(quantilesSEXP); rcpp_result_gen = Rcpp::wrap(CPP_stats(rast, rast_ext, rast_res, weights, wkb, default_value, default_weight, coverage_areas, p_area_method, stats, max_cells_in_memory, grid_compat_tol, quantiles)); return rcpp_result_gen; END_RCPP } // CPP_update_max_coverage void CPP_update_max_coverage(Rcpp::NumericVector& extent, Rcpp::NumericVector& res, Rcpp::NumericMatrix& max_coverage, Rcpp::IntegerMatrix& max_coverage_index, Rcpp::NumericMatrix& tot_coverage, const Rcpp::RawVector& wkb, int index); RcppExport SEXP _exactextractr_CPP_update_max_coverage(SEXP extentSEXP, SEXP resSEXP, SEXP max_coverageSEXP, SEXP max_coverage_indexSEXP, SEXP tot_coverageSEXP, SEXP wkbSEXP, SEXP indexSEXP) { BEGIN_RCPP Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::NumericVector& >::type extent(extentSEXP); Rcpp::traits::input_parameter< Rcpp::NumericVector& >::type res(resSEXP); Rcpp::traits::input_parameter< Rcpp::NumericMatrix& >::type max_coverage(max_coverageSEXP); Rcpp::traits::input_parameter< Rcpp::IntegerMatrix& >::type max_coverage_index(max_coverage_indexSEXP); Rcpp::traits::input_parameter< Rcpp::NumericMatrix& >::type tot_coverage(tot_coverageSEXP); Rcpp::traits::input_parameter< const Rcpp::RawVector& >::type wkb(wkbSEXP); Rcpp::traits::input_parameter< int >::type index(indexSEXP); CPP_update_max_coverage(extent, res, max_coverage, max_coverage_index, tot_coverage, wkb, index); return R_NilValue; END_RCPP } // CPP_resample Rcpp::S4 CPP_resample(Rcpp::S4& rast_in, Rcpp::S4& rast_out, Rcpp::Nullable p_stat, Rcpp::Nullable p_fun, bool coverage_area, std::string area_method); RcppExport SEXP _exactextractr_CPP_resample(SEXP rast_inSEXP, SEXP rast_outSEXP, SEXP p_statSEXP, SEXP p_funSEXP, SEXP coverage_areaSEXP, SEXP area_methodSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< Rcpp::S4& >::type rast_in(rast_inSEXP); Rcpp::traits::input_parameter< Rcpp::S4& >::type rast_out(rast_outSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable >::type p_stat(p_statSEXP); Rcpp::traits::input_parameter< Rcpp::Nullable >::type p_fun(p_funSEXP); Rcpp::traits::input_parameter< bool >::type coverage_area(coverage_areaSEXP); Rcpp::traits::input_parameter< std::string >::type area_method(area_methodSEXP); rcpp_result_gen = Rcpp::wrap(CPP_resample(rast_in, rast_out, p_stat, p_fun, coverage_area, area_method)); return rcpp_result_gen; END_RCPP } static const R_CallMethodDef CallEntries[] = { {"_exactextractr_CPP_coverage_fraction", (DL_FUNC) &_exactextractr_CPP_coverage_fraction, 3}, {"_exactextractr_CPP_exact_extract", (DL_FUNC) &_exactextractr_CPP_exact_extract, 19}, {"_exactextractr_CPP_stats", (DL_FUNC) &_exactextractr_CPP_stats, 13}, {"_exactextractr_CPP_update_max_coverage", (DL_FUNC) &_exactextractr_CPP_update_max_coverage, 7}, {"_exactextractr_CPP_resample", (DL_FUNC) &_exactextractr_CPP_resample, 6}, {NULL, NULL, 0} }; RcppExport void R_init_exactextractr(DllInfo *dll) { R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); } exactextractr/src/resample.cpp0000644000176200001440000001376414776002150016253 0ustar liggesusers// Copyright (c) 2018-2022 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include "s4_raster_source.h" #include "raster_utils.h" #include "exactextract/src/box.h" #include "exactextract/src/raster_stats.h" using exactextract::Box; using exactextract::RasterStats; using exactextract::RasterView; // TODO merge with nearly-identical code in exact_extract.cpp static double get_stat_value(const RasterStats & stats, const std::string & stat_name) { if (stat_name == "mean") return stats.mean(); else if (stat_name == "sum") return stats.sum(); else if (stat_name == "count") return stats.count(); else if (stat_name == "min") return stats.min().value_or(NA_REAL); else if (stat_name == "max") return stats.max().value_or(NA_REAL); else if (stat_name == "mode") return stats.mode().value_or(NA_REAL); else if (stat_name == "majority") return stats.mode().value_or(NA_REAL); else if (stat_name == "minority") return stats.minority().value_or(NA_REAL); else if (stat_name == "variety") return stats.variety(); else if (stat_name == "weighted_mean") return stats.weighted_mean(); else if (stat_name == "weighted_sum") return stats.weighted_sum(); else if (stat_name == "variance") return stats.variance(); else if (stat_name == "stdev") return stats.stdev(); else if (stat_name == "coefficient_of_variation") return stats.coefficient_of_variation(); else Rcpp::stop("Unknown stat: " + stat_name); } // [[Rcpp::export]] Rcpp::S4 CPP_resample(Rcpp::S4 & rast_in, Rcpp::S4 & rast_out, Rcpp::Nullable p_stat, Rcpp::Nullable p_fun, bool coverage_area, std::string area_method) { try { Rcpp::Environment raster = Rcpp::Environment::namespace_env("raster"); Rcpp::Environment xx = Rcpp::Environment::namespace_env("exactextractr"); Rcpp::Function rasterFn = raster["raster"]; Rcpp::Function valuesFn = xx[".setValues"]; Rcpp::Function nlyrFn = xx[".numLayers"]; int numLayers = Rcpp::as(nlyrFn(rast_in)); S4RasterSource rsrc(rast_in); Rcpp::S4 out = rasterFn(rast_out); auto grid_in = make_grid(rast_in); auto grid_out = make_grid(rast_out); std::string stat_name; bool store_values = false; bool r_summary_function = false; if (p_stat.isNotNull()) { Rcpp::CharacterVector stat = p_stat.get(); stat_name = Rcpp::as(stat[0]); store_values = requires_stored_values(stat_name); } else { r_summary_function = true; } Rcpp::NumericMatrix values_out = Rcpp::no_init(grid_out.rows(), grid_out.cols()); std::vector> values(numLayers); for (size_t row = 0; row < grid_out.rows(); row++) { // Read enough source raster data to process an entire destination row at // a time, since getValuesBlock calls have a lot of overhead. auto y = grid_out.y_for_row(row); auto ymin = y - grid_out.dy(); auto ymax = y + grid_out.dy(); Box row_box{ grid_out.xmin(), ymin, grid_out.xmax(), ymax }; for (int i = 0; i < numLayers; i++) { values[i] = std::unique_ptr( static_cast(rsrc.read_box(row_box, i).release())); } for (size_t col = 0; col < grid_out.cols(); col++) { Box cell = grid_cell(grid_out, row, col); auto coverage_fraction = raster_cell_intersection(grid_in, cell); auto& cov_grid = coverage_fraction.grid(); if (coverage_area) { auto areas = get_area_raster(area_method, cov_grid); for (size_t i = 0; i < coverage_fraction.rows(); i++) { for (size_t j = 0; j < coverage_fraction.cols(); j++) { coverage_fraction(i, j) = coverage_fraction(i, j) * (*areas)(i, j); } } } if (r_summary_function) { Rcpp::Function summary_fun = p_fun.get(); // Transform values to same grid as coverage fractions auto coverage_vec = as_vector(coverage_fraction); Rcpp::NumericVector result; if (numLayers == 1) { RasterView rt(*(values[0]), cov_grid); auto value_vec = as_vector(rt); result = summary_fun(value_vec, coverage_vec); } else { Rcpp::NumericMatrix value_mat = Rcpp::no_init(coverage_vec.size(), numLayers); for (int i = 0; i < numLayers; i++) { RasterView rt(*(values[i]), cov_grid); auto value_vec = as_vector(rt); value_mat(Rcpp::_, i) = value_vec; } result = summary_fun(value_mat, coverage_vec); } if (result.size() != 1) { Rcpp::stop("Summary function must return a single value"); } values_out(row, col) = result[0]; } else { RasterStats stats{store_values}; if (!cov_grid.empty()) { stats.process(coverage_fraction, *(values[0])); } values_out(row, col) = get_stat_value(stats, stat_name); } } } out = valuesFn(out, values_out); return out; } catch (std::exception & e) { // throw predictable exception class #ifdef __SUNPRO_CC // Rcpp::stop crashes CRAN Solaris build // https://github.com/RcppCore/Rcpp/issues/1159 Rf_error(e.what()); return R_NilValue; #else Rcpp::stop(e.what()); #endif } } exactextractr/src/Makevars.win0000644000176200001440000000143715113334453016221 0ustar liggesusersVERSION = 2.2.3 COMPILED_BY ?= gcc-4.9.3 RWINLIB = ../windows/gdal2-$(VERSION) PKG_LIBS = -lgeos_c -lgeos -L$(RWINLIB)/lib/$(R_ARCH) PKG_CXXFLAGS = -I$(RWINLIB)/include/geos EE = exactextract/src SOURCES = $(EE)/measures.cpp $(EE)/box.cpp $(EE)/cell.cpp $(EE)/coordinate.cpp $(EE)/floodfill.cpp $(EE)/geos_utils.cpp $(EE)/grid.cpp $(EE)/perimeter_distance.cpp $(EE)/raster_cell_intersection.cpp $(EE)/side.cpp $(EE)/traversal.cpp $(EE)/traversal_areas.cpp $(EE)/weighted_quantiles.cpp OBJECTS = RcppExports.o exact_extract.o raster_utils.o coverage_fraction.o rasterize.o resample.o $(SOURCES:.cpp=.o) all: clean winlibs winlibs: mkdir -p ../inst "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" --vanilla "../tools/winlibs.R" $(VERSION) clean: rm -f $(SHLIB) $(OBJECTS) .PHONY: all winlibs clean exactextractr/src/Makevars.ucrt0000644000176200001440000000101315113334453016367 0ustar liggesusersPKG_LIBS = -lgeos_c -lgeos EE = exactextract/src SOURCES = $(EE)/measures.cpp $(EE)/box.cpp $(EE)/cell.cpp $(EE)/coordinate.cpp $(EE)/floodfill.cpp $(EE)/geos_utils.cpp $(EE)/grid.cpp $(EE)/perimeter_distance.cpp $(EE)/raster_cell_intersection.cpp $(EE)/side.cpp $(EE)/traversal.cpp $(EE)/traversal_areas.cpp $(EE)/weighted_quantiles.cpp OBJECTS = RcppExports.o exact_extract.o raster_utils.o coverage_fraction.o rasterize.o resample.o $(SOURCES:.cpp=.o) all: clean clean: rm -f $(SHLIB) $(OBJECTS) .PHONY: all clean exactextractr/src/s4_raster_source.h0000644000176200001440000000777114776002150017377 0ustar liggesusers// Copyright (c) 2018-2021 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #pragma once #include #include #include "exactextract/src/grid.h" #include "numeric_vector_raster.h" #include "raster_utils.h" // Read raster values from an R raster object class S4RasterSource { public: explicit S4RasterSource(SEXP rast, double default_value = std::numeric_limits::quiet_NaN()) : m_grid(exactextract::Grid::make_empty()), m_rast(rast), m_last_box(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()), m_default_value(default_value) { m_grid = make_grid(rast); } S4RasterSource(SEXP rast, const Rcpp::NumericVector & ext, const Rcpp::NumericVector & res, double default_value = std::numeric_limits::quiet_NaN()) : m_grid(exactextract::Grid::make_empty()), m_rast(rast), m_last_box(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()), m_default_value(default_value) { m_grid = make_grid(ext, res); } const exactextract::Grid &grid() const { return m_grid; } std::unique_ptr> read_box(const exactextract::Box & box, int layer) { auto cropped_grid = m_grid.crop(box); if (!(box == m_last_box)) { m_last_box = box; Rcpp::Environment xx = Rcpp::Environment::namespace_env("exactextractr"); Rcpp::Function getValuesBlockFn = xx[".getValuesBlock"]; if (cropped_grid.empty()) { m_rast_values = Rcpp::no_init(0, 0); } else { // Instead of reading only values for the requested band, we read values // for all requested bands and then cache them to return from subsequent // calls to read_box. There are two reasons for this: // 1) Use of the 'lyrs` argument to getValuesBlock does not work for // a single band in the CRAN version of raster // 2) Reading everything once avoids the very significant per-call // overhead of getValuesBlock, which largely comes from operations // like setting names on the result vector. m_rast_values = getValuesBlockFn(m_rast, 1 + cropped_grid.row_offset(m_grid), cropped_grid.rows(), 1 + cropped_grid.col_offset(m_grid), cropped_grid.cols()); if (!std::isnan(m_default_value)) { for (double& x : m_rast_values) { if (std::isnan(x)) { x = m_default_value; } } } } } if (cropped_grid.empty()) { return std::make_unique(m_rast_values, cropped_grid); } else { return std::make_unique(m_rast_values(Rcpp::_, layer), cropped_grid); } } private: exactextract::Grid m_grid; SEXP m_rast; Rcpp::NumericMatrix m_rast_values; exactextract::Box m_last_box; double m_default_value; }; exactextractr/src/exactextract/0000755000176200001440000000000015113334656016430 5ustar liggesusersexactextractr/src/exactextract/CMakeLists.txt0000644000176200001440000002022714776002150021166 0ustar liggesuserscmake_minimum_required(VERSION 3.8) project(exactextract) set(DEFAULT_BUILD_TYPE "Release") set(LIB_NAME exactextract) set(BIN_NAME exactextract_bin) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) # gcc 4.9 doesn't fully support C++14, yet CMake doesn't bail when we # set CMAKE_CXX_STANDARD_REQUIRED # https://cmake.org/pipermail/cmake/2017-March/065102.html message(FATAL_ERROR "gcc 5.0+ is required to build exactextract") endif() include(GNUInstallDirs) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) include(VersionSource) find_package(GEOS REQUIRED) #Configure some options the various components this module can build option(BUILD_CLI "Build the exactextract cli binary" ON) #requires gdal, cli11 option(BUILD_TEST "Build the exactextract tests" ON) #requires catch option(BUILD_DOC "Build documentation" ON) #requires doxygen if(BUILD_CLI) # Create our main program, statically linked to our library # Unlike the library, this depends on GDAL find_package(GDAL) if (GDAL_FOUND) # Check GDAL version (requires CMake 3.14) if (${CMAKE_VERSION} VERSION_LESS 3.14.0) message(WARNING "GDAL 2.0+ is required but detected GDAL version is unknown.") elseif(${GDAL_VERSION} VERSION_LESS 2.0) unset(GDAL_FOUND) endif() endif() #GDAL_FOUND if (NOT GDAL_FOUND) message(FATAL_ERROR "GDAL version >= 2.0 was not found. It is still possible to build and test libexactextract, but the " "exactextract executable cannot be built or installed.") endif() #NOT GDAL_FOUND # Download CLI11 (header-only library) set(CLI11_INCLUDE_DIR ${CMAKE_BINARY_DIR}/CLI11) set(CLI11_INCLUDE ${CLI11_INCLUDE_DIR}/CLI11.hpp) if (NOT EXISTS ${CLI11_INCLUDE}) file(DOWNLOAD https://github.com/CLIUtils/CLI11/releases/download/v1.6.0/CLI11.hpp ${CLI11_INCLUDE} SHOW_PROGRESS) endif() #Configure the exactextract CLI target set(BIN_SOURCES src/exactextract.cpp src/gdal_raster_wrapper.h src/gdal_raster_wrapper.cpp src/gdal_dataset_wrapper.h src/gdal_dataset_wrapper.cpp src/gdal_writer.h src/gdal_writer.cpp src/processor.h src/feature_sequential_processor.cpp src/feature_sequential_processor.h src/raster_sequential_processor.cpp src/raster_sequential_processor.h ) add_executable(${BIN_NAME} ${BIN_SOURCES}) set_target_properties(${BIN_NAME} PROPERTIES OUTPUT_NAME "exactextract") target_compile_definitions(${BIN_NAME} PRIVATE GEOS_USE_ONLY_R_API) target_link_libraries( ${BIN_NAME} PRIVATE ${LIB_NAME} ${GDAL_LIBRARY} ${GEOS_LIBRARY} ) target_include_directories( ${BIN_NAME} PRIVATE ${CMAKE_BINARY_DIR}/generated ${CMAKE_SOURCE_DIR}/src ${GEOS_INCLUDE_DIR} ${GDAL_INCLUDE_DIR} ) # Include CLI11 as a system include so that -Wshadow warnings are suppressed. target_include_directories( ${BIN_NAME} SYSTEM PRIVATE ${CLI11_INCLUDE_DIR} ) target_compile_options( ${BIN_NAME} PRIVATE $<$:-Werror -Wall -Wextra -Wshadow> $<$:-Werror -Wall -Wextra -Wshadow -Wdouble-promotion>) install(TARGETS ${BIN_NAME} RUNTIME DESTINATION bin) endif() #BUILD_CLI if(BUILD_TEST) #Build the test suite # Download Catch (header-only library) set(CATCH_INCLUDE_DIR ${CMAKE_BINARY_DIR}/catch) set(CATCH_INCLUDE ${CATCH_INCLUDE_DIR}/catch.hpp) if (NOT EXISTS ${CATCH_INCLUDE}) file(DOWNLOAD https://github.com/catchorg/Catch2/releases/download/v2.13.8/catch.hpp ${CATCH_INCLUDE} SHOW_PROGRESS) endif() set(TEST_SOURCES test/test_box.cpp test/test_cell.cpp test/test_geos_utils.cpp test/test_grid.cpp test/test_main.cpp test/test_perimeter_distance.cpp test/test_raster.cpp test/test_raster_area.cpp test/test_raster_cell_intersection.cpp test/test_raster_iterator.cpp test/test_traversal_areas.cpp test/test_stats.cpp test/test_utils.cpp) # Create an executable to run the unit tests add_executable(catch_tests ${TEST_SOURCES}) target_include_directories( catch_tests PRIVATE ${CATCH_INCLUDE_DIR} ${GEOS_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/src ) target_link_libraries( catch_tests PRIVATE ${LIB_NAME} ${GEOS_LIBRARY} ) endif() #BUILD_TEST message(STATUS "Source version: " ${EXACTEXTRACT_VERSION_SOURCE}) configure_file(src/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/generated/version.h) if (GEOS_VERSION_MAJOR LESS 3 OR GEOS_VERSION_MINOR LESS 5) message(FATAL_ERROR "GEOS version 3.5 or later is required.") endif() # Define coverage build type set(CMAKE_CXX_FLAGS_COVERAGE "-fprofile-arcs -ftest-coverage") # Make sure we know our build type if(NOT CMAKE_BUILD_TYPE) message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified") set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}") endif() set(PROJECT_SOURCES src/measures.cpp src/measures.h src/box.h src/box.cpp src/cell.cpp src/cell.h src/coordinate.cpp src/coordinate.h src/crossing.h src/floodfill.cpp src/floodfill.h src/geos_utils.cpp src/geos_utils.h src/grid.h src/grid.cpp src/matrix.h src/perimeter_distance.cpp src/perimeter_distance.h src/raster.h src/raster_area.h src/raster_cell_intersection.cpp src/raster_cell_intersection.h src/raster_stats.h src/side.cpp src/side.h src/traversal.cpp src/traversal.h src/traversal_areas.cpp src/traversal_areas.h src/output_writer.h src/output_writer.cpp src/operation.h src/raster_source.h src/stats_registry.h src/utils.h src/utils.cpp src/weighted_quantiles.h src/weighted_quantiles.cpp src/variance.h vend/optional.hpp) add_library(${LIB_NAME} ${PROJECT_SOURCES}) # Check matrix bounds for debug builds set_target_properties(${LIB_NAME} PROPERTIES COMPILE_DEFINITIONS $<$:MATRIX_CHECK_BOUNDS>) target_include_directories( ${LIB_NAME} PRIVATE ${GEOS_INCLUDE_DIR} ) target_compile_definitions( ${LIB_NAME} PRIVATE GEOS_USE_ONLY_R_API ) target_compile_options( ${LIB_NAME} PRIVATE $<$:-Werror -Wall -Wextra -Wshadow -Wdouble-promotion> $<$:-Werror -Wall -Wextra -Wshadow -Wdouble-promotion> ) target_link_libraries( ${LIB_NAME} PUBLIC ${GEOS_LIBRARY} ) set_target_properties(${LIB_NAME} PROPERTIES OUTPUT_NAME ${LIB_NAME}) if(BUILD_DOC) # Doxygen configuration from https://vicrucann.github.io/tutorials/quick-cmake-doxygen/ # check if Doxygen is installed find_package(Doxygen) if (DOXYGEN_FOUND) # set input and output files set(DOXYGEN_IN ${CMAKE_SOURCE_DIR}/docs/Doxyfile.in) set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) # request to configure the file configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) message("Doxygen build started") # note the option ALL which allows to build the docs together with the application add_custom_target( doc_doxygen ALL COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating API documentation with Doxygen" VERBATIM ) else (DOXYGEN_FOUND) message("Doxygen need to be installed to generate the doxygen documentation") endif (DOXYGEN_FOUND) endif() #BUILD_DOC exactextractr/src/exactextract/Dockerfile0000644000176200001440000000054314776002150020417 0ustar liggesusersFROM isciences/exactextract-build-env:latest LABEL maintainer="dbaston@isciences.com" COPY . /exactextract RUN mkdir /cmake-build-release && \ cd /cmake-build-release && \ cmake -DCMAKE_BUILD_TYPE=Release /exactextract && \ make && \ ./catch_tests && \ make install && \ rm -rf /cmake-build-release ENTRYPOINT ["exactextract"] exactextractr/src/exactextract/cmake/0000755000176200001440000000000014776002150017503 5ustar liggesusersexactextractr/src/exactextract/cmake/FindGEOS.cmake0000644000176200001440000001520414776002150022045 0ustar liggesusers# Find GEOS # ~~~~~~~~~ # Copyright (c) 2008, Mateusz Loskot # (based on FindGDAL.cmake by Magnus Homann) # Redistribution and use is allowed according to the terms of the BSD license. # For details see the accompanying COPYING-CMAKE-SCRIPTS file. # # CMake module to search for GEOS library # # If it's found it sets GEOS_FOUND to TRUE # and following variables are set: # GEOS_INCLUDE_DIR # GEOS_LIBRARY # IF(WIN32) IF (MINGW) FIND_PATH(GEOS_INCLUDE_DIR geos_c.h /usr/local/include /usr/include c:/msys/local/include) FIND_LIBRARY(GEOS_LIBRARY NAMES geos_c PATHS /usr/local/lib /usr/lib c:/msys/local/lib) ENDIF (MINGW) IF (MSVC) IF(DEFINED ENV{OSGEO4W_ROOT}) SET(OSGEO4W_ROOT_DIR $ENV{OSGEO4W_ROOT}) ELSE() SET(OSGEO4W_ROOT_DIR c:/OSGeo4W64) ENDIF() FIND_PATH(GEOS_INCLUDE_DIR geos_c.h $ENV{LIB_DIR}/include $ENV{INCLUDE} ${OSGEO4W_ROOT_DIR}/include) FIND_LIBRARY(GEOS_LIBRARY NAMES geos geos_c PATHS "$ENV{LIB}/lib" $ENV{LIB} #mingw c:/msys/local/lib ${OSGEO4W_ROOT_DIR}/lib NO_DEFAULT_PATH ) IF (GEOS_LIBRARY) SET ( GEOS_LIBRARY GEOS_LIBRARY;odbc32;odbccp32 CACHE STRING INTERNAL) ENDIF (GEOS_LIBRARY) ENDIF (MSVC) ELSE(WIN32) IF(UNIX) # try to use framework on mac # want clean framework path, not unix compatibility path IF (APPLE) IF (CMAKE_FIND_FRAMEWORK MATCHES "FIRST" OR CMAKE_FRAMEWORK_PATH MATCHES "ONLY" OR NOT CMAKE_FIND_FRAMEWORK) SET (CMAKE_FIND_FRAMEWORK_save ${CMAKE_FIND_FRAMEWORK} CACHE STRING "" FORCE) SET (CMAKE_FIND_FRAMEWORK "ONLY" CACHE STRING "" FORCE) FIND_LIBRARY(GEOS_LIBRARY GEOS) IF (GEOS_LIBRARY) # they're all the same in a framework SET (GEOS_INCLUDE_DIR ${GEOS_LIBRARY}/Headers CACHE PATH "Path to a file.") SET (GEOS_CONFIG ${GEOS_LIBRARY}/Programs/geos-config CACHE FILEPATH "Path to a program.") ENDIF (GEOS_LIBRARY) SET (CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK_save} CACHE STRING "" FORCE) ENDIF () ENDIF (APPLE) IF (NOT GEOS_INCLUDE_DIR OR NOT GEOS_LIBRARY OR NOT GEOS_CONFIG) # didn't find OS X framework, and was not set by user SET(GEOS_CONFIG_PREFER_PATH "$ENV{GEOS_HOME}/bin" CACHE STRING "preferred path to GEOS (geos-config)") FIND_PROGRAM(GEOS_CONFIG geos-config ${GEOS_CONFIG_PREFER_PATH} /usr/local/bin/ /usr/bin/ ) #MESSAGE("DBG GEOS_CONFIG ${GEOS_CONFIG}") IF (GEOS_CONFIG) EXEC_PROGRAM(${GEOS_CONFIG} ARGS --version OUTPUT_VARIABLE GEOS_VERSION) STRING(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\1" GEOS_VERSION_MAJOR "${GEOS_VERSION}") STRING(REGEX REPLACE "([0-9]+)\\.([0-9]+)\\.([0-9]+)" "\\2" GEOS_VERSION_MINOR "${GEOS_VERSION}") IF (GEOS_VERSION_MAJOR LESS 3) MESSAGE (FATAL_ERROR "GEOS version is too old (${GEOS_VERSION}). Use 3.0.0 or higher.") ENDIF (GEOS_VERSION_MAJOR LESS 3) # set INCLUDE_DIR to prefix+include EXEC_PROGRAM(${GEOS_CONFIG} ARGS --prefix OUTPUT_VARIABLE GEOS_PREFIX) FIND_PATH(GEOS_INCLUDE_DIR geos_c.h ${GEOS_PREFIX}/include /usr/local/include /usr/include ) ## extract link dirs for rpath EXEC_PROGRAM(${GEOS_CONFIG} ARGS --libs OUTPUT_VARIABLE GEOS_CONFIG_LIBS ) ## split off the link dirs (for rpath) ## use regular expression to match wildcard equivalent "-L*" ## with is a space or a semicolon STRING(REGEX MATCHALL "[-][L]([^ ;])+" GEOS_LINK_DIRECTORIES_WITH_PREFIX "${GEOS_CONFIG_LIBS}" ) #MESSAGE("DBG GEOS_LINK_DIRECTORIES_WITH_PREFIX=${GEOS_LINK_DIRECTORIES_WITH_PREFIX}") ## remove prefix -L because we need the pure directory for LINK_DIRECTORIES IF (GEOS_LINK_DIRECTORIES_WITH_PREFIX) STRING(REGEX REPLACE "[-][L]" "" GEOS_LINK_DIRECTORIES ${GEOS_LINK_DIRECTORIES_WITH_PREFIX} ) ENDIF (GEOS_LINK_DIRECTORIES_WITH_PREFIX) ### XXX - mloskot: geos-config --libs does not return -lgeos_c, so set it manually ## split off the name ## use regular expression to match wildcard equivalent "-l*" ## with is a space or a semicolon #STRING(REGEX MATCHALL "[-][l]([^ ;])+" # GEOS_LIB_NAME_WITH_PREFIX # "${GEOS_CONFIG_LIBS}" ) #MESSAGE("DBG GEOS_CONFIG_LIBS=${GEOS_CONFIG_LIBS}") #MESSAGE("DBG GEOS_LIB_NAME_WITH_PREFIX=${GEOS_LIB_NAME_WITH_PREFIX}") SET(GEOS_LIB_NAME_WITH_PREFIX -lgeos_c CACHE STRING INTERNAL) ## remove prefix -l because we need the pure name IF (GEOS_LIB_NAME_WITH_PREFIX) STRING(REGEX REPLACE "[-][l]" "" GEOS_LIB_NAME ${GEOS_LIB_NAME_WITH_PREFIX} ) ENDIF (GEOS_LIB_NAME_WITH_PREFIX) #MESSAGE("DBG GEOS_LIB_NAME=${GEOS_LIB_NAME}") IF (APPLE) IF (NOT GEOS_LIBRARY) # work around empty GEOS_LIBRARY left by framework check # while still preserving user setting if given # ***FIXME*** need to improve framework check so below not needed SET(GEOS_LIBRARY ${GEOS_LINK_DIRECTORIES}/lib${GEOS_LIB_NAME}.dylib CACHE STRING INTERNAL FORCE) ENDIF (NOT GEOS_LIBRARY) ELSE (APPLE) SET(GEOS_LIBRARY ${GEOS_LINK_DIRECTORIES}/lib${GEOS_LIB_NAME}.so CACHE STRING INTERNAL) ENDIF (APPLE) #MESSAGE("DBG GEOS_LIBRARY=${GEOS_LIBRARY}") ELSE(GEOS_CONFIG) MESSAGE("FindGEOS.cmake: geos-config not found. Please set it manually if you would also like it configured. GEOS_CONFIG=${GEOS_CONFIG}") ENDIF(GEOS_CONFIG) ENDIF(NOT GEOS_INCLUDE_DIR OR NOT GEOS_LIBRARY OR NOT GEOS_CONFIG) ENDIF(UNIX) ENDIF(WIN32) IF (GEOS_INCLUDE_DIR AND GEOS_LIBRARY) SET(GEOS_FOUND TRUE) ENDIF (GEOS_INCLUDE_DIR AND GEOS_LIBRARY) if (NOT GEOS_INCLUDE_DIR OR NOT GEOS_LIBRARY) include(SelectLibraryConfigurations) select_library_configurations(GEOS) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GEOS REQUIRED_VARS GEOS_LIBRARY GEOS_INCLUDE_DIR) if(GEOS_FOUND) set(GEOS_INCLUDE_DIRS ${GEOS_INCLUDE_DIR}) if(NOT GEOS_LIBRARIES) set(GEOS_LIBRARIES ${GEOS_LIBRARY}) endif() endif() endif() IF (GEOS_FOUND) IF (NOT GEOS_FIND_QUIETLY) MESSAGE(STATUS "Found GEOS: ${GEOS_LIBRARY}") ENDIF (NOT GEOS_FIND_QUIETLY) ELSE (GEOS_FOUND) message(STATUS "Could not find GEOS") ENDIF (GEOS_FOUND) exactextractr/src/exactextract/cmake/VersionSource.cmake0000644000176200001440000000222414776002150023313 0ustar liggesusers# Adapted from https://gitlab.kitware.com/cmake/cmake/blob/aec06dd4922187ce5346d20a9f0d53f01b6ce9fc/Source/CMakeVersionSource.cmake # Try to identify the current development source version. set(EXACTEXTRACT_VERSION_SOURCE "Unknown") if(EXISTS ${CMAKE_SOURCE_DIR}/.git/HEAD) find_program(GIT_EXECUTABLE NAMES git git.cmd) mark_as_advanced(GIT_EXECUTABLE) if(GIT_EXECUTABLE) execute_process( COMMAND ${GIT_EXECUTABLE} rev-parse --verify -q --short=7 HEAD OUTPUT_VARIABLE head OUTPUT_STRIP_TRAILING_WHITESPACE WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) if(head) set(EXACTEXTRACT_VERSION_SOURCE "${head}") execute_process( COMMAND ${GIT_EXECUTABLE} update-index -q --refresh WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) execute_process( COMMAND ${GIT_EXECUTABLE} diff-index --name-only HEAD -- OUTPUT_VARIABLE dirty OUTPUT_STRIP_TRAILING_WHITESPACE WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) if(dirty) set(EXACTEXTRACT_VERSION_SOURCE "${EXACTEXTRACT_VERSION_SOURCE} (dirty)") endif() endif() endif() endif() unset(dirty) unset(head) exactextractr/src/exactextract/doc/0000755000176200001440000000000014776002150017170 5ustar liggesusersexactextractr/src/exactextract/doc/exactextract.svg0000644000176200001440000002254014776002150022413 0ustar liggesusers image/svg+xml exactextractr/src/exactextract/doc/readme_example_values.svg0000644000176200001440000000175514776002150024250 0ustar liggesusers 1 2 3 4 exactextractr/src/exactextract/doc/readme_example_weights.svg0000644000176200001440000000175514776002150024423 0ustar liggesusers 5 6 7 8 exactextractr/src/exactextract/src/0000755000176200001440000000000015113334656017217 5ustar liggesusersexactextractr/src/exactextract/src/raster_sequential_processor.cpp0000644000176200001440000001150214776002150025546 0ustar liggesusers// Copyright (c) 2019-2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "raster_sequential_processor.h" #include #include #include namespace exactextract { void RasterSequentialProcessor::read_features() { while (m_shp.next()) { Feature feature = std::make_pair( m_shp.feature_field(m_shp.id_field()), geos_ptr(m_geos_context, m_shp.feature_geometry(m_geos_context))); m_features.push_back(std::move(feature)); } } void RasterSequentialProcessor::populate_index() { for (const Feature& f : m_features) { // TODO compute envelope of dataset, and crop raster by that extent before processing? GEOSSTRtree_insert_r(m_geos_context, m_feature_tree.get(), f.second.get(), (void *) &f); } } void RasterSequentialProcessor::process() { read_features(); populate_index(); for (const auto& op : m_operations) { m_output.add_operation(op); } auto grid = common_grid(m_operations.begin(), m_operations.end()); for (const auto &subgrid : subdivide(grid, m_max_cells_in_memory)) { std::vector hits; auto query_rect = geos_make_box_polygon(m_geos_context, subgrid.extent()); GEOSSTRtree_query_r(m_geos_context, m_feature_tree.get(), query_rect.get(), [](void *hit, void *userdata) { auto feature = static_cast(hit); auto vec = static_cast *>(userdata); vec->push_back(feature); }, &hits); std::map>> raster_values; for (const auto &f : hits) { std::unique_ptr> coverage; std::set> processed; for (const auto &op : m_operations) { // Avoid processing same values/weights for different stats auto key = std::make_pair(op.weights, op.values); if (processed.find(key) != processed.end()) { continue; } else { processed.insert(key); } if (!op.values->grid().extent().contains(subgrid.extent())) { continue; } if (op.weighted() && !op.weights->grid().extent().contains(subgrid.extent())) { continue; } // Lazy-initialize coverage if (coverage == nullptr) { coverage = std::make_unique>( raster_cell_intersection(subgrid, m_geos_context, f->second.get())); } // FIXME need to ensure that no values are read from a raster that have already been read. // This may be possible when reading box is expanded slightly from floating-point roundoff problems. auto values = raster_values[op.values].get(); if (values == nullptr) { raster_values[op.values] = op.values->read_box(subgrid.extent().intersection(op.values->grid().extent())); values = raster_values[op.values].get(); } if (op.weighted()) { auto weights = raster_values[op.weights].get(); if (weights == nullptr) { raster_values[op.weights] = op.weights->read_box(subgrid.extent().intersection(op.weights->grid().extent())); weights = raster_values[op.weights].get(); } m_reg.stats(f->first, op).process(*coverage, *values, *weights); } else { m_reg.stats(f->first, op).process(*coverage, *values); } progress(); } } progress(subgrid.extent()); } for (const auto& f : m_features) { m_output.write(f.first); m_reg.flush_feature(f.first); } } } exactextractr/src/exactextract/src/raster_sequential_processor.h0000644000176200001440000000243314776002150025216 0ustar liggesusers// Copyright (c) 2019 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #ifndef EXACTEXTRACT_RASTER_SEQUENTIAL_PROCESSOR_H #define EXACTEXTRACT_RASTER_SEQUENTIAL_PROCESSOR_H #include "geos_utils.h" #include "processor.h" namespace exactextract { class RasterSequentialProcessor : public Processor { public: using Processor::Processor; void read_features(); void populate_index(); void process() override; private: using Feature=std::pair; std::vector m_features; tree_ptr_r m_feature_tree{geos_ptr(m_geos_context, GEOSSTRtree_create_r(m_geos_context, 10))}; }; } #endif //EXACTEXTRACT_RASTER_SEQUENTIAL_PROCESSOR_H exactextractr/src/exactextract/src/crossing.h0000644000176200001440000000224714776002150021217 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_CELL_CROSSING_H #define EXACTEXTRACT_CELL_CROSSING_H #include "coordinate.h" #include "side.h" namespace exactextract { class Crossing { public: Crossing(Side s, double x, double y) : m_side{s}, m_coord{x, y} {} Crossing(Side s, const Coordinate &c) : m_side{s}, m_coord{c} {} const Side &side() const { return m_side; } const Coordinate &coord() const { return m_coord; } private: Side m_side; Coordinate m_coord; }; } #endif exactextractr/src/exactextract/src/raster_stats.h0000644000176200001440000004410014776002150022100 0ustar liggesusers// Copyright (c) 2018-2022 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_RASTER_STATS_H #define EXACTEXTRACT_RASTER_STATS_H #include #include #include #include #include "raster_cell_intersection.h" #include "weighted_quantiles.h" #include "variance.h" #include "../vend/optional.hpp" namespace exactextract { template class RasterStats { public: /** * Compute raster statistics from a Raster representing intersection percentages, * a Raster representing data values, and (optionally) a Raster representing weights. * and a set of raster values. */ explicit RasterStats(bool store_values = false) : m_min{std::numeric_limits::max()}, m_max{std::numeric_limits::lowest()}, m_sum_ciwi{0}, m_sum_ci{0}, m_sum_xici{0}, m_sum_xiciwi{0}, m_store_values{store_values} {} void process(const Raster & intersection_percentages, const AbstractRaster & rast) { std::unique_ptr> rvp; if (rast.grid() != intersection_percentages.grid()) { rvp = std::make_unique>(rast, intersection_percentages.grid()); } const AbstractRaster& rv = rvp ? *rvp : rast; for (size_t i = 0; i < rv.rows(); i++) { for (size_t j = 0; j < rv.cols(); j++) { float pct_cov = intersection_percentages(i, j); T val; if (pct_cov > 0 && rv.get(i, j, val)) { process_value(val, pct_cov, 1.0); } } } } void process(const Raster & intersection_percentages, const AbstractRaster & rast, const AbstractRaster & weights) { // Process the entire intersection_percentages grid, even though it may // be outside the extent of the weighting raster. Although we've been // provided a weighting raster, we still need to calculate correct values // for unweighted stats. auto& common = intersection_percentages.grid(); if (common.empty()) return; // If the value or weights grids do not correspond to the intersection_percentages grid, // construct a RasterView to perform the transformation. Even a no-op RasterView can be // expensive, so we avoid doing this unless necessary. std::unique_ptr> rvp; std::unique_ptr> wvp; if (rast.grid() != common) { rvp = std::make_unique>(rast, common); } if (weights.grid() != common) { wvp = std::make_unique>(weights, common); } const AbstractRaster& rv = rvp ? *rvp : rast; const AbstractRaster& wv = wvp ? *wvp : weights; for (size_t i = 0; i < rv.rows(); i++) { for (size_t j = 0; j < rv.cols(); j++) { float pct_cov = intersection_percentages(i, j); T weight; T val; if (pct_cov > 0 && rv.get(i, j, val)) { if (wv.get(i, j, weight)) { process_value(val, pct_cov, weight); } else { // Weight is NODATA, convert to NAN process_value(val, pct_cov, std::numeric_limits::quiet_NaN()); } } } } } void process_value(const T& val, float coverage, double weight) { m_sum_ci += static_cast(coverage); m_sum_xici += val*static_cast(coverage); m_variance.process(val, coverage); double ciwi = static_cast(coverage)*weight; m_sum_ciwi += ciwi; m_sum_xiciwi += val * ciwi; m_weighted_variance.process(val, ciwi); if (val < m_min) { m_min = val; } if (val > m_max) { m_max = val; } if (m_store_values) { auto& entry = m_freq[val]; entry.m_sum_ci += static_cast(coverage); entry.m_sum_ciwi += ciwi; m_quantiles.reset(); } } /** * The mean value of cells covered by this polygon, weighted * by the percent of the cell that is covered. */ float mean() const { return sum() / count(); } /** * The mean value of cells covered by this polygon, weighted * by the percent of the cell that is covered and a secondary * weighting raster. * * If any weights are undefined, will return NAN. If this is undesirable, * caller should replace undefined weights with a suitable default * before computing statistics. */ float weighted_mean() const { return weighted_sum() / weighted_count(); } /** The fraction of weighted cells to unweighted cells. * Meaningful only when the values of the weighting * raster are between 0 and 1. */ float weighted_fraction() const { return weighted_sum() / sum(); } /** * The raster value occupying the greatest number of cells * or partial cells within the polygon. When multiple values * cover the same number of cells, the greatest value will * be returned. Weights are not taken into account. */ nonstd::optional mode() const { if (variety() == 0) { return nonstd::nullopt; } return std::max_element(m_freq.cbegin(), m_freq.cend(), [](const auto &a, const auto &b) { return a.second.m_sum_ci < b.second.m_sum_ci || (a.second.m_sum_ci == b.second.m_sum_ci && a.first < b.first); })->first; } /** * The minimum value in any raster cell wholly or partially covered * by the polygon. Weights are not taken into account. */ nonstd::optional min() const { if (m_sum_ci == 0) { return nonstd::nullopt; } return m_min; } /** * The maximum value in any raster cell wholly or partially covered * by the polygon. Weights are not taken into account. */ nonstd::optional max() const { if (m_sum_ci == 0) { return nonstd::nullopt; } return m_max; } /** * The given quantile (0-1) of raster cell values. Coverage fractions * are taken into account but weights are not. */ nonstd::optional quantile(double q) const { if (m_sum_ci == 0) { return nonstd::nullopt; } // The weighted quantile computation is not processed incrementally. // Create it on demand and retain it in case we want multiple quantiles. if (!m_quantiles) { m_quantiles = std::make_unique(); for (const auto& entry : m_freq) { m_quantiles->process(entry.first, entry.second.m_sum_ci); } } return m_quantiles->quantile(q); } /** * The sum of raster cells covered by the polygon, with each raster * value weighted by its coverage fraction. */ float sum() const { return (float) m_sum_xici; } /** * The sum of raster cells covered by the polygon, with each raster * value weighted by its coverage fraction and weighting raster value. * * If any weights are undefined, will return NAN. If this is undesirable, * caller should replace undefined weights with a suitable default * before computing statistics. */ float weighted_sum() const { return (float) m_sum_xiciwi; } /** * The number of raster cells with any defined value * covered by the polygon. Weights are not taken * into account. */ float count() const { return (float) m_sum_ci; } /** * The number of raster cells with a specific value * covered by the polygon. Weights are not taken * into account. */ nonstd::optional count(const T& value) const { const auto& entry = m_freq.find(value); if (entry == m_freq.end()) { return nonstd::nullopt; } return static_cast(entry->second.m_sum_ci); } /** * The fraction of defined raster cells covered by the polygon with * a value that equals the specified value. * Weights are not taken into account. */ nonstd::optional frac(const T& value) const { auto count_for_value = count(value); if (!count_for_value.has_value()) { return count_for_value; } return count_for_value.value() / count(); } /** * The weighted fraction of defined raster cells covered by the polygon with * a value that equals the specified value. * Weights are not taken into account. */ nonstd::optional weighted_frac(const T& value) const { auto count_for_value = weighted_count(value); if (!count_for_value.has_value()) { return count_for_value; } return count_for_value.value() / weighted_count(); } /** * The population variance of raster cells touched * by the polygon. Cell coverage fractions are taken * into account; values of a weighting raster are not. */ float variance() const { return static_cast(m_variance.variance()); } /** * The population variance of raster cells touched * by the polygon, taking into account cell coverage * fractions and values of a weighting raster. */ float weighted_variance() const { return static_cast(m_weighted_variance.variance()); } /** * The population standard deviation of raster cells * touched by the polygon. Cell coverage fractions * are taken into account; values of a weighting * raster are not. */ float stdev() const { return static_cast(m_variance.stdev()); } /** * The population standard deviation of raster cells * touched by the polygon, taking into account cell * coverage fractions and values of a weighting raster. */ float weighted_stdev() const { return static_cast(m_weighted_variance.stdev()); } /** * The population coefficient of variation of raster * cells touched by the polygon. Cell coverage fractions * are taken into account; values of a weighting * raster are not. */ float coefficient_of_variation() const { return static_cast(m_variance.coefficent_of_variation()); } /** * The sum of weights for each cell covered by the * polygon, with each weight multiplied by the coverage * coverage fraction of each cell. * * If any weights are undefined, will return NAN. If this is undesirable, * caller should replace undefined weights with a suitable default * before computing statistics. */ float weighted_count() const { return (float) m_sum_ciwi; } /** * The sum of weights for each cell of a specific value covered by the * polygon, with each weight multiplied by the coverage coverage fraction * of each cell. * * If any weights are undefined, will return NAN. If this is undesirable, * caller should replace undefined weights with a suitable default * before computing statistics. */ nonstd::optional weighted_count(const T& value) const { const auto& entry = m_freq.find(value); if (entry == m_freq.end()) { return nonstd::nullopt; } return static_cast(entry->second.m_sum_ciwi); } /** * The raster value occupying the least number of cells * or partial cells within the polygon. When multiple values * cover the same number of cells, the lowest value will * be returned. * * Cell weights are not taken into account. */ nonstd::optional minority() const { if (variety() == 0) { return nonstd::nullopt; } return std::min_element(m_freq.cbegin(), m_freq.cend(), [](const auto &a, const auto &b) { return a.second.m_sum_ci < b.second.m_sum_ci || (a.second.m_sum_ci == b.second.m_sum_ci && a.first < b.first); })->first; } /** * The number of distinct defined raster values in cells wholly * or partially covered by the polygon. */ size_t variety() const { return m_freq.size(); } bool stores_values() const { return m_store_values; } private: T m_min; T m_max; // ci: coverage fraction of pixel i // wi: weight of pixel i // xi: value of pixel i double m_sum_ciwi; double m_sum_ci; double m_sum_xici; double m_sum_xiciwi; WestVariance m_variance; WestVariance m_weighted_variance; mutable std::unique_ptr m_quantiles; struct ValueFreqEntry { double m_sum_ci = 0; double m_sum_ciwi = 0; }; std::unordered_map m_freq; bool m_store_values; struct Iterator { using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = const T; using pointer = value_type*; using reference = value_type&; using underlying_iterator = decltype(m_freq.cbegin()); Iterator(underlying_iterator it) : m_iterator(it) {} reference operator*() const { return m_iterator->first; } pointer operator->() const { return &(m_iterator->first); } // prefix Iterator& operator++() { m_iterator++; return *this; } // postfix Iterator operator++(int) { Iterator tmp = *this; ++(*this); return tmp; } friend bool operator==(const Iterator& a, const Iterator& b) { return a.m_iterator == b.m_iterator; } friend bool operator!=(const Iterator& a, const Iterator& b) { return !(a == b); } private: underlying_iterator m_iterator; }; public: Iterator begin() const { return Iterator(m_freq.cbegin()); } Iterator end() const { return Iterator(m_freq.cend()); } }; template std::ostream& operator<<(std::ostream& os, const RasterStats & stats) { os << "{" << std::endl; os << " \"count\" : " << stats.count() << "," << std::endl; os << " \"min\" : "; if (stats.min().has_value()) { os << stats.min().value(); } else { os << "null"; } os << "," << std::endl; os << " \"max\" : "; if (stats.max().has_value()) { os << stats.max().value(); } else { os << "null"; } os << "," << std::endl; os << " \"mean\" : " << stats.mean() << "," << std::endl; os << " \"sum\" : " << stats.sum() << "," << std::endl; os << " \"weighted_mean\" : " << stats.weighted_mean() << "," << std::endl; os << " \"weighted_sum\" : " << stats.weighted_sum(); if (stats.stores_values()) { os << "," << std::endl; os << " \"mode\" : "; if (stats.mode().has_value()) { os << stats.mode().value(); } else { os << "null"; } os << "," << std::endl; os << " \"minority\" : "; if (stats.minority().has_value()) { os << stats.minority().value(); } else { os << "null"; } os << "," << std::endl; os << " \"variety\" : " << stats.variety() << std::endl; } else { os << std::endl; } os << "}" << std::endl; return os; } } #endif exactextractr/src/exactextract/src/matrix.h0000644000176200001440000001010414776002150020663 0ustar liggesusers// Copyright (c) 2018-2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_MATRIX_H #define EXACTEXTRACT_MATRIX_H #include #include #include #include #include namespace exactextract { template class Matrix { public: using value_type = T; Matrix(size_t rows, size_t cols) : m_rows{rows}, m_cols{cols} { if (m_rows > 0 && m_cols > 0) { // new T[]() initializes to zero m_data = std::unique_ptr(new T[m_rows * m_cols]()); } } Matrix(size_t rows, size_t cols, T value) : m_rows{rows}, m_cols{cols} { if (m_rows > 0 && m_cols > 0) { // new T[] does not initialize m_data = std::unique_ptr(new T[m_rows * m_cols]); } std::fill(m_data.get(), m_data.get() + m_rows*m_cols, value); } explicit Matrix(const std::vector> & data) : m_rows{data.size()}, m_cols{data[0].size()} { m_data = std::unique_ptr(new T[m_rows*m_cols]()); auto lastpos = m_data.get(); for (auto& row : data) { lastpos = std::copy(row.begin(), row.end(), lastpos); } } Matrix(Matrix&& m) noexcept : m_rows{m.rows()}, m_cols{m.cols()} { m_data = std::move(m.m_data); } T& operator()(size_t row, size_t col) { check(row, col); return m_data[row*m_cols + col]; } T operator()(size_t row, size_t col) const { check(row, col); return m_data[row*m_cols + col]; } bool operator==(const Matrix & other) const { if (m_rows != other.m_rows) { return false; } if (m_cols != other.m_cols) { return false; } return 0 == memcmp(m_data.get(), other.m_data.get(), m_rows*m_cols*sizeof(T)); } void increment(size_t row, size_t col, const T & val) { check(row, col); m_data[row*m_cols + col] += val; } size_t rows() const { return m_rows; } size_t cols() const { return m_cols; } T* row(size_t row) { return &(m_data[row*m_cols]); } T* data() { return m_data.get(); } #ifdef MATRIX_CHECK_BOUNDS void check(size_t row, size_t col) const { if (row + 1 > m_rows) { throw std::out_of_range("Row " + std::to_string(row) + " is out of range."); } if (col + 1 > m_cols) { throw std::out_of_range("Col " + std::to_string(col) + " is out of range."); } } #else void check(size_t, size_t) const {} #endif private: std::unique_ptr m_data; size_t m_rows; size_t m_cols; }; template std::ostream& operator<<(std::ostream & os, const Matrix & m) { for (size_t i = 0; i < m.rows(); i++) { for (size_t j = 0; j < m.cols(); j++) { if (m(i, j) != 0) { os << std::right << std::fixed << std::setw(10) << std::setprecision(6) << m(i, j) << " "; } else { os << " "; } } os << std::endl; } return os; } } #endif exactextractr/src/exactextract/src/raster_cell_intersection.h0000644000176200001440000000424614776002150024456 0ustar liggesusers// Copyright (c) 2018-2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_RASTER_CELL_INTERSECTION_H #define EXACTEXTRACT_RASTER_CELL_INTERSECTION_H #include #include #include "grid.h" #include "matrix.h" #include "raster.h" namespace exactextract { class RasterCellIntersection { public: RasterCellIntersection(const Grid &raster_grid, GEOSContextHandle_t context, const GEOSGeometry *g); RasterCellIntersection(const Grid &raster_grid, const Box & box); size_t rows() const { return m_results->rows(); } size_t cols() const { return m_results->cols(); } const Matrix &results() const { return *m_results; } Grid m_geometry_grid; private: void process(GEOSContextHandle_t context, const GEOSGeometry *g); void process_line(GEOSContextHandle_t context, const GEOSGeometry *ls, bool exterior_ring); void process_rectangular_ring(const Box & box, bool exterior_ring); void add_ring_results(size_t i0, size_t j0, const Matrix &areas, bool exterior_ring); void set_areal(bool areal); std::unique_ptr> m_results; bool m_first_geom; bool m_areal; }; Raster raster_cell_intersection(const Grid & raster_grid, GEOSContextHandle_t context, const GEOSGeometry* g); Raster raster_cell_intersection(const Grid & raster_grid, const Box & box); Box processing_region(const Box & raster_extent, const std::vector & component_boxes); } #endif exactextractr/src/exactextract/src/utils.cpp0000644000176200001440000001004114776002150021052 0ustar liggesusers// Copyright (c) 2019 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "utils.h" #include #include #include namespace exactextract { std::pair parse_dataset_descriptor(const std::string & descriptor) { if (descriptor.empty()) throw std::runtime_error("Empty descriptor."); auto pos = descriptor.rfind('['); if (pos == std::string::npos) { return std::make_pair(descriptor, "0"); } return std::make_pair(descriptor.substr(0, pos), descriptor.substr(pos + 1, descriptor.length() - pos - 2)); } std::tuple parse_raster_descriptor(const std::string &descriptor) { if (descriptor.empty()) throw std::runtime_error("Empty descriptor."); auto pos1 = descriptor.find(':'); auto pos2 = descriptor.rfind('['); if (pos1 != std::string::npos && pos2 < pos1) { // Ignore [ character within name pos2 = std::string::npos; } std::string name; std::string fname; int band; if (pos1 != std::string::npos) { name = descriptor.substr(0, pos1); } if (pos2 == std::string::npos) { // No band was specified; set it to 1. fname = descriptor.substr(pos1 + 1); band = 1; } else { fname = descriptor.substr(pos1 + 1, pos2 - pos1 - 1); auto rest = descriptor.substr(pos2 + 1); band = std::stoi(rest); } if (pos1 == std::string::npos) { // No name was provided, so just use the filename name = fname; } if (fname.empty()) throw std::runtime_error("Descriptor has no filename."); return std::make_tuple(name, fname, band); } StatDescriptor parse_stat_descriptor(const std::string & descriptor) { if (descriptor.empty()) { throw std::runtime_error("Invalid stat descriptor."); } StatDescriptor ret; const std::regex re_result_name("^(\\w+)="); std::smatch result_name_match; if (std::regex_search(descriptor, result_name_match, re_result_name)) { ret.name = result_name_match[1].str(); } const std::regex re_func_name("=?(\\w+)\\("); std::smatch func_name_match; if (std::regex_search(descriptor, func_name_match, re_func_name)) { ret.stat = func_name_match[1].str(); } else { throw std::runtime_error("Invalid stat descriptor."); } const std::regex re_args(R"(\(([,\w]+)+\)$)"); std::smatch arg_names_match; if (std::regex_search(descriptor, arg_names_match, re_args)) { auto args = arg_names_match[1].str(); auto pos = args.find(','); if (pos == std::string::npos) { ret.values = std::move(args); } else { ret.values = args.substr(0, pos); ret.weights = args.substr(pos + 1); } } else { throw std::runtime_error("Invalid stat descriptor."); } if (ret.name.empty()) { std::ostringstream ss; ss << ret.values << '_' << ret.stat; if (!ret.weights.empty()) { ss << '_' << ret.weights; } ret.name = ss.str(); } return ret; } } exactextractr/src/exactextract/src/grid.h0000644000176200001440000003326714776002150020323 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_GRID_H #define EXACTEXTRACT_GRID_H #include #include #include #include "box.h" namespace exactextract { struct infinite_extent { static const size_t padding = 1; }; struct bounded_extent { static const size_t padding = 0; }; static inline bool is_integral(double d, double tol) { return std::abs(d - std::round(d)) <= tol; } template class Grid { public: Grid(const Box & extent, double dx, double dy) : m_extent{extent}, m_dx{dx}, m_dy{dy}, m_num_rows{2*extent_tag::padding + (extent.ymax > extent.ymin ? static_cast(std::round((extent.ymax - extent.ymin) / dy)) : 0)}, m_num_cols{2*extent_tag::padding + (extent.xmax > extent.xmin ? static_cast(std::round((extent.xmax - extent.xmin) / dx)) : 0)} {} static Grid make_empty() { return Grid({0, 0, 0, 0}, 0, 0); } size_t get_column(double x) const { if (extent_tag::padding) { if (x < m_extent.xmin) return 0; if (x > m_extent.xmax) return m_num_cols - 1; if (x == m_extent.xmax) // special case, returning the cell for which xmax is the right return m_num_cols - 2; } else { if (x < m_extent.xmin || x > m_extent.xmax) throw std::out_of_range("x"); if (x == m_extent.xmax) return m_num_cols - 1; } // Since we've already range-checked our x value, make sure that // the computed column index is no greater than the column index // associated with xmax. return std::min( extent_tag::padding + static_cast(std::floor((x - m_extent.xmin) / m_dx)), get_column(m_extent.xmax)); } size_t get_row(double y) const { if (extent_tag::padding) { if (y > m_extent.ymax) return 0; if (y < m_extent.ymin) return m_num_rows - 1; if (y == m_extent.ymin) // special case, returning the cell for which ymin is the bottom return m_num_rows - 2; } else { if (y < m_extent.ymin || y > m_extent.ymax) throw std::out_of_range("y"); if (y == m_extent.ymin) return m_num_rows - 1; } // Since we've already range-checked our y value, make sure that // the computed row index is no greater than the column index // associated with ymin. return std::min( extent_tag::padding + static_cast(std::floor((m_extent.ymax - y) / m_dy)), get_row(m_extent.ymin)); } bool empty() const { return m_num_rows <= 2*extent_tag::padding && m_num_cols <= 2*extent_tag::padding; } size_t rows() const { return m_num_rows; } size_t cols() const { return m_num_cols; } size_t size() const { return rows()*cols(); } double xmin() const { return m_extent.xmin; } double xmax() const { return m_extent.xmax; } double ymin() const { return m_extent.ymin; } double ymax() const { return m_extent.ymax; } double dx() const { return m_dx; } double dy() const { return m_dy; } const Box& extent() const { return m_extent; } size_t row_offset(const Grid & other) const { return static_cast(std::round(std::abs(other.m_extent.ymax - m_extent.ymax) / m_dy)); } size_t col_offset(const Grid & other) const { return static_cast(std::round(std::abs(m_extent.xmin - other.m_extent.xmin) / m_dx)); } double x_for_col(size_t col) const { return m_extent.xmin + ((col - extent_tag::padding) + 0.5) * m_dx; } double y_for_row(size_t row) const { return m_extent.ymax - ((row - extent_tag::padding) + 0.5) * m_dy; } Grid crop(const Box & b) const { if (extent().intersects(b)) { return shrink_to_fit(b.intersection(extent())); } else { return make_empty(); } } Grid shrink_to_fit(const Box & b) const { if (b.xmin < m_extent.xmin || b.ymin < m_extent.ymin || b.xmax > m_extent.xmax || b.ymax > m_extent.ymax) { throw std::range_error("Cannot shrink extent to bounds larger than original."); } size_t col0 = get_column(b.xmin); size_t row1 = get_row(b.ymax); // Shrink xmin and ymax to fit the upper-left corner of the supplied extent double snapped_xmin = m_extent.xmin + (col0 - extent_tag::padding) * m_dx; double snapped_ymax = m_extent.ymax - (row1 - extent_tag::padding) * m_dy; // Make sure x0 and y1 are within the reduced extent. Because of // floating point round-off errors, this is not always the case. if (b.xmin < snapped_xmin) { snapped_xmin -= m_dx; col0--; } if (b.ymax > snapped_ymax) { snapped_ymax += m_dy; row1--; } size_t col1 = get_column(b.xmax); size_t row0 = get_row(b.ymin); size_t num_rows = 1 + (row0 - row1); size_t num_cols = 1 + (col1 - col0); // If xmax or ymin falls cleanly on a cell boundary, we don't // need as many rows or columns as we otherwise would, because // we assume that the rightmost cell of the grid is a closed // interval in x, and the lowermost cell of the grid is a // closed interval in y. if (num_rows > 2 && (snapped_ymax - (num_rows-1)*m_dy <= b.ymin)) { num_rows--; } if (num_cols > 2 && (snapped_xmin + (num_cols-1)*m_dx >= b.xmax)) { num_cols--; } // Perform offsets relative to the new xmin, ymax origin // points. If this is not done, then floating point roundoff // error can cause progressive shrink() calls with the same // inputs to produce different results. Box reduced_box = { snapped_xmin, std::min(snapped_ymax - num_rows * m_dy, b.ymin), std::max(snapped_xmin + num_cols * m_dx, b.xmax), snapped_ymax }; // Fudge computed xmax and ymin, if needed, to prevent extent // from growing during a shrink operation. if (reduced_box.xmax > m_extent.xmax) { if (std::round((reduced_box.xmax - reduced_box.xmin)/m_dx) == std::round((m_extent.xmax - reduced_box.xmin)/m_dx)) { reduced_box.xmax = m_extent.xmax; } else { throw std::runtime_error("Shrink operation failed."); } } if (reduced_box.ymin < m_extent.ymin) { if (std::round((reduced_box.ymax - reduced_box.ymin)/m_dy) == std::round((reduced_box.ymax - m_extent.ymin)/m_dy)) { reduced_box.ymin = m_extent.ymin; } else { throw std::runtime_error("Shrink operation failed."); } } Grid reduced{reduced_box, m_dx, m_dy}; if (b.xmin < reduced.xmin() || b.ymin < reduced.ymin() || b.xmax > reduced.xmax() || b.ymax > reduced.ymax()) { throw std::runtime_error("Shrink operation failed."); } return reduced; } template bool compatible_with(const Grid &b, double compatability_tol) const { // Define a tolerance for grid compatibility, to be used in the following ways: // Grid resolutions must be integer multiples of each other within a factor of 'compatibility_tol' // Grid origin points must differ by an integer multiple of the smaller of the two grid resolutions, // within a factor of 'compatibility_tol'. if (empty() || b.empty()) { return true; } double xtol = std::min(m_dx, b.m_dx) * compatability_tol; double ytol = std::min(m_dy, b.m_dy) * compatability_tol; // Check x-resolution compatibility if (!is_integral(std::max(m_dx, b.m_dx) / std::min(m_dx, b.m_dx), xtol)) { return false; } // Check y-resolution compatibility if (!is_integral(std::max(m_dy, b.m_dy) / std::min(m_dy, b.m_dy), ytol)) { return false; } // Check left-hand boundary compatibility if (!is_integral(std::abs(b.m_extent.xmin - m_extent.xmin) / std::min(m_dx, b.m_dx), xtol)) { return false; } // Check upper boundary compatibility if (!is_integral(std::abs(b.m_extent.ymax - m_extent.ymax) / std::min(m_dy, b.m_dy), ytol)) { return false; } return true; } template Grid common_grid(const Grid &b, double tol = 1e-6) const { if (!compatible_with(b, tol)) { throw std::runtime_error("Incompatible extents."); } if (b.empty()) { return *this; } const double common_dx = std::min(m_dx, b.m_dx); const double common_dy = std::min(m_dy, b.m_dy); const double common_xmin = std::min(m_extent.xmin, b.m_extent.xmin); const double common_ymax = std::max(m_extent.ymax, b.m_extent.ymax); double common_xmax = std::max(m_extent.xmax, b.m_extent.xmax); double common_ymin = std::min(m_extent.ymin, b.m_extent.ymin); const long nx = static_cast(std::round((common_xmax - common_xmin) / common_dx)); const long ny = static_cast(std::round((common_ymax - common_ymin) / common_dy)); common_xmax = std::max(common_xmax, common_xmin + nx*common_dx); common_ymin = std::min(common_ymin, common_ymax - ny*common_dy); return {{ common_xmin, common_ymin, common_xmax, common_ymax}, common_dx, common_dy }; } template Grid overlapping_grid(const Grid &b, double tol = 1e-6) const { if (!compatible_with(b, tol)) { throw std::runtime_error("Incompatible extents."); } if (empty() || b.empty()) { return make_empty(); } const double common_dx = std::min(m_dx, b.m_dx); const double common_dy = std::min(m_dy, b.m_dy); const double common_xmin = std::max(m_extent.xmin, b.m_extent.xmin); const double common_ymax = std::min(m_extent.ymax, b.m_extent.ymax); double common_xmax = std::min(m_extent.xmax, b.m_extent.xmax); double common_ymin = std::max(m_extent.ymin, b.m_extent.ymin); const long nx = static_cast(std::round((common_xmax - common_xmin) / common_dx)); const long ny = static_cast(std::round((common_ymax - common_ymin) / common_dy)); common_xmax = std::max(common_xmax, common_xmin + nx*common_dx); common_ymin = std::min(common_ymin, common_ymax - ny*common_dy); return {{ common_xmin, common_ymin, common_xmax, common_ymax}, common_dx, common_dy }; } bool operator==(const Grid &b) const { return m_extent == b.m_extent && m_dx == b.m_dx && m_dy == b.m_dy; } bool operator!=(const Grid &b) const { return !(*this == b); } private: Box m_extent; double m_dx; double m_dy; size_t m_num_rows; size_t m_num_cols; }; Box grid_cell(const Grid & grid, size_t row, size_t col); Box grid_cell(const Grid & grid, size_t row, size_t col); Grid make_infinite(const Grid & grid); Grid make_finite(const Grid & grid); std::vector> subdivide(const Grid & grid, size_t max_size); template Grid common_grid(T begin, T end) { if (begin == end) { return Grid::make_empty(); } else if (std::next(begin) == end) { return begin->grid(); } return std::accumulate( std::next(begin), end, begin->grid(), [](auto& acc, auto& op) { return acc.common_grid(op.grid()); }); } } #endif //EXACTEXTRACT_INFINITEGRID_H exactextractr/src/exactextract/src/raster.h0000644000176200001440000002452415113334453020672 0ustar liggesusers// Copyright (c) 2018-2021 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_RASTER_H #define EXACTEXTRACT_RASTER_H #include #include #include #include "grid.h" #include "matrix.h" namespace exactextract { template class AbstractRaster { public: using value_type = T; explicit AbstractRaster(const Grid & ex) : m_grid{ex}, m_nodata{std::is_floating_point::value ? std::numeric_limits::quiet_NaN() : std::numeric_limits::min()}, m_has_nodata{false} {} AbstractRaster(const Grid & ex, const T& nodata_val) : m_grid{ex}, m_nodata{nodata_val}, m_has_nodata{true} {} virtual ~AbstractRaster() = default; size_t rows() const { return m_grid.rows(); } size_t cols() const { return m_grid.cols(); } size_t size() const { return rows() * cols(); } double xres() const { return m_grid.dx(); } double yres() const { return m_grid.dy(); } double xmin() const { return m_grid.xmin(); } double ymin() const { return m_grid.ymin(); } double xmax() const { return m_grid.xmax(); } double ymax() const { return m_grid.ymax(); } const Grid& grid() const { return m_grid; } virtual T operator()(size_t row, size_t col) const = 0; bool has_nodata() const { return m_has_nodata; } T nodata() const { return m_nodata; } bool get(size_t row, size_t col, T & val) const { val = operator()(row, col); if (m_has_nodata && val == m_nodata) { return false; } if (std::is_floating_point::value && std::isnan(val)) { return false; } return true; } void set_nodata(const T & val) { m_has_nodata = true; m_nodata = val; } bool operator==(const AbstractRaster & other) const { if (rows() != other.rows()) return false; if (cols() != other.cols()) return false; if (xres() != other.xres()) return false; if (yres() != other.yres()) return false; if (xmin() != other.xmin()) return false; if (ymin() != other.ymin()) return false; // Do the rasters differ in their definition of NODATA? If so, we need to do some more detailed // checking below. bool nodata_differs = has_nodata() != other.has_nodata() || (has_nodata() && other.has_nodata() && nodata() != other.nodata()); for (size_t i = 0; i < rows(); i++) { for (size_t j = 0; j < cols(); j++) { if(operator()(i, j) != other(i, j)) { // Override default behavior of NAN != NAN if (!std::isnan(operator()(i, j)) || !std::isnan(other(i, j))) { return false; } } else if (nodata_differs && (operator()(i, j) == nodata() || other(i, j) == other.nodata())) { // For data types that do not have NAN, or even for floating point types where the user has // selected some other value to represent NODATA, we need to reverse a positive equality test // where the value is considered to be NODATA in one raster but not the other. return false; } } } return true; } bool operator!=(const AbstractRaster & other) const { return !(operator==(other)); } class Iterator { public: using iterator_category = std::forward_iterator_tag; using value_type = T; using reference = T&; using difference_type = std::ptrdiff_t; using pointer = T*; Iterator(const AbstractRaster* r, size_t i, size_t j) : m_rast(r), m_row(i), m_col(j) {} const T& operator*() const { m_val = m_rast->operator()(m_row, m_col); return m_val; } Iterator& operator++() { m_col++; if (m_col == m_rast->cols()) { m_col = 0; m_row++; } return *this; } friend bool operator==(const Iterator& a, const Iterator& b) { return a.m_rast == b.m_rast && a.m_row == b.m_row && a.m_col == b.m_col; } friend bool operator!=(const Iterator& a, const Iterator& b) { return a.m_row != b.m_row || a.m_col != b.m_col || a.m_rast != b.m_rast; } private: const AbstractRaster* m_rast; mutable T m_val; size_t m_row; size_t m_col; }; Iterator begin() const { return Iterator(this, 0, 0); } Iterator end() const { return Iterator(this, rows(), 0); } private: Grid m_grid; T m_nodata; bool m_has_nodata; }; template class Raster : public AbstractRaster { public: Raster(Matrix&& values, const Box & box) : AbstractRaster( Grid( box, (box.xmax - box.xmin) / values.cols(), (box.ymax - box.ymin) / values.rows())), m_values{std::move(values)} {} Raster(Matrix&& values, const Grid & g) : AbstractRaster(g), m_values{std::move(values)} {} Raster(const Box & box, size_t nrow, size_t ncol) : AbstractRaster(Grid(box, (box.xmax-box.xmin) / ncol, (box.ymax-box.ymin) / nrow)), m_values{nrow, ncol} {} explicit Raster(const Grid & ex) : AbstractRaster(ex), m_values{ex.rows(), ex.cols()} {} Matrix& data() { return m_values; } T& operator()(size_t row, size_t col) { return m_values(row, col); } T operator()(size_t row, size_t col) const override { return m_values(row, col); } private: Matrix m_values; }; template class RasterView : public AbstractRaster{ public: // Construct a view of a raster r at an extent ex that is larger // and/or of finer resolution than r RasterView(const AbstractRaster & r, Grid ex) : AbstractRaster(ex), m_raster{r}, m_x_off{0}, m_y_off{0}, m_rx{1}, m_ry{1} { if (!this->grid().empty()) { double disaggregation_factor_x = r.xres() / ex.dx(); double disaggregation_factor_y = r.yres() / ex.dy(); if (std::abs(disaggregation_factor_x - std::round(disaggregation_factor_x)) > 1e-6 || std::abs(disaggregation_factor_y - std::round(disaggregation_factor_y)) > 1e-6) { throw std::runtime_error("Must construct view at resolution that is an integer multiple of original."); } if (disaggregation_factor_x < 0 || disaggregation_factor_y < 0) { throw std::runtime_error("Must construct view at equal or higher resolution than original."); } m_x_off = static_cast(std::round((ex.xmin() - r.xmin()) / ex.dx())); m_y_off = static_cast(std::round((r.ymax() - ex.ymax()) / ex.dy())); m_rx = static_cast(std::round(disaggregation_factor_x)); m_ry = static_cast(std::round(disaggregation_factor_y)); } if (r.has_nodata()) { this->set_nodata(r.nodata()); } } T operator()(size_t row, size_t col) const override { if (m_raster.grid().empty()) { return this->nodata(); } if (m_x_off < 0 && static_cast(-m_x_off) > col) { return this->nodata(); } if (m_y_off < 0 && static_cast(-m_y_off) > row) { return this->nodata(); } size_t i0 = (row + m_y_off) / m_ry; size_t j0 = (col + m_x_off) / m_rx; if (i0 > m_raster.rows() - 1 || j0 > m_raster.cols() - 1) { return this->nodata(); } return m_raster(i0, j0); } private: const AbstractRaster& m_raster; long m_x_off; long m_y_off; size_t m_rx; size_t m_ry; }; template std::ostream& operator<<(std::ostream & os, const AbstractRaster & m) { for (size_t i = 0; i < m.rows(); i++) { for (size_t j = 0; j < m.cols(); j++) { if (m(i, j) != 0) { os << std::right << std::fixed << std::setw(10) << std::setprecision(6) << m(i, j) << " "; } else { os << " "; } } os << std::endl; } return os; } } #endif //EXACTEXTRACT_RASTER_H exactextractr/src/exactextract/src/measures.cpp0000644000176200001440000000271514776002150021547 0ustar liggesusers// Copyright (c) 2018-2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "measures.h" #include #include namespace exactextract { double area_signed(const std::vector &ring) { if (ring.size() < 3) { return 0; } double sum{0}; double x0{ring[0].x}; for (size_t i = 1; i < ring.size() - 1; i++) { double x = ring[i].x - x0; double y1 = ring[i + 1].y; double y2 = ring[i - 1].y; sum += x * (y2 - y1); } return sum / 2.0; } double area(const std::vector &ring) { return std::abs(area_signed(ring)); } double length(const std::vector & coords) { double sum{0}; for (size_t i = 1; i < coords.size(); i++) { sum += coords[i-1].distance(coords[i]); } return sum; } } exactextractr/src/exactextract/src/traversal.h0000644000176200001440000000322414776002150021367 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_TRAVERSAL_H #define EXACTEXTRACT_TRAVERSAL_H #include #include "coordinate.h" #include "side.h" namespace exactextract { class Traversal { public: Traversal() : m_entry{Side::NONE}, m_exit{Side::NONE} {} bool is_closed_ring() const; bool empty() const; bool entered() const; bool exited() const; bool traversed() const; bool multiple_unique_coordinates() const; void enter(const Coordinate &c, Side s); void exit(const Coordinate &c, Side s); Side entry_side() const { return m_entry; } Side exit_side() const { return m_exit; } const Coordinate &last_coordinate() const; const Coordinate &exit_coordinate() const; void add(const Coordinate &c); void force_exit(Side s) { m_exit = s; } const std::vector &coords() const { return m_coords; } private: std::vector m_coords; Side m_entry; Side m_exit; }; } #endif exactextractr/src/exactextract/src/traversal_areas.cpp0000644000176200001440000001011114776002150023066 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include #include "measures.h" #include "box.h" #include "coordinate.h" #include "perimeter_distance.h" namespace exactextract { struct CoordinateChain { double start; double stop; const std::vector *coordinates; bool visited; CoordinateChain(double p_start, double p_stop, const std::vector* p_coords) : start{p_start}, stop{p_stop}, coordinates{p_coords}, visited{false} {} }; static double exit_to_entry_perimeter_distance_ccw(const CoordinateChain &c1, const CoordinateChain &c2, double perimeter) { return perimeter_distance_ccw(c1.stop, c2.start, perimeter); } static CoordinateChain *next_chain(std::vector &chains, const CoordinateChain *chain, const CoordinateChain *kill, double perimeter) { CoordinateChain *min = nullptr; double min_distance = std::numeric_limits::max(); for (CoordinateChain &candidate : chains) { if (candidate.visited && std::addressof(candidate) != kill) { continue; } double distance = exit_to_entry_perimeter_distance_ccw(*chain, candidate, perimeter); if (distance < min_distance) { min_distance = distance; min = std::addressof(candidate); } } return min; } double left_hand_area(const Box &box, const std::vector *> &coord_lists) { std::vector chains; for (const auto &coords : coord_lists) { double start = perimeter_distance(box, (*coords)[0]); double stop = perimeter_distance(box, (*coords)[coords->size() - 1]); chains.emplace_back(start, stop, coords); } double height{box.height()}; double width{box.width()}; double perimeter{box.perimeter()}; // create coordinate lists for corners std::vector bottom_left = {Coordinate(box.xmin, box.ymin)}; std::vector top_left = {Coordinate(box.xmin, box.ymax)}; std::vector top_right = {Coordinate(box.xmax, box.ymax)}; std::vector bottom_right = {Coordinate(box.xmax, box.ymin)}; // Add chains for corners chains.emplace_back(0.0, 0.0, &bottom_left); chains.emplace_back(height, height, &top_left); chains.emplace_back(height + width, height + width, &top_right); chains.emplace_back(2 * height + width, 2 * height + width, &bottom_right); double sum{0.0}; for (auto &chain_ref : chains) { if (chain_ref.visited || chain_ref.coordinates->size() == 1) { continue; } std::vector coords; CoordinateChain *chain = std::addressof(chain_ref); CoordinateChain *first_chain = chain; do { chain->visited = true; coords.insert(coords.end(), chain->coordinates->cbegin(), chain->coordinates->cend()); chain = next_chain(chains, chain, first_chain, perimeter); } while (chain != first_chain); coords.push_back(coords[0]); sum += area(coords); } return sum; } } exactextractr/src/exactextract/src/operation.h0000644000176200001440000000666714776002150021402 0ustar liggesusers// Copyright (c) 2019-2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_OPERATION_H #define EXACTEXTRACT_OPERATION_H #include #include #include "grid.h" #include "gdal_raster_wrapper.h" #include "raster_stats.h" namespace exactextract { class Operation { public: Operation(std::string p_stat, std::string p_name, RasterSource* p_values, RasterSource* p_weights = nullptr) : stat{std::move(p_stat)}, name{std::move(p_name)}, values{p_values}, weights{p_weights} {} bool weighted() const { return weights != nullptr; } Grid grid() const { if (weighted()) { return values->grid().common_grid(weights->grid()); } else { return values->grid(); } } std::function(const RasterStats&)> result_fetcher() const { if (stat == "mean") { return [](const RasterStats & s) { return s.mean(); }; } else if (stat == "sum") { return [](const RasterStats & s) { return s.sum(); }; } else if (stat == "count") { return [](const RasterStats & s) { return s.count(); }; } else if (stat == "weighted_mean") { return [](const RasterStats & s) { return s.weighted_mean(); }; } else if (stat == "weighted_sum") { return [](const RasterStats & s) { return s.weighted_sum(); }; } else if (stat == "min") { return [](const RasterStats & s) { return s.min(); }; } else if (stat == "max") { return [](const RasterStats & s) { return s.max(); }; } else if (stat == "majority" || stat == "mode") { return [](const RasterStats & s) { return s.mode(); }; } else if (stat == "minority") { return [](const RasterStats & s) { return s.minority(); }; } else if (stat == "variety") { return [](const RasterStats & s) { return s.variety(); }; } else if (stat == "stdev") { return [](const RasterStats & s) { return s.stdev(); }; } else if (stat == "variance") { return [](const RasterStats & s) { return s.variance(); }; } else if (stat == "coefficient_of_variation") { return [](const RasterStats & s) { return s.coefficient_of_variation(); }; } else { throw std::runtime_error("Unknown stat: '" + stat + "'"); } } std::string stat; std::string name; RasterSource* values; RasterSource* weights; }; } #endif //EXACTEXTRACT_OPERATION_H exactextractr/src/exactextract/src/floodfill.cpp0000644000176200001440000000277214776002150021700 0ustar liggesusers// Copyright (c) 2018-2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include "grid.h" #include "floodfill.h" #include "geos_utils.h" namespace exactextract { FloodFill::FloodFill(GEOSContextHandle_t context, const GEOSGeometry *g, const Grid &extent) : m_extent{extent}, m_geos_context{context}, m_g{nullptr}, m_pg{nullptr} { geom_ptr_r ring_copy = geos_ptr(context, GEOSGeom_clone_r(context, g)); m_g = geos_ptr(context, GEOSGeom_createPolygon_r(context, ring_copy.release(), nullptr, 0)); m_pg = GEOSPrepare_ptr(context, m_g.get()); } bool FloodFill::cell_is_inside(size_t i, size_t j) const { double x = m_extent.x_for_col(j); double y = m_extent.y_for_row(i); auto point = GEOSGeom_createPoint_ptr(m_geos_context, x, y); return GEOSPreparedContains_r(m_geos_context, m_pg.get(), point.get()); } } exactextractr/src/exactextract/src/side.h0000644000176200001440000000161414776002150020311 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_SIDE_H #define EXACTEXTRACT_SIDE_H #include namespace exactextract { enum class Side { NONE, LEFT, RIGHT, TOP, BOTTOM }; std::ostream &operator<<(std::ostream &os, const Side &s); } #endif exactextractr/src/exactextract/src/feature_sequential_processor.h0000644000176200001440000000165414776002150025355 0ustar liggesusers// Copyright (c) 2019 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_FEATURE_SEQUENTIAL_PROCESSOR_H #define EXACTEXTRACT_FEATURE_SEQUENTIAL_PROCESSOR_H #include "processor.h" namespace exactextract { class FeatureSequentialProcessor : public Processor { public: using Processor::Processor; void process() override; }; } #endif exactextractr/src/exactextract/src/geos_utils.h0000644000176200001440000001062114776002150021540 0ustar liggesusers// Copyright (c) 2018-2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_GEOS_UTILS_H #define EXACTEXTRACT_GEOS_UTILS_H #include #include #include #include #include #include #include #define HAVE_370 (GEOS_VERSION_MAJOR >= 3 && GEOS_VERSION_MINOR >= 7) #define HAVE_380 (GEOS_VERSION_MAJOR >= 3 && GEOS_VERSION_MINOR >= 8) #define HAVE_3100 (GEOS_VERSION_MAJOR >= 3 && GEOS_VERSION_MINOR >= 10) #include "box.h" #include "coordinate.h" namespace exactextract { using geom_ptr_r = std::unique_ptr>; using tree_ptr_r = std::unique_ptr>; using seq_ptr_r = std::unique_ptr>; using prep_geom_ptr_r = std::unique_ptr>; inline geom_ptr_r geos_ptr(GEOSContextHandle_t context, GEOSGeometry* geom) { auto deleter = [context](GEOSGeometry* g){ GEOSGeom_destroy_r(context, g); }; return geom_ptr_r{geom, deleter}; } inline tree_ptr_r geos_ptr(GEOSContextHandle_t context, GEOSSTRtree* tree) { auto deleter = [context](GEOSSTRtree_t* t){ GEOSSTRtree_destroy_r(context, t); }; return tree_ptr_r{tree, deleter}; } inline seq_ptr_r geos_ptr(GEOSContextHandle_t context, GEOSCoordSequence* seq) { auto deleter = [context](GEOSCoordSequence* s){ GEOSCoordSeq_destroy_r(context, s); }; return seq_ptr_r{seq, deleter}; } inline prep_geom_ptr_r GEOSPrepare_ptr(GEOSContextHandle_t context, const GEOSGeometry *g) { auto deleter = [context](const GEOSPreparedGeometry* pg) { GEOSPreparedGeom_destroy_r(context, pg); }; return prep_geom_ptr_r{GEOSPrepare_r(context, g), deleter}; } inline seq_ptr_r GEOSCoordSeq_create_ptr(GEOSContextHandle_t context, unsigned int size, unsigned int dims) { return geos_ptr(context, GEOSCoordSeq_create_r(context, size, dims)); } inline geom_ptr_r GEOSGeom_createPoint_ptr(GEOSContextHandle_t context, double x, double y) { #if HAVE_380 return geos_ptr(context, GEOSGeom_createPointFromXY_r(context, x, y)); #else auto seq = GEOSCoordSeq_create_ptr(context, 1, 2); GEOSCoordSeq_setX_r(context, seq.get(), 0, x); GEOSCoordSeq_setY_r(context, seq.get(), 0, y); return geos_ptr(context, GEOSGeom_createPoint_r(context, seq.release())); #endif } inline geom_ptr_r GEOSGeom_createLineString_ptr(GEOSContextHandle_t context, GEOSCoordSequence *seq) { return geos_ptr(context, GEOSGeom_createLineString_r(context, seq)); } inline unsigned int geos_get_num_points(GEOSContextHandle_t context, const GEOSCoordSequence *s) { unsigned int result; if (!GEOSCoordSeq_getSize_r(context, s, &result)) { throw std::runtime_error("Error calling GEOSCoordSeq_getSize_r."); } return result; } inline geom_ptr_r GEOSGeom_read_r(GEOSContextHandle_t context, const std::string &s) { return geos_ptr(context, GEOSGeomFromWKT_r(context, s.c_str())); } geom_ptr_r geos_make_box_polygon(GEOSContextHandle_t context, const Box & b); Box geos_get_box(GEOSContextHandle_t context, const GEOSGeometry* g); std::vector geos_get_component_boxes(GEOSContextHandle_t context, const GEOSGeometry* g); bool segment_intersection(GEOSContextHandle_t context, const Coordinate &a0, const Coordinate &a1, const Coordinate &b0, const Coordinate &b1, Coordinate &result); bool geos_is_ccw(GEOSContextHandle_t context, const GEOSCoordSequence *s); std::vector read(GEOSContextHandle_t context, const GEOSCoordSequence *s); } #endif //RASTER_OVERLAY_CPP_GEOS_UTILS_H exactextractr/src/exactextract/src/measures.h0000644000176200001440000000174014776002150021211 0ustar liggesusers// Copyright (c) 2018-2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_MEASURES_H #define EXACTEXTRACT_MEASURES_H #include #include #include #include "coordinate.h" namespace exactextract { double area_signed(const std::vector &ring); double area(const std::vector &ring); double length(const std::vector & coords); } #endif exactextractr/src/exactextract/src/processor.h0000644000176200001440000000474114776002150021410 0ustar liggesusers// Copyright (c) 2019 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_PROCESSOR_H #define EXACTEXTRACT_PROCESSOR_H #include #include #include "gdal_dataset_wrapper.h" #include "output_writer.h" #include "stats_registry.h" static void errorHandler(const char *fmt, ...) { char buf[BUFSIZ], *p; va_list ap; va_start(ap, fmt); vsprintf(buf, fmt, ap); va_end(ap); p = buf + strlen(buf) - 1; if(strlen(buf) > 0 && *p == '\n') *p = '\0'; std::cerr << buf << std::endl; } namespace exactextract { class Processor { public: // FIXME add GEOS error/notice handlers Processor(GDALDatasetWrapper & ds, OutputWriter & out, const std::vector & ops) : m_reg{}, m_geos_context{initGEOS_r(errorHandler, errorHandler)}, m_output{out}, m_shp{ds}, m_operations{ops} { m_output.set_registry(&m_reg); } virtual ~Processor() { finishGEOS_r(m_geos_context); } virtual void process()= 0; void set_max_cells_in_memory(size_t n) { m_max_cells_in_memory = n; } void show_progress(bool val) { m_show_progress = val; } protected: template void progress(const T & name) const { if (m_show_progress) std::cout << std::endl << "Processing " << name << std::flush; } void progress() const { if (m_show_progress) std::cout << "." << std::flush; } StatsRegistry m_reg; GEOSContextHandle_t m_geos_context; OutputWriter& m_output; GDALDatasetWrapper& m_shp; bool m_show_progress=false; std::vector m_operations; size_t m_max_cells_in_memory = 1000000L; }; } #endif //EXACTEXTRACT_PROCESSOR_H exactextractr/src/exactextract/src/utils.h0000644000176200001440000000327514776002150020532 0ustar liggesusers// Copyright (c) 2019 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_UTILS_H #define EXACTEXTRACT_UTILS_H #include #include #include #include namespace exactextract { struct StatDescriptor { std::string name; std::string values; std::string weights; std::string stat; }; std::pair parse_dataset_descriptor(const std::string &descriptor); std::tuple parse_raster_descriptor(const std::string &descriptor); StatDescriptor parse_stat_descriptor(const std::string & descriptor); // https://stackoverflow.com/a/2072890 inline bool ends_with(std::string const & value, std::string const & suffix) { if (suffix.size() > value.size()) return false; return std::equal(suffix.rbegin(), suffix.rend(), value.rbegin()); } inline bool starts_with(std::string const & value, std::string const & prefix) { if (prefix.size() > value.size()) return false; return std::equal(prefix.cbegin(), prefix.cend(), value.cbegin()); } } #endif //EXACTEXTRACT_UTILS_H exactextractr/src/exactextract/src/floodfill.h0000644000176200001440000001046314776002150021341 0ustar liggesusers// Copyright (c) 2018-2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_FLOODFILL_H #define EXACTEXTRACT_FLOODFILL_H #include #include #include "grid.h" #include "geos_utils.h" #include "matrix.h" namespace exactextract { template struct fill_values { static T EXTERIOR; }; template<> struct fill_values { static constexpr float EXTERIOR{0.0f}; // Cell is known to be entirely outside the polygon static constexpr float INTERIOR{1.0f}; // Cell is known to be entirely within the polygon static constexpr float FILLABLE{-1.0f}; // Cell location relative to polygon unknown, but // can be determined by fill. static constexpr float UNKNOWN{-2.0f}; // Cell location relative to polygon unknown // and cannot be determined from a flood fill // (must be explicitly tested) }; class FloodFill { public: FloodFill(GEOSContextHandle_t context, const GEOSGeometry *g, const Grid &extent); template void flood(Matrix &arr) const; bool cell_is_inside(size_t i, size_t j) const; private: Grid m_extent; GEOSContextHandle_t m_geos_context; geom_ptr_r m_g; prep_geom_ptr_r m_pg; }; template void flood_from_pixel(Matrix &arr, size_t i, size_t j, T fill_value) { std::queue > locations; locations.emplace(i, j); while (!locations.empty()) { i = locations.front().first; j = locations.front().second; locations.pop(); if (arr(i, j) == fill_value) { continue; } // Left if (j > 0 && arr(i, j - 1) == fill_values::FILLABLE) { locations.emplace(i, j - 1); } auto j0 = j; // Fill along this row until we hit something for (; j < arr.cols() && arr(i, j) == fill_values::FILLABLE; j++) { arr(i, j) = fill_value; } auto j1 = j; // Initiate scanlines above our current row if (i > 0) { for (j = j0; j < j1; j++) { // Up if (arr(i - 1, j) == fill_values::FILLABLE) { locations.emplace(i - 1, j); } } } // Initiate scanlines below our current row if (i < arr.rows() - 1) { for (j = j0; j < j1; j++) { // Down if (arr(i + 1, j) == fill_values::FILLABLE) { locations.emplace(i + 1, j); } } } } } template void FloodFill::flood(Matrix &arr) const { for (size_t i = 0; i < arr.rows(); i++) { for (size_t j = 0; j < arr.cols(); j++) { if (arr(i, j) == fill_values::UNKNOWN) { throw std::runtime_error("Cell with unknown position encountered."); } else if (arr(i, j) == fill_values::FILLABLE) { // Cell position relative to polygon is unknown but can // be determined from adjacent cells. if (cell_is_inside(i, j)) { flood_from_pixel(arr, i, j, fill_values::INTERIOR); } else { flood_from_pixel(arr, i, j, fill_values::EXTERIOR); } } } } } } #endif exactextractr/src/exactextract/src/stats_registry.h0000644000176200001440000000566014776002150022460 0ustar liggesusers// Copyright (c) 2019 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_STATS_REGISTRY_H #define EXACTEXTRACT_STATS_REGISTRY_H #include #include #include "operation.h" #include "raster_stats.h" namespace exactextract { class StatsRegistry { public: RasterStats &stats(const std::string &feature, const Operation &op) { // TODO come up with a better storage method. auto& stats_for_feature = m_feature_stats[feature]; // can't use find because this requires RasterStats to be copy-constructible before C++ 17 auto exists = stats_for_feature.count(op_key(op)); if (!exists) { // can't use emplace because this requires RasterStats be copy-constructible before C++17 RasterStats new_stats(requires_stored_values(op.stat)); stats_for_feature[op_key(op)] = std::move(new_stats); } return stats_for_feature[op_key(op)]; } const RasterStats &stats(const std::string &feature, const Operation &op) const { // TODO come up with a better storage method. return m_feature_stats.at(feature).at(op_key(op)); } bool contains (const std::string & feature, const Operation & op) const { const auto& m = m_feature_stats; auto it = m.find(feature); if (it == m.end()) { return false; } const auto& m2 = it->second; return m2.find(op_key(op)) != m2.end(); } void flush_feature(const std::string &fid) { std::unordered_map vals; // TODO assemble vals; m_feature_stats.erase(fid); } std::string op_key(const Operation & op) const { if (op.weighted()) { return op.values->name() + "|" + op.weights->name(); } else { return op.values->name(); } } static bool requires_stored_values(const std::string & stat) { return stat == "mode" || stat == "minority" || stat == "majority" || stat == "variety"; } private: std::unordered_map>> m_feature_stats{}; }; } #endif //EXACTEXTRACT_STATS_REGISTRY_H exactextractr/src/exactextract/src/coordinate.cpp0000644000176200001440000000147714776002150022056 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "coordinate.h" namespace exactextract { std::ostream &operator<<(std::ostream &os, const Coordinate &c) { os << "POINT (" << c.x << " " << c.y << ")"; return os; } } exactextractr/src/exactextract/src/gdal_writer.cpp0000644000176200001440000001172714776002150022231 0ustar liggesusers// Copyright (c) 2019 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "gdal_writer.h" #include "gdal_dataset_wrapper.h" #include "stats_registry.h" #include "utils.h" #include "gdal.h" #include "ogr_api.h" #include "cpl_string.h" #include namespace exactextract { GDALWriter::GDALWriter(const std::string & filename) { auto driver_name = get_driver_name(filename); auto driver = GDALGetDriverByName(driver_name.c_str()); if (driver == nullptr) { throw std::runtime_error("Could not load output driver: " + driver_name); } char** layer_creation_options = nullptr; if (driver_name == "NetCDF") { //creation_options = CSLSetNameValue(creation_options, "FORMAT", "NC3"); // crashes w/NC4C layer_creation_options = CSLSetNameValue(layer_creation_options, "RECORD_DIM_NAME", "id"); } m_dataset = GDALCreate(driver, filename.c_str(), 0, 0, 0, GDT_Unknown, nullptr); m_layer = GDALDatasetCreateLayer(m_dataset, "output", nullptr, wkbNone, layer_creation_options); CSLDestroy(layer_creation_options); } GDALWriter::~GDALWriter() { if (m_dataset != nullptr) { GDALClose(m_dataset); } } void GDALWriter::copy_id_field(const GDALDatasetWrapper & w) { if (id_field_defined) { throw std::runtime_error("ID field already defined."); } w.copy_field(w.id_field(), m_layer); id_field_defined = true; } void GDALWriter::add_id_field(const std::string & field_name, const std::string & field_type) { if (id_field_defined) { throw std::runtime_error("ID field already defined."); } OGRFieldType ogr_type; if (field_type == "int" || field_type == "int32") { ogr_type = OFTInteger; } else if (field_type == "long" || field_type == "int64") { ogr_type = OFTInteger64; } else if (field_type == "text" || field_type == "string") { ogr_type = OFTString; } else if (field_type == "double" || field_type == "float" || field_type == "real") { ogr_type = OFTReal; } else { throw std::runtime_error("Unknown field type: " + field_type); } auto def = OGR_Fld_Create(field_name.c_str(), ogr_type); OGR_L_CreateField(m_layer, def, true); OGR_Fld_Destroy(def); id_field_defined = true; } void GDALWriter::add_operation(const Operation & op) { if (!id_field_defined) { throw std::runtime_error("Must define ID field before adding operations."); } // TODO set type here? auto def = OGR_Fld_Create(op.name.c_str(), OFTReal); OGR_L_CreateField(m_layer, def, true); OGR_Fld_Destroy(def); m_ops.push_back(&op); } void GDALWriter::set_registry(const StatsRegistry* reg) { m_reg = reg; } void GDALWriter::write(const std::string & fid) { auto feature = OGR_F_Create(OGR_L_GetLayerDefn(m_layer)); OGR_F_SetFieldString(feature, 0, fid.c_str()); for (const auto &op : m_ops) { if (m_reg->contains(fid, *op)) { const auto field_pos = OGR_F_GetFieldIndex(feature, op->name.c_str()); const auto &stats = m_reg->stats(fid, *op); // TODO store between features auto fetcher = op->result_fetcher(); auto val = fetcher(stats); if (val.has_value()) { OGR_F_SetFieldDouble(feature, field_pos, val.value()); } else { OGR_F_SetFieldDouble(feature, field_pos, std::numeric_limits::quiet_NaN()); } } } if (OGR_L_CreateFeature(m_layer, feature) != OGRERR_NONE) { throw std::runtime_error("Error writing results for record: " + fid); } OGR_F_Destroy(feature); } std::string GDALWriter::get_driver_name(const std::string & filename) { if (ends_with(filename, ".csv")) { return "CSV"; } else if (ends_with(filename, ".dbf")) { return "ESRI Shapefile"; } else if (ends_with(filename, ".nc")) { return "NetCDF"; } else if (starts_with(filename, "PG:")) { return "PostgreSQL"; } else { throw std::runtime_error("Unknown output format: " + filename); } } } exactextractr/src/exactextract/src/raster_source.h0000644000176200001440000000236214776002150022246 0ustar liggesusers// Copyright (c) 2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_RASTER_SOURCE_H #define EXACTEXTRACT_RASTER_SOURCE_H #include "box.h" #include "grid.h" #include "raster.h" namespace exactextract { class RasterSource { public: virtual const Grid &grid() const = 0; virtual std::unique_ptr> read_box(const Box &box) = 0; virtual ~RasterSource() = default; void set_name(const std::string & name) { m_name = name; } std::string name() const { return m_name; } private: std::string m_name; }; } #endif //EXACTEXTRACT_RASTER_SOURCE_H exactextractr/src/exactextract/src/gdal_raster_wrapper.cpp0000644000176200001440000000717214776002150023754 0ustar liggesusers// Copyright (c) 2018-2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include "gdal_raster_wrapper.h" #include namespace exactextract { GDALRasterWrapper::GDALRasterWrapper(const std::string &filename, int bandnum) : m_grid{Grid::make_empty()} { auto rast = GDALOpen(filename.c_str(), GA_ReadOnly); if (!rast) { throw std::runtime_error("Failed to open " + filename); } int has_nodata; auto band = GDALGetRasterBand(rast, bandnum); double nodata_value = GDALGetRasterNoDataValue(band, &has_nodata); m_rast = rast; m_band = band; m_nodata_value = nodata_value; m_has_nodata = static_cast(has_nodata); set_name(filename); compute_raster_grid(); } GDALRasterWrapper::~GDALRasterWrapper() { // We can't use a std::unique_ptr because GDALDatasetH is an incomplete type. // So we include a destructor and move constructor to manage the resource. if (m_rast != nullptr) GDALClose(m_rast); } std::unique_ptr> GDALRasterWrapper::read_box(const Box &box) { auto cropped_grid = m_grid.shrink_to_fit(box); auto vals = std::make_unique>(cropped_grid); if (m_has_nodata) { vals->set_nodata(m_nodata_value); } auto error = GDALRasterIO(m_band, GF_Read, (int) cropped_grid.col_offset(m_grid), (int) cropped_grid.row_offset(m_grid), (int) cropped_grid.cols(), (int) cropped_grid.rows(), vals->data().data(), (int) cropped_grid.cols(), (int) cropped_grid.rows(), GDT_Float64, 0, 0); if (error) { throw std::runtime_error("Error reading from raster."); } return vals; } void GDALRasterWrapper::compute_raster_grid() { double adfGeoTransform[6]; if (GDALGetGeoTransform(m_rast, adfGeoTransform) != CE_None) { throw std::runtime_error("Error reading transform"); } double dx = std::abs(adfGeoTransform[1]); double dy = std::abs(adfGeoTransform[5]); double ulx = adfGeoTransform[0]; double uly = adfGeoTransform[3]; int nx = GDALGetRasterXSize(m_rast); int ny = GDALGetRasterYSize(m_rast); Box box{ulx, uly - ny * dy, ulx + nx * dx, uly}; m_grid = {box, dx, dy}; } GDALRasterWrapper::GDALRasterWrapper(exactextract::GDALRasterWrapper && src) noexcept : m_rast{src.m_rast}, m_band{src.m_band}, m_nodata_value{src.m_nodata_value}, m_has_nodata{src.m_has_nodata}, m_grid{src.m_grid} { src.m_rast = nullptr; } } exactextractr/src/exactextract/src/gdal_writer.h0000644000176200001440000000310414776002150021664 0ustar liggesusers// Copyright (c) 2019 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_GDAL_WRITER_H #define EXACTEXTRACT_GDAL_WRITER_H #include "output_writer.h" namespace exactextract { class GDALDatasetWrapper; class GDALWriter : public OutputWriter { public: explicit GDALWriter(const std::string & filename); ~GDALWriter() override; static std::string get_driver_name(const std::string & filename); void add_operation(const Operation & op) override; void set_registry(const StatsRegistry* reg) override; void write(const std::string & fid) override; void add_id_field(const std::string & field_name, const std::string & field_type); void copy_id_field(const GDALDatasetWrapper & w); private: using GDALDatasetH = void*; using OGRLayerH = void*; GDALDatasetH m_dataset; OGRLayerH m_layer; const StatsRegistry* m_reg; bool id_field_defined = false; }; } #endif //EXACTEXTRACT_GDAL_WRITER_H exactextractr/src/exactextract/src/variance.h0000644000176200001440000000366214776002150021162 0ustar liggesusers// Copyright (c) 2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_VARIANCE_H #define EXACTEXTRACT_VARIANCE_H #include namespace exactextract { class WestVariance { /** \brief Implements an incremental algorithm for weighted standard * deviation, variance, and coefficient of variation, as described in * formula WV2 of West, D.H.D. (1979) "Updating Mean and Variance * Estimates: An Improved Method". Communications of the ACM 22(9). */ private: double sum_w = 0; double mean = 0; double t = 0; public: /** \brief Update variance estimate with another value * * @param x value to add * @param w weight of `x` */ void process(double x, double w) { if (w == 0) { return; } double mean_old = mean; sum_w += w; mean += (w / sum_w) * (x - mean_old); t += w * (x - mean_old) * (x - mean); } /** \brief Return the population variance. */ constexpr double variance() const { return t / sum_w; } /** \brief Return the population standard deviation */ double stdev() const { return std::sqrt(variance()); } /** \brief Return the population coefficient of variation */ double coefficent_of_variation() const { return stdev() / mean; } }; } #endif //EXACTEXTRACT_VARIANCE_H exactextractr/src/exactextract/src/coordinate.h0000644000176200001440000000304414776002150021513 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_COORDINATE_H #define EXACTEXTRACT_COORDINATE_H #include #include namespace exactextract { struct Coordinate { double x; double y; Coordinate() = default; Coordinate(double p_x, double p_y) : x{p_x}, y{p_y} {} double distance(const Coordinate &other) const { double dx = other.x - x; double dy = other.y - y; return std::sqrt(dx*dx + dy*dy); } bool equals(const Coordinate &other, double tol) const { return std::abs(other.x - x) < tol && std::abs(other.y - y) < tol; } bool operator==(const Coordinate &other) const { return x == other.x && y == other.y; } bool operator!=(const Coordinate &other) const { return !(*this == other); } }; std::ostream &operator<<(std::ostream &os, const Coordinate &c); } #endif exactextractr/src/exactextract/src/processor.cpp0000644000176200001440000000007314776002150021735 0ustar liggesusers// // Created by dan on 3/5/19. // #include "Processor.h" exactextractr/src/exactextract/src/gdal_raster_wrapper.h0000644000176200001440000000315114776002150023412 0ustar liggesusers// Copyright (c) 2018-2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_GDAL_RASTER_WRAPPER_H #define EXACTEXTRACT_GDAL_RASTER_WRAPPER_H #include "box.h" #include "grid.h" #include "raster.h" #include "raster_source.h" namespace exactextract { class GDALRasterWrapper : public RasterSource { public: GDALRasterWrapper(const std::string &filename, int bandnum); const Grid &grid() const override { return m_grid; } std::unique_ptr> read_box(const Box &box) override; ~GDALRasterWrapper() override; GDALRasterWrapper(const GDALRasterWrapper &) = delete; GDALRasterWrapper(GDALRasterWrapper &&) noexcept; private: using GDALDatasetH=void*; using GDALRasterBandH=void*; GDALDatasetH m_rast; GDALRasterBandH m_band; double m_nodata_value; bool m_has_nodata; Grid m_grid; void compute_raster_grid(); }; } #endif //EXACTEXTRACT_GDAL_RASTER_WRAPPER_H exactextractr/src/exactextract/src/grid.cpp0000644000176200001440000001002114776002150020635 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "grid.h" namespace exactextract { Box grid_cell(const Grid & grid, size_t row, size_t col) { // The ternary clauses below are used to make sure that the cells along // the right and bottom edges of our grid are slightly larger than m_dx,dy // if needed to make sure that we capture our whole extent. This is necessary // because xmin + nx*m_dx may be less than xmax because of floating point // errors. return { grid.xmin() + col * grid.dx(), row == (grid.rows() - 1) ? grid.ymin() : (grid.ymax() - (row + 1) * grid.dy()), col == (grid.cols() - 1) ? grid.xmax() : (grid.xmin() + (col + 1) * grid.dx()), grid.ymax() - row * grid.dy() }; } Box grid_cell(const Grid & grid, size_t row, size_t col) { double xmin, xmax, ymin, ymax; if (col == 0) { xmin = std::numeric_limits::lowest(); } else if (col == grid.cols() - 1) { xmin = grid.xmax(); // because rightmost col of regular grid may have different width from others } else { xmin = grid.xmin() + (col - 1) * grid.dx(); } switch(grid.cols() - col) { case 1: xmax = std::numeric_limits::max(); break; case 2: xmax = grid.xmax(); break; default: xmax = grid.xmin() + col*grid.dx(); } if (row == 0) { ymax = std::numeric_limits::max(); } else if (row == grid.rows() - 1) { ymax = grid.ymin(); // because bottom row of regular grid may have different height from others } else { ymax = grid.ymax() - (row - 1) * grid.dy(); } switch(grid.rows() - row) { case 1: ymin = std::numeric_limits::lowest(); break; case 2: ymin = grid.ymin(); break; default: ymin = grid.ymax() - row*grid.dy(); } return { xmin, ymin, xmax, ymax }; } Grid make_infinite(const Grid & grid) { return { grid.extent(), grid.dx(), grid.dy() }; } Grid make_finite(const Grid & grid) { return { grid.extent(), grid.dx(), grid.dy() }; } std::vector> subdivide(const Grid & grid, size_t max_size) { if (grid.size() < max_size) { return { grid }; } size_t cols_per_block = std::min(max_size, grid.cols()); size_t rows_per_block = max_size / cols_per_block; size_t col_blocks = (grid.cols() - 1) / cols_per_block + 1; size_t row_blocks = (grid.rows() - 1) / rows_per_block + 1; std::vector> subgrids; for (size_t i = 0; i < row_blocks; i++) { for (size_t j = 0; j < col_blocks; j++) { double xmin = grid.xmin() + grid.dx()*cols_per_block*j; double xmax = j == (col_blocks - 1) ? grid.xmax() : (grid.xmin() + grid.dx()*cols_per_block*(j+1)); double ymax = grid.ymax() - grid.dy()*rows_per_block*i; double ymin = i == (row_blocks - 1) ? grid.ymin() : (grid.ymax() - grid.dy()*rows_per_block*(i+1)); Box reduced = {xmin, ymin, xmax, ymax}; subgrids.emplace_back(reduced, grid.dx(), grid.dy()); } } return subgrids; } } exactextractr/src/exactextract/src/weighted_quantiles.h0000644000176200001440000000347314776002150023257 0ustar liggesusers// Copyright (c) 2019-2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_WEIGHTED_QUANTILES_H #define EXACTEXTRACT_WEIGHTED_QUANTILES_H #include #include #include #include namespace exactextract { class WeightedQuantiles { // Compute weighted quantiles based on https://stats.stackexchange.com/a/13223 public: void process(double x, double w) { if (w < 0) { throw std::runtime_error("Weighted quantile calculation does not support negative weights."); } if (!std::isfinite(w)) { throw std::runtime_error("Weighted quantile does not support non-finite weights."); } m_ready_to_query = false; m_elems.emplace_back(x, w); } double quantile(double q) const; private: struct elem_t { elem_t(double _x, double _w) : x(_x), w(_w), cumsum(0), s(0) {} double x; double w; double cumsum; double s; }; void prepare() const; mutable std::vector m_elems; mutable double m_sum_w; mutable bool m_ready_to_query; }; } #endif //EXACTEXTRACT_WEIGHTED_QUANTILES_H exactextractr/src/exactextract/src/raster_cell_intersection.cpp0000644000176200001440000004046214776002150025011 0ustar liggesusers// Copyright (c) 2018-2022 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include "measures.h" #include "cell.h" #include "floodfill.h" #include "geos_utils.h" #include "raster_cell_intersection.h" namespace exactextract { Raster raster_cell_intersection(const Grid & raster_grid, GEOSContextHandle_t context, const GEOSGeometry* g) { RasterCellIntersection rci(raster_grid, context, g); return { std::move(const_cast&>(rci.results())), make_finite(rci.m_geometry_grid) }; } Raster raster_cell_intersection(const Grid & raster_grid, const Box & box) { RasterCellIntersection rci(raster_grid, box); return { std::move(const_cast&>(rci.results())), make_finite(rci.m_geometry_grid) }; } static Cell *get_cell(Matrix> &cells, const Grid &ex, size_t row, size_t col) { //std::cout << " getting cell " << row << ", " << col << std::endl; if (cells(row, col) == nullptr) { cells(row, col) = std::make_unique(grid_cell(ex, row, col)); } return cells(row, col).get(); } Box processing_region(const Box & raster_extent, const std::vector & component_boxes) { Box ret = Box::make_empty(); for (const auto& box : component_boxes) { if (ret == raster_extent) { // No more expansion is possible return ret; } if (!box.intersects(raster_extent)) { continue; } Box isect = raster_extent.intersection(box); if (ret.empty()) { ret = isect; } else if (!ret.contains(isect)) { ret = ret.expand_to_include(isect); } } return ret; } static Grid get_geometry_grid(const Grid &raster_grid, GEOSContextHandle_t context, const GEOSGeometry* g) { if (GEOSisEmpty_r(context, g)) { throw std::invalid_argument("Can't get statistics for empty geometry"); } Box region = processing_region(raster_grid.extent(), geos_get_component_boxes(context, g)); if (!region.empty()) { return make_infinite(raster_grid.shrink_to_fit(region)); } else { return Grid::make_empty(); } } static Grid get_geometry_grid(const Grid & raster_grid, const Box & box) { auto region = box.intersection(raster_grid.extent()); if (!region.empty()) { return make_infinite(raster_grid.shrink_to_fit(region)); } else { return Grid::make_empty(); } } RasterCellIntersection::RasterCellIntersection(const Grid &raster_grid, GEOSContextHandle_t context, const GEOSGeometry *g) : m_geometry_grid{get_geometry_grid(raster_grid, context, g)}, m_results{std::make_unique>(m_geometry_grid.rows() - 2, m_geometry_grid.cols() - 2)}, m_first_geom{true}, m_areal{false} { if (GEOSGeom_getDimensions_r(context, g) == 0) { throw std::invalid_argument("Unsupported geometry type."); } if (!m_geometry_grid.empty()) { process(context, g); } } RasterCellIntersection::RasterCellIntersection(const Grid & raster_grid, const Box & box) : m_geometry_grid{get_geometry_grid(raster_grid, box)}, m_results{std::make_unique>(m_geometry_grid.rows() - 2, m_geometry_grid.cols() - 2)} { if (!m_geometry_grid.empty()) { process_rectangular_ring(box, true); } } void RasterCellIntersection::process(GEOSContextHandle_t context, const GEOSGeometry *g) { auto type = GEOSGeomTypeId_r(context, g); if (type == GEOS_POLYGON) { set_areal(true); process_line(context, GEOSGetExteriorRing_r(context, g), true); for (int i = 0; i < GEOSGetNumInteriorRings_r(context, g); i++) { process_line(context, GEOSGetInteriorRingN_r(context, g, i), false); } } else if (type == GEOS_LINESTRING || type == GEOS_LINEARRING) { set_areal(false); process_line(context, g, true); } else if (type == GEOS_GEOMETRYCOLLECTION || type == GEOS_MULTILINESTRING || type == GEOS_MULTIPOLYGON) { for (int i = 0; i < GEOSGetNumGeometries_r(context, g); i++) { process(context, GEOSGetGeometryN_r(context, g, i)); } } else { throw std::invalid_argument("Unsupported geometry type."); } } static Grid get_box_grid(const Box & box, const Grid & geometry_grid) { Box cropped_ring_extent = geometry_grid.extent().intersection(box); return geometry_grid.shrink_to_fit(cropped_ring_extent); } static Grid get_ring_grid(GEOSContextHandle_t context, const GEOSGeometry* ls, const Grid & geometry_grid) { return get_box_grid(geos_get_box(context, ls), geometry_grid); } #if 0 static Grid get_ring_grid(GEOSContextHandle_t context, const GEOSGeometry* ls, const Grid & geometry_grid) { Box cropped_ring_extent = geometry_grid.extent().intersection(geos_get_box(context, ls)); return geometry_grid.shrink_to_fit(cropped_ring_extent); } #endif void RasterCellIntersection::process_rectangular_ring(const Box& box, bool exterior_ring) { if (!box.intersects(m_geometry_grid.extent())) { return; } auto ring_grid = get_box_grid(box, m_geometry_grid); auto row_min = ring_grid.get_row(box.ymax); auto row_max = ring_grid.get_row(box.ymin); auto col_min = ring_grid.get_column(box.xmin); auto col_max = ring_grid.get_column(box.xmax); Matrix areas(ring_grid.rows() - 2, ring_grid.cols() - 2); // upper-left if (row_min > 0 && col_min > 0) { auto ul = grid_cell(ring_grid, row_min, col_min); areas(row_min - 1, col_min - 1) = ul.intersection(box).area() / ul.area(); } // upper-right if (row_min > 0 && col_max < ring_grid.cols() - 1) { auto ur = grid_cell(ring_grid, row_min, col_max); auto frac = ur.intersection(box).area() / ur.area(); areas(row_min - 1, col_max - 1) = frac; } // lower-left if (row_max < ring_grid.rows() - 1 && col_min > 0) { auto ll = grid_cell(ring_grid, row_max, col_min); areas(row_max - 1, col_min - 1) = ll.intersection(box).area() / ll.area(); } // lower-right if (row_max < ring_grid.rows() - 1 && col_max < ring_grid.cols() - 1) { auto lr = grid_cell(ring_grid, row_max, col_max); areas(row_max - 1, col_max - 1) = lr.intersection(box).area() / lr.area(); } // left if (col_min > 0) { auto left = grid_cell(ring_grid, row_min + 1, col_min); auto frac = left.intersection(box).area() / left.area(); for (size_t row = row_min + 1; row < row_max; row++) { areas(row - 1, col_min - 1) = frac; } } // right if (col_max < ring_grid.cols() - 1) { auto right = grid_cell(ring_grid, row_min + 1, col_max); auto frac = right.intersection(box).area() / right.area(); for (size_t row = row_min + 1; row < row_max; row++) { areas(row - 1, col_max - 1) = frac; } } // top if (row_min > 0) { auto top = grid_cell(ring_grid, row_min, col_min + 1); auto frac = top.intersection(box).area() / top.area(); for (size_t col = col_min + 1; col < col_max; col++) { areas(row_min - 1, col - 1) = frac; } } // bottom if (row_max < ring_grid.rows() - 1) { auto bottom = grid_cell(ring_grid, row_max, col_min + 1); auto frac = bottom.intersection(box).area() / bottom.area(); for (size_t col = col_min + 1; col < col_max; col++) { areas(row_max - 1, col - 1) = frac; } } // interior for (size_t row = row_min + 1; row < row_max; row++) { for(size_t col = col_min + 1; col < col_max; col++) { areas(row - 1, col - 1) = 1.0f; } } // Transfer these areas to our global set size_t i0 = ring_grid.row_offset(m_geometry_grid); size_t j0 = ring_grid.col_offset(m_geometry_grid); add_ring_results(i0, j0, areas, exterior_ring); } void RasterCellIntersection::set_areal(bool areal) { if (m_first_geom) { m_first_geom = false; m_areal = areal; } else { if (m_areal != areal) { throw std::runtime_error("Mixed-type geometries not supported."); } } } void RasterCellIntersection::process_line(GEOSContextHandle_t context, const GEOSGeometry *ls, bool exterior_ring) { auto geom_box = geos_get_box(context, ls); if (!geom_box.intersects(m_geometry_grid.extent())) { return; } const GEOSCoordSequence *seq = GEOSGeom_getCoordSeq_r(context, ls); auto coords = read(context, seq); if (m_areal && coords.size() == 5) { if (area(coords) == geom_box.area()) { process_rectangular_ring(geom_box, exterior_ring); return; } } Grid ring_grid = get_ring_grid(context, ls, m_geometry_grid); size_t rows = ring_grid.rows(); size_t cols = ring_grid.cols(); // Short circuit for small geometries that are entirely contained within a single grid cell. if (rows == (1 + 2*infinite_extent::padding) && cols == (1 + 2*infinite_extent::padding) && grid_cell(ring_grid, 1, 1).contains(geom_box)) { size_t i0 = ring_grid.row_offset(m_geometry_grid); size_t j0 = ring_grid.col_offset(m_geometry_grid); if (m_areal) { auto ring_area = area(coords) / grid_cell(ring_grid, 1, 1).area(); if (exterior_ring) { m_results->increment(i0, j0, ring_area); } else { m_results->increment(i0, j0, -1 * ring_area); } } else { m_results->increment(i0, j0, length(coords)); } return; } Matrix> cells(rows, cols); if (m_areal && !geos_is_ccw(context, seq)) { std::reverse(coords.begin(), coords.end()); } size_t pos = 0; size_t row = ring_grid.get_row(coords.front().y); size_t col = ring_grid.get_column(coords.front().x); const Coordinate* last_exit = nullptr; while (pos < coords.size()) { Cell &cell = *get_cell(cells, ring_grid, row, col); while (pos < coords.size()) { const Coordinate* next_coord = last_exit ? last_exit : &coords[pos]; const Coordinate* prev_coord = pos > 0 ? &coords[pos - 1] : nullptr; cell.take(*next_coord, prev_coord); if (cell.last_traversal().exited()) { // Only push our exit coordinate if it's not same as the // coordinate we just took. This covers the case where // the next coordinate in the stack falls exactly on // the cell boundary. const Coordinate& exc = cell.last_traversal().exit_coordinate(); if (exc != *next_coord) { last_exit = &exc; } break; } else { if (last_exit) { last_exit = nullptr; } else { pos++; } } } cell.force_exit(); if (cell.last_traversal().exited()) { // When we start in the middle of a cell, for a polygon (areal) calculation, // we need to save the coordinates from our incomplete traversal and reprocess // them at the end of the line. The effect is just to restructure the polygon // so that the start/end coordinate falls on a cell boundary. if (m_areal && !cell.last_traversal().traversed()) { for (const auto &coord : cell.last_traversal().coords()) { coords.push_back(coord); } } switch (cell.last_traversal().exit_side()) { case Side::TOP: row--; break; case Side::BOTTOM: row++; break; case Side::LEFT: col--; break; case Side::RIGHT: col++; break; default: throw std::runtime_error("Invalid traversal"); } } } // Compute the fraction covered for all cells and assign it to // the area matrix // TODO avoid copying matrix when geometry has only one polygon, and polygon has only one ring Matrix areas(rows - 2, cols - 2, m_areal ? fill_values::FILLABLE : fill_values::EXTERIOR); if (m_areal) { FloodFill ff(context, ls, make_finite(ring_grid)); for (size_t i = 1; i <= areas.rows(); i++) { for (size_t j = 1; j <= areas.cols(); j++) { if (cells(i, j) != nullptr) { // When we encounter a cell that has been processed (ie, it is not nullptr) // but has zero covered fraction, we have no way to know if that cell is on // the inside of the polygon. So we perform point-in-polygon test and set // the covered fraction to 1.0 if needed. auto frac = static_cast(cells(i, j)->covered_fraction()); if (frac == 0) { areas(i-1, j-1) = ff.cell_is_inside(i-1, j-1) ? fill_values::INTERIOR : fill_values::EXTERIOR; } else { areas(i - 1, j - 1) = frac; } } } } ff.flood(areas); } else { for (size_t i = 1; i <= areas.rows(); i++) { for (size_t j = 1; j <= areas.cols(); j++) { if (cells(i, j) != nullptr) { areas(i - 1, j - 1) = static_cast(cells(i, j)->traversal_length()); } } } } // Transfer these areas to our global set size_t i0 = ring_grid.row_offset(m_geometry_grid); size_t j0 = ring_grid.col_offset(m_geometry_grid); add_ring_results(i0, j0, areas, exterior_ring || !m_areal); } void RasterCellIntersection::add_ring_results(size_t i0, size_t j0, const Matrix &areas, bool exterior_ring) { int factor = exterior_ring ? 1 : -1; for (size_t i = 0; i < areas.rows(); i++) { for (size_t j = 0; j < areas.cols(); j++) { m_results->increment(i0 + i, j0 + j, factor * areas(i, j)); } } } } exactextractr/src/exactextract/src/perimeter_distance.cpp0000644000176200001440000000351014776002150023563 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include "perimeter_distance.h" namespace exactextract { double perimeter_distance(double xmin, double ymin, double xmax, double ymax, double x, double y) { if (x == xmin) { // Left return y - ymin; } if (y == ymax) { // Top return (ymax - ymin) + x - xmin; } if (x == xmax) { // Right return (xmax - xmin) + (ymax - ymin) + ymax - y; } if (y == ymin) { // Bottom return (xmax - xmin) + 2 * (ymax - ymin) + (xmax - x); } throw std::runtime_error("Never get here"); } double perimeter_distance(double xmin, double ymin, double xmax, double ymax, const Coordinate &c) { return perimeter_distance(xmin, ymin, xmax, ymax, c.x, c.y); } double perimeter_distance(const Box &b, const Coordinate &c) { return perimeter_distance(b.xmin, b.ymin, b.xmax, b.ymax, c.x, c.y); } double perimeter_distance_ccw(double measure1, double measure2, double perimeter) { if (measure2 <= measure1) { return measure1 - measure2; } return perimeter + measure1 - measure2; } } exactextractr/src/exactextract/src/cell.h0000644000176200001440000000420314776002150020301 0ustar liggesusers// Copyright (c) 2018-2021 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_CELL_H #define EXACTEXTRACT_CELL_H #include #include "box.h" #include "crossing.h" #include "coordinate.h" #include "side.h" #include "traversal.h" namespace exactextract { class Cell { public: Cell(double xmin, double ymin, double xmax, double ymax) : m_box{xmin, ymin, xmax, ymax} {} explicit Cell(const Box & b) : m_box{b} {} void force_exit(); double width() const; double height() const; double area() const; double traversal_length() const; double covered_fraction() const; Traversal &last_traversal(); const Box &box() const { return m_box; } //! Take a coordinate and add it to a traversal in progress, or start a new traversal //! \param c Coordinate to process //! \param prev_original The last *uninterpolated* coordinate preceding `c` in the //! boundary being processed //! \return `true` if `c` was consumed by this cell, or `false` if this cell was //! exited before `c` was reached. bool take(const Coordinate& c, const Coordinate* prev_original = nullptr); private: enum class Location { INSIDE, OUTSIDE, BOUNDARY }; Box m_box; std::vector m_traversals; Side side(const Coordinate &c) const; Location location(const Coordinate &c) const; Traversal &traversal_in_progress(); }; } #endif exactextractr/src/exactextract/src/gdal_dataset_wrapper.cpp0000644000176200001440000000664314776002150024103 0ustar liggesusers// Copyright (c) 2018-2019 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "gdal_dataset_wrapper.h" #include #include #include namespace exactextract { GDALDatasetWrapper::GDALDatasetWrapper(const std::string & filename, const std::string & layer, std::string id_field) : m_id_field{std::move(id_field)} { m_dataset = GDALOpenEx(filename.c_str(), GDAL_OF_VECTOR, nullptr, nullptr, nullptr); if (m_dataset == nullptr) { throw std::runtime_error("Failed to open " + filename); } bool numeric = std::all_of(layer.begin(),layer.end(), [](char c) { return std::isdigit(c); }); if (numeric) { m_layer = GDALDatasetGetLayer(m_dataset, std::stoi(layer)); } else { m_layer = GDALDatasetGetLayerByName(m_dataset, layer.c_str()); } if (m_layer == nullptr) { throw std::runtime_error("No layer " + layer + " found in " + filename); } OGR_L_ResetReading(m_layer); m_feature = nullptr; auto defn = OGR_L_GetLayerDefn(m_layer); auto index = OGR_FD_GetFieldIndex(defn, m_id_field.c_str()); if (index == -1) { throw std::runtime_error("ID field '" + m_id_field + "' not found in " + filename + "."); } } bool GDALDatasetWrapper::next() { if (m_feature != nullptr) { OGR_F_Destroy(m_feature); } m_feature = OGR_L_GetNextFeature(m_layer); return m_feature != nullptr; } GEOSGeometry* GDALDatasetWrapper::feature_geometry(const GEOSContextHandle_t &geos_context) const { OGRGeometryH geom = OGR_F_GetGeometryRef(m_feature); auto sz = static_cast(OGR_G_WkbSize(geom)); auto buff = std::make_unique(sz); OGR_G_ExportToWkb(geom, wkbXDR, buff.get()); return GEOSGeomFromWKB_buf_r(geos_context, buff.get(), sz); } std::string GDALDatasetWrapper::feature_field(const std::string &field_name) const { int index = OGR_F_GetFieldIndex(m_feature, field_name.c_str()); // TODO check handling of invalid field name return OGR_F_GetFieldAsString(m_feature, index); } void GDALDatasetWrapper::copy_field(const std::string & name, OGRLayerH copy_to) const { auto src_layer_defn = OGR_L_GetLayerDefn(m_layer); auto src_index = OGR_FD_GetFieldIndex(src_layer_defn, name.c_str()); if (src_index == -1) { throw std::runtime_error("Cannot find field " + name); } auto src_field_defn = OGR_FD_GetFieldDefn(src_layer_defn, src_index); OGR_L_CreateField(copy_to, src_field_defn, true); } GDALDatasetWrapper::~GDALDatasetWrapper(){ GDALClose(m_dataset); if (m_feature != nullptr) { OGR_F_Destroy(m_feature); } } } exactextractr/src/exactextract/src/traversal_areas.h0000644000176200001440000000162214776002150022542 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_TRAVERSAL_AREAS_H #define EXACTEXTRACT_TRAVERSAL_AREAS_H #include #include "box.h" #include "coordinate.h" namespace exactextract { double left_hand_area(const Box &box, const std::vector *> &coord_lists); } #endif exactextractr/src/exactextract/src/output_writer.cpp0000644000176200001440000000125314776002150022653 0ustar liggesusers// Copyright (c) 2019 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "output_writer.h" namespace exactextract {} exactextractr/src/exactextract/src/output_writer.h0000644000176200001440000000227014776002150022320 0ustar liggesusers// Copyright (c) 2019 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_OUTPUT_WRITER_H #define EXACTEXTRACT_OUTPUT_WRITER_H #include #include namespace exactextract { class Operation; class StatsRegistry; class OutputWriter { public: virtual void write(const std::string & fid) = 0; virtual void add_operation(const Operation & op) = 0; virtual void set_registry(const StatsRegistry* reg) = 0; virtual void finish() {}; virtual ~OutputWriter()= default; std::vector m_ops; }; } #endif //EXACTEXTRACT_OUTPUT_WRITER_H exactextractr/src/exactextract/src/traversal.cpp0000644000176200001440000000416714776002150021731 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include "traversal.h" namespace exactextract { void Traversal::add(const Coordinate &c) { m_coords.push_back(c); } bool Traversal::empty() const { return m_coords.empty(); } void Traversal::enter(const Coordinate &c, Side s) { if (!m_coords.empty()) { throw std::runtime_error("Traversal already started"); } add(c); m_entry = s; } void Traversal::exit(const Coordinate &c, Side s) { add(c); m_exit = s; } bool Traversal::is_closed_ring() const { return m_coords.size() >= 3 && m_coords[0] == m_coords[m_coords.size() - 1]; } bool Traversal::entered() const { return m_entry != Side::NONE; } bool Traversal::exited() const { return m_exit != Side::NONE; } bool Traversal::multiple_unique_coordinates() const { for (size_t i = 1; i < m_coords.size(); i++) { if (m_coords[0] != m_coords[i]) { return true; } } return false; } bool Traversal::traversed() const { return entered() && exited(); } const Coordinate &Traversal::last_coordinate() const { return m_coords.at(m_coords.size() - 1); } const Coordinate &Traversal::exit_coordinate() const { if (!exited()) { throw std::runtime_error("Can't get exit coordinate from incomplete traversal."); } return last_coordinate(); } } exactextractr/src/exactextract/src/exactextract.cpp0000644000176200001440000001457214776002150022426 0ustar liggesusers// Copyright (c) 2018-2019 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include #include #include #include #include "CLI11.hpp" #include "gdal_dataset_wrapper.h" #include "gdal_raster_wrapper.h" #include "gdal_writer.h" #include "operation.h" #include "processor.h" #include "feature_sequential_processor.h" #include "raster_sequential_processor.h" #include "utils.h" #include "version.h" using exactextract::GDALDatasetWrapper; using exactextract::GDALRasterWrapper; using exactextract::Operation; static GDALDatasetWrapper load_dataset(const std::string & descriptor, const std::string & field_name); static std::unordered_map load_rasters(const std::vector & descriptors); static std::vector prepare_operations(const std::vector & descriptors, std::unordered_map & rasters); int main(int argc, char** argv) { CLI::App app{"Zonal statistics using exactextract: build " + exactextract::version()}; std::string poly_descriptor, field_name, output_filename, strategy, id_type, id_name; std::vector stats; std::vector raster_descriptors; size_t max_cells_in_memory = 30; bool progress; app.add_option("-p,--polygons", poly_descriptor, "polygon dataset")->required(true); app.add_option("-r,--raster", raster_descriptors, "raster dataset")->required(true); app.add_option("-f,--fid", field_name, "id from polygon dataset to retain in output")->required(true); app.add_option("-o,--output", output_filename, "output filename")->required(true); app.add_option("-s,--stat", stats, "statistics")->required(true)->expected(-1); app.add_option("--max-cells", max_cells_in_memory, "maximum number of raster cells to read in memory at once, in millions")->required(false)->default_val("30"); app.add_option("--strategy", strategy, "processing strategy")->required(false)->default_val("feature-sequential"); app.add_option("--id-type", id_type, "override type of id field in output")->required(false); app.add_option("--id-name", id_name, "override name of id field in output")->required(false); app.add_flag("--progress", progress); app.set_config("--config"); if (argc == 1) { std::cout << app.help(); return 0; } CLI11_PARSE(app, argc, argv) if (id_name.empty() != id_type.empty()) { std::cerr << "Must specify both --id_type and --id_name" << std::endl; return 1; } max_cells_in_memory *= 1000000; std::unique_ptr proc; std::unique_ptr writer; try { GDALAllRegister(); auto rasters = load_rasters(raster_descriptors); GDALDatasetWrapper shp = load_dataset(poly_descriptor, field_name); auto gdal_writer = std::make_unique(output_filename); if (!id_name.empty() && !id_type.empty()) { gdal_writer->add_id_field(id_name, id_type); } else { gdal_writer->copy_id_field(shp); } writer = std::move(gdal_writer); auto operations = prepare_operations(stats, rasters); if (strategy == "feature-sequential") { proc = std::make_unique(shp, *writer, operations); } else if (strategy == "raster-sequential") { proc = std::make_unique(shp, *writer, operations); } else { throw std::runtime_error("Unknown processing strategy: " + strategy); } proc->set_max_cells_in_memory(max_cells_in_memory); proc->show_progress(progress); proc->process(); writer->finish(); return 0; } catch (const std::exception & e) { std::cerr << "Error: " << e.what() << std::endl; return 1; } catch (...) { std::cerr << "Unknown error." << std::endl; return 1; } } static GDALDatasetWrapper load_dataset(const std::string & descriptor, const std::string & field_name) { auto parsed = exactextract::parse_dataset_descriptor(descriptor); return GDALDatasetWrapper{parsed.first, parsed.second, field_name}; } static std::unordered_map load_rasters(const std::vector & descriptors) { std::unordered_map rasters; for (const auto &descriptor : descriptors) { auto parsed = exactextract::parse_raster_descriptor(descriptor); auto name = std::get<0>(parsed); rasters.emplace(name, GDALRasterWrapper{std::get<1>(parsed), std::get<2>(parsed)}); rasters.at(name).set_name(name); } return rasters; } static std::vector prepare_operations(const std::vector & descriptors, std::unordered_map & rasters) { std::vector ops; for (const auto &descriptor : descriptors) { auto stat = exactextract::parse_stat_descriptor(descriptor); auto values_it = rasters.find(stat.values); if (values_it == rasters.end()) { throw std::runtime_error("Unknown raster " + stat.values + " in stat descriptor: " + descriptor); } GDALRasterWrapper* values = &(values_it->second); GDALRasterWrapper* weights; if (stat.weights.empty()) { weights = nullptr; } else { auto weights_it = rasters.find(stat.weights); if (weights_it == rasters.end()) { throw std::runtime_error("Unknown raster " + stat.weights + " in stat descriptor: " + descriptor); } weights = &(weights_it->second); } ops.emplace_back(stat.stat, stat.name, values, weights); } return ops; } exactextractr/src/exactextract/src/feature_sequential_processor.cpp0000644000176200001440000000702314776002150025704 0ustar liggesusers// Copyright (c) 2019-2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include "box.h" #include "feature_sequential_processor.h" #include "geos_utils.h" #include "grid.h" namespace exactextract { void FeatureSequentialProcessor::process() { for (const auto& op : m_operations) { m_output.add_operation(op); } while (m_shp.next()) { std::string name{m_shp.feature_field(m_shp.id_field())}; auto geom = geos_ptr(m_geos_context, m_shp.feature_geometry(m_geos_context)); progress(name); Box feature_bbox = exactextract::geos_get_box(m_geos_context, geom.get()); auto grid = common_grid(m_operations.begin(), m_operations.end()); if (feature_bbox.intersects(grid.extent())) { // Crop grid to portion overlapping feature auto cropped_grid = grid.crop(feature_bbox); for (const auto &subgrid : subdivide(cropped_grid, m_max_cells_in_memory)) { std::unique_ptr> coverage; std::set> processed; for (const auto &op : m_operations) { // TODO avoid reading same values/weights multiple times. Just use a map? // Avoid processing same values/weights for different stats auto key = std::make_pair(op.weights, op.values); if (processed.find(key) != processed.end()) { continue; } else { processed.insert(key); } if (!op.values->grid().extent().contains(subgrid.extent())) { continue; } if (op.weighted() && !op.weights->grid().extent().contains(subgrid.extent())) { continue; } // Lazy-initialize coverage if (coverage == nullptr) { coverage = std::make_unique>( raster_cell_intersection(subgrid, m_geos_context, geom.get())); } auto values = op.values->read_box(subgrid.extent().intersection(op.values->grid().extent())); if (op.weighted()) { auto weights = op.weights->read_box(subgrid.extent().intersection(op.weights->grid().extent())); m_reg.stats(name, op).process(*coverage, *values, *weights); } else { m_reg.stats(name, op).process(*coverage, *values); } progress(); } } } m_output.write(name); m_reg.flush_feature(name); } } } exactextractr/src/exactextract/src/cell.cpp0000644000176200001440000001320614776002150020637 0ustar liggesusers// Copyright (c) 2018-2021 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include "measures.h" #include "cell.h" #include "crossing.h" #include "traversal_areas.h" #include "geos_utils.h" namespace exactextract { double Cell::height() const { return m_box.height(); } double Cell::width() const { return m_box.width(); } double Cell::area() const { return m_box.area(); } Side Cell::side(const Coordinate &c) const { if (c.x == m_box.xmin) { return Side::LEFT; } else if (c.x == m_box.xmax) { return Side::RIGHT; } else if (c.y == m_box.ymin) { return Side::BOTTOM; } else if (c.y == m_box.ymax) { return Side::TOP; } return Side::NONE; } void Cell::force_exit() { if (last_traversal().exited()) { return; } const Coordinate &last = last_traversal().last_coordinate(); if (location(last) == Location::BOUNDARY) { last_traversal().force_exit(side(last)); } } Cell::Location Cell::location(const Coordinate &c) const { if (m_box.strictly_contains(c)) { return Cell::Location::INSIDE; } if (m_box.contains(c)) { return Cell::Location::BOUNDARY; } return Cell::Location::OUTSIDE; } Traversal &Cell::traversal_in_progress() { if (m_traversals.empty() || m_traversals[m_traversals.size() - 1].exited()) { m_traversals.emplace_back(); } return m_traversals[m_traversals.size() - 1]; } Traversal &Cell::last_traversal() { return m_traversals.at(m_traversals.size() - 1); } bool Cell::take(const Coordinate& c, const Coordinate* prev_original) { Traversal &t = traversal_in_progress(); if (t.empty()) { //std::cout << "Entering " << m_box << " from " << side(c) << " at " << c << std::endl; t.enter(c, side(c)); return true; } if (location(c) != Cell::Location::OUTSIDE) { //std::cout << "Still in " << m_box << " with " << c << std::endl; t.add(c); return true; } // We need to calculate the coordinate of the cell exit point using only uninterpolated coordinates. // (The previous point in the traversal may be an interpolated coordinate.) If an interpolated coordinate // is used, it can cause an error in the relative position two traversals, inverting the fraction of // the cell that is considered covered. (See robustness regression test #7). Crossing x = prev_original ? m_box.crossing(*prev_original, c) : m_box.crossing(t.last_coordinate(), c); t.exit(x.coord(), x.side()); //std::cout << "Leaving " << m_box << " from " << x.side() << " at " << x.coord(); //std::cout << " on the way to " << c << std::endl; return false; } double Cell::traversal_length() const { return std::accumulate(m_traversals.begin(), m_traversals.end(), 0.0, [](double tot, const Traversal & t) { return tot + length(t.coords()); }); } double Cell::covered_fraction() const { // Handle the special case of a ring that is enclosed within a // single pixel of our raster if (m_traversals.size() == 1 && m_traversals[0].is_closed_ring()) { return exactextract::area(m_traversals[0].coords()) / area(); } // TODO consider porting in simplified single-traversal area calculations // from Java code. Do they really make a performance difference? //if (m_traversals.size() == 1) { // double a = area(); // return (a - area_right_of(m_traversals.at(m_traversals.size() - 1))) / a; //} std::vector *> coord_lists; for (const auto &t : m_traversals) { if (!t.traversed() || !t.multiple_unique_coordinates()) { continue; } coord_lists.push_back(&t.coords()); } return left_hand_area(m_box, coord_lists) / area(); } #if 0 Crossing Cell::crossing(const Coordinate & c1, const Coordinate & c2) const { Coordinate result(0, 0); if (c2.y > c1.y && segment_intersection(c1, c2, m_box.upper_left(), m_box.upper_right(), result)) { return Crossing(Side::TOP, result); } if (c2.y < c1.y && segment_intersection(c1, c2, m_box.lower_right(), m_box.lower_left(), result)) { return Crossing(Side::BOTTOM, result); } if (c2.x < c1.x && segment_intersection(c1, c2, m_box.lower_left(), m_box.upper_left(), result)) { return Crossing(Side::LEFT, result); } if (c2.x > c1.x && segment_intersection(c1, c2, m_box.lower_right(), m_box.upper_right(), result)) { return Crossing(Side::RIGHT, result); } throw std::runtime_error("Never get here!"); } #endif } exactextractr/src/exactextract/src/side.cpp0000644000176200001440000000243414776002150020645 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include "side.h" namespace exactextract { std::ostream &operator<<(std::ostream &os, const Side &s) { switch (s) { case Side::NONE: os << "none"; return os; case Side::LEFT: os << "left"; return os; case Side::RIGHT: os << "right"; return os; case Side::TOP: os << "top"; return os; case Side::BOTTOM: os << "bottom"; return os; } return os; // unreachable statement needed for -Werror=return-type } } exactextractr/src/exactextract/src/raster_area.h0000644000176200001440000000456314776002150021663 0ustar liggesusers// Copyright (c) 2021 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_RASTER_AREA_H #define EXACTEXTRACT_RASTER_AREA_H #include "raster.h" namespace exactextract { template class CartesianAreaRaster : public AbstractRaster { public: explicit CartesianAreaRaster(const Grid & ex) : AbstractRaster(ex), m_area(grid_cell(AbstractRaster::grid(), 0, 0).area()) {} T operator()(size_t row, size_t column) const override { (void) row; (void) column; return m_area; } private: double m_area; }; template class SphericalAreaRaster : public AbstractRaster { public: explicit SphericalAreaRaster(const Grid &ex) : AbstractRaster(ex), m_areas(ex.rows()) { const auto& g = AbstractRaster::grid(); double dlon = g.dx(); double dlat = g.dy(); for (size_t i = 0; i < g.rows(); i++) { double y = g.y_for_row(i); double ymin = y - 0.5 * dlat; double ymax = y + 0.5 * dlat; m_areas[i] = EARTH_RADIUS_SQ * PI_180 * std::abs(std::sin(ymin * PI_180) - std::sin(ymax * PI_180)) * dlon; } } T operator()(size_t row, size_t column) const override { (void) column; return m_areas[row]; } private: static constexpr double EARTH_RADIUS = 6378137; static constexpr double EARTH_RADIUS_SQ = EARTH_RADIUS * EARTH_RADIUS; static constexpr double PI_180 = 3.141592653589793238462643383279502884197169399375105 / 180; std::vector m_areas; }; } #endif //EXACTEXTRACT_RASTER_AREA_H exactextractr/src/exactextract/src/geos_utils.cpp0000644000176200001440000002067214776002150022102 0ustar liggesusers// Copyright (c) 2018-2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "geos_utils.h" #include #if !HAVE_380 static inline int GEOSCoordSeq_setXY_r(GEOSContextHandle_t context, GEOSCoordSequence* seq, unsigned int idx, double x, double y) { return GEOSCoordSeq_setX_r(context, seq, idx, x) && GEOSCoordSeq_setY_r(context, seq, idx, y); } #endif namespace exactextract { geom_ptr_r geos_make_box_polygon(GEOSContextHandle_t context, const Box & b) { auto seq = geos_ptr(context, GEOSCoordSeq_create_r(context, 5, 2)); GEOSCoordSeq_setXY_r(context, seq.get(), 0, b.xmin, b.ymin); GEOSCoordSeq_setXY_r(context, seq.get(), 1, b.xmax, b.ymin); GEOSCoordSeq_setXY_r(context, seq.get(), 2, b.xmax, b.ymax); GEOSCoordSeq_setXY_r(context, seq.get(), 3, b.xmin, b.ymax); GEOSCoordSeq_setXY_r(context, seq.get(), 4, b.xmin, b.ymin); auto shell = geos_ptr(context, GEOSGeom_createLinearRing_r(context, seq.release())); return geos_ptr(context, GEOSGeom_createPolygon_r(context, shell.release(), nullptr, 0)); } bool segment_intersection( GEOSContextHandle_t context, const Coordinate &a0, const Coordinate &a1, const Coordinate &b0, const Coordinate &b1, Coordinate &result) { #if HAVE_370 int code = GEOSSegmentIntersection_r(context, a0.x, a0.y, a1.x, a1.y, b0.x, b0.y, b1.x, b1.y, &result.x, &result.y); if (!code) { throw std::runtime_error("Error in GEOSSegmentIntersection_r"); } return code == 1; #else auto seqa = GEOSCoordSeq_create_ptr(context, 2, 2); auto seqb = GEOSCoordSeq_create_ptr(context, 2, 2); GEOSCoordSeq_setX_r(context, seqa.get(), 0, a0.x); GEOSCoordSeq_setY_r(context, seqa.get(), 0, a0.y); GEOSCoordSeq_setX_r(context, seqa.get(), 1, a1.x); GEOSCoordSeq_setY_r(context, seqa.get(), 1, a1.y); GEOSCoordSeq_setX_r(context, seqb.get(), 0, b0.x); GEOSCoordSeq_setY_r(context, seqb.get(), 0, b0.y); GEOSCoordSeq_setX_r(context, seqb.get(), 1, b1.x); GEOSCoordSeq_setY_r(context, seqb.get(), 1, b1.y); auto geom_a = GEOSGeom_createLineString_ptr(context, seqa.release()); auto geom_b = GEOSGeom_createLineString_ptr(context, seqb.release()); geom_ptr_r intersection = geos_ptr(context, GEOSIntersection_r(context, geom_a.get(), geom_b.get())); if (GEOSisEmpty_r(context, intersection.get())) { return false; } if (GEOSGeomTypeId_r(context, intersection.get()) != GEOS_POINT) { return false; } GEOSGeomGetX_r(context, intersection.get(), &result.x); GEOSGeomGetY_r(context, intersection.get(), &result.y); return true; #endif } Box geos_get_box(GEOSContextHandle_t context, const GEOSGeometry* g) { double xmin, ymin, xmax, ymax; #if HAVE_370 if (!(GEOSGeom_getXMin_r(context, g, &xmin) && GEOSGeom_getYMin_r(context, g, &ymin) && GEOSGeom_getXMax_r(context, g, &xmax) && GEOSGeom_getYMax_r(context, g, &ymax))) { throw std::runtime_error("Error getting geometry extent."); } #else xmin = std::numeric_limits::max(); ymin = std::numeric_limits::max(); xmax = std::numeric_limits::lowest(); ymax = std::numeric_limits::lowest(); geom_ptr_r env = geos_ptr(context, GEOSEnvelope_r(context, g)); int dim = GEOSGeom_getDimensions_r(context, g); const GEOSCoordSequence* seq; if (dim == 2) { const GEOSGeometry* ring = GEOSGetExteriorRing_r(context, env.get()); seq = GEOSGeom_getCoordSeq_r(context, ring); } else { seq = GEOSGeom_getCoordSeq_r(context, g); } unsigned int npts; GEOSCoordSeq_getSize_r(context, seq, &npts); for (unsigned int i = 0; i < npts; i++) { double x, y; if (!GEOSCoordSeq_getX_r(context, seq, i, &x) || !GEOSCoordSeq_getY_r(context, seq, i, &y)) { throw std::runtime_error("Error reading coordinates."); } xmin = std::min(xmin, x); ymin = std::min(ymin, y); xmax = std::max(xmax, x); ymax = std::max(ymax, y); } #endif return {xmin, ymin, xmax, ymax}; } std::vector geos_get_component_boxes(GEOSContextHandle_t context, const GEOSGeometry* g) { size_t n = static_cast(GEOSGetNumGeometries_r(context, g)); std::vector boxes; boxes.reserve(n); for (size_t i = 0; i < n; i++) { boxes.push_back(geos_get_box(context, GEOSGetGeometryN_r(context, g, i))); } return boxes; } bool geos_is_ccw(GEOSContextHandle_t context, const GEOSCoordSequence *s) { #if HAVE_370 char result; if (!GEOSCoordSeq_isCCW_r(context, s, &result)) { throw std::runtime_error("Error calling GEOSCoordSeq_isCCW_r."); } return result; #else std::vector coords = read(context, s); if (coords.size() < 4) { throw std::runtime_error("Ring has fewer than 4 points, so orientation cannot be determined."); } // find highest point size_t hi_index = (size_t) std::distance( coords.begin(), std::max_element(coords.begin(), coords.end(), [](const auto& a, const auto&b) { return a.y < b.y; }) ); // find distinct point before highest point size_t i_prev = hi_index; do { if (i_prev == 0) { i_prev = coords.size() - 1; } else { i_prev--; } } while (i_prev != hi_index && coords[i_prev] == coords[hi_index]); // find distinct point after highest point size_t i_next = hi_index; do { i_next = (i_next + 1) % coords.size(); } while (i_next != hi_index && coords[i_next] == coords[hi_index]); Coordinate& a = coords[i_prev]; Coordinate& b = coords[hi_index]; Coordinate& c = coords[i_next]; if (a == b || b == c || a == c) { return false; } int disc = GEOSOrientationIndex_r(context, a.x, a.y, b.x, b.y, c.x, c.y); if (disc == 0) { // poly is CCW if prev x is right of next x return (a.x > b.x); } else { // if area is positive, points are ordered CCW return disc > 0; } #endif } std::vector read(GEOSContextHandle_t context, const GEOSCoordSequence *s) { unsigned int size; if (!GEOSCoordSeq_getSize_r(context, s, &size)) { throw std::runtime_error("Error calling GEOSCoordSeq_getSize."); } std::vector coords{size}; #if HAVE_3100 if (!GEOSCoordSeq_copyToBuffer_r(context, s, reinterpret_cast(coords.data()), false, false)) { throw std::runtime_error("Error reading coordinates."); } #elif HAVE_380 for (unsigned int i = 0; i < size; i++) { if (!GEOSCoordSeq_getXY_r(context, s, i, &(coords[i].x), &(coords[i].y))) { throw std::runtime_error("Error reading coordinates."); } } #else for (unsigned int i = 0; i < size; i++) { if (!GEOSCoordSeq_getX_r(context, s, i, &(coords[i].x)) || !GEOSCoordSeq_getY_r(context, s, i, &(coords[i].y))) { throw std::runtime_error("Error reading coordinates."); } } #endif return coords; } } exactextractr/src/exactextract/src/version.h.in0000644000176200001440000000154114776002150021456 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_VERSION_H #include namespace exactextract { inline std::string version() { return { "@EXACTEXTRACT_VERSION_SOURCE@" }; } } #define EXACTEXTRACT_VERSION_H #endif //EXACTEXTRACT_VERSION_H exactextractr/src/exactextract/src/box.h0000644000176200001440000001001314776002150020146 0ustar liggesusers// Copyright (c) 2018-2019 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_BOX_H #define EXACTEXTRACT_BOX_H #include "coordinate.h" #include "crossing.h" #include "side.h" #include namespace exactextract { struct Box { double xmin; double ymin; double xmax; double ymax; Box(double p_xmin, double p_ymin, double p_xmax, double p_ymax) : xmin{p_xmin}, ymin{p_ymin}, xmax{p_xmax}, ymax{p_ymax} {} static Box maximum_finite() { return { std::numeric_limits::lowest(), std::numeric_limits::lowest(), std::numeric_limits::max(), std::numeric_limits::max() }; } static Box make_empty() { return {0, 0, 0, 0}; } double width() const { return xmax - xmin; } double height() const { return ymax - ymin; } double area() const { return width() * height(); } double perimeter() const { return 2 * width() + 2 * height(); } bool intersects(const Box & other) const { if (other.ymin > ymax) return false; if (other.ymax < ymin) return false; if (other.xmin > xmax) return false; if (other.xmax < xmin) return false; return true; } Box intersection(const Box & other) const { return { std::max(xmin, other.xmin), std::max(ymin, other.ymin), std::min(xmax, other.xmax), std::min(ymax, other.ymax) }; } Box translate(double dx, double dy) const { return { xmin + dx, ymin + dy, xmax + dx, ymax + dy }; } Coordinate upper_left() const { return Coordinate{xmin, ymax}; } Coordinate upper_right() const { return Coordinate{xmax, ymax}; } Coordinate lower_left() const { return Coordinate{xmin, ymin}; } Coordinate lower_right() const { return Coordinate{xmax, ymin}; } Side side(const Coordinate &c) const; Crossing crossing(const Coordinate &c1, const Coordinate &c2) const; bool empty() const { return xmin >= xmax || ymin >= ymax; } Box expand_to_include(const Box & other) const { if (empty()) { return other; } if (other.empty()) { return *this; } return { std::min(xmin, other.xmin), std::min(ymin, other.ymin), std::max(xmax, other.xmax), std::max(ymax, other.ymax) }; } bool contains(const Box &b) const; bool contains(const Coordinate &c) const; bool strictly_contains(const Coordinate &c) const; bool operator==(const Box& other) const { return xmin == other.xmin && xmax == other.xmax && ymin == other.ymin && ymax == other.ymax; } friend std::ostream &operator<<(std::ostream &os, const Box &c); }; std::ostream &operator<<(std::ostream &os, const Box &c); } #endif exactextractr/src/exactextract/src/weighted_quantiles.cpp0000644000176200001440000000472415100140550023577 0ustar liggesusers// Copyright (c) 2019-2020 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "weighted_quantiles.h" #include namespace exactextract { void WeightedQuantiles::prepare() const { std::sort(m_elems.begin(), m_elems.end(), [](const elem_t &a, const elem_t &b) { return a.x < b.x; }); m_sum_w = 0; // relies on map being sorted which it is no for (size_t i = 0; i < m_elems.size(); i++) { m_sum_w += m_elems[i].w; if (i == 0) { m_elems[i].s = 0; m_elems[i].cumsum = m_elems[i].w; } else { m_elems[i].cumsum = m_elems[i - 1].cumsum + m_elems[i].w; m_elems[i].s = i * m_elems[i].w + (static_cast(m_elems.size()) - 1) * m_elems[i - 1].cumsum; } } m_ready_to_query = true; } double WeightedQuantiles::quantile(double q) const { if (!std::isfinite(q) || q < 0 || q > 1) { throw std::runtime_error("Quantile must be between 0 and 1."); } if (!m_ready_to_query) { prepare(); } auto sn = m_sum_w * (static_cast(m_elems.size()) - 1); elem_t lookup(0, 0); // create a dummy element to use with std::upper_bound lookup.s = q*sn; // get first element that is greater than the lookup value (q * sn) auto right = std::upper_bound(m_elems.cbegin(), m_elems.cend(), lookup, [](const elem_t & a, const elem_t & b) { return a.s < b.s; }); // since the minimum value of "lookup" is zero, and the first value of "s" is zero, // we are guaranteed to have at least one element to the left of "right" auto left = std::prev(right, 1); if (right == m_elems.cend()) { return left->x; } return left->x + (q*sn - left->s)*(right->x - left->x)/(right->s - left->s); } } exactextractr/src/exactextract/src/gdal_dataset_wrapper.h0000644000176200001440000000273714776002150023550 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_GDAL_DATASET_WRAPPER_H #define EXACTEXTRACT_GDAL_DATASET_WRAPPER_H #include #include #include namespace exactextract { class GDALDatasetWrapper { public: GDALDatasetWrapper(const std::string &filename, const std::string & layer, std::string id_field); bool next(); GEOSGeometry* feature_geometry(const GEOSContextHandle_t &geos_context) const; std::string feature_field(const std::string &field_name) const; const std::string& id_field() const { return m_id_field; } void copy_field(const std::string & field_name, OGRLayerH to) const; ~GDALDatasetWrapper(); private: GDALDatasetH m_dataset; OGRFeatureH m_feature; OGRLayerH m_layer; std::string m_id_field; }; } #endif //EXACTEXTRACT_GDAL_DATASET_WRAPPER_H exactextractr/src/exactextract/src/box.cpp0000644000176200001440000001066014776002150020511 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include "box.h" namespace exactextract { static inline double clamp(double x, double low, double high) { return std::min(std::max(x, low), high); } Side Box::side(const Coordinate &c) const { if (c.x == xmin) { return Side::LEFT; } else if (c.x == xmax) { return Side::RIGHT; } else if (c.y == ymin) { return Side::BOTTOM; } else if (c.y == ymax) { return Side::TOP; } return Side::NONE; } Crossing Box::crossing(const Coordinate &c1, const Coordinate &c2) const { // vertical line if (c1.x == c2.x) { if (c2.y >= ymax) { return Crossing{Side::TOP, c1.x, ymax}; } else if (c2.y <= ymin) { return Crossing{Side::BOTTOM, c1.x, ymin}; } else { throw std::runtime_error("Never get here."); } } // horizontal line if (c1.y == c2.y) { if (c2.x >= xmax) { return Crossing{Side::RIGHT, xmax, c1.y}; } else if (c2.x <= xmin) { return Crossing{Side::LEFT, xmin, c1.y}; } else { throw std::runtime_error("Never get here"); } } double m = std::abs((c2.y - c1.y) / (c2.x - c1.x)); bool up = c2.y > c1.y; bool right = c2.x > c1.x; if (up) { if (right) { // 1st quadrant double y2 = c1.y + m * (xmax - c1.x); if (y2 < ymax) { return Crossing{Side::RIGHT, xmax, clamp(y2, ymin, ymax)}; } else { double x2 = c1.x + (ymax - c1.y) / m; return Crossing{Side::TOP, clamp(x2, xmin, xmax), ymax}; } } else { // 2nd quadrant double y2 = c1.y + m * (c1.x - xmin); if (y2 < ymax) { return Crossing{Side::LEFT, xmin, clamp(y2, ymin, ymax)}; } else { double x2 = c1.x - (ymax - c1.y) / m; return Crossing{Side::TOP, clamp(x2, xmin, xmax), ymax}; } } } else { if (right) { // 4th quadrant double y2 = c1.y - m * (xmax - c1.x); if (y2 > ymin) { return Crossing{Side::RIGHT, xmax, clamp(y2, ymin, ymax)}; } else { double x2 = c1.x + (c1.y - ymin) / m; return Crossing{Side::BOTTOM, clamp(x2, xmin, xmax), ymin}; } } else { // 3rd quadrant double y2 = c1.y - m * (c1.x - xmin); if (y2 > ymin) { return Crossing{Side::LEFT, xmin, clamp(y2, ymin, ymax)}; } else { double x2 = c1.x - (c1.y - ymin) / m; return Crossing{Side::BOTTOM, clamp(x2, xmin, xmax), ymin}; } } } } bool Box::contains(const Box& b) const { return b.xmin >= xmin && b.xmax <= xmax && b.ymin >= ymin && b.ymax <= ymax; } bool Box::contains(const Coordinate &c) const { return c.x >= xmin && c.x <= xmax && c.y >= ymin && c.y <= ymax; } bool Box::strictly_contains(const Coordinate &c) const { return c.x > xmin && c.x < xmax && c.y > ymin && c.y < ymax; } std::ostream &operator<<(std::ostream &os, const Box &b) { os << "POLYGON (("; os << b.xmin << " " << b.ymin << ", "; os << b.xmax << " " << b.ymin << ", "; os << b.xmax << " " << b.ymax << ", "; os << b.xmin << " " << b.ymax << ", "; os << b.xmin << " " << b.ymin << "))"; return os; } } exactextractr/src/exactextract/src/perimeter_distance.h0000644000176200001440000000220214776002150023225 0ustar liggesusers// Copyright (c) 2018 ISciences, LLC. // All rights reserved. // // This software is licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef EXACTEXTRACT_PERIMETERDISTANCE_H #define EXACTEXTRACT_PERIMETERDISTANCE_H #include "box.h" #include "coordinate.h" namespace exactextract { double perimeter_distance(double xmin, double ymin, double xmax, double ymax, double x, double y); double perimeter_distance(double xmin, double ymin, double xmax, double ymax, const Coordinate &c); double perimeter_distance(const Box &b, const Coordinate &c); double perimeter_distance_ccw(double measure1, double measure2, double perimeter); } #endif exactextractr/src/exactextract/test/0000755000176200001440000000000014776002150017402 5ustar liggesusersexactextractr/src/exactextract/test/resources/0000755000176200001440000000000014776002150021414 5ustar liggesusersexactextractr/src/exactextract/test/resources/regression6.wkt0000644000176200001440000000636014776002150024416 0ustar liggesusers"POLYGON ((146.375 -33.475, 146.425 -33.475, 146.475 -33.475, 146.475 -33.525, 146.525 -33.525, 146.575 -33.525, 146.575 -33.575, 146.625 -33.575, 146.675 -33.575, 146.725 -33.575, 146.775 -33.575, 146.775 -33.525, 146.825 -33.525, 146.875 -33.525, 146.925 -33.525, 146.975 -33.525, 147.025 -33.525, 147.075 -33.525, 147.125 -33.525, 147.125 -33.575, 147.175 -33.575, 147.225 -33.575, 147.225 -33.525, 147.275 -33.525, 147.325 -33.525, 147.375 -33.525, 147.375 -33.575, 147.375 -33.625, 147.375 -33.675, 147.375 -33.725, 147.375 -33.775, 147.375 -33.825, 147.375 -33.875, 147.375 -33.925, 147.375 -33.975, 147.375 -34.025, 147.375 -34.075, 147.375 -34.125, 147.375 -34.175, 147.325 -34.175, 147.275 -34.175, 147.275 -34.125, 147.225 -34.125, 147.225 -34.175, 147.225 -34.225, 147.175 -34.225, 147.175 -34.275, 147.225 -34.275, 147.225 -34.325, 147.175 -34.325, 147.175 -34.375, 147.125 -34.375, 147.075 -34.375, 147.075 -34.425, 147.125 -34.425, 147.125 -34.475, 147.125 -34.525, 147.125 -34.575, 147.175 -34.575, 147.175 -34.625, 147.175 -34.675, 147.175 -34.725, 147.225 -34.725, 147.225 -34.775, 147.225 -34.825, 147.175 -34.825, 147.175 -34.875, 147.225 -34.875, 147.225 -34.925, 147.275 -34.925, 147.275 -34.975, 147.325 -34.975, 147.375 -34.975, 147.375 -35.025, 147.375 -35.075, 147.375 -35.125, 147.375 -35.175, 147.375 -35.225, 147.325 -35.225, 147.325 -35.175, 147.275 -35.175, 147.275 -35.225, 147.275 -35.275, 147.275 -35.325, 147.275 -35.375, 147.225 -35.375, 147.225 -35.325, 147.175 -35.325, 147.125 -35.325, 147.125 -35.375, 147.125 -35.425, 147.075 -35.425, 147.025 -35.425, 147.025 -35.475, 146.975 -35.475, 146.925 -35.475, 146.925 -35.425, 146.875 -35.425, 146.825 -35.425, 146.775 -35.425, 146.775 -35.475, 146.725 -35.475, 146.725 -35.525, 146.675 -35.525, 146.675 -35.475, 146.625 -35.475, 146.625 -35.425, 146.575 -35.425, 146.525 -35.425, 146.525 -35.475, 146.525 -35.525, 146.475 -35.525, 146.475 -35.475, 146.425 -35.475, 146.375 -35.475, 146.325 -35.475, 146.275 -35.475, 146.225 -35.475, 146.175 -35.475, 146.125 -35.475, 146.075 -35.475, 146.025 -35.475, 146.025 -35.425, 146.025 -35.375, 146.025 -35.325, 145.975 -35.325, 145.925 -35.325, 145.925 -35.275, 145.925 -35.225, 145.975 -35.225, 145.975 -35.175, 145.975 -35.125, 145.975 -35.075, 146.025 -35.075, 146.025 -35.025, 146.025 -34.975, 146.025 -34.925, 146.025 -34.875, 146.075 -34.875, 146.125 -34.875, 146.125 -34.825, 146.125 -34.775, 146.175 -34.775, 146.175 -34.725, 146.175 -34.675, 146.175 -34.625, 146.225 -34.625, 146.225 -34.575, 146.275 -34.575, 146.275 -34.525, 146.275 -34.475, 146.325 -34.475, 146.325 -34.425, 146.325 -34.375, 146.375 -34.375, 146.375 -34.325, 146.425 -34.325, 146.425 -34.275, 146.425 -34.225, 146.375 -34.225, 146.375 -34.175, 146.425 -34.175, 146.425 -34.125, 146.425 -34.075, 146.425 -34.025, 146.475 -34.025, 146.475 -33.975, 146.475 -33.925, 146.425 -33.925, 146.375 -33.925, 146.375 -33.875, 146.375 -33.825, 146.325 -33.825, 146.275 -33.825, 146.275 -33.775, 146.225 -33.775, 146.225 -33.725, 146.175 -33.725, 146.175 -33.675, 146.125 -33.675, 146.125 -33.625, 146.175 -33.625, 146.175 -33.575, 146.175 -33.525, 146.225 -33.525, 146.275 -33.525, 146.325 -33.525, 146.375 -33.525, 146.375 -33.475), (147.175 -35.225, 147.175 -35.175, 147.125 -35.175, 147.125 -35.225, 147.175 -35.225))"exactextractr/src/exactextract/test/resources/russia.wkt0000644000176200001440000004323514776002150023460 0ustar liggesusers"MULTIPOLYGON (((143.648007440362875 50.747600409541519,144.654147577085638 48.976390692737596,143.173927850517231 49.306551418650372,142.558668247650104 47.861575018904915,143.533492466404056 46.836728013692493,143.505277134372619 46.137907619809482,142.747700636973917 46.740764878926569,142.092030064054512 45.966755276058791,141.906925083585037 46.805928860046549,142.018442824470895 47.780132961612935,141.904444614835057 48.85918854429957,142.135800002205684 49.615163072297463,142.179983351815309 50.952342434281917,141.594075962490052 51.935434882202543,141.68254601457366 53.301966457728781,142.606934035410774 53.762145087287905,142.209748976815405 54.225475979216867,142.65478641171299 54.365880845753878,142.914615513276573 53.704577541714741,143.260847609632066 52.740760403039047,143.235267775647657 51.756660264688747,143.648007440362875 50.747600409541519)),((22.731098667092652 54.327536932993326,20.892244500418656 54.312524929412575,19.660640089606403 54.426083889373984,19.888481479581344 54.866160386771497,21.268448927503499 55.190481675835287,22.315723504330606 55.0152985703659,22.757763706155288 54.856574408581423,22.651051873472568 54.582740993866707,22.731098667092652 54.327536932993326)),((-175.01425 66.58435,-174.33983 66.33556,-174.57182 67.06219,-171.85731 66.91308,-169.89958 65.97724,-170.89107 65.54139,-172.53025 65.43791,-172.555 64.46079,-172.95533 64.25269,-173.89184 64.2826,-174.65392 64.63125,-175.98353 64.92288,-176.20716 65.35667,-177.22266 65.52024,-178.35993 65.39052,-178.90332 65.74044,-178.68611 66.11211,-179.88377 65.87456,-179.43268 65.40411,-180.0 64.979708702198366,-180.0 68.963636363636368,-177.55 68.2,-174.92825 67.20589,-175.01425 66.58435)),((180.0 70.832199208546683,178.903425000000112 70.78114,178.7253 71.0988,180.0 71.515714336428218,180.0 70.832199208546683)),((-178.69378 70.89302,-180.0 70.832199208546683,-180.0 71.51571433642826,-179.871875 71.55762,-179.02433 71.55553,-177.577945 71.26948,-177.663575 71.13277,-178.69378 70.89302)),((143.60385 73.21244,142.08763 73.20544,140.038155 73.31692,139.86312 73.36983,140.81171 73.76506,142.06207 73.85758,143.48283 73.47525,143.60385 73.21244)),((150.73167 75.08406,149.575925 74.68892,147.977465 74.778355,146.11919 75.17298,146.358485 75.49682,148.22223 75.345845,150.73167 75.08406)),((145.086285 75.562625,144.3 74.82,140.61381 74.84768,138.95544 74.61148,136.97439 75.26167,137.51176 75.94917,138.831075 76.13676,141.471615 76.09289,145.086285 75.562625)),((57.5356925799924 70.72046397570216,56.944979282463947 70.632743231886678,53.677375115784201 70.76265778266847,53.412016635965387 71.206661688920207,51.601894565645722 71.474759019650492,51.455753615124223 72.014881089965144,52.478275180883571 72.22944163684096,52.444168735570855 72.774731350384855,54.427613559797663 73.627547512497586,53.50828982932515 73.749813951300155,55.902458937407658 74.627486477345343,55.631932814359715 75.081412258597169,57.868643833248854 75.609390367323215,61.170044386647504 76.251883450008137,64.498368361270224 76.439055487769281,66.210977003855106 76.809782213031241,68.157059767534832 76.939696763812918,68.852211134725138 76.544811306454619,68.180572544227658 76.23364166940911,64.637326287703019 75.737754625136233,61.583507521414759 75.260884507946798,58.47708214705338 74.309056301562833,56.986785516188007 73.333043524866241,55.419335971910954 72.371267605265984,55.622837762276305 71.54059479439033,57.5356925799924 70.72046397570216)),((106.970130000000125 76.97419,107.240000000000151 76.48,108.1538 76.723350000000153,111.077260000000166 76.71,113.33151 76.22224,114.13417 75.84764,113.88539 75.327790000000135,112.77918 75.03186,110.151250000000203 74.47673,109.4 74.18,110.64 74.04,112.11919 73.787740000000127,113.019540000000262 73.976930000000152,113.529580000000323 73.335050000000109,113.96881 73.594880000000103,115.56782 73.75285,118.776330000000229 73.58772,119.02 73.12,123.200660000000113 72.97122,123.257770000000193 73.735030000000108,125.380000000000194 73.56,126.97644 73.56549,128.59126 73.03871,129.05157 72.39872,128.460000000000122 71.98,129.715990000000232 71.19304,131.28858000000028 70.786990000000117,132.253500000000173 71.836300000000108,133.857660000000322 71.386420000000157,135.56193 71.655250000000137,137.49755 71.34763,138.23409000000018 71.62803,139.869830000000121 71.487830000000145,139.14791 72.416190000000114,140.46817 72.849410000000148,149.5 72.2,150.351180000000198 71.60643,152.968900000000218 70.84222,157.00688 71.03141,158.99779 70.86672,159.830310000000253 70.45324,159.70866 69.72198,160.940530000000336 69.437280000000101,162.279070000000132 69.64204,164.052480000000145 69.66823,165.940370000000229 69.47199,167.83567 69.58269,169.577630000000198 68.6938,170.816880000000282 69.01363,170.008200000000187 69.65276,170.453450000000288 70.09703,173.643910000000261 69.81743,175.724030000000226 69.877250000000231,178.6 69.4,180.0 68.96363636363661,180.0 64.97970870219838,179.99281 64.97433,178.707200000000256 64.53493,177.411280000000176 64.60821,178.313000000000244 64.07593,178.908250000000209 63.251970000000142,179.37034 62.982620000000111,179.48636 62.56894,179.228250000000145 62.304100000000147,177.3643 62.5219,174.569290000000223 61.76915,173.68013 61.65261,172.15 60.95,170.698500000000109 60.33618,170.330850000000311 59.88177,168.90046 60.57355,166.294980000000322 59.788550000000214,165.840000000000231 60.16,164.87674 59.7316,163.539290000000136 59.86871,163.217110000000247 59.21101,162.017330000000101 58.24328,162.05297 57.83912,163.19191 57.615030000000104,163.057940000000173 56.159240000000125,162.129580000000232 56.12219,161.70146 55.285680000000156,162.117490000000174 54.85514,160.368770000000353 54.34433,160.021730000000247 53.20257,158.530940000000186 52.958680000000243,158.23118 51.94269,156.789790000000295 51.01105,156.420000000000158 51.7,155.99182 53.15895,155.433660000000117 55.381030000000123,155.91442000000032 56.767920000000146,156.75815 57.3647,156.810350000000113 57.83204,158.364330000000194 58.05575,160.150640000000152 59.314770000000124,161.87204 60.343000000000131,163.66969 61.140900000000101,164.473550000000131 62.55061,163.2584200000002 62.46627,162.65791 61.6425,160.121480000000105 60.54423,159.30232 61.773960000000102,156.720680000000101 61.43442,154.218060000000349 59.758180000000124,155.04375 59.14495,152.81185 58.88385,151.265730000000275 58.78089,151.338150000000127 59.50396,149.78371 59.655730000000148,148.54481 59.16448,145.48722 59.33637,142.197820000000178 59.03998,138.958480000000321 57.08805,135.12619 54.72959,136.70171 54.603550000000126,137.19342 53.97732,138.1647 53.755010000000254,138.80463 54.254550000000108,139.90151 54.18968000000018,141.34531 53.089570000000123,141.37923 52.23877,140.597420000000199 51.239670000000103,140.51308 50.045530000000127,140.061930000000217 48.446710000000166,138.554720000000231 46.99965,138.21971 46.30795,136.86232 45.143500000000188,135.515350000000211 43.989,134.869390000000266 43.39821,133.536870000000278 42.81147,132.906270000000148 42.798490000000101,132.27807000000027 43.284560000000113,130.935870000000165 42.55274,130.78 42.220000000000198,130.640000000000185 42.395,130.63386640840983 42.903014634770557,131.14468794161499 42.929989732426947,131.288555129115622 44.111519680348266,131.025190000000265 44.96796,131.88345421765959 45.321161607436522,133.097120000000217 45.14409,133.769643996313192 46.116926988299156,134.112350000000191 47.212480000000141,134.50081 47.578450000000146,135.026311476786788 48.478229885443909,133.373595819228029 48.183441677434843,132.506690000000134 47.78896,130.987260000000134 47.79013,130.582293328982672 48.729687404976204,129.397817824420514 49.440600084015614,127.657400000000365 49.76027,127.287455682484932 50.739797268265448,126.939156528837856 51.35389415140591,126.564399041857001 51.784255479532703,125.946348911646481 52.79279857035695,125.068211297710462 53.161044826868931,123.57147 53.4588,122.245747918793057 53.431725979213695,121.003084751470368 53.251401068731241,120.177088657716894 52.753886216841209,120.725789015792003 52.516226304730907,120.7382 51.96411,120.182080000000184 51.64355,119.27939 50.58292,119.288460728025854 50.142882798861962,117.879244419426499 49.510983384797044,116.678800897286209 49.888531399121405,115.485695428531443 49.805177313834747,114.962109816550395 50.140247300815133,114.362456496235353 50.248302720737485,112.897739699354389 49.543565375356991,111.581230910286678 49.377968248077678,110.662010532678863 49.130128078805853,109.402449171996722 49.292960516957692,108.475167270951289 49.282547715850711,107.86817589725112 49.793705145865886,106.888804152455322 50.27429596618029,105.886591424586896 50.406019192092174,104.62158 50.275320000000164,103.676545444760364 50.089966132195144,102.255890000000107 50.510560000000112,102.06521 51.259910000000104,100.889480421962645 51.516855780638423,99.981732212323578 51.634006252643957,98.861490513100506 52.047366034546712,97.825739780674525 51.01099518493325,98.231761509191728 50.42240062112873,97.259760000000227 49.72605,95.81402000000017 49.977460000000121,94.815949334698786 50.013433335970888,94.147566359435615 50.480536607457168,93.10421 50.49529,92.23471154171969 50.802170722041751,90.713667433640779 50.331811835321105,88.805566847695587 49.470520738312473,87.751264276076853 49.297197984405557,87.359970330762707 49.214980780629162,86.829356723989662 49.82667470966814,85.5412699726825 49.692858588248157,85.11555952346211 50.117302964877638,84.416377394553052 50.311399644565824,83.935114780618932 50.889245510453577,83.383003778012466 51.069182847693895,81.945985548839957 50.812195949906339,80.56844689323546 51.388336493528442,80.035559523441719 50.864750881547224,77.800915561844334 53.404414984747547,76.525179477854778 54.177003485727141,76.891100294913457 54.490524400441927,74.384820000000133 53.54685000000012,73.425678745420527 53.489810289109755,73.508516066384374 54.035616766976602,72.224150018202209 54.376655381886792,71.180131056609497 54.133285224008262,70.865266554655165 55.169733588270105,69.068166945272907 55.385250149143502,68.169100376258911 54.97039175070438,65.666870000000102 54.601250000000164,65.178533563095954 54.354227810272079,61.436600000000141 54.00625,60.97806644068325 53.664993394579142,61.699986199800634 52.979996446334269,60.739993117114551 52.719986477257748,60.927268507740251 52.447548326215014,59.967533807215574 51.960420437215674,61.588003371024143 51.272658799843185,61.337424350841019 50.799070136104262,59.932807244715576 50.842194118851836,59.642282342370578 50.545442206415714,58.36332000000013 51.06364,56.77798 51.04355,55.716940000000108 50.621710000000149,54.532878452376195 51.026239732459373,52.328723585831057 51.718652248738096,50.766648390512188 51.692762356159875,48.702381626181051 50.605128485712839,48.577841424357615 49.874759629915644,47.549480421749394 50.454698391311126,46.751596307162771 49.356005764353739,47.043671502476599 49.152038886097586,46.466445753776298 48.39415233010493,47.315240000000159 47.71585,48.05725 47.74377,48.694733514201886 47.075628160177899,48.593250000000154 46.561040000000105,49.101160000000135 46.399330000000106,48.645410000000112 45.80629,47.67591 45.641490000000118,46.68201 44.609200000000101,47.59094 43.660160000000133,47.49252 42.98658,48.584370000000177 41.80888,47.98728315612604 41.405819200194401,47.81566572448466 41.151416124021353,47.373315464066394 41.219732367511142,46.686070591016716 41.827137152669906,46.404950799348939 41.860675157227433,45.7764 42.092440000000238,45.470279168485916 42.502780666670049,44.537622918482072 42.711992702803684,43.931210000000107 42.554960000000108,43.755990000000196 42.74083,42.394400000000161 43.2203,40.922190000000143 43.382150000000138,40.076964959479852 43.553104153002494,39.955008579271095 43.434997666999294,38.68 44.28,37.539120000000111 44.65721,36.675460000000129 45.24469,37.40317 45.404510000000101,38.23295 46.24087,37.67372 46.63657,39.14767 47.044750000000136,39.12120000000013 47.26336,38.223538038899477 47.102189846375978,38.255112339029807 47.54640045835697,38.77057 47.825620000000242,39.738277622238996 47.898937079452082,39.89562000000015 48.23241,39.67465 48.783820000000134,40.080789015469492 49.307429917999372,40.069040000000115 49.60105,38.59498823421356 49.926461900423732,38.010631137857075 49.915661526074729,37.393459506995242 50.383953355503678,36.626167840325394 50.225590928745135,35.356116163888117 50.577197374059153,35.37791 50.77394,35.022183058417937 51.207572333371502,34.224815708154409 51.255993150428935,34.141978387190619 51.566413479206204,34.391730584457235 51.768881740925906,33.752699822735877 52.33507457133166,32.71576053236717 52.238465481162166,32.412058139787774 52.288694973349777,32.159440000000217 52.061250000000115,31.78597 52.10168,31.540018344862261 52.742052313846443,31.305200636527985 53.073995876673308,31.49764 53.167430000000138,32.304519484188376 53.132726141972853,32.693643019346126 53.351420803432148,32.405598585751164 53.618045355842014,31.731272820774592 53.794029446012019,31.791424187962406 53.974638576872195,31.384472283663825 54.157056382862379,30.757533807098781 54.811770941784403,30.971835971813249 55.081547756564134,30.873909132620071 55.550976467503517,29.896294386522442 55.789463202530499,29.37157189303079 55.670090643936277,29.229513380660393 55.918344224666413,28.17670942557794 56.169129950578792,27.855282016722526 56.759326483784378,27.770015903440992 57.244258124411203,27.288184848751655 57.474528306703917,27.716685825315778 57.79189911562446,27.420150000000206 58.724570000000142,28.131699253051863 59.300825100330997,27.98112 59.47537,29.1177 60.028050000000121,28.07 60.503520000000151,30.211107212044652 61.780027777749694,31.139991082491036 62.357692776124452,31.516092156711267 62.867687486412905,30.035872430142803 63.552813625738565,30.44468468600374 64.204453436939076,29.544429559047018 64.948671576590556,30.21765 65.80598,29.054588657352383 66.944286200622031,29.977426385220696 67.698297024192755,28.445943637818772 68.364612942164001,28.591929559043365 69.064776923286701,29.39955 69.156920000000184,31.10108000000011 69.55811,32.132720000000262 69.905950000000246,33.77547 69.301420000000121,36.51396 69.06342,40.292340000000166 67.9324,41.059870000000132 67.45713000000012,41.125950000000188 66.791580000000124,40.01583 66.266180000000134,38.38295 65.999530000000107,33.918710000000175 66.75961,33.18444 66.63253,34.81477 65.900150000000139,34.878574253078767 65.436212877048206,34.943910000000159 64.414370000000162,36.23129 64.10945,37.012730000000118 63.849830000000111,37.141970000000157 64.33471,36.539579035089815 64.76446,37.176040000000143 65.143220000000127,39.59345 64.520790000000176,40.435600000000107 64.76446,39.762600000000162 65.49682,42.093090000000103 66.47623,43.016040000000118 66.418580000000105,43.949750000000137 66.06908,44.53226 66.756340000000137,43.69839 67.35245,44.187950000000143 67.95051,43.45282 68.57079,46.250000000000142 68.25,46.821340000000163 67.68997,45.55517 67.56652,45.562020000000103 67.010050000000206,46.349150000000151 66.667670000000101,47.894160000000255 66.884550000000161,48.13876 67.52238,50.227660000000157 67.998670000000146,53.717430000000178 68.85738000000012,54.47171 68.80815,53.485820000000132 68.20131,54.72628 68.09702,55.442680000000138 68.43866,57.317020000000156 68.46628,58.80200000000022 68.88082,59.941420000000193 68.278440000000103,61.07784000000018 68.94069,60.03 69.52,60.55 69.85,63.504000000000161 69.54739,64.888115 69.234835000000146,68.512160000000137 68.092330000000175,69.18068 68.615630000000124,68.16444 69.14436,68.13522 69.35649,66.930080000000117 69.454610000000116,67.25976 69.92873,66.724920000000139 70.708890000000139,66.69466 71.028970000000243,68.540060000000125 71.934500000000241,69.196360000000112 72.84336000000016,69.94 73.040000000000134,72.58754 72.776290000000103,72.79603 72.22006,71.848110000000105 71.40898,72.47011 71.09019,72.79188 70.39114,72.564700000000215 69.02085,73.66787 68.4079,73.2387 67.7404,71.280000000000115 66.320000000000164,72.423010000000176 66.172670000000181,72.82077 66.53267,73.92099000000016 66.789460000000133,74.186510000000197 67.28429,75.052 67.760470000000169,74.469260000000162 68.32899,74.935840000000127 68.98918,73.84236 69.07146,73.601870000000218 69.62763,74.3998 70.63175,73.1011 71.447170000000256,74.890820000000218 72.12119,74.65926 72.83227,75.158010000000189 72.854970000000122,75.68351 72.300560000000132,75.288980000000123 71.33556,76.35911 71.152870000000149,75.903130000000175 71.87401,77.576650000000114 72.26717,79.652020000000135 72.32011,81.5 71.75,80.610710000000125 72.582850000000121,80.51109 73.6482,82.25 73.850000000000108,84.65526 73.805910000000182,86.82230000000024 73.93688,86.00956 74.459670000000159,87.166820000000172 75.11643,88.315710000000109 75.14393,90.26 75.64,92.90058 75.77333,93.234210000000161 76.0472,95.860000000000156 76.1400000000001,96.67821 75.91548,98.922540000000225 76.44689,100.759670000000227 76.43028,101.03532 76.86189,101.990840000000134 77.287540000000206,104.351600000000104 77.69792,106.066640000000149 77.37389,104.70500000000024 77.1274,106.970130000000125 76.97419)),((105.07547 78.30689,99.43814 77.921,101.2649 79.23399,102.08635 79.34641,102.837815 79.28129,105.37243 78.71334,105.07547 78.30689)),((51.13618655783128 80.547280178540944,49.793684523320707 80.415427761548216,48.894411248577541 80.339566758943704,48.754936557821765 80.175468248200843,47.586119012244154 80.010181179515342,46.502825962109654 80.247246812654367,47.072455275262911 80.559424140129465,44.846958042181114 80.589809882317184,46.799138624871233 80.771917629713641,48.318477410684665 80.784009914869955,48.522806023966695 80.514568996900152,49.097189568890911 80.753985907708426,50.039767693894618 80.918885403151819,51.522932977103693 80.69972565380192,51.13618655783128 80.547280178540944)),((99.93976 78.88094,97.75794 78.7562,94.97259 79.044745,93.31288 79.4265,92.5454 80.14379,91.18107 80.34146,93.77766 81.0246,95.940895 81.2504,97.88385 80.746975,100.186655 79.780135,99.93976 78.88094)))" exactextractr/src/exactextract/test/resources/regression4.wkt0000644000176200001440000053323314776002150024420 0ustar liggesusers"POLYGON (( -160.9833333333333201 70.3166666666666771, -160.9825524224175126 70.3243221706814268, -160.9343861897786212 70.3256700303819571, -160.9322825113932254 70.3326568603515625, -160.8927174886067633 70.3340098063151089, -160.8906158447265398 70.3409901936849025, -160.8510508219400776 70.3423431396484489, -160.8489573160807140 70.3492964002821282, -160.8260426839192405 70.3507035997178889, -160.8239573160807083 70.3576297336154539, -160.8010426839192633 70.3590369330512289, -160.7989573160807026 70.3659630669487939, -160.7760426839192576 70.3673702663845546, -160.7739491780598655 70.3743235270182339, -160.7343841552734318 70.3756764729817803, -160.7322906494140398 70.3826297336154596, -160.7093760172525947 70.3840369330512203, -160.7073031955295050 70.3909216986762232, -160.6926968044704722 70.3924116346571225, -160.6907023111978958 70.3990356445312528, -160.6842976888020758 70.4009643554687585, -160.6823031955294994 70.4075883653428889, -160.6676968044704665 70.4090783013237882, -160.6656239827473769 70.4159630669487910, -160.6427093505859318 70.4173702663845518, -160.6406127929687386 70.4243340386284871, -160.5843872070312273 70.4256659613715357, -160.5822906494140341 70.4326297336154568, -160.5593760172525890 70.4340369330512175, -160.5573689778645701 70.4407023111979242, -160.5509643554687216 70.4426310221354299, -160.5489573160807026 70.4492964002821225, -160.5260426839192576 70.4507035997178832, -160.5239573160806970 70.4576297336154624, -160.5010426839192519 70.4590369330512232, -160.4989698621961622 70.4659216986762260, -160.4843634711371294 70.4674116346571253, -160.4822852240668283 70.4743140326606010, -160.4510481092664804 70.4756859673394160, -160.4489698621961509 70.4825883653428917, -160.4343634711371465 70.4840783013237910, -160.4322906494140284 70.4909630669487939, -160.4093760172525833 70.4923702663845546, -160.4072906494140511 70.4992964002821338, -160.3843760172525776 70.5007035997178946, -160.3822906494140454 70.5076297336154596, -160.3593760172525720 70.5090369330512203, -160.3572906494140398 70.5159630669487996, -160.3343760172525947 70.5173702663845603, -160.3322906494140341 70.5242964002821253, -160.3093760172525890 70.5257035997178860, -160.3073031955294994 70.5325883653428889, -160.2926968044704665 70.5340783013237882, -160.2906239827473769 70.5409630669487910, -160.2677093505859034 70.5423702663845518, -160.2656365288628137 70.5492550320095546, -160.2510301378038093 70.5507449679904539, -160.2489573160807197 70.5576297336154568, -160.2260426839192462 70.5590369330512175, -160.2239573160807140 70.5659630669487967, -160.2010426839192405 70.5673702663845575, -160.1989698621961509 70.5742550320095603, -160.1843634711371180 70.5757449679904596, -160.1823031955294994 70.5825883653428860, -160.1676968044704665 70.5840783013237996, -160.1656185574001370 70.5909806993272610, -160.1499999999999773 70.5916666666666828, -160.1495382029882251 70.5871398380253936, -160.1597137451171875 70.5806961059570312, -160.1950531005859375 70.5678329467773438, -160.1874237060546875 70.5665512084960938, -160.1719665527343750 70.5705490112304688, -160.1408440792089891 70.5832494497494167, -160.1403579186419961 70.5831998566917349, -160.1382598876953125 70.5764846801757812, -160.1165771484375000 70.5690612792968750, -160.1071729397799572 70.5669787037403751, -160.1068854437933737 70.5660237630208371, -160.1033072568295097 70.5661226426814494, -160.1022796630859375 70.5658950805664062, -160.0744781494140625 70.5653457641601562, -160.0601806640625000 70.5670700073242188, -160.0594408379018034 70.5673348478175484, -160.0593861897786212 70.5673363579644217, -160.0593800978870149 70.5673565913247813, -160.0484161376953125 70.5712814331054688, -160.0425947116933685 70.5751988854332239, -160.0343760172525833 70.5757035997178974, -160.0332192618348017 70.5795458545753007, -160.0000000000000000 70.5770568847656250, -159.9951477050781250 70.5766906738281250, -159.9772644042968750 70.5767059326171875, -159.9175491703567786 70.5820980463596896, -159.9159815400432194 70.5768917155312749, -159.9211883544921875 70.5740509033203125, -159.9156036376953125 70.5664901733398438, -159.9165039062500000 70.5490798950195312, -159.9098052978515625 70.5376129150390625, -159.9116973876953125 70.5364456176757812, -159.9290771484375000 70.5385360717773438, -159.9432067871093750 70.5310668945312500, -159.9379272460937500 70.5277786254882812, -159.9213867187500000 70.5289306640625000, -159.9104919433593750 70.5314788818359375, -159.9062652587890625 70.5273971557617188, -159.9132672534269943 70.5164356132204233, -159.9157023111978901 70.5157023111979271, -159.9162097090517705 70.5140171409690311, -159.9232342816385994 70.5089201692917555, -159.9685956122317805 70.5083609215974008, -159.9905548095703125 70.5100021362304688, -159.9973227125775566 70.5080067528721059, -160.0239427354600537 70.5076785617404624, -160.0252809729229853 70.5032334220974661, -160.0330810546875000 70.5029144287109375, -160.0373807466494043 70.4995870417929353, -160.0409942626952784 70.4992184109158018, -160.0410528466941287 70.4967453271401752, -160.0458679199218750 70.4930191040039062, -160.0490570068359375 70.4832000732421875, -160.0455474853515625 70.4740600585937500, -160.0501821072706718 70.4715429794480315, -160.0509172227647241 70.4744076199001768, -160.0987850613063870 70.4755672878689268, -160.0999999999999659 70.4708333333333456, -160.1034216986761862 70.4718634711371550, -160.1049380832247948 70.4867285834418453, -160.1124999999999829 70.4875000000000114, -160.1121466742621351 70.4767422146267393, -160.1043246510614040 70.4688953185463163, -160.1143398728970624 70.4669366850548613, -160.1774244520399009 70.4663133409288207, -160.1790602776703452 70.4646888843790720, -160.1836700439453125 70.4648437500000000, -160.1943654405694133 70.4631267797047798, -160.2002563476562500 70.4621810913085938, -160.1944274902343750 70.4599304199218750, -160.1832275390625000 70.4595031738281250, -160.1731109619140625 70.4618911743164062, -160.1539459228515625 70.4584426879882812, -160.1374969482421875 70.4583435058593750, -160.1116027832031250 70.4630126953125000, -160.1051483154296875 70.4619979858398438, -160.1124114990234375 70.4577407836914062, -160.1017456054687500 70.4562683105468750, -160.0963626688391912 70.4582532060109230, -160.0852966308593750 70.4623336791992188, -160.0566253662109375 70.4612960815429688, -160.0326885965444035 70.4662224621687869, -160.0314203802561792 70.4662582353471834, -160.0052947998046875 70.4659500122070312, -159.9956143208813728 70.4672682338481309, -159.9927424112955521 70.4673492431640653, -159.9924618388073725 70.4682579265360403, -159.9763730785450377 70.4751751247769107, -159.9677318996853330 70.4757168240017506, -159.9666019326352284 70.4793761292170586, -159.9664916992187500 70.4794235229492188, -159.9372458159930659 70.4833206584574441, -159.9119698648650569 70.4827160605293415, -159.9111480712890625 70.4785003662109375, -159.9089050292968750 70.4668655395507812, -159.9165236339602245 70.4620367604732820, -159.9181557549370609 70.4673221164279653, -159.9249232670388210 70.4669088691466499, -159.9251403808593750 70.4673995971679688, -159.9316349945957825 70.4664990282591788, -159.9406009250216982 70.4659515380859460, -159.9409807297367649 70.4647215581642428, -159.9491834136809985 70.4584184431334108, -159.9572809855143021 70.4575744628906335, -159.9589973656810855 70.4520159932426253, -159.9671630859375000 70.4490203857421875, -159.9792872911450274 70.4412200284780852, -159.9826219346788037 70.4408725314670221, -159.9827049103819832 70.4390212327149925, -159.9862670898437500 70.4367294311523438, -160.0000000000000000 70.4356918334960938, -160.0100250244140625 70.4349365234375000, -160.0110015869140625 70.4330749511718750, -160.0000000000000000 70.4321899414062500, -159.9915313720703125 70.4315109252929688, -159.9852447509765625 70.4277267456054688, -159.9860992431640625 70.4139404296875000, -159.9934997558593750 70.4045867919921875, -160.0000000000000000 70.4012145996093750, -160.0029317463873326 70.3996944348159417, -160.0072809855143134 70.3992411295572964, -160.0079434409041426 70.3970957783998159, -160.0082397460937500 70.3969421386718750, -160.0081384179545125 70.3964643483486583, -160.0093163384331660 70.3926496717665060, -160.0156836615668396 70.3906836615668539, -160.0173568022463257 70.3852649625993791, -160.0385742187500000 70.3836059570312500, -160.0591735839843750 70.3780059814453125, -160.0592498779296875 70.3741760253906250, -160.0464782714843750 70.3732147216796875, -160.0466308593750000 70.3704605102539062, -160.0604248046875000 70.3670120239257812, -160.0622307572400587 70.3661611180114193, -160.0656014336479984 70.3659498426649321, -160.0660989553873890 70.3643385492640903, -160.0821228027343750 70.3567886352539062, -160.0834655761718750 70.3504791259765625, -160.0841719822707887 70.3502086271622744, -160.0989347669813299 70.3492831759982664, -160.1000140093981940 70.3457881241746037, -160.1129784330251766 70.3435309254181220, -160.1177673339843750 70.3426971435546875, -160.1500051955506194 70.3437551591474062, -160.1761169433593750 70.3446121215820312, -160.1820831298828125 70.3428268432617188, -160.1412811279296875 70.3374710083007812, -160.1221313476562500 70.3391189575195312, -160.1085354621890531 70.3374973958521821, -160.1357577853732437 70.3371466742621578, -160.1392422146267052 70.3336866590711907, -160.1440911187065694 70.3329800075954950, -160.1475755479600593 70.3295199924045278, -160.1499999999999773 70.3291666666666799, -160.1496466742621294 70.3267422146267478, -160.1457704678962841 70.3228535999887043, -160.1464233398437500 70.3224487304687500, -160.1481018066406250 70.3204193115234375, -160.1419648259427504 70.3187125772465151, -160.1416666666666515 70.3166666666666771, -160.1440911187065694 70.3163133409288292, -160.1455615517751028 70.3148532106221609, -160.1524047851562500 70.3132247924804688, -160.1731258360557035 70.3038236832779262, -160.1732577853732380 70.3038133409288264, -160.1733489691119132 70.3037224481505092, -160.1794281005859375 70.3009643554687500, -160.1896003127014581 70.2955054140337836, -160.1899244520399179 70.2954800075954864, -160.1902464758053668 70.2951586490920448, -160.2432681694295411 70.2667044331368515, -160.2482577853732550 70.2663133409288321, -160.2491019520068392 70.2654750890478397, -160.2581024169921875 70.2636108398437500, -160.2630157470703125 70.2613449096679688, -160.2735748291015625 70.2553558349609375, -160.2761888700981956 70.2509027931929921, -160.2779235839843750 70.2479476928710938, -160.2681579589843750 70.2473831176757812, -160.2635040283203125 70.2541351318359375, -160.2466278076171875 70.2614593505859375, -160.2322235107421875 70.2650909423828125, -160.2155761718750000 70.2756195068359375, -160.1850433349609375 70.2874603271484375, -160.1462097167968750 70.3105239868164062, -160.1375274658203125 70.3115005493164062, -160.1333333333332973 70.3135017715816844, -160.1208343505859375 70.3194656372070312, -160.1040039062500000 70.3240127563476562, -160.0967840009483325 70.3250899815801631, -160.0760526869032105 70.3256700303819571, -160.0752511884767273 70.3283322969502080, -160.0668996962055530 70.3333095903322771, -160.0591047498914747 70.3341047498914946, -160.0586865393359801 70.3382044390617551, -160.0571136474609375 70.3391418457031250, -160.0571289062500000 70.3411407470703125, -160.0571746826171875 70.3463821411132812, -160.0500471323438205 70.3496318950801367, -160.0260479397243785 70.3506859673394160, -160.0246305698019853 70.3553935987190471, -160.0215911865234375 70.3553619384765625, -160.0122132726726250 70.3583509414070960, -160.0010426839192519 70.3590369330512289, -160.0000853759974859 70.3622164406300641, -160.0000000000000000 70.3622436523437500, -159.9973602294921875 70.3630828857421875, -159.9857482910156250 70.3647384643554688, -159.9813385047624195 70.3670450472822040, -159.9760426839192746 70.3673702663845546, -159.9751690038058882 70.3702720666331913, -159.9657877522629690 70.3751790243399284, -159.9633081850181497 70.3750308800864133, -159.9543609619140625 70.3716125488281250, -159.9491833301713086 70.3681206576527813, -159.9489698621961793 70.3674116346571310, -159.9479827141537953 70.3673109398735335, -159.9241099966595812 70.3512107350518505, -159.9240358140733349 70.3509643554687614, -159.9235098183070249 70.3508059636047847, -159.9169616699218750 70.3463897705078125, -159.9168132865604548 70.3463202782209720, -159.9156239827473769 70.3423702663845631, -159.9072857567877293 70.3418582561619274, -159.8976898193359375 70.3373641967773438, -159.8993072509765625 70.3342666625976562, -159.9016152280907477 70.3331685967571048, -159.9073031955294937 70.3325883653428860, -159.9080502736945846 70.3301069963120824, -159.9295349121093750 70.3198852539062500, -159.9259643554687500 70.3122177124023438, -159.9223937988281250 70.3099060058593750, -159.9165344238281250 70.3061218261718750, -159.9056091308593750 70.3029098510742188, -159.9066162109375000 70.2997741699218750, -159.9157104492187500 70.2956314086914062, -159.9101409912109375 70.2942352294921875, -159.8964080810546875 70.2963943481445312, -159.8881683349609375 70.2952651977539062, -159.8875732421875000 70.2879867553710938, -159.8997802734375000 70.2850952148437500, -159.8980255126953125 70.2751998901367188, -159.9039154052734375 70.2723541259765625, -159.9139404296875000 70.2712478637695312, -159.9151916503906250 70.2664260864257812, -159.8931427001953125 70.2567977905273438, -159.8773040771484375 70.2522277832031250, -159.8710937500000000 70.2520675659179688, -159.8531951904296875 70.2514190673828125, -159.8463961890863914 70.2491673929405351, -159.8414001464843750 70.2475128173828125, -159.8300170898437500 70.2308502197265625, -159.8236236572265625 70.2274780273437500, -159.8099365234375000 70.2260131835937500, -159.7902832031250000 70.2120437622070312, -159.7767639160156250 70.1971588134765625, -159.7745056152343750 70.1859436035156250, -159.7653198242187500 70.1843185424804688, -159.7568008977869454 70.1797294779442780, -159.7565911187065808 70.1795199924045221, -159.7563455963188233 70.1794842113796165, -159.7550659179687500 70.1787948608398438, -159.7542296198355700 70.1791758411005162, -159.7509460449218750 70.1806716918945312, -159.7534332275390625 70.1878509521484375, -159.7670593261718750 70.1929473876953125, -159.7695312500000000 70.1977844238281250, -159.7554168701171875 70.2007598876953125, -159.7603607177734375 70.2044677734375000, -159.7590942382812500 70.2176055908203125, -159.7701721191406250 70.2204055786132812, -159.7847900390625000 70.2272567749023438, -159.7902679443359375 70.2335586547851562, -159.8325805664062500 70.2499618530273438, -159.8383636474609375 70.2569198608398438, -159.8397064208984375 70.2627487182617188, -159.8278503417968750 70.2769470214843750, -159.8277130126953125 70.2880172729492188, -159.8283233642578125 70.2887191772460938, -159.8324279785156250 70.2934112548828125, -159.8507843017578125 70.3004760742187500, -159.8573338197330713 70.3048582971549365, -159.8573410796615235 70.3048631547070499, -159.8578635479240688 70.3066903670914201, -159.8558654785156250 70.3076019287109375, -159.8307189941406250 70.3086471557617188, -159.8086547851562500 70.3062515258789062, -159.8021697998046875 70.3077774047851562, -159.8023071289062500 70.3099136352539062, -159.8352813720703125 70.3131790161132812, -159.8412780761718750 70.3173751831054688, -159.8391909831912585 70.3244646735475953, -159.8326653374565751 70.3265306260850735, -159.8334486142531716 70.3439704981944516, -159.8316650390625000 70.3500289916992188, -159.8338241287316350 70.3523314307644227, -159.8340445624457402 70.3572394476996550, -159.8402768163971643 70.3592125345782335, -159.8408036749281393 70.3597743730608585, -159.8427412245008554 70.3658942328559078, -159.8559954163540340 70.3673046616428337, -159.8574399592656903 70.3680114101975818, -159.8594078911675354 70.3742275661892478, -159.8731298973524986 70.3756877768721694, -159.8740395418763001 70.3761328241929078, -159.8760019938150947 70.3823313395182311, -159.8840013292100650 70.3848639594184107, -159.8830462077743562 70.3942143284155861, -159.8760768674642350 70.3998853325593785, -159.8674682617187273 70.4008015950520871, -159.8659581907011784 70.4149911227805489, -159.8649621430305388 70.4158872054498914, -159.8590632120768191 70.4177547878689296, -159.8578558860228895 70.4366316225165434, -159.8295288085937500 70.4367980957031250, -159.8220672607421875 70.4389114379882812, -159.8199475474874021 70.4422912030111803, -159.8177636040581433 70.4423689100477475, -159.8165606615060597 70.4461681874133916, -159.8007049560546875 70.4540328979492188, -159.8021746648878434 70.4589641147802865, -159.8007120768229186 70.4594272189670221, -159.7995133024142262 70.4856443815166642, -159.7972106933593750 70.4881134033203125, -159.7870358800602162 70.4919158076994705, -159.7625074917552297 70.4910717190257543, -159.7635979301503539 70.4829857905983914, -159.7673346625433908 70.4818027072482778, -159.7657053629557140 70.4760148790147696, -159.7645858044466536 70.4756604057893838, -159.7652893066406250 70.4704437255859375, -159.7572021484375000 70.4665451049804688, -159.7075958251953125 70.4642028808593750, -159.6923675537109375 70.4617843627929688, -159.6872558593750000 70.4582824707031250, -159.6896209716796875 70.4503250122070312, -159.6839752197265625 70.4502029418945312, -159.6750030517578125 70.4562606811523438, -159.6635437011718750 70.4574813842773438, -159.6598865782578116 70.4590326985690183, -159.6594219631618898 70.4590616861979271, -159.6593604843950516 70.4592558622272378, -159.6503601074218750 70.4630737304687500, -159.6388244628906250 70.4608840942382812, -159.5971527099609375 70.4662322998046875, -159.5955086495526700 70.4671439433644196, -159.5844353569878251 70.4673526340060903, -159.5823313395182197 70.4739980061848996, -159.5760019938150833 70.4760019938151174, -159.5751021846660080 70.4788441155548639, -159.5710144042968750 70.4780654907226562, -159.5581970214843750 70.4794158935546875, -159.5440216064453125 70.4832382202148438, -159.5307464599609375 70.4830627441406250, -159.5281896951975114 70.4839682649943455, -159.5260988023545963 70.4840291341145928, -159.5258564648467825 70.4847946174102304, -159.5204925537109375 70.4866943359375000, -159.5124390185941934 70.4921165467203537, -159.5094078911675126 70.4924391004774407, -159.5087165824826343 70.4946227546066950, -159.5008668425938367 70.4999077559943714, -159.4927412245008611 70.5007724338107664, -159.4918955017988651 70.5034438593235677, -159.4821319580078125 70.5067214965820312, -159.4774322509765625 70.5060043334960938, -159.4770050048828125 70.5021438598632812, -159.4625396728515625 70.5029602050781250, -159.4624272995735907 70.5029827078622020, -159.4617801242404482 70.4924431694878564, -159.4541666666666515 70.4916666666666742, -159.4538085937499829 70.4941236707899463, -159.4482577853732437 70.4996466742621664, -159.4458333333333258 70.5000000000000142, -159.4433781941731638 70.4996422661675410, -159.4409750242846258 70.4972325799776911, -159.4350585937500000 70.4799423217773438, -159.4385070800781250 70.4748229980468750, -159.4331970214843750 70.4740676879882812, -159.4317529759928789 70.4755065250199237, -159.4162750244140625 70.4909286499023438, -159.4089050292968750 70.4938888549804688, -159.3882141113281250 70.4955902099609375, -159.3704681396484375 70.5000305175781250, -159.3635892084686816 70.5031848648242487, -159.3423203776488606 70.5129377284845305, -159.3353933161941711 70.5128312206063299, -159.3255920410156250 70.5119552612304688, -159.3188162769413907 70.5125763383175865, -159.3005280816670393 70.5122951459407545, -159.3000793457031250 70.5073699951171875, -159.2950286865234375 70.5038604736328125, -159.2863616943359375 70.5022506713867188, -159.2825164794921875 70.5045700073242188, -159.2892608642578125 70.5107498168945312, -159.2872674417421024 70.5120912553543491, -159.2868805065615447 70.5120853059852806, -159.2780914306640625 70.5113296508789062, -159.2768683047203524 70.5119313621547974, -159.2657775878906250 70.5173873901367188, -159.3458251953125000 70.5168380737304688, -159.3700256347656250 70.5100250244140625, -159.4018402099609375 70.5066680908203125, -159.4381806232519523 70.5099643727143501, -159.4461669921875000 70.5106887817382812, -159.4582366943359375 70.5084533691406250, -159.4609832763671875 70.5092620849609375, -159.4587707519531250 70.5144500732421875, -159.4619353486277475 70.5139963259248645, -159.4668745771983538 70.5132881277107089, -159.4676079644097229 70.5160681830512175, -159.5231991149358919 70.5170841726592386, -159.5199432373046875 70.5179977416992188, -159.5254364013671875 70.5196151733398438, -159.5408172607421875 70.5171432495117188, -159.5483398437500000 70.5124664306640625, -159.5514221191406250 70.5105438232421875, -159.5461730957031250 70.5014877319335938, -159.5491685309393119 70.5010150148103776, -159.5509406195746465 70.5077317979600764, -159.6070810953775947 70.5089131673177150, -159.6075330474425584 70.5072000393323037, -159.6207733154296875 70.4990005493164062, -159.6168800234653986 70.4893244337132074, -159.6173463609483463 70.4842054578993213, -159.6237477620442462 70.4827534993489735, -159.6259357028537238 70.4910471598307424, -159.6570810953776061 70.4922465006510492, -159.6590514718765235 70.4847779027420387, -159.6615362323906027 70.4838066878863145, -159.6724771629287432 70.4828630827068281, -159.6719970703125000 70.4854812622070312, -159.6745450309478542 70.4857754494428121, -159.6759396023220461 70.4910614013671903, -159.7237477620442689 70.4922465006510492, -159.7253417315310173 70.4862045417668384, -159.7328238520922241 70.4855689247288808, -159.7342030843098826 70.4907969156901117, -159.7407969156900833 70.4925364176432367, -159.7425364176432083 70.4991302490234517, -159.7491302490234375 70.5008697509765767, -159.7508697509765625 70.5074635823567775, -159.7574635823567633 70.5092030843099025, -159.7592646280924384 70.5160295274522610, -159.7782664776273123 70.5170760424453391, -159.7827955195436402 70.5187951153213106, -159.7842544555663835 70.5243245442708400, -159.7990788777669309 70.5256754557291714, -159.8009211222330634 70.5326578776041799, -159.8157455444335824 70.5340087890625114, -159.8175364176432254 70.5407969156901089, -159.8243630303276746 70.5425977918836935, -159.8256369696723027 70.5657355414496692, -159.8324635823567519 70.5675364176432396, -159.8342030843098769 70.5741302490234403, -159.8409913804795792 70.5759209526909785, -159.8411779296381781 70.5779686376555020, -159.8393707275390625 70.5813598632812500, -159.8328552246093750 70.5779876708984375, -159.8304919215405562 70.5779275869221152, -159.8169737287505825 70.5775839040545634, -159.8134372287326244 70.5744327121310846, -159.8019561767578125 70.5755672878689353, -159.7999764885421428 70.5774046692061603, -159.7949981689453125 70.5782928466796875, -159.7971198078381860 70.5800560017596155, -159.7972106933593750 70.5801315307617188, -159.8146667480468750 70.5809555053710938, -159.8329772949218750 70.5873641967773438, -159.8370056152343750 70.5905838012695312, -159.8418700159244565 70.5910648213934877, -159.8424835205078125 70.5911254882812500, -159.8483581542968750 70.5848693847656250, -159.8493930790312447 70.5851997183000321, -159.8509355333116275 70.5910461425781364, -159.8794194333511882 70.5921697072769518, -159.8828878828906568 70.5941448846655248, -159.8842030843098883 70.5991302490234460, -159.8893107971621816 70.6004777018123093, -159.8913256023811016 70.6028738136264167, -159.8925364176432140 70.6074635823567860, -159.8991302490234148 70.6092030843099110, -159.9009355333116105 70.6160461425781278, -159.9219507129646445 70.6168750990751306, -159.9327100352054458 70.6184707194074406, -159.9342688666449703 70.6243794759114678, -159.9657311333550354 70.6256205240885464, -159.9676079644097229 70.6327348497178917, -159.9840852477242379 70.6330359902493825, -159.9738616943359375 70.6378173828125000, -159.9794616699218750 70.6381378173828125, -159.9940323294213158 70.6332177841417348, -160.0320810953776061 70.6339131673177150, -160.0335787657286346 70.6282366955019540, -160.0396423339843750 70.6271743774414062, -160.0442704660728168 70.6250860739380926, -160.0574020385742244 70.6243628607855953, -160.0589563275058538 70.6184722387249764, -160.0618695057885930 70.6171603438691733, -160.0824020385742017 70.6160295274522696, -160.0842030843098769 70.6092030843099110, -160.0909913804796076 70.6074123806423728, -160.0914994874014496 70.6018349026595473, -160.0976028021570130 70.5913465311878667, -160.0987477620442689 70.5910868326823078, -160.1008811102973084 70.5991733127170278, -160.1020868414236134 70.5994468434513607, -160.1009826660156250 70.6015701293945312, -160.1173248291015625 70.6020889282226562, -160.1188507080078125 70.6040878295898438, -160.1264495849609375 70.6017608642578125, -160.1292115778481957 70.6000118132574102, -160.1337710910373175 70.6012145996093778, -160.1324912177191777 70.6076914469401089, -160.1092646280924328 70.6089704725477532, -160.1074635823567576 70.6157969156901117, -160.1008697509765568 70.6175364176432367, -160.0990687052408816 70.6243628607855953, -160.0759312947591013 70.6256371392144189, -160.0740687052408759 70.6326961941189353, -160.0509312947590956 70.6339704725477588, -160.0490788777669309 70.6409912109375142, -160.0342544555664119 70.6423421223958456, -160.0324122111002509 70.6493245442708400, -160.0175877888997320 70.6506754557291714, -160.0157455444335994 70.6576578776041799, -160.0009211222330521 70.6590087890625114, -159.9990788777669195 70.6659912109375057, -159.9842544555664006 70.6673421223958371, -159.9824020385742074 70.6743628607856067, -159.9592646280924271 70.6756371392144160, -159.9574122111002623 70.6826578776041714, -159.9425877888997434 70.6840087890625171, -159.9407311333550297 70.6910461425781307, -159.9249999999999829 70.6916666666666771, -159.9242233276366960 70.6992801242404596, -159.9010426839192576 70.7007035997178974, -159.8989520602756045 70.7076473659939353, -159.8677146063910470 70.7090193006727503, -159.8656239827473939 70.7159630669487882, -159.8427093505859204 70.7173702663845631, -159.8406239827473883 70.7242964002821282, -159.8177093505859148 70.7257035997178889, -159.8156365288628251 70.7325883653428917, -159.8010301378038207 70.7340783013237910, -159.7989573160807311 70.7409630669487939, -159.7760426839192576 70.7423702663845546, -159.7739573160807254 70.7492964002821338, -159.7510426839192519 70.7507035997178946, -159.7489698621961622 70.7575883653428974, -159.7343634711371294 70.7590783013237967, -159.7323031955295107 70.7659216986762232, -159.7176968044704779 70.7674116346571225, -159.7156365288628308 70.7742550320095631, -159.7010301378037980 70.7757449679904624, -159.6989698621961793 70.7825883653428889, -159.6843634711371465 70.7840783013238024, -159.6822906494140568 70.7909630669487910, -159.6593760172525833 70.7923702663845660, -159.6572906494140511 70.7992964002821310, -159.6343760172526061 70.8007035997178917, -159.6322825113932140 70.8076568603515710, -159.5927174886067519 70.8090098063151174, -159.5906158447265568 70.8159901936849110, -159.5510508219400947 70.8173431396484432, -159.5489473130967895 70.8243299696180628, -159.5010526869031935 70.8256700303819571, -159.4989473130967781 70.8326633029514028, -159.4510526869032105 70.8340033637152828, -159.4489459567599567 70.8410007052951443, -159.3927207099066834 70.8423326280382071, -159.3906126234266480 70.8493340386284842, -159.3343873765733463 70.8506659613715328, -159.3318852742512775 70.8589762369791742, -159.3176983303493728 70.8575931125217124, -159.3154879936211330 70.8502518204674772, -159.2643851511340642 70.8586238046427752, -159.2826114230685732 70.8590311686198078, -159.2828850640190694 70.8659349229600792, -159.2507709079318374 70.8672078450520928, -159.2505177815754962 70.8608188205295306, -159.2507302768226793 70.8602125184035003, -159.2433166503906250 70.8600463867187500, -159.2297668457031250 70.8621978759765625, -159.2165985107421875 70.8647918701171875, -159.1899871826171875 70.8625030517578125, -159.1835174560546875 70.8636016845703125, -159.1805114746093750 70.8597869873046875, -159.1834506397768507 70.8585718778956135, -159.1677098592122377 70.8576314290364735, -159.1657024807400092 70.8509643554687614, -159.1592975192599795 70.8490356445312557, -159.1573031955294937 70.8424116346571253, -159.1426968044704608 70.8409216986762260, -159.1407023111979129 70.8342976888020956, -159.1342976888020644 70.8323689778645900, -159.1323689778645587 70.8259643554687557, -159.1243570963541458 70.8235521104600849, -159.1260080973307254 70.8173190646701443, -159.1511802704342813 70.8170838501441295, -159.1338653564453125 70.8157043457031250, -159.1170806884765625 70.8178558349609375, -159.0931701660156250 70.8155059814453125, -159.0893249511718750 70.8165435791015625, -159.0794067382812500 70.8101577758789062, -159.0744323730468750 70.7974700927734375, -159.0654144287109375 70.7962646484375000, -159.0437927246093750 70.7989578247070312, -159.0235443115234375 70.7970352172851562, -158.9989624771594094 70.7926359269649481, -159.0011247422959855 70.7992987738715414, -159.0673472086588447 70.8007405598958428, -159.0657158745659672 70.8173472086588589, -159.0261203342013800 70.8159525553385549, -159.0238800048827841 70.8090484619140739, -158.9844533284505133 70.8076182047526146, -158.9822079128689154 70.8006988525390710, -158.9094587537977361 70.7993011474609517, -158.9067603217230840 70.7909861246744896, -158.5761298285590044 70.7923509385850735, -158.5734269883897412 70.8006805419921932, -158.5427836100260208 70.7992757161458428, -158.5400936550564097 70.7909861246744896, -158.5094502766926894 70.7923909505208400, -158.5072163899739337 70.7992750379774378, -158.4761169433593579 70.8007249620225849, -158.4734269883897468 70.8090138753255331, -158.4508229573567633 70.8075734456380275, -158.4499999999999886 70.8000000000000114, -158.3842546251084968 70.8005764431423756, -158.3824337429470575 70.8076721191406335, -158.3675662570529425 70.8089945475260549, -158.3658157348632756 70.8158159044053974, -158.3591842651367188 70.8175174289279568, -158.3571183946397412 70.8255672878689353, -158.3508619520399350 70.8241923014323049, -158.3490907457139656 70.8172902425130388, -158.3259092542860174 70.8160430908203296, -158.3237850613064097 70.8077660454644189, -158.3175286187066035 70.8091410319010492, -158.3157453748914918 70.8160902235243128, -158.1842546251085082 70.8172431098090414, -158.1824125501844662 70.8244215223524378, -158.0759207831488595 70.8255784776475821, -158.0740805731879277 70.8327494303385521, -158.0009194268120609 70.8339172363281335, -157.9990829467773494 70.8410739474826556, -157.9509170532226392 70.8422593858507099, -157.9490829467773381 70.8494072808159814, -157.9009170532226562 70.8505927191840357, -157.8990829467773267 70.8577406141493213, -157.8509170532226449 70.8589260525173756, -157.8490843031141537 70.8660681830512260, -157.8092490302191777 70.8672651502821225, -157.8074176364474681 70.8744015163845660, -157.7675823635525205 70.8755984836154624, -157.7657533433702213 70.8827256944444599, -157.7342466566297503 70.8839409722222342, -157.7324200100368898 70.8910590277777857, -157.7009133232964189 70.8922743055555742, -157.6990907457139599 70.8993764241536581, -157.6759092542860117 70.9006235758463674, -157.6740907457139542 70.9077097574869981, -157.6509092542860060 70.9089569091796932, -157.6490866767035470 70.9160590277777914, -157.6175799899631045 70.9172743055555657, -157.6157533433702156 70.9243923611111171, -157.5842466566297730 70.9256076388889056, -157.5824200100368842 70.9327256944444571, -157.5509133232964416 70.9339409722222314, -157.5490907457139542 70.9410430908203296, -157.5259092542860060 70.9422902425130388, -157.5240866767035470 70.9493923611111228, -157.4925799899631045 70.9506076388888971, -157.4907574123806171 70.9577097574869953, -157.4675759209526689 70.9589569091797046, -157.4657574123806398 70.9660430908203210, -157.4425759209526916 70.9672902425130303, -157.4407670762803662 70.9743387858073049, -157.4258995903862797 70.9756612141927263, -157.4240907457139542 70.9827097574869867, -157.4009092542860060 70.9839569091796960, -157.3990907457139770 70.9910430908203267, -157.3759092542860003 70.9922902425130360, -157.3740907457139713 70.9993764241536525, -157.3509092542860230 71.0006235758463617, -157.3490907457139656 71.0077097574869924, -157.3259092542860174 71.0089569091797017, -157.3240907457139599 71.0160430908203182, -157.3009092542860117 71.0172902425130275, -157.2991004096137146 71.0243387858073021, -157.2842329237195997 71.0256612141927235, -157.2824337429470347 71.0326721191406421, -157.2675662570529482 71.0339945475260492, -157.2657670762803548 71.0410054524739678, -157.2508995903862683 71.0423278808593892, -157.2491004096137033 71.0493387858073078, -157.2342329237196168 71.0506612141927150, -157.2324824015299214 71.0574825710720575, -157.2258509318033646 71.0591840955946310, -157.2241004096136976 71.0660054524739735, -157.2092329237196111 71.0673278808593949, -157.2074337429470461 71.0743387858072992, -157.1925662570529312 71.0756612141927206, -157.1908157348632642 71.0824825710720631, -157.1841842651367074 71.0841840955946367, -157.1824337429470404 71.0910054524739650, -157.1675662570529255 71.0923278808593864, -157.1657670762803605 71.0993387858073049, -157.1508995903862740 71.1006612141927263, -157.1491492377386976 71.1074825710720688, -157.1425174289279312 71.1091840955946282, -157.1407670762803548 71.1160054524739706, -157.1258995903862683 71.1173278808593921, -157.1241004096137033 71.1243387858073106, -157.1092329237196168 71.1256612141927178, -157.1074825710720404 71.1324825710720603, -157.1008507622612740 71.1341840955946338, -157.0991004096136976 71.1410054524739763, -157.0842329237196111 71.1423278808593835, -157.0825581359355567 71.1488535080694930, -157.0825805664062500 71.1488800048828125, -157.0825479265652689 71.1488932877628741, -157.0824198404947651 71.1493923611111256, -157.0812065614184974 71.1494391602868319, -157.0751190185546875 71.1519165039062500, -157.0664449542050818 71.1521733668596568, -157.0658952501084968 71.1575619167751938, -157.0510301378037923 71.1590783013238024, -157.0489698621961736 71.1659216986762289, -157.0343634711371408 71.1674116346571282, -157.0323689778645644 71.1740358140733633, -157.0259643554687443 71.1759641859266594, -157.0239698621961679 71.1825883653428946, -157.0093634711371351 71.1840783013237939, -157.0073031955294880 71.1909216986762345, -156.9926968044704836 71.1924116346571338, -156.9906365288628365 71.1992550320095603, -156.9760301378038037 71.2007449679904596, -156.9739698621961566 71.2075883653429003, -156.9593634711371237 71.2090783013237996, -156.9573031955295050 71.2159216986762260, -156.9426968044704722 71.2174116346571395, -156.9406365288628251 71.2242550320095660, -156.9260301378037923 71.2257449679904653, -156.9239698621961736 71.2325883653428917, -156.9093634711371408 71.2340783013238052, -156.9073689778645644 71.2407024807400262, -156.9009643554687443 71.2426308525933365, -156.8989573160807254 71.2492964002821338, -156.8760426839192519 71.2507035997178946, -156.8739698621961622 71.2575883653428974, -156.8593634711371294 71.2590783013237967, -156.8573031955295107 71.2659216986762232, -156.8426968044704779 71.2674116346571367, -156.8406365288628308 71.2742550320095631, -156.8260301378037980 71.2757449679904624, -156.8239698621961509 71.2825883653429031, -156.8093634711371465 71.2840783013238024, -156.8072906494140568 71.2909630669488052, -156.7843760172525833 71.2923702663845660, -156.7823689778645644 71.2990358140733633, -156.7791666666666401 71.3000000000000114, -156.7594533284504905 71.3007149590386433, -156.7572224934895644 71.3075902303060047, -156.7344441731770530 71.3090764363606979, -156.7322224934895587 71.3159235636393447, -156.7094441731770758 71.3174097696940237, -156.7072875976562329 71.3240558200412522, -156.7009792751735802 71.3256805419921989, -156.6991770426432140 71.3090930514865562, -156.6764784071180259 71.3076527913411553, -156.6743194580078011 71.3150936550564438, -156.6825568305121408 71.3177668253580919, -156.6840138753254905 71.3324734157986313, -156.6594441731770644 71.3340764363606894, -156.6572214762369697 71.3409267849392563, -156.6343126085069173 71.3423472086588646, -156.6325103759765511 71.3257597181532219, -156.6098117404513630 71.3243194580078210, -156.6076527913411383 71.3317603217231095, -156.6158901638454779 71.3344334920247576, -156.6173472086588276 71.3491488986545335, -156.5926459418402601 71.3506805419922046, -156.5908440483940751 71.3340952555338674, -156.5674892849392279 71.3325714111328324, -156.5658803304036155 71.3177636040581717, -156.5593543158636862 71.3156458536784044, -156.5572363959418283 71.3091195000542655, -156.5427636040581376 71.3075471666124230, -156.5406456841362797 71.3010208129882983, -156.5343543158637090 71.2989791870117386, -156.5322133382161383 71.2923816257053033, -156.4927866617838390 71.2909517076280537, -156.4905463324652715 71.2840477837456774, -156.4515730116102361 71.2826527913411638, -156.4489017062716698 71.2908845689561730, -156.4349063449435562 71.2923472086588674, -156.4323123508029312 71.2843541463216326, -156.4260209825303605 71.2823125203450729, -156.4239030626084741 71.2757861667209340, -156.4094302707248119 71.2742138332790915, -156.4072163899739394 71.2673916286892535, -156.3761169433593636 71.2659417046441064, -156.3738739013671761 71.2590299818251083, -156.2932396782768762 71.2576527913411581, -156.2905463324652544 71.2659522162543482, -156.2515730116102191 71.2673472086588617, -156.2488745795355669 71.2590320163303090, -156.1761254204643876 71.2576346503363851, -156.1738891601562216 71.2507431030273608, -156.1511108398437386 71.2492568969726676, -156.1488745795355726 71.2423653496636433, -156.0760331895615991 71.2409662882487140, -156.0743194580077784 71.2343126085069542, -156.0908562554253365 71.2325159708659044, -156.0923472086588220 71.2181450737847399, -156.0849063449435619 71.2159861246744867, -156.0822994656032847 71.2240188598632926, -156.0765730116102077 71.2256805419922046, -156.0738891601562273 71.2174097696940294, -156.0511108398437159 71.2159235636393362, -156.0489030626084741 71.2091195000542712, -156.0341447618272355 71.2075161404080035, -156.0326527913411212 71.1932396782769246, -156.0406456841362512 71.1906458536784044, -156.0426876491970347 71.1843541463216241, -156.0489790174696054 71.1823125203450644, -156.0511199951171761 71.1757149590386433, -156.0910990397135265 71.1742650349935104, -156.0909583197699533 71.1673704359266566, -156.0099063449435448 71.1659861246744896, -156.0072363959418169 71.1742138332790972, -155.9927636040581262 71.1757861667209397, -155.9905497233072538 71.1826083713107778, -155.9591583251952898 71.1840718587239678, -155.9575469970702954 71.1989030626085224, -155.9510209825303662 71.2010208129882898, -155.9488830566405966 71.2076090494791742, -155.9182396782768762 71.2090138753255388, -155.9155697292751483 71.2007861667209312, -155.9007619222005019 71.1991773817274520, -155.8992340087890511 71.1757614135742358, -155.8759792751735915 71.1743194580078296, -155.8749999999999716 71.1833333333333513, -155.8427215576171534 71.1839962429470603, -155.8402187771267222 71.1923094007704123, -155.7343905978732437 71.1910118950737996, -155.7322767469617872 71.1839904785156392, -155.6424492730034501 71.1826722886827383, -155.6416666666666515 71.1750000000000114, -155.5843909369574476 71.1743460761176294, -155.5823031955295050 71.1674116346571282, -155.5673645019531079 71.1658877902560931, -155.5659806993272412 71.1343814425998460, -155.5592975192599852 71.1323689778646013, -155.5573691474066607 71.1259643554687671, -155.5509641859266310 71.1240356445312614, -155.5490358140733349 71.1176310221354271, -155.5426308525933052 71.1157023111979356, -155.5406365288628479 71.1090783013237910, -155.5260301378038150 71.1075883653428917, -155.5240358140733292 71.1009643554687614, -155.5174116346570941 71.0989698621961992, -155.5159216986762090 71.0843634711371664, -155.5076905992295906 71.0818854437934107, -155.5092554728190066 71.0759769015842124, -155.5157024807400035 71.0740356445312642, -155.5176968044704608 71.0674116346571338, -155.5323031955294937 71.0659216986762345, -155.5343760172525833 71.0590369330512317, -155.5572906494140568 71.0576297336154710, -155.5593760172525890 71.0507035997178917, -155.5822906494140625 71.0492964002821310, -155.5842975192599624 71.0426310221354242, -155.5907024807399921 71.0407023111979328, -155.5931147257486771 71.0326904296875057, -155.6073016696506102 71.0340735541449817, -155.6097813924153570 71.0423095703125114, -155.6322901407877453 71.0409647623698106, -155.6343634711371351 71.0340783013238024, -155.6492285834418396 71.0325619167751796, -155.6507809109157847 71.0173444959852560, -155.6989473130967667 71.0159966362847399, -155.7009641859266367 71.0092976888020928, -155.7075883653428718 71.0073031955295306, -155.7090783013237854 70.9926968044704978, -155.7157024807400205 70.9907023111979356, -155.7181147257486771 70.9826904296875085, -155.7489517211913892 70.9840186225043510, -155.7514480590820085 70.9923095703125142, -155.7656350029839416 70.9909264458550524, -155.7677093505859318 70.9840369330512289, -155.7906239827473769 70.9826297336154681, -155.7926308525933052 70.9759643554687614, -155.7990358140733349 70.9740356445312557, -155.8010301378038207 70.9674116346571253, -155.8156365288628251 70.9659216986762260, -155.8181147257486998 70.9576904296875171, -155.8240230984157790 70.9592556423611285, -155.8260301378037980 70.9659216986762260, -155.8406365288628308 70.9674116346571253, -155.8431147257486771 70.9756429036458485, -155.8656234741210938 70.9742980957031335, -155.8677247789170792 70.9673190646701499, -155.9989418877495666 70.9660142686632014, -156.0010301378038093 70.9590783013237996, -156.0083333333333258 70.9583333333333428, -156.0164635552300183 70.9575039333767421, -156.0159186469183794 70.9507215711805657, -155.9927093505859261 70.9492964002821367, -155.9907023111978788 70.9426310221354299, -155.9842976888020587 70.9407023111979242, -155.9823689778645530 70.9342976888020900, -155.9757449679904369 70.9323031955295278, -155.9742550320095233 70.9176968044704950, -155.9668982611761976 70.9154819064670221, -155.9674150254991218 70.9090528700086935, -155.9902187771266995 70.9076904296875057, -155.9927147759331376 70.9159806993272639, -156.0162753585201187 70.9170154805646149, -156.0185852050781250 70.9108352661132812, -156.0458526611328125 70.9115295410156250, -156.0530090332031250 70.9129562377929688, -156.0829252815353243 70.9123336153657533, -156.0833022233963447 70.9086383155009941, -156.0817565917968750 70.9087066650390625, -156.0807036597872468 70.9083066585004786, -156.0177480061848598 70.9076680501302263, -156.0168202600663960 70.9046636562145096, -156.0060424804687500 70.9009246826171875, -156.0070558843460731 70.9004967707923157, -155.9510779486761862 70.8993245442708400, -155.9490169949001483 70.8926496717665060, -155.9426496717664747 70.8906836615668539, -155.9406836615668226 70.8843163384331660, -155.9343163384331490 70.8823503282335139, -155.9323503282334968 70.8759830050998403, -155.9243445502386862 70.8735110812717153, -155.9260050455728788 70.8674302842882042, -155.9423221164279312 70.8657301161024407, -155.9406873914930429 70.8590555826823021, -155.9160112169053605 70.8575086805555685, -155.9176391601562273 70.8507225884331717, -155.9423221164279312 70.8491753472222285, -155.9407626681857266 70.8426740858290032, -155.9326778835720120 70.8401777479383838, -155.9343634711371180 70.8340053982204978, -155.9749999999999659 70.8333333333333428, -155.9743299696180259 70.8093861897786496, -155.9676310221353788 70.8073689778645985, -155.9657023111979015 70.8009643554687642, -155.9592976888020530 70.7990356445312585, -155.9573031955294766 70.7924116346571282, -155.9410237630208087 70.7907511393229214, -155.9425591362847001 70.7843190511067775, -155.9490356445312216 70.7823689778645928, -155.9509643554687273 70.7759643554687585, -155.9589762369791401 70.7735521104600735, -155.9575581868489280 70.7590057373047046, -155.9291666666666458 70.7583333333333400, -155.9249999999999829 70.7583333333333400, -155.8927215576171648 70.7589962429470631, -155.8907024807400035 70.7657023111979271, -155.8826905992295906 70.7681145562066121, -155.8842554728190066 70.7740230984158103, -155.8907024807400035 70.7759643554687585, -155.8926968044704893 70.7825883653428889, -155.9073031955294937 70.7840783013238024, -155.9092975192599795 70.7907023111979328, -155.9157024807400092 70.7926310221354242, -155.9176308525933052 70.7990356445312585, -155.9256427341037181 70.8014478895399435, -155.9242272271050354 70.8159684922960224, -155.8927146063910527 70.8173526340060846, -155.8907024807400035 70.8240356445312642, -155.8842975192599738 70.8259643554687557, -155.8822733561197822 70.8326873779297017, -155.6833333333333371 70.8333333333333428, -155.6824362860785413 70.8245388454861171, -155.6668665568033703 70.8257900661892421, -155.6674165513780395 70.8326341417100878, -155.6833333333333371 70.8333333333333428, -155.6840086195203980 70.8407457139757071, -155.6922465006510379 70.8429189046224081, -155.6905086941189040 70.8505798339843835, -155.6592692057291458 70.8493804931640767, -155.6574635823567689 70.8425364176432453, -155.6508697509765398 70.8407969156901203, -155.6491302490234148 70.8342030843099053, -155.6425364176432140 70.8324635823567803, -155.6407455444335710 70.8256754557291828, -155.6259211222330521 70.8243245442708513, -155.6240687052408873 70.8173038058810818, -155.6124999999999829 70.8166666666666771, -155.6083333333333201 70.8166666666666771, -155.6075883653428775 70.8239698621961935, -155.5993572658962592 70.8264478895399350, -155.6009221394856752 70.8323564317491474, -155.6075883653428775 70.8343634711371664, -155.6090783013237626 70.8489698621961850, -155.6173094007703810 70.8514478895399407, -155.6157631768120666 70.8673095703125142, -155.6093193901909615 70.8657748752170278, -155.6073031955295107 70.8590783013237910, -155.5924380832248062 70.8575619167751825, -155.5907511393229186 70.8410237630208428, -155.5677101982964246 70.8423672146267478, -155.5656187269422617 70.8493140326606010, -155.5343812730577042 70.8506859673394160, -155.5318852742512945 70.8589762369791742, -155.4924468994140625 70.8576483832465414, -155.4907511393228958 70.8410237630208428, -155.4843190511067519 70.8425591362847342, -155.4823031955295107 70.8492550320095660, -155.4749999999999943 70.8500000000000085, -155.4757865058051038 70.8575469970703296, -155.4923221164279425 70.8592698838975821, -155.4907569037543169 70.8656609429253592, -155.4826778835720233 70.8681555853949732, -155.4840315077039747 70.8989288330078296, -155.4909079657660413 70.9010521782769274, -155.4924253675672503 70.9156144883897639, -155.4990169949001597 70.9176496717665117, -155.5009830050998119 70.9240169949001853, -155.5089887830945941 70.9264889187283103, -155.5075951470269047 70.9493130154079950, -155.4510771009657049 70.9506788465711935, -155.4489476521809763 70.9575744628906335, -155.4343856811523210 70.9590922037760521, -155.4322681003146442 70.9659498426649407, -155.4093985663519959 70.9673834906684107, -155.4072809855143191 70.9742411295572992, -155.3927190144856638 70.9757588704427178, -155.3906836615668396 70.9823503282335224, -155.3843163384331376 70.9843163384331746, -155.3823503282334855 70.9906836615668482, -155.3759830050998119 70.9926496717665003, -155.3739220513237740 70.9993245442708485, -155.3094112820095347 71.0006754557291799, -155.3072625054253422 71.0076344807942803, -155.2760708279079722 71.0090321858724138, -155.2735109117295735 71.0173221164279624, -155.2593870374891480 71.0159125434027914, -155.2573503282334855 71.0093163384331660, -155.2509830050998119 71.0073503282335139, -155.2489347669813355 71.0007168240017421, -155.2260652330186588 70.9992831759982721, -155.2240169949001540 70.9926496717665003, -155.2176496717664804 70.9906836615668482, -155.2155887179904425 70.9840087890625142, -155.1833333333333087 70.9833333333333485, -155.1674763997395701 70.9840582953559220, -155.1658942328558908 70.9989254421658131, -155.1576653374565922 71.0015306260850849, -155.1592946370442689 71.0073184543185931, -155.1656646728515341 71.0093353271484489, -155.1676686604817519 71.0156646728515710, -155.1739980061848883 71.0176686604817888, -155.1760745578341982 71.0242275661892535, -155.1905921088324476 71.0257724338107721, -155.1927547878689211 71.0326032850477560, -155.2155785454643819 71.0340633816189353, -155.2177412245008554 71.0408942328559192, -155.2322587754991048 71.0424391004774378, -155.2344078911675069 71.0492275661892450, -155.2489254421657847 71.0507724338107778, -155.2510881212022298 71.0576032850477617, -155.2756679958767165 71.0591756184895900, -155.2742050170898267 71.0742513020833400, -155.2510881212022298 71.0757300482856067, -155.2489118787977418 71.0826032850477532, -155.2260881212022525 71.0840633816189325, -155.2239062839084056 71.0909545898437614, -155.1927603827582232 71.0923787434895900, -155.1905785454644047 71.0992699517144189, -155.1677547878689154 71.1007300482855982, -155.1651360405815865 71.1090013292100878, -155.1510759141710025 71.1075653076172074, -155.1489980061848826 71.1010019938151174, -155.1426686604817462 71.0989980061849138, -155.1406646728515568 71.0926686604817775, -155.1343353271484204 71.0906646728515739, -155.1323313395182026 71.0843353271484517, -155.1260019938150947 71.0823313395182481, -155.1239980061848769 71.0760019938151117, -155.1176686604817689 71.0739980061849081, -155.1151360405815751 71.0659986707899378, -155.0758110894097115 71.0673784044053889, -155.0742275661892222 71.0822587754991417, -155.0659986707899236 71.0848639594184135, -155.0673950195312329 71.1072448730468949, -155.0739980061848939 71.1093353271484574, -155.0760938856336679 71.1159545898437671, -155.1090013292100593 71.1174591064453239, -155.1073387993706376 71.1242835150824817, -155.0743320041232494 71.1257924397786638, -155.0759897867838504 71.1325971815321338, -155.1006679958767336 71.1341756184895928, -155.0990675184461622 71.1506679958767450, -155.0926927354600480 71.1490743001302235, -155.0905785454643819 71.1423967149522696, -155.0674740261501654 71.1409186469184220, -155.0657087537977361 71.1243320041232749, -155.0427558051215158 71.1257266574435931, -155.0401360405815865 71.1340013292100792, -155.0343482123480783 71.1323720296224167, -155.0323313395182083 71.1260019938151231, -155.0257724338107437 71.1239254421658131, -155.0242275661892108 71.1094078911675496, -155.0159986707899122 71.1068027072482778, -155.0171457176541310 71.1027279620002872, -155.0165405273437500 71.1027603149414062, -155.0000000000000000 71.0920257568359375, -154.9999404120397344 71.0919871074228240, -154.9991960313584798 71.0992746988932396, -154.9677605523003194 71.1007120768229299, -154.9651360405815694 71.1090013292100878, -154.9510759141709855 71.1075653076172074, -154.9488949245876483 71.1006768120659842, -154.8749999999999716 71.1000000000000085, -154.8743920220268819 71.0758487277560960, -154.8678812662760151 71.0745622422960253, -154.8654144287109204 71.0839131673177178, -154.8598403036083937 71.0826488170852144, -154.8614501953125000 71.0844039916992188, -154.8480377197265625 71.0860290527343750, -154.8433380126953125 71.0765914916992188, -154.8438446579781953 71.0756470768746738, -154.8427320692274236 71.0757168240017450, -154.8406144883897468 71.0825744628906335, -154.8260521782768819 71.0840922037760521, -154.8235110812716755 71.0923221164279653, -154.8093872070312216 71.0909125434027942, -154.8072625054253422 71.0840321858724025, -154.7760708279079722 71.0826344807942831, -154.7739345974392222 71.0757168240017450, -154.7510654025607550 71.0742831759982749, -154.7489345974392165 71.0673834906684192, -154.7260654025607494 71.0659498426649492, -154.7239478217230726 71.0590922037760606, -154.7093855116102361 71.0575744628906421, -154.7072811550563927 71.0507588704427206, -154.6927188449435562 71.0492411295573021, -154.6906012641058794 71.0423834906684135, -154.6677320692274122 71.0409498426649435, -154.6656012641058737 71.0340501573350878, -154.6427320692274066 71.0326165093316035, -154.6406012641058965 71.0257168240017478, -154.6177320692274009 71.0242831759982778, -154.6156144883897241 71.0174255371093892, -154.6010521782768876 71.0159077962239706, -154.5989478217230726 71.0090922037760492, -154.5843855116102361 71.0075744628906307, -154.5823503282334741 71.0009830050998403, -154.5743445502386919 70.9985110812717153, -154.5759416368272241 70.9926625569661525, -154.5823503282334741 70.9906836615668482, -154.5843163384331547 70.9843163384331746, -154.5906836615668283 70.9823503282335224, -154.5926496717664804 70.9759830050998346, -154.5990169949001540 70.9740169949001825, -154.6009830050998062 70.9676496717665088, -154.6076344807942462 70.9655958387586878, -154.6090321858723655 70.9344041612413321, -154.6173221164279425 70.9318444146050524, -154.6157250298393819 70.9259958902994896, -154.6076778835720233 70.9235110812717124, -154.6091244167751597 70.9090189615885578, -154.6655897352430316 70.9076544867621692, -154.6677188449435505 70.9007588704427150, -154.6822811550564154 70.8992411295573106, -154.6844129774305259 70.8923363579644246, -154.7742631700303662 70.8909915500217096, -154.7746493869357494 70.8845116509331774, -154.7676496717664634 70.8823503282335139, -154.7651777479383384 70.8743445502387317, -154.7427324083116105 70.8757151285807367, -154.7401777479383611 70.8839887830946225, -154.7174041748046704 70.8825981987847342, -154.7158420138888744 70.8576778835720660, -154.7091016981336509 70.8592946370442860, -154.7075412326388744 70.8742662217882042, -154.6843987358940922 70.8757168240017421, -154.6822679307725537 70.8826165093316121, -154.6593987358940865 70.8840501573350821, -154.6568444146050183 70.8923221164279624, -154.6427205403645644 70.8909125434027914, -154.6406012641058965 70.8840501573350821, -154.6174587673610858 70.8825995551215442, -154.6159077962239508 70.8677188449435818, -154.6093163384331319 70.8656836615668482, -154.6072811550563983 70.8590922037760578, -154.5924530029296591 70.8575469970703296, -154.5908803304036212 70.8424530029297017, -154.5743445502386919 70.8407301161024350, -154.5759680853949476 70.8341010199652885, -154.5923221164279369 70.8323967827691092, -154.5906985812716812 70.8257676866319628, -154.5833333333333144 70.8250000000000171, -154.5791666666666515 70.8250000000000171, -154.5175879584418226 70.8255771213107721, -154.5154517279730726 70.8339006212022753, -154.5091952853732380 70.8325256347656307, -154.5074127197265454 70.8255794949001825, -154.4583333333333428 70.8250000000000171, -154.4574178059895928 70.8339762369791828, -154.3507829454210025 70.8326751708984546, -154.3490844726562443 70.8160237630208513, -154.3424199422200331 70.8176147460937671, -154.3407511393229186 70.8339762369791828, -154.3343190511067746 70.8324408637152914, -154.3322806464301209 70.8256700303819571, -154.2843860202365533 70.8243299696180628, -154.2822853936089302 70.8173526340060846, -154.2510479397243728 70.8159806993272696, -154.2489698621961622 70.8090783013237939, -154.2343634711371578 70.8075883653428946, -154.2323691474066720 70.8009643554687642, -154.2243572658962592 70.7985521104600792, -154.2259221394856752 70.7926435682508810, -154.2339760674370552 70.7902187771267535, -154.2325585259331433 70.7756778293185818, -154.1843860202365306 70.7743299696180657, -154.1818852742513002 70.7660237630208400, -154.1676983303493955 70.7674068874783160, -154.1656187269422560 70.7743140326605982, -154.1343812730577270 70.7756859673394274, -154.1322853936089246 70.7826473659939381, -154.1010479397243955 70.7840193006727532, -154.0989573160807140 70.7909630669487910, -154.0760426839192689 70.7923702663845660, -154.0739573160807083 70.7992964002821310, -154.0510426839192633 70.8007035997178917, -154.0489698621961736 70.8075883653428946, -154.0343634711371408 70.8090783013237939, -154.0322853936089302 70.8159806993272696, -154.0010479397243728 70.8173526340060846, -153.9990358140733520 70.8240356445312642, -153.9926308525933223 70.8259643554687557, -153.9907024807399978 70.8323689778645900, -153.9842975192599681 70.8342976888020956, -153.9823031955295107 70.8409216986762260, -153.9676968044704779 70.8424116346571253, -153.9657024807400205 70.8490356445312557, -153.9592975192599624 70.8509643554687614, -153.9573691474066663 70.8573689778645956, -153.9509641859266367 70.8592976888021013, -153.9490358140733406 70.8657023111979214, -153.9426308525933109 70.8676310221354271, -153.9407024807400148 70.8740356445312614, -153.9342975192599852 70.8759643554687671, -153.9322853936089359 70.8826473659939325, -153.9010479397243785 70.8840193006727475, -153.8989422268337535 70.8910135904948078, -153.7760577731662295 70.8923197428385521, -153.7735519409179688 70.9006429036458456, -153.7010557386610117 70.8993398030599025, -153.6989437527126654 70.8923251681857778, -153.6583333333333314 70.8916666666666799, -153.6541666666666686 70.8916666666666799, -153.4010601467556398 70.8923119439019160, -153.3989473130967838 70.8993299696180657, -153.3510526869031878 70.9006700303819599, -153.3489473130967724 70.9076633029514056, -153.3010526869032049 70.9090033637152857, -153.2989520602755817 70.9159806993272639, -153.2677146063910527 70.9173526340060931, -153.2652186075846146 70.9256429036458513, -153.1174499511718636 70.9243462456597342, -153.1166666666666458 70.9166666666666856, -153.1254611545138857 70.9157694498697992, -153.1242099338107607 70.9002000596788378, -153.1173658582899293 70.9007497151692831, -153.1166666666666458 70.9166666666666856, -153.1094078911675069 70.9158942328559192, -153.1068027072482494 70.9076653374566064, -153.1010148790147412 70.9092946370442831, -153.0984693739149236 70.9173346625434107, -153.0590627034505076 70.9159522162543539, -153.0583333333333087 70.9000000000000057, -153.0740383572048415 70.8993103027343921, -153.0756429036458144 70.8925601535373318, -153.0592488606770587 70.8910237630208400, -153.0583333333333087 70.9000000000000057, -153.0508165147569173 70.9008165147569542, -153.0490207248263630 70.9173472086588674, -153.0427124023437386 70.9157223171658160, -153.0405558268228958 70.9090762668185874, -153.0159861246744697 70.9074734157986200, -153.0176045735676951 70.9010477701823021, -153.0256805419921591 70.8984269883897724, -153.0235215928819343 70.8909861246744981, -153.0094319661458258 70.8924479166666828, -153.0067603217230783 70.9006805419922017, -152.9090545654296704 70.8993004692925410, -152.9083333333333030 70.8750000000000142, -152.9009209526909672 70.8743245442708485, -152.8990790473090158 70.8673421223958400, -152.8842542860242872 70.8659912109375085, -152.8824123806423358 70.8590087890625142, -152.8675876193576357 70.8576578776041828, -152.8654144287109204 70.8494201660156335, -152.8176062689886976 70.8506052652994924, -152.8157969156901004 70.8574635823567860, -152.8092030843098712 70.8592030843099110, -152.8074635823567462 70.8657969156901117, -152.8008697509765454 70.8675364176432367, -152.7991302490234204 70.8741302490234517, -152.7925364176432197 70.8758697509765767, -152.7904144287109318 70.8839131673177150, -152.7799814588025527 70.8836545481774181, -152.7799224853515625 70.8838729858398438, -152.7787864657365731 70.8836249259239111, -152.7426062689887090 70.8827280680338703, -152.7407969156900833 70.8758697509765767, -152.7327534993489451 70.8737477620442888, -152.7331788175176257 70.8718726671381489, -152.7244959262404222 70.8724264578416552, -152.7256429036458201 70.8679477267795193, -152.7175821940103901 70.8660237630208485, -152.7168868804299393 70.8728409263918735, -152.7076416015625000 70.8732223510742188, -152.7037435178066858 70.8748699411416823, -152.6653551175386383 70.8744835994252611, -152.6599884033203125 70.8731155395507812, -152.6587560839484183 70.8724807991739141, -152.6574337429470347 70.8673278808593921, -152.6469397427157446 70.8663944638794590, -152.6443023681640625 70.8650360107421875, -152.6410573649864943 70.8601250995680374, -152.6408159044053718 70.8591840955946282, -152.6403579314574301 70.8590665933831190, -152.6289049325830547 70.8417338953633617, -152.6324825710720177 70.8408159044053889, -152.6335057745788504 70.8368279736663169, -152.6358337402343750 70.8355407714843750, -152.6442222849770189 70.8334200055952152, -152.6574242485893933 70.8327097574869953, -152.6582548220327453 70.8294726014923697, -152.6646790627150665 70.8244407597621120, -152.6660054524739394 70.8241004096137203, -152.6660722404676562 70.8233495413131635, -152.6677093505859375 70.8220672607421875, -152.6841438723817248 70.8000993693073326, -152.6877353539676960 70.8004013376509675, -152.6923951139767155 70.8070059073868521, -152.6925174289279425 70.8074825710720575, -152.6927787151269058 70.8075496093247665, -152.7088011246580379 70.8302591739547012, -152.7089945475260322 70.8324337429470603, -152.7106317615249509 70.8328538502524481, -152.7205963134765625 70.8469772338867188, -152.7332916259765625 70.8563232421875000, -152.7339215728921999 70.8564587435895845, -152.7342329237195884 70.8576721191406307, -152.7416666666666458 70.8583333333333485, -152.7458333333333087 70.8583333333333485, -152.7458333333333087 70.8250000000000171, -152.7462903649555983 70.8198622870769725, -152.7464006212022412 70.8186228434244924, -152.7535993787977304 70.8105438232421989, -152.7550540500216698 70.8048750135633753, -152.7647104899088504 70.8035993787977560, -152.7700866699218523 70.7994327121310931, -152.7897104899088276 70.8005672878689296, -152.8020718044704722 70.8120402018229242, -152.8061228434244754 70.8089006212022696, -152.8083333333333087 70.8083333333333371, -152.8407477484808794 70.8077473958333456, -152.8425174289279482 70.8008507622612910, -152.8493859185112740 70.7990882025824817, -152.8505672878688983 70.7727895100911581, -152.8660993787977134 70.7563771565755246, -152.8673278808593636 70.7425662570529568, -152.8741492377386919 70.7408159044053946, -152.8758507622612797 70.7341840955946282, -152.8826721191406079 70.7324337429470518, -152.8833333333333258 70.7250000000000085, -152.8844919840494754 70.7209472656250142, -152.8895063612195884 70.7172841389973996, -152.8938269721136862 70.7160491943359517, -152.8978396945529425 70.7131174723307367, -152.9229936387803548 70.7118825276692746, -152.9270063612196111 70.7089508056640739, -152.9381174723307026 70.7073337131076443, -152.9313269721137090 70.7006174723307339, -152.9243825276692519 70.6986321343316035, -152.9256174723307140 70.6811730278862882, -152.9285491943359148 70.6771603054470603, -152.9297841389973769 70.6686730278862996, -152.9381174723307026 70.6590701633029568, -152.9368825276692405 70.6395063612196310, -152.9327158610025776 70.6338026258680713, -152.9339508056640398 70.6311730278862910, -152.9368825276692405 70.6271603054470631, -152.9381174723307026 70.6228396945529653, -152.9452158610025947 70.6146603054470603, -152.9458333333333258 70.6125000000000114, -152.9532670762803548 70.6131612141927150, -152.9541666666666515 70.6166666666666742, -152.9565911187065694 70.6170199924045221, -152.9642422146267222 70.6246466742621664, -152.9815911187065751 70.6253533257378621, -152.9892422146267279 70.6329800075954921, -152.9940911187065922 70.6336866590711878, -153.0017422146267165 70.6413133409288321, -153.0041666666666629 70.6416666666666799, -153.0045199924045107 70.6565911187066007, -153.0183827718098826 70.6704793294270956, -153.0475463867187216 70.6711866590711963, -153.0517422146267279 70.6670199924045193, -153.0749999999999886 70.6666666666666714, -153.0783159044053718 70.6675174289279653, -153.0797339545355840 70.6730438232421960, -153.0827660454643819 70.6769561767578267, -153.0839006212022468 70.6813771565755360, -153.0878960503472115 70.6865322536892506, -153.0938771565755019 70.6880672878689325, -153.1019561767577954 70.6952660454644217, -153.1074825710720404 70.6966840955946338, -153.1089006212022525 70.7022104899088646, -153.1134904649522355 70.7077141655816064, -153.1249999999999716 70.7083333333333428, -153.1256429036458258 70.7104682074652828, -153.1334832085503308 70.7196238199869924, -153.1396348741319287 70.7214762369791714, -153.1478651258680372 70.7285237630208456, -153.1521348741319173 70.7298095703125114, -153.1645316229925982 70.7410237630208485, -153.1908888075086566 70.7423736572265796, -153.1924116346571054 70.7573031955295306, -153.1979683770073564 70.7589762369791799, -153.2061982896592838 70.7660237630208400, -153.2124999999999773 70.7666666666666799, -153.2188268025716127 70.7660491943359489, -153.2228398640950502 70.7631174723307339, -153.2549033270941834 70.7618825276692860, -153.2645065307617074 70.7702158610026117, -153.2743552313910413 70.7716491699218864, -153.2749999999999773 70.8000000000000114, -153.2813771565755019 70.8005672878689296, -153.2852895100911326 70.8035993787977560, -153.2938771565755189 70.8047339545356067, -153.2977895100911212 70.8077660454644189, -153.3237850613064097 70.8089006212022696, -153.3255672878689211 70.8019561767578267, -153.3327660454644104 70.7938771565755331, -153.3342207166883497 70.7882083468967096, -153.3438771565755019 70.7869327121310903, -153.3477895100911326 70.7839006212022639, -153.3772104899088333 70.7827660454644274, -153.3852895100911269 70.7755672878689381, -153.3897104899088504 70.7744327121310874, -153.3936228434244526 70.7714006212022610, -153.4007466634114394 70.7702660454644246, -153.4061228434244697 70.7744327121310874, -153.4249999999999829 70.7750000000000057, -153.4315911187065922 70.7753533257378535, -153.4350755479600537 70.7788133409288349, -153.4458333333333258 70.7791666666666828, -153.4469397650824476 70.7752966986762289, -153.4575966729057939 70.7660491943359489, -153.4729934692382756 70.7672841389974110, -153.4786975436740306 70.7714508056640739, -153.5116414388020587 70.7701711018880246, -153.5160490247938299 70.7646603054470660, -153.5172843085394732 70.7603396945529681, -153.5202156914604927 70.7563269721137260, -153.5214509752061645 70.7520063612196282, -153.5243823581271556 70.7479936387803860, -153.5258026123046875 70.7382351345486171, -153.5333333333333314 70.7375000000000114, -153.5354809231228046 70.7381303575303946, -153.5395190768771556 70.7410363091362910, -153.5521475897894845 70.7422970241970575, -153.5561857435438355 70.7452029758029681, -153.5729809231228273 70.7464636908637203, -153.5770190768771499 70.7493696424696310, -153.5938142564561417 70.7506303575303974, -153.6103524102104814 70.7660363091362967, -153.6156417846679574 70.7672970241970631, -153.6174999660915717 70.7548221164279596, -153.6777589586045849 70.7535363091362939, -153.6797968546549384 70.7604807535807367, -153.6841357760958999 70.7659634060329950, -153.7075507269965158 70.7673712836371607, -153.7089635213216070 70.7795630560980982, -153.7047968546549441 70.7853525797526117, -153.7041666666666515 70.7958333333333485, -153.7047843085394732 70.8021603054470603, -153.7084472656249829 70.8071746826172017, -153.7146601359049498 70.8089508056640682, -153.7229166666666629 70.8161156548394217, -153.7311731974283759 70.8089508056640682, -153.7354934692382642 70.8077158610026203, -153.7395065307617017 70.8047841389974053, -153.7438268025716184 70.8035491943359432, -153.7561731974283816 70.7922841389974025, -153.8073255750867929 70.7910244411892506, -153.8092597113715101 70.7842597113715328, -153.8146601359049441 70.7827158610026146, -153.8187499999999943 70.7797281901041799, -153.8228398640950445 70.7827158610026146, -153.8421357896592667 70.7839508056640767, -153.8478398640950502 70.7797841389973996, -153.8588024563259467 70.7785491943359517, -153.8645065307617017 70.7827158610026146, -153.8674033270941663 70.7839508056640767, -153.8770065307617188 70.7756174723307367, -153.8825651380750799 70.7740281846788264, -153.8839509752061474 70.7645063612196310, -153.8910490247938299 70.7563269721137260, -153.8924018012152715 70.7424692789713703, -153.9021601359049498 70.7410491943359432, -153.9061731974283873 70.7381174723307424, -153.9124999999999943 70.7375000000000114, -153.9128533257378422 70.7399244520399435, -153.9225487603081604 70.7496459960937614, -153.9517144097222001 70.7503533257378621, -153.9579784817165660 70.7441202799479214, -153.9586866590711622 70.7309088812934164, -153.9683820936414804 70.7211873372395985, -153.9833333333333201 70.7208333333333456, -153.9855438232421818 70.7202660454644274, -153.9894561767578125 70.7172339545356010, -153.9980438232421704 70.7160993787977503, -154.0019561767578011 70.7130672878689381, -154.0176038953993043 70.7119327121310874, -154.0282469007703980 70.7214162190755360, -154.0300540500217039 70.7284583197699703, -154.0407792833116218 70.7298750135633725, -154.0422339545355896 70.7355438232421960, -154.0468124389648494 70.7410339355468807, -154.0647104899088333 70.7422339545356067, -154.0686228434244640 70.7452660454644189, -154.0907574123806398 70.7464569091796989, -154.0922339545355726 70.7522104899088617, -154.0952660454643990 70.7561228434244924, -154.0965418497721373 70.7657792833116446, -154.1022104899088561 70.7672339545355982, -154.1061228434244583 70.7702660454644246, -154.1116490681966127 70.7716840955946225, -154.1133619520399236 70.7783589680989706, -154.1196183946397582 70.7797339545356010, -154.1217390272352361 70.7714711507161525, -154.1397104899088504 70.7702660454644246, -154.1437499999999829 70.7671352810329921, -154.1477895100911439 70.7702660454644246, -154.1612850613064154 70.7714006212022610, -154.1633509318033930 70.7633507622612967, -154.1688771565755189 70.7619327121310846, -154.1727895100911496 70.7589006212022724, -154.1855438232421704 70.7577660454644217, -154.1910273234049384 70.7531931559244924, -154.1922339545355953 70.7367533365885492, -154.1879687839084170 70.7312500000000171, -154.1910993787977304 70.7272104899088703, -154.1922339545355953 70.7227895100911610, -154.2035993787977475 70.7105438232422046, -154.2047339545355840 70.7061228434244953, -154.2140304565429574 70.6958736843533018, -154.2186228434244697 70.6994327121310846, -154.2407574123806455 70.7006235758463646, -154.2425723605685732 70.7076955159505331, -154.2605438232421875 70.7089006212022610, -154.2644561767578182 70.7119327121310874, -154.2757466634114394 70.7130672878689381, -154.2812500000000000 70.7088019476996692, -154.2852895100911326 70.7119327121310874, -154.3083333333333371 70.7125000000000057, -154.3092466566297674 70.7160590277777885, -154.3249999999999886 70.7166666666666828, -154.3255672878689211 70.6977895100911553, -154.3297339545355840 70.6924133300781392, -154.3285222371419252 70.6759039984809192, -154.3202660454643933 70.6737850613064325, -154.3214006212022582 70.6561228434244839, -154.3244327121310846 70.6522104899088674, -154.3249999999999886 70.6458333333333428, -154.3399244520399236 70.6461866590711907, -154.3479159884982721 70.6541527642144160, -154.3517422146267393 70.6503533257378535, -154.3565911187066035 70.6496466742621578, -154.3642422146267279 70.6420199924045278, -154.3690911187065922 70.6413133409288321, -154.3725755479600537 70.6378533257378507, -154.4041666666666686 70.6375000000000028, -154.4107577853732494 70.6371466742621692, -154.4163085937499886 70.6316236707899350, -154.4170199924045050 70.6267422146267450, -154.4204800075954722 70.6232577853732693, -154.4208333333333201 70.6208333333333371, -154.4208333333333201 70.6125000000000114, -154.4202660454644160 70.6102895100911496, -154.4172339545355896 70.6063771565755331, -154.4160993787977247 70.5977895100911610, -154.4077660454643990 70.5884372287326443, -154.4093107435438412 70.5841518825954921, -154.4160430908202954 70.5824242485894189, -154.4172902425130189 70.5592424180772610, -154.4230438232421818 70.5577660454644189, -154.4269561767578125 70.5547339545355925, -154.4313771565755076 70.5535993787977560, -154.4352895100911383 70.5505672878689296, -154.4397104899088617 70.5494327121310789, -154.4477895100911553 70.5422339545356039, -154.4541666666666515 70.5416666666666714, -154.4549472384982494 70.5493187798394246, -154.4979682074652487 70.5506429036458371, -154.5020317925346944 70.5535237630208485, -154.5083333333333258 70.5541666666666742, -154.5083333333333258 70.5500000000000114, -154.5104431152343523 70.5493320041232721, -154.5145568847655966 70.5465013292100735, -154.5187764485676780 70.5451653374566092, -154.5312235514322765 70.5340013292100849, -154.5489169650607550 70.5325876871744839, -154.5510881212022412 70.5257300482855953, -154.5739118787977304 70.5242699517144160, -154.5760938856336679 70.5173787434895871, -154.6062764485676837 70.5159986707899407, -154.6145568847656193 70.5090013292100792, -154.6168351915147241 70.5076653374566007, -154.6228902180989451 70.5118320041232778, -154.6585018581814097 70.5131679958767421, -154.6661482069227134 70.5079060872395900, -154.6960944281683794 70.5090932210286496, -154.6923346625433737 70.5145568847656392, -154.6909986707899236 70.5359693739149378, -154.6979431152343523 70.5381679958767478, -154.7020568847655966 70.5409986707899463, -154.7210018581814097 70.5423346625434107, -154.7270568847656023 70.5381679958767478, -154.7367055257161326 70.5366309271918510, -154.7383015950520644 70.5216349283854242, -154.7532975938584912 70.5200388590494924, -154.7550533718532790 70.5090169270833371, -154.8574466281466755 70.5076497395833428, -154.8590013292100593 70.4978902180989735, -154.8618320041232437 70.4937764485677150, -154.8635979546440922 70.4881985134548756, -154.9075219048393990 70.4867919921875057, -154.9083333333333030 70.4791666666666714, -154.9166666666666572 70.4791666666666714, -154.9190911187065751 70.4788133409288235, -154.9225755479600650 70.4753533257378564, -154.9315911187065922 70.4746466742621607, -154.9392422146267165 70.4670199924045164, -154.9458333333333258 70.4666666666666686, -155.0065911187065808 70.4670199924045164, -155.0100755479600423 70.4704800075954978, -155.0249999999999773 70.4708333333333456, -155.0312889946831376 70.4701778835720631, -155.0395443386501597 70.4631554497612882, -155.0742607964409672 70.4617933485243100, -155.0757168240017165 70.4385654025607693, -155.0823503282335025 70.4365169949001881, -155.0839887830946111 70.4312110053168539, -155.0868445502387090 70.4271223280164946, -155.0884830050998175 70.4218163384331746, -155.0948503282334912 70.4198503282335082, -155.0964887830945997 70.4145443386501881, -155.0993445502386976 70.4104556613498289, -155.1006554497612626 70.4080874972873403, -155.0956685384114451 70.4009484185112910, -155.0841525607638687 70.3991536458333371, -155.0826778835720461 70.3883666992187642, -155.1021223280164918 70.3868445502387203, -155.1074896918402715 70.3830956353081660, -155.1083333333333201 70.3750000000000142, -155.1322113037109318 70.3742913140191035, -155.1344533284505189 70.3673817952473968, -155.1742902967664861 70.3659369574652800, -155.1749999999999829 70.3333333333333428, -155.1938017103406935 70.3339762369791686, -155.2020316229926209 70.3410237630208428, -155.2156365288628308 70.3424116346571253, -155.2177146063910413 70.3493140326606010, -155.2492211235893933 70.3506978352864678, -155.2507746378580578 70.3659274631076528, -155.2688017103407105 70.3673095703125142, -155.2729166666666458 70.3702270507812528, -155.2770316229926095 70.3673095703125142, -155.2839670817057254 70.3660237630208343, -155.2936982896592610 70.3743570963541742, -155.3114618937174214 70.3757188585069571, -155.3131427341037067 70.3813015407986171, -155.3166431003146499 70.3862382676866360, -155.3229683770073564 70.3881429036458428, -155.3270316229926209 70.3910237630208400, -155.3294884575737740 70.3923095703125057, -155.3353649563259467 70.3881429036458428, -155.3396350436740363 70.3868570963541771, -155.3478649563259353 70.3798095703125028, -155.3521350436740249 70.3785237630208371, -155.3603649563259523 70.3714762369791771, -155.3777186075846259 70.3701904296875114, -155.3791666666666629 70.3750000000000142, -155.3857577853732437 70.3753533257378479, -155.3892422146267336 70.3788133409288292, -155.3958333333333144 70.3791666666666771, -155.4146223280164918 70.3798221164279596, -155.4187110053168226 70.3826778835720575, -155.4324808756510379 70.3841128879123374, -155.4339887830946054 70.3937889946831632, -155.4382629394531250 70.3993045383029568, -155.4825373331705691 70.4006944444444542, -155.4840969509548358 70.4156616210937614, -155.4901775783962421 70.4173221164279539, -155.4926496717664861 70.4093163384331717, -155.4979556613498062 70.4076778835720631, -155.5028518676757585 70.4042578803168482, -155.5051496717664747 70.3968163384331689, -155.5104556613498232 70.3951778835720603, -155.5145443386501540 70.3923221164279624, -155.5326107449001540 70.3909077962239706, -155.5339887830945997 70.3645443386501768, -155.5368445502386976 70.3604556613498318, -155.5381554497612626 70.3330874972873374, -155.5333921644422617 70.3262685139974053, -155.5406836615668169 70.3240169949001768, -155.5416666666666572 70.3208333333333400, -155.5427163018120496 70.3173472086588589, -155.5781170315212592 70.3159861246744811, -155.5802215576171648 70.3089962429470603, -155.6438017103407105 70.3076904296875114, -155.6496782090928832 70.3035237630208343, -155.6604683770073621 70.3048095703125000, -155.6663448757595347 70.3089762369791771, -155.6713716295030281 70.3063456217447964, -155.6743572658962478 70.3021348741319514, -155.6756427341037181 70.2978651258680571, -155.6785239325629107 70.2938015407986114, -155.6806147257486828 70.2868570963541686, -155.6950614081488595 70.2882656521267393, -155.6958333333333258 70.2958333333333343, -155.7032670762803832 70.2964945475260521, -155.7050175984700502 70.3033159044053946, -155.7105438232421761 70.3047339545355925, -155.7160761515299328 70.3093478732639028, -155.7166666666666686 70.3333333333333428, -155.7399244520399293 70.3329800075954950, -155.7434088812933908 70.3295199924045278, -155.7649244520399066 70.3288133409288321, -155.7704784817165660 70.3232869466145871, -155.7708333333333144 70.3166666666666771, -155.7549012925889542 70.3159525553385549, -155.7535595364040830 70.2934390597873318, -155.7603776719834912 70.2881554497612910, -155.7706685384114564 70.2865515814887232, -155.7756554497612740 70.2794125027126739, -155.7743445502387090 70.2687110053168453, -155.7672921074761234 70.2604203965928917, -155.7714887830946111 70.2544125027126825, -155.7700205485026004 70.2377593994140739, -155.7646223280164861 70.2339887830946310, -155.7520443386501654 70.2326778835720518, -155.7479556613498062 70.2298221164279539, -155.7395443386501768 70.2285112169053889, -155.7354556613498175 70.2256554497612910, -155.7166666666666686 70.2250000000000085, -155.7163133409288207 70.2225755479600764, -155.7128533257378251 70.2190911187066007, -155.7121466742621294 70.2142422146267364, -155.7086866590711622 70.2107577853732749, -155.7083333333333144 70.1875000000000000, -155.7094424777560562 70.1835167778862967, -155.7144939846462535 70.1797715928819485, -155.7521726820203867 70.1785617404513999, -155.7561606513129107 70.1756049262152857, -155.7783665974934877 70.1743262396918510, -155.7796105278862626 70.1659084743923671, -155.7756050957573564 70.1605061848958371, -155.7749999999999773 70.1375000000000028, -155.7645316229926209 70.1381429036458428, -155.7586551242404482 70.1423095703125057, -155.7382617526584170 70.1409386528862910, -155.7285239325629220 70.1298360188802121, -155.7304307725694343 70.1256764729817803, -155.7604683770073564 70.1243570963541742, -155.7645316229926209 70.1214762369791771, -155.7745746188693374 70.1199456108941064, -155.7839760674370666 70.1089670817057282, -155.7823455810546704 70.1001705593533018, -155.7771350436740363 70.0964762369791714, -155.7708097669813299 70.0945716010199646, -155.7673094007703867 70.0896348741319457, -155.7659688313802064 70.0591122097439296, -155.7520316229926038 70.0576904296875114, -155.7479683770073677 70.0548095703125000, -155.7395316229926152 70.0535237630208343, -155.7313017103406878 70.0464762369791742, -155.7145316229926095 70.0451904296875085, -155.7063017103407105 70.0381429036458343, -155.6757498847113652 70.0368008083767393, -155.6750339084201187 70.0278903537326443, -155.6790913899739621 70.0221679687500114, -155.6688017103406878 70.0131429036458428, -155.6631701999240249 70.0114471435546903, -155.6624999999999943 69.9875000000000256, -155.6688771565755189 69.9880672878689438, -155.6729166666666515 69.9911978827582715, -155.6769561767578125 69.9880672878689438, -155.6980438232421875 69.9869327121310931, -155.7019561767577898 69.9839006212022809, -155.7076248168945085 69.9824459499783131, -155.7089006212022468 69.9727895100911610, -155.7119327121310732 69.9688771565755445, -155.7130672878689097 69.9644561767578352, -155.7160993787977361 69.9605438232422046, -155.7166666666666686 69.9333333333333513, -155.7173278808593579 69.9258995903863081, -155.7241490681965956 69.9241490681966411, -155.7255672878689268 69.9186228434245010, -155.7327660454643876 69.9105438232422074, -155.7339892917209170 69.8967931111653797, -155.7394561767578125 69.8922339545356124, -155.7522104899088333 69.8910993787977617, -155.7575866699218636 69.8869327121310988, -155.7948242187499943 69.8880969577365647, -155.7994327121310789 69.8936228434244953, -155.8007085164387888 69.9032792833116474, -155.8063771565755076 69.9047339545356152, -155.8144561767578011 69.9119327121310903, -155.8188771565755246 69.9130672878689410, -155.8227895100911269 69.9160993787977674, -155.8285837809244754 69.9175864325629561, -155.8369327121310732 69.9269561767578267, -155.8374999999999773 69.9291666666666742, -155.8440911187065865 69.9288133409288406, -155.8642422146267279 69.9086866590712077, -155.8690911187065922 69.9079800075955120, -155.8729173448350593 69.9041805691189495, -155.8809088812934078 69.9121466742621749, -155.8857577853732437 69.9128533257378706, -155.8892422146267336 69.9163133409288378, -155.9058795505099795 69.9170199924045335, -155.9100755479600480 69.9128533257378706, -155.9225489298502509 69.9121466742621749, -155.9309088812933908 69.9204800075955006, -155.9416666666666629 69.9208333333333343, -155.9423321194118728 69.8841154310438526, -155.9573031955295050 69.8825883653428974, -155.9597813924153513 69.8743572658962933, -155.9729683770073621 69.8756427341037494, -155.9788448757595347 69.8798094007704123, -155.9896350436740420 69.8785239325629561, -155.9936982896592781 69.8756427341037494, -156.0229683770073734 69.8743572658962933, -156.0270316229926095 69.8714760674370865, -156.0406365288628479 69.8700883653428946, -156.0423094007703924 69.8645316229926436, -156.0451905992295849 69.8604683770073933, -156.0470241970486143 69.8543779161241503, -156.0603649563259410 69.8423094007704179, -156.0749999999999886 69.8416666666666828, -156.0774244520399350 69.8413133409288349, -156.0871458265516480 69.8316182454427263, -156.0878533257378535 69.8107871161567033, -156.0836866590711622 69.8065911187066206, -156.0829800075954665 69.7975755479600934, -156.0795199924044994 69.7940911187066177, -156.0791666666666515 69.7708333333333570, -156.0791666666666515 69.7666666666666799, -156.1107577853732664 69.7663133409288321, -156.1142422146267279 69.7628533257378649, -156.1208333333333371 69.7625000000000171, -156.1229934692382812 69.7618823581271954, -156.1270065307617188 69.7589509752061758, -156.1313268025716070 69.7577156914605183, -156.1353398640950445 69.7547843085395129, -156.1438268025715956 69.7535490247938554, -156.1530602349175183 69.7455366346571424, -156.1547843085394902 69.7395065307617301, -156.1743823581271613 69.7188268025716269, -156.1757351345486029 69.7049692789713760, -156.1854934692382812 69.7035490247938583, -156.1989117092556398 69.6913063897027030, -156.2006176418728103 69.6853398640950701, -156.2035490247938299 69.6813268025716326, -156.2050930447048529 69.6759263780382128, -156.2104934692382869 69.6743823581271897, -156.2145065307617244 69.6714509752061844, -156.2213024563259580 69.6702156914605268, -156.2270065307617131 69.6743823581271897, -156.2338024563259467 69.6756176418728472, -156.2395065307617017 69.6714509752061844, -156.2521601359049441 69.6702156914605268, -156.2603398640950445 69.6631176418728444, -156.2646601359049328 69.6618823581271869, -156.2728398640950331 69.6547843085395186, -156.2979934692382642 69.6535490247938611, -156.3020065307617017 69.6506176418728415, -156.3063268025716184 69.6493823581271840, -156.3103398640950559 69.6464509752061787, -156.3229934692382699 69.6452156914605212, -156.3270065307617074 69.6422843085395158, -156.3354934692382869 69.6410490247938583, -156.3395065307617244 69.6381176418728529, -156.3438268025716127 69.6368823581271812, -156.3488411797417541 69.6332194010416856, -156.3499999999999659 69.6291666666666913, -156.3522104899088276 69.6297339545356095, -156.3561228434244583 69.6327660454644217, -156.3740943060980726 69.6339709811740590, -156.3749999999999716 69.6375000000000171, -156.3791666666666345 69.6375000000000171, -156.3857577853732437 69.6378533257378649, -156.3892422146267052 69.6413133409288321, -156.3940911187065694 69.6420199924045278, -156.3975755479600593 69.6454800075955092, -156.4149244520399122 69.6461866590712049, -156.4184088812933737 69.6496466742621720, -156.4642130533854072 69.6503533257378677, -156.4684088812933851 69.6461866590712049, -156.4732577853732494 69.6454800075955092, -156.4767422146267108 69.6420199924045278, -156.4808824327256787 69.6413133409288321, -156.4892422146267279 69.6496466742621720, -156.4940911187065637 69.6503533257378677, -156.4975755479600537 69.6538133409288349, -156.5065911187065808 69.6545199924045306, -156.5100755479600423 69.6579800075954978, -156.5274244520399236 69.6586866590711935, -156.5312499999999716 69.6624854193793510, -156.5350755479600480 69.6586866590711935, -156.5524244520399009 69.6579800075954978, -156.5559088812933908 69.6545199924045306, -156.6058797200520587 69.6538133409288349, -156.6100755479600366 69.6579800075954978, -156.6249999999999716 69.6583333333333456, -156.6274244520399179 69.6579800075954978, -156.6309088812933794 69.6545199924045306, -156.6399244520399066 69.6538133409288349, -156.6475755479600593 69.6461866590712049, -156.6524244520399236 69.6454800075955092, -156.6559088812933851 69.6420199924045278, -156.6624999999999943 69.6416666666666799, -156.6772104899088447 69.6422339545356124, -156.6811228434244470 69.6452660454644246, -156.6980438232421591 69.6464006212022753, -156.7019561767577898 69.6494327121310874, -156.7174133300781023 69.6505672878689381, -156.7238627115885379 69.6455686781141736, -156.7249999999999943 69.6500000000000199, -156.7315911187065751 69.6503533257378677, -156.7350755479600650 69.6538133409288349, -156.7440911187065922 69.6545199924045306, -156.7475755479600537 69.6579800075954978, -156.7565911187065808 69.6586866590711935, -156.7600755479600423 69.6621466742621749, -156.7624999999999886 69.6625000000000227, -156.7916666666666572 69.6625000000000227, -156.8063771565755076 69.6619327121310903, -156.8144561767578011 69.6547339545356010, -156.8605438232421818 69.6535993787977645, -156.8644561767577841 69.6505672878689381, -156.8730438232421704 69.6494327121310874, -156.8769561767578011 69.6464006212022753, -156.9032548692491105 69.6452190823025319, -156.9047339545355726 69.6394561767578324, -156.9173122829861029 69.6259034898546219, -156.9208333333333201 69.6250000000000142, -156.9208333333333201 69.6208333333333513, -156.9249999999999829 69.6208333333333513, -156.9357577853732550 69.6204800075955035, -156.9392422146267165 69.6170199924045363, -156.9649244520399236 69.6163133409288406, -156.9684088812933851 69.6128533257378592, -156.9815911187065751 69.6121466742621635, -156.9850755479600650 69.6086866590711963, -156.9933797200520758 69.6079800075955006, -156.9979173448350593 69.6124860975477588, -157.0059088812933794 69.6045199924045335, -157.0274244520399236 69.6038133409288378, -157.0309088812933851 69.6003533257378706, -157.0350463867187329 69.5996466742621749, -157.0392422146267108 69.6038133409288378, -157.0565911187065922 69.6045199924045335, -157.0600755479600537 69.6079800075955006, -157.0690911187065808 69.6086866590711963, -157.0725755479600423 69.6121466742621635, -157.0975463867187329 69.6128533257378592, -157.1017422146267108 69.6086866590711963, -157.1440911187065694 69.6079800075955006, -157.1475755479600593 69.6045199924045335, -157.1541666666666401 69.6041666666666856, -157.1563771565755019 69.6035993787977674, -157.1686228434244583 69.5922339545356010, -157.1782792833116105 69.5909581502278769, -157.1800175984700445 69.5841842651367415, -157.1855438232421704 69.5827660454644246, -157.1977895100911269 69.5714006212022724, -157.2022104899088504 69.5702660454644217, -157.2102895100911439 69.5630672878689467, -157.2355438232421818 69.5619327121310960, -157.2394561767578125 69.5589006212022696, -157.2449824015299384 69.5574824015299669, -157.2464006212022412 69.5519561767578267, -157.2535993787977304 69.5438771565755331, -157.2541666666666629 69.5291666666666828, -157.2532609727647355 69.5256376478407248, -157.2352895100911212 69.5244327121310874, -157.2313771565755189 69.5214006212022753, -157.2227895100911326 69.5202660454644246, -157.2188771565755019 69.5172339545356124, -157.2092207166883497 69.5159581502278741, -157.2077660454643819 69.5102895100911553, -157.2035993787977191 69.5049133300781392, -157.2047339545355840 69.4977895100911667, -157.2089006212022468 69.4924133300781364, -157.2077660454643819 69.4894561767578267, -157.2047339545355840 69.4855438232422102, -157.2034637451171761 69.4759292602539205, -157.1938771565754962 69.4672339545356010, -157.1894561767578011 69.4660993787977645, -157.1855438232421704 69.4630672878689381, -157.1769561767578125 69.4619327121310874, -157.1688771565755189 69.4547339545356124, -157.1666666666666572 69.4541666666666799, -157.1666666666666572 69.4375000000000142, -157.1672843085394788 69.4353398640950701, -157.1702156914604984 69.4313268025716326, -157.1714509752061417 69.4270065307617301, -157.1785490247938242 69.4188268025716297, -157.1798838297525833 69.4051528930664290, -157.1854934692382812 69.4035490247938469, -157.1895065307617188 69.4006176418728415, -157.1992058648003194 69.3992058648003649, -157.2006176418728103 69.3895065307617358, -157.2047843085394732 69.3838024563259665, -157.2032352023654482 69.3752799140082601, -157.1979934692382699 69.3714509752061730, -157.1958333333333258 69.3708333333333513, -157.1954800075954779 69.3684088812934192, -157.1878533257378194 69.3607577853732806, -157.1875000000000000 69.3583333333333485, -157.1871419270833314 69.3558764987521812, -157.1815911187065922 69.3503533257378564, -157.1791666666666458 69.3500000000000227, -157.1749999999999829 69.3500000000000227, -157.1753536648220404 69.3267140706380331, -157.1809088812933908 69.3211866590712020, -157.1833333333333087 69.3208333333333542, -157.1958333333333258 69.3208333333333542, -157.1965035332573564 69.2968860202365562, -157.2032024807400035 69.2948691474067004, -157.2051808675130076 69.2882978651258838, -157.2164472791883441 69.2865810818142478, -157.2214760674370382 69.2794884575738052, -157.2201905992295963 69.2728649563259751, -157.2118572658962421 69.2631337483724110, -157.2132110595703125 69.2382775200737939, -157.2271350436740249 69.2368572658962762, -157.2311982896592610 69.2339760674370837, -157.2438017103407049 69.2326905992296133, -157.2478649563259410 69.2298094007704208, -157.2771350436740363 69.2285239325629504, -157.2811982896592724 69.2256427341037437, -157.2865358140733463 69.2240358140733605, -157.2874999999999943 69.2208333333333456, -157.2940911187065751 69.2211866590711935, -157.3017422146267279 69.2288133409288378, -157.3308795505099624 69.2295199924045335, -157.3350755479600593 69.2253533257378564, -157.3642128838432939 69.2246466742621607, -157.3684088812933908 69.2288133409288378, -157.3982577853732607 69.2295199924045335, -157.4142422146267108 69.2454800075955035, -157.4190911187065751 69.2461866590711992, -157.4225755479600650 69.2496466742621664, -157.4274244520399293 69.2503533257378621, -157.4309088812933908 69.2538133409288292, -157.4433795505099738 69.2545199924045249, -157.4475755479600707 69.2503533257378621, -157.4524244520399066 69.2496466742621664, -157.4559088812933965 69.2461866590711992, -157.4690911187065865 69.2454800075955035, -157.4725755479600480 69.2420199924045221, -157.5100462171766367 69.2413133409288406, -157.5142422146267336 69.2454800075955035, -157.5190911187065979 69.2461866590711992, -157.5225755479600593 69.2496466742621664, -157.5291666666666686 69.2500000000000142, -157.5399244520399122 69.2503533257378621, -157.5434088812934021 69.2538133409288292, -157.5565911187065922 69.2545199924045249, -157.5600755479600537 69.2579800075955063, -157.5649244520399179 69.2586866590712020, -157.5684088812933794 69.2621466742621692, -157.5774244520399066 69.2628533257378649, -157.5871422661675183 69.2725448608398580, -157.5874999999999773 69.2750000000000199, -157.5958333333333314 69.2750000000000199, -157.5965138753255133 69.2770972357856039, -157.5993194580078125 69.2812360975477617, -157.6006805419921761 69.2854305691189438, -157.6087515936957288 69.2950449625651146, -157.6312639024522468 69.2965138753255303, -157.6354027642144047 69.2993194580078296, -157.6867704603407105 69.3007169935438583, -157.6875000000000000 69.3208333333333542, -157.6908157348632642 69.3216842651367386, -157.6916666666666629 69.3250000000000171, -157.6949824015299271 69.3258509318034015, -157.6958333333333258 69.3291666666666799, -157.6958333333333258 69.3625000000000114, -157.6990736219617872 69.3634263780382128, -157.7006176418728103 69.3688268025716326, -157.7118823581271556 69.3811731974283958, -157.7131176418728273 69.3854934692382983, -157.7167805989583371 69.3905078464084397, -157.7229934692382756 69.3922843085395158, -157.7270065307617131 69.3952156914605212, -157.7396601359049271 69.3964509752061787, -157.7436731974283646 69.3993823581271840, -157.7754691229926038 69.4006176418728415, -157.7811731974283873 69.3964509752061787, -157.8104934692382812 69.3952156914605212, -157.8145065307617188 69.3922843085395158, -157.8354934692382585 69.3910490247938583, -157.8395065307616960 69.3881176418728387, -157.8688268025716184 69.3868823581271812, -157.8728398640950559 69.3839509752061758, -157.8813268025716070 69.3827156914605183, -157.8853398640950445 69.3797843085395129, -157.8938268025715956 69.3785490247938554, -157.8978398640950331 69.3756176418728501, -157.9115003797743100 69.3743823581271926, -157.9131176418728160 69.3854934692382983, -157.9160490247938355 69.3895065307617358, -157.9172843085394788 69.3938268025716241, -157.9300354003906079 69.4076677110460167, -157.9458333333333258 69.4083333333333456, -157.9690911187065865 69.4079800075954978, -157.9725755479600480 69.4045199924045306, -157.9833333333333201 69.4041666666666828, -157.9855438232421818 69.4035993787977645, -157.9894561767578125 69.4005672878689381, -157.9980438232421704 69.3994327121310874, -158.0061228434244640 69.3922339545356124, -158.0272104899088390 69.3910993787977617, -158.0311228434244697 69.3880672878689353, -158.0355438232421932 69.3869327121310988, -158.0394561767577954 69.3839006212022724, -158.0647104899088333 69.3827660454644217, -158.0727895100911269 69.3755672878689325, -158.0866004096137090 69.3743387858073106, -158.0883509318033759 69.3675175984700729, -158.0916666666666686 69.3666666666666885, -158.0937889946831660 69.3660112169053917, -158.1029032389322708 69.3582578870985458, -158.1048221164279539 69.3520443386501881, -158.1128707885742131 69.3425828721788378, -158.1229556613498062 69.3410112169054003, -158.1312110053168283 69.3339887830946395, -158.1354556613498232 69.3326778835720603, -158.1478776719835082 69.3214887830946367, -158.1533540513780451 69.3197976006402001, -158.1550240410698791 69.3090816921658188, -158.1742941962347970 69.3075731065538321, -158.1756934271918453 69.2719058566623431, -158.1791666666666742 69.2708333333333428, -158.2107577853732607 69.2704800075954950, -158.2184088812933851 69.2628533257378649, -158.2607577853732721 69.2621466742621692, -158.2642422146267336 69.2586866590712020, -158.2750000000000057 69.2583333333333542, -158.3115804036458201 69.2589172363281449, -158.3124999999999716 69.2625000000000171, -158.3208333333333258 69.2625000000000171, -158.3666666666666458 69.2625000000000171, -158.3980438232421761 69.2630672878689353, -158.4019561767577784 69.2660993787977617, -158.4272104899088447 69.2672339545355982, -158.4311228434244470 69.2702660454644246, -158.4343214246961509 69.2714006212022753, -158.4477895100911269 69.2589006212022724, -158.4616004096137090 69.2576721191406364, -158.4634134928385265 69.2506074693468037, -158.4791666666666572 69.2500000000000142, -158.4916666666666458 69.2500000000000142, -158.4922841389973769 69.2563268025716354, -158.4966332329643990 69.2617645263672017, -158.5116973876952784 69.2632351345486228, -158.5132683648003251 69.2740283542209312, -158.5188269721136862 69.2756176418728415, -158.5228396945529425 69.2785490247938469, -158.5604936387803718 69.2797843085395186, -158.5645063612195997 69.2827156914605240, -158.5688269721136976 69.2839509752061815, -158.5728396945529255 69.2868823581271869, -158.6021603054470290 69.2881176418728444, -158.6061730278862569 69.2910490247938498, -158.6271603054470347 69.2922843085395073, -158.6311730278862626 69.2952156914605126, -158.6409315321180316 69.2966359456380303, -158.6416666666666515 69.3041666666666885, -158.7022104899088504 69.3047339545356067, -158.7061228434244526 69.3077660454644189, -158.7090799967447765 69.3089006212022696, -158.7144561767578068 69.3047339545356067, -158.7199825710720233 69.3033157348632898, -158.7214006212022355 69.2977895100911638, -158.7244327121310619 69.2938771565755331, -158.7259141710069343 69.2881040785047873, -158.7590799967447879 69.2869327121310903, -158.7645833333333201 69.2911978827582686, -158.7686228434244526 69.2880672878689410, -158.7833333333333030 69.2875000000000085, -158.7916666666666572 69.2875000000000085, -158.8065911187065922 69.2871466742621749, -158.8100755479600537 69.2836866590711935, -158.8166666666666629 69.2833333333333456, -158.8199825710720177 69.2824824015299612, -158.8208333333333258 69.2791666666666828, -158.8201195610893990 69.2632386949327383, -158.8083333333333087 69.2625000000000171, -158.7853776719834968 69.2631554497612996, -158.7794125027126597 69.2673221164279624, -158.7260769314235915 69.2659871419270985, -158.7239644368489451 69.2591459486219776, -158.7145443386501427 69.2576778835720575, -158.7104556613498119 69.2548221164279596, -158.6853776719835025 69.2535112169053946, -158.6812889946831433 69.2506554497612967, -158.6770443386501483 69.2493445502387317, -158.6729556613498175 69.2464887830946338, -158.6687110053168226 69.2451778835720688, -158.6646223280164634 69.2423221164279710, -158.6603776719834968 69.2410112169053917, -158.6562889946831376 69.2381554497612939, -158.6437110053168169 69.2368445502387289, -158.6396223280164861 69.2339887830946310, -158.6270443386501654 69.2326778835720660, -158.6229556613498062 69.2298221164279681, -158.6187110053168112 69.2285112169054031, -158.6145833333333144 69.2256281534830862, -158.6085791693793112 69.2298221164279681, -158.5799126519096944 69.2284491644965385, -158.5756266276041515 69.2229187011718921, -158.5785112169053548 69.2187889946831802, -158.5798509385850537 69.1590952555338703, -158.5979556613498005 69.1576778835720631, -158.6062110053168226 69.1506554497613024, -158.6187889946831433 69.1493445502387232, -158.6228776719834741 69.1464887830946253, -158.6271223280164691 69.1451778835720603, -158.6312110053168283 69.1423221164279624, -158.6365169949001483 69.1406836615668539, -158.6381554497612569 69.1353776719835196, -158.6451778835720177 69.1271223280165117, -158.6458333333333144 69.1208333333333513, -158.6815911187065922 69.1211866590711992, -158.6850755479600537 69.1246466742621664, -158.6916666666666345 69.1250000000000142, -158.6940911187065808 69.1253533257378621, -158.6979166666666572 69.1291520860460196, -158.7017422146267052 69.1253533257378621, -158.7600463867187273 69.1246466742621664, -158.7642422146267052 69.1288133409288292, -158.7808797200520701 69.1295199924045249, -158.7850755479600480 69.1253533257378621, -158.8100463867187386 69.1246466742621664, -158.8142422146267165 69.1288133409288292, -158.9041666666666401 69.1291666666666771, -158.9190911187065751 69.1288133409288292, -158.9225755479600650 69.1253533257378621, -158.9399244520399179 69.1246466742621664, -158.9434088812933794 69.1211866590711992, -158.9499999999999886 69.1208333333333513, -158.9541666666666515 69.1208333333333513, -158.9857577853732380 69.1204800075955035, -158.9892422146267279 69.1170199924045221, -159.0149244520399066 69.1163133409288264, -159.0184088812933965 69.1128533257378592, -159.0308797200520701 69.1121466742621635, -159.0350755479600480 69.1163133409288264, -159.0690911187065808 69.1170199924045221, -159.0725755479600423 69.1204800075955035, -159.1899244520399179 69.1211866590711992, -159.1934088812933794 69.1246466742621664, -159.1958333333333258 69.1250000000000142, -159.1999999999999886 69.1250000000000142, -159.2065911187065694 69.1253533257378621, -159.2184088812933851 69.1371466742621692, -159.2482577853732550 69.1378533257378649, -159.2517422146267165 69.1413133409288321, -159.2708333333333144 69.1416666666666799, -159.2716842651367131 69.1383507622612967, -159.2749999999999773 69.1375000000000171, -159.3149244520399179 69.1371466742621692, -159.3184088812933794 69.1336866590711878, -159.3600462171766310 69.1329800075955063, -159.3642422146267279 69.1371466742621692, -159.3933795505099624 69.1378533257378649, -159.3975755479600593 69.1336866590711878, -159.4274244520399293 69.1329800075955063, -159.4309088812933908 69.1295199924045249, -159.4350462171766480 69.1288133409288292, -159.4392422146267165 69.1329800075955063, -159.4607577853732607 69.1336866590711878, -159.4642422146267222 69.1371466742621692, -159.5249999999999773 69.1375000000000171, -159.5249999999999773 69.1166666666666742, -159.5315911187065865 69.1163133409288264, -159.5350755479600480 69.1128533257378592, -159.5374999999999943 69.1125000000000114, -159.5625000000000000 69.1125000000000114, -159.5625000000000000 69.0958333333333456, -159.5577660454644047 69.0946183946397667, -159.5589006212022412 69.0769561767578324, -159.5619327121310675 69.0730438232422017, -159.5630672878689040 69.0644561767578296, -159.5660993787977304 69.0605438232421989, -159.5666666666666629 69.0541666666666742, -159.5730438232421875 69.0535993787977560, -159.5769561767577898 69.0505672878689438, -159.5813771565755133 69.0494327121310931, -159.5852895100911439 69.0464006212022667, -159.6605438232421648 69.0452660454644160, -159.6644561767577954 69.0422339545356039, -159.6855438232421704 69.0410993787977532, -159.6894561767578011 69.0380672878689410, -159.7313771565755189 69.0369327121310903, -159.7352895100911496 69.0339006212022639, -159.7699186537000742 69.0327307807074817, -159.7708333333333144 69.0291666666666828, -159.8065911187065922 69.0288133409288349, -159.8142422146267165 69.0211866590711907, -159.8392128838433166 69.0204800075954950, -159.8434088812933851 69.0246466742621720, -159.8642128838432939 69.0253533257378535, -159.8687499999999773 69.0208479139540003, -159.8725755479600537 69.0246466742621720, -159.8767128838433109 69.0253533257378535, -159.8809088812934078 69.0211866590711907, -159.8892128838432996 69.0204800075954950, -159.8937499999999829 69.0249854193793482, -159.8979166666666458 69.0208479139540003, -159.9017422146267222 69.0246466742621720, -159.9541666666666515 69.0250000000000057, -159.9565911187065979 69.0246466742621720, -159.9600755479600593 69.0211866590711907, -159.9874999999999829 69.0208333333333428, -159.9982577853732550 69.0211866590711907, -160.0020833333333314 69.0249854193793482, -160.0059088812934078 69.0211866590711907, -160.0100462171766367 69.0204800075954950, -160.0142422146267336 69.0246466742621720, -160.0440911187066035 69.0253533257378535, -160.0475755479600650 69.0288133409288349, -160.0808795505099624 69.0295199924045306, -160.0850755479600593 69.0253533257378535, -160.1267128838433109 69.0246466742621720, -160.1309088812934078 69.0288133409288349, -160.1600462171766424 69.0295199924045306, -160.1642422146267393 69.0253533257378535, -160.1982577853732437 69.0246466742621720, -160.2079755995008554 69.0149549696180742, -160.2083333333333144 69.0125000000000171, -160.2200237698025092 69.0131880018446253, -160.2216495090060562 69.0243570963541799, -160.2813268025716127 69.0256174723307367, -160.2853398640950502 69.0285491943359517, -160.3088024563259637 69.0297841389974138, -160.3154541015624943 69.0249254014757128, -160.3166666666666629 69.0291666666666828, -160.3291666666666515 69.0291666666666828, -160.3940911187065694 69.0295199924045306, -160.3975755479600309 69.0329800075955120, -160.4333333333333087 69.0333333333333456, -160.4374999999999716 69.0333333333333456, -160.4940911187065637 69.0336866590711935, -160.4975755479600537 69.0371466742621749, -160.5267130533853788 69.0378533257378706, -160.5309088812933851 69.0336866590711935, -160.5607577853732266 69.0329800075955120, -160.5663123236761862 69.0274532741970717, -160.5666666666666345 69.0166666666666799, -160.5663133409287866 69.0100755479600849, -160.5625145806206433 69.0062500000000227, -160.5663133409287866 69.0024244520399463, -160.5666666666666345 68.9916666666666885, -160.5475755479600366 68.9920199924045363, -160.5440911187065751 68.9954800075955035, -160.5166666666666515 68.9958333333333513, -160.5059088812933794 68.9954800075955035, -160.5024244520398895 68.9920199924045363, -160.4934088812933624 68.9913133409288406, -160.4899244520399009 68.9878533257378592, -160.4791666666666288 68.9875000000000114, -160.4797339545355612 68.9852895100911638, -160.4869327121310505 68.9772104899088703, -160.4883507622612626 68.9716840955946395, -160.4951249864365934 68.9699459499783245, -160.4965362548827841 68.9592624240451642, -160.5061228434244640 68.9505672878689495, -160.5119171142577841 68.9490804036458513, -160.5202660454643819 68.9397104899088760, -160.5214006212022184 68.9352895100911667, -160.5285993787977077 68.9272104899088731, -160.5301344129773895 68.9212293836805685, -160.5367533365885038 68.9160993787977674, -160.5656653510199305 68.9172726101345603, -160.5702660454643933 68.9227895100911638, -160.5717329237195941 68.9285054524739706, -160.5855438232421477 68.9297339545356067, -160.5894561767577784 68.9327660454644331, -160.6124999999999829 68.9333333333333513, -160.6124999999999829 68.9125000000000227, -160.6130672878688870 68.9102895100911610, -160.6202660454643762 68.9022104899088674, -160.6217207166883441 68.8965416802300581, -160.6313771565754962 68.8952660454644246, -160.6352895100911269 68.8922339545356124, -160.6397104899088220 68.8910993787977617, -160.6436228434244526 68.8880672878689353, -160.6480438232421477 68.8869327121310988, -160.6519561767577784 68.8839006212022724, -160.6563771565755019 68.8827660454644217, -160.6602895100911326 68.8797339545356095, -160.6647104899088276 68.8785993787977588, -160.6686228434244583 68.8755672878689467, -160.6897104899088333 68.8744327121310960, -160.6936228434244640 68.8714006212022696, -160.7022104899088220 68.8702660454644331, -160.7061228434244526 68.8672339545356067, -160.7324215359157620 68.8660525851779681, -160.7341840955945997 68.8591840955946424, -160.7422339545355499 68.8571183946397696, -160.7408589680989337 68.8508619520399492, -160.7341840955945997 68.8491492377387289, -160.7324825710720120 68.8425174289279624, -160.7258507622612456 68.8408159044054031, -160.7249999999999659 68.8375000000000199, -160.7246466742621180 68.8309088812934249, -160.7211866590711509 68.8274244520399492, -160.7208333333333030 68.8208333333333542, -160.5017422146267165 68.8211866590712020, -160.4979166666666401 68.8249854193793595, -160.4933797200520473 68.8204800075955063, -160.4850755479600366 68.8211866590712020, -160.4808797200520587 68.8253533257378649, -160.4684088812933851 68.8246466742621692, -160.4649244520398952 68.8211866590712020, -160.4517130533853901 68.8204783121745010, -160.4461866590711452 68.8149244520399463, -160.4458333333332973 68.8125000000000142, -160.4464006212022298 68.8102895100911667, -160.4535993787977191 68.8022104899088731, -160.4550174289279312 68.7966840955946282, -160.4605438232421477 68.7952660454644302, -160.4644561767577784 68.7922339545356039, -160.4730438232421648 68.7910993787977532, -160.4769561767577954 68.7880672878689410, -160.4855438232421534 68.7869327121310903, -160.4894561767577841 68.7839006212022781, -160.5105438232421591 68.7827660454644274, -160.5144561767577898 68.7797339545356010, -160.5199825710720347 68.7783159044054031, -160.5217451307508441 68.7714474148220631, -160.5480438232421534 68.7702660454644246, -160.5519561767577841 68.7672339545356124, -160.5579372829860745 68.7656989203559164, -160.5619327121310391 68.7605438232422017, -160.5624999999999716 68.7583333333333542, -160.5612382676866048 68.7541432698567831, -160.5563015407985858 68.7506429036458542, -160.5520317925347058 68.7493570963541885, -160.5479682074652601 68.7464762369791771, -160.5228651258680372 68.7451904296875114, -160.5188015407985915 68.7423095703125142, -160.5061984592013573 68.7410237630208485, -160.5021348741319116 68.7381429036458513, -160.4936984592013687 68.7368570963541856, -160.4896348741319230 68.7339762369791885, -160.4770317925346887 68.7326904296875227, -160.4729682074652430 68.7298095703125114, -160.4549170600043055 68.7284257676866446, -160.4464762369791515 68.7188015407986228, -160.4448689778645587 68.7134643554687727, -160.4395317925346944 68.7118570963541799, -160.4271348741319230 68.7006429036458428, -160.4228651258680145 68.6993570963541771, -160.4188015407985972 68.6964762369791799, -160.4051968044704495 68.6950883653428974, -160.4031239827473598 68.6882035997178946, -160.3799431694878308 68.6867801242404710, -160.3784216986761919 68.6718634711371720, -160.3709832085503137 68.6696238199869953, -160.3631429036458087 68.6604682074652999, -160.3615356445312159 68.6551310221354356, -160.3561984592013516 68.6535237630208428, -160.3503217909070884 68.6493570963541799, -160.3478651258680259 68.6506429036458456, -160.3438015407985802 68.6535237630208428, -160.3374999999999773 68.6541666666666828, -160.3369327121310448 68.6477895100911581, -160.3327660454643819 68.6424133300781421, -160.3339006212022184 68.6227895100911667, -160.3420325385199305 68.6136630588107721, -160.3339162190754905 68.6115804036458456, -160.3255672878688927 68.6022104899088703, -160.3249999999999886 68.6000000000000085, -160.3260053846571225 68.5964836968316121, -160.3658525254991218 68.5951765272352532, -160.3674608018663150 68.5841274685329978, -160.3788355509440180 68.5824717203776117, -160.3827156914605041 68.5771603054470518, -160.3847011990017393 68.5702158610026089, -160.4063268025716127 68.5714508056640710, -160.4103398640950502 68.5743825276692860, -160.4188268025716013 68.5756174723307339, -160.4245308770073848 68.5797841389974110, -160.4313268025716184 68.5785491943359489, -160.4370308770073734 68.5743825276692860, -160.4408059014214416 68.5761559380425467, -160.4416666666666345 68.5791666666666799, -160.4438771565755246 68.5797339545355982, -160.4479166666666572 68.5828647189670164, -160.4519561767578182 68.5797339545355982, -160.4699276394314325 68.5785288492838703, -160.4708333333333314 68.5750000000000028, -160.4772481282551837 68.5755296495225792, -160.4810852050781023 68.5786370171441177, -160.4968509250216755 68.5796963161892563, -160.5034081353081206 68.5740539550781421, -160.5049333360459798 68.5675788031684164, -160.5092739529079608 68.5661370171441149, -160.5144185384114337 68.5703036838107778, -160.5272481282551666 68.5713629828559164, -160.5310852050780852 68.5744703504774407, -160.5343509250216698 68.5755296495225792, -160.5409081353081433 68.5698872884114792, -160.5421963161892052 68.5644185384114735, -160.5453036838107437 68.5605814615885549, -160.5466000027126370 68.5550788031684135, -160.5509406195746180 68.5536370171441121, -160.5560852050780909 68.5578036838107749, -160.5605814615885265 68.5588629828559135, -160.5644185384114166 68.5619703504774520, -160.5958333333333030 68.5625000000000142, -160.5964945475260208 68.5550662570529710, -160.5999999999999659 68.5541666666666885, -160.6022104899088276 68.5547339545356067, -160.6061228434244583 68.5577660454644189, -160.6154517279730669 68.5589006212022696, -160.6175723605685448 68.5506378173828352, -160.6355438232421591 68.5494327121310931, -160.6394561767577898 68.5464006212022667, -160.6522104899088390 68.5452660454644302, -160.6561228434244413 68.5422339545356039, -160.6772104899088163 68.5410993787977532, -160.6811228434244470 68.5380672878689410, -160.6897104899088333 68.5369327121310903, -160.6936228434244640 68.5339006212022781, -160.6980438232421591 68.5327660454644274, -160.7019561767577898 68.5297339545356010, -160.7063771565754848 68.5285993787977645, -160.7102895100911155 68.5255672878689381, -160.7188771565755019 68.5244327121310874, -160.7227895100911326 68.5214006212022753, -160.7283159044053491 68.5199825710720631, -160.7291666666666288 68.5166666666666799, -160.7522104899088333 68.5172339545356124, -160.7561228434244640 68.5202660454644246, -160.7632466634114223 68.5214006212022753, -160.7686228434244526 68.5172339545356124, -160.7840799967447651 68.5160993787977617, -160.7894561767577954 68.5202660454644246, -160.8090799967447708 68.5214006212022753, -160.8145965576171648 68.5171251085069599, -160.8352895100911155 68.5369327121310903, -160.8397104899088390 68.5380672878689410, -160.8436228434244413 68.5410993787977532, -160.8491492377386862 68.5425174289279653, -160.8505672878688983 68.5480438232422102, -160.8560421413845347 68.5546084933810960, -160.8617858886718466 68.5533209906684249, -160.8630672878688870 68.5436228434245010, -160.8676795111761919 68.5380923800998403, -160.9112850613063870 68.5369327121310903, -160.9134056939018933 68.5451955159505388, -160.9313771565755076 68.5464006212022667, -160.9352895100911098 68.5494327121310931, -160.9590799967447765 68.5505672878689438, -160.9644561767577784 68.5464006212022667, -160.9772104899088276 68.5452660454644302, -160.9811228434244583 68.5422339545356039, -160.9897104899088163 68.5410993787977532, -160.9948655870225593 68.5371039496527885, -160.9966840955945884 68.5300174289279624, -161.0033159044053548 68.5283159044054031, -161.0050174289279141 68.5216840955946367, -161.0105438232421591 68.5202660454644246, -161.0144561767577898 68.5172339545356124, -161.0313771565755019 68.5160993787977617, -161.0352895100911326 68.5130672878689353, -161.0382466634114280 68.5119327121310846, -161.0436228434244583 68.5160993787977617, -161.0532792833116105 68.5173750135633810, -161.0547339545355783 68.5230438232422046, -161.0587293836805429 68.5281989203559192, -161.0647104899088333 68.5297339545356010, -161.0698655870225480 68.5337293836805657, -161.0714006212022298 68.5397104899088703, -161.0759904649522412 68.5452141655816121, -161.0980438232421648 68.5464006212022667, -161.1019561767577954 68.5494327121310931, -161.1257466634114337 68.5505672878689438, -161.1311228434244640 68.5464006212022667, -161.1416666666666515 68.5458333333333485, -161.1423085530598769 68.5658237033420335, -161.1505405002170050 68.5672715928819656, -161.1561604817707973 68.5631049262152885, -161.1778377956813983 68.5618950737847399, -161.1801510281032677 68.5702016194661610, -161.2041666666666515 68.5708333333333542, -161.2063771565754848 68.5714006212022724, -161.2102895100911155 68.5744327121310846, -161.2188771565755019 68.5755672878689353, -161.2227895100911326 68.5785993787977617, -161.2283159044053491 68.5800174289279596, -161.2300838894313983 68.5869072808159927, -161.2772104899088390 68.5880672878689381, -161.2811228434244697 68.5910993787977645, -161.2833333333333030 68.5916666666666828, -161.2842451307508327 68.5881140814887260, -161.3105438232421704 68.5869327121310874, -161.3198961046006730 68.5785993787977617, -161.3230438232421591 68.5797339545356124, -161.3269561767577898 68.5827660454644246, -161.3291666666666515 68.5833333333333428, -161.3315911187065694 68.5829800075954950, -161.3350755479600593 68.5795199924045278, -161.3416666666666401 68.5791666666666799, -161.3521474202473769 68.5785363091362967, -161.3603525797525720 68.5714636908637374, -161.3771474202473826 68.5702029758029710, -161.3812499999999659 68.5672505696614678, -161.3853525797525776 68.5702029758029710, -161.4239718967013744 68.5714965820312727, -161.4256303575303662 68.5771474202474138, -161.4299692789713276 68.5826300726996720, -161.4521474202473712 68.5839636908637260, -161.4561859130859034 68.5868696424696367, -161.4615546332465215 68.5884453667534899, -161.4635314941405966 68.5951812744140739, -161.5212297227647298 68.5964636908637289, -161.5270833333333087 68.5922505696614735, -161.5327524820963276 68.5963307698567917, -161.5450775146484261 68.5950744628906364, -161.5465372721353958 68.5804487440321395, -161.5503963894313983 68.5785363091362967, -161.5561859130859261 68.5827029758029596, -161.5604807535807197 68.5839636908637260, -161.5654354519313927 68.5875291612413349, -161.5672970241970177 68.5811859130859602, -161.5702029758029425 68.5771474202474138, -161.5720645480685675 68.5708041720920249, -161.5770192464192405 68.5743696424696338, -161.5878963894313927 68.5756303575304003, -161.5936859130859204 68.5714636908637374, -161.6499999999999773 68.5708333333333542, -161.6508507622612569 68.5675174289279710, -161.6579372829860972 68.5656989203559135, -161.6630672878688983 68.5590799967448135, -161.6618262396918340 68.5467298719618157, -161.6550174289279198 68.5449825710720688, -161.6535993787977361 68.5394561767578239, -161.6505672878689097 68.5355438232422074, -161.6499999999999773 68.5291666666666828, -161.6607577853732494 68.5288133409288349, -161.6684088812933737 68.5211866590711907, -161.6916666666666345 68.5208333333333428, -161.6922339545355669 68.5272104899088674, -161.6952660454643933 68.5311228434244981, -161.6967329237195941 68.5368387858073049, -161.7116004096137090 68.5381612141927263, -161.7133110894097001 68.5448265923394189, -161.7174133300781023 68.5464006212022667, -161.7240322536892165 68.5412706163194656, -161.7259107801649236 68.5339504665798813, -161.7507466634114337 68.5327660454644274, -161.7561228434244640 68.5369327121310903, -161.7772104899088390 68.5380672878689410, -161.7811228434244697 68.5410993787977532, -161.7924133300781193 68.5422339545356039, -161.7977895100911212 68.5380672878689410, -161.8230438232421591 68.5369327121310903, -161.8269561767577898 68.5339006212022781, -161.8313771565755133 68.5327660454644274, -161.8352895100911155 68.5297339545356010, -161.8522104899088276 68.5285993787977645, -161.8561228434244583 68.5255672878689381, -161.8647104899088447 68.5244327121310874, -161.8686228434244754 68.5214006212022753, -161.8907575819227134 68.5202097574869953, -161.8916666666666515 68.5166666666666799, -161.8910993787977191 68.5144561767578324, -161.8869327121310562 68.5090799967448021, -161.8881737603081490 68.4967298719618185, -161.8952097574869526 68.4949242485894274, -161.8958333333333144 68.4833333333333485, -161.9024244520399236 68.4829800075955006, -161.9059088812933851 68.4795199924045335, -161.9083333333333030 68.4791666666666714, -161.9357577853732550 68.4788133409288378, -161.9434088812933794 68.4711866590711935, -161.9482577853732437 68.4704800075954978, -161.9517422146267336 68.4670199924045306, -161.9565911187065694 68.4663133409288349, -161.9600755479600593 68.4628533257378677, -161.9642130533854072 68.4621466742621720, -161.9684088812933851 68.4663133409288349, -161.9808797200520587 68.4670199924045306, -161.9850755479600650 68.4628533257378677, -161.9874999999999829 68.4625000000000199, -161.9880672878689154 68.4647104899088674, -161.9952660454644047 68.4727895100911610, -161.9967390272352361 68.4785288492838617, -162.0147104899088504 68.4797339545356039, -162.0186228434244526 68.4827660454644302, -162.0230438232421761 68.4839006212022667, -162.0269561767578068 68.4869327121310931, -162.0355438232421648 68.4880672878689438, -162.0394561767577954 68.4910993787977560, -162.0480438232421818 68.4922339545356067, -162.0531989203558965 68.4962293836805713, -162.0547339545355783 68.5022104899088617, -162.0577660454644047 68.5061228434244924, -162.0589006212022412 68.5105438232422017, -162.0619327121310675 68.5144561767578324, -162.0634128146701300 68.5202236599392478, -162.0924133300781023 68.5214006212022753, -162.0977895100911326 68.5172339545356124, -162.1022104899088276 68.5160993787977617, -162.1061228434244583 68.5130672878689353, -162.1257466634114337 68.5119327121310846, -162.1311228434244640 68.5160993787977617, -162.1509372287326300 68.5172339545356124, -162.1602895100911326 68.5089006212022724, -162.1632466634114564 68.5077660454644217, -162.1686228434244583 68.5119327121310846, -162.1833333333333087 68.5125000000000028, -162.1938142564561360 68.5118696424696338, -162.2020190768771499 68.5047970241970603, -162.2438142564561474 68.5035363091362939, -162.2478524102104984 68.5006303575303974, -162.2521475897894732 68.4993696424696310, -162.2577524820963504 68.4953358968099053, -162.2701276991102191 68.4965972900390767, -162.2708333333333144 68.5083333333333542, -162.2708333333333144 68.5125000000000028, -162.2857577853732494 68.5121466742621692, -162.2892422146267108 68.5086866590712020, -162.2958333333333201 68.5083333333333400, -162.2970967610677064 68.5042419433593892, -162.3062110053168396 68.4964887830946338, -162.3104556613498062 68.4951778835720688, -162.3145443386501654 68.4923221164279710, -162.3229556613498232 68.4910112169053917, -162.3289208306206604 68.4868445502387289, -162.3396223280164747 68.4881554497612939, -162.3437110053168340 68.4910112169053917, -162.3479556613498005 68.4923221164279710, -162.3520443386501597 68.4951778835720688, -162.3604556613498175 68.4964887830946338, -162.3664208306206547 68.5006554497612967, -162.3742563883463390 68.4990837944878592, -162.3757578531900947 68.4799028184678917, -162.3989347669813128 68.4784498426649435, -162.4013147989908816 68.4707421196831802, -162.4080874972873119 68.4660112169054003, -162.4104556613498005 68.4673221164279653, -162.4164208306206376 68.4714887830946282, -162.4199469672308851 68.4695370144314381, -162.4214887830946168 68.4645443386501853, -162.4243445502387146 68.4604556613498403, -162.4249999999999829 68.4583333333333428, -162.4541666666666515 68.4583333333333428, -162.4605438232421761 68.4577660454644246, -162.4699137369791515 68.4494171142578267, -162.4714006212022355 68.4436228434244924, -162.4759195963541458 68.4382042778862996, -162.4866126166449476 68.4367916531033131, -162.4874999999999829 68.4333333333333371, -162.4916666666666458 68.4333333333333371, -162.4920199924044937 68.4357577853732693, -162.4954800075954608 68.4392422146267450, -162.4958333333333087 68.4416666666666771, -162.5024244520399179 68.4420199924045249, -162.5059088812933794 68.4454800075954921, -162.5208333333333144 68.4458333333333400, -162.5232577853732323 68.4454800075954921, -162.5309088812933851 68.4378533257378621, -162.5440911187065751 68.4371466742621664, -162.5475755479600366 68.4336866590711850, -162.5600463867187386 68.4329800075954893, -162.5642422146267165 68.4371466742621664, -162.5666666666666345 68.4375000000000142, -162.5690911187065808 68.4371466742621664, -162.5788089328341925 68.4274549696180685, -162.5791666666666515 68.4250000000000114, -162.5798278808593693 68.4175662570529539, -162.5855438232421761 68.4160993787977532, -162.5894561767578068 68.4130672878689268, -162.5991126166449590 68.4117916531033075, -162.6009090847439040 68.4047902425130303, -162.6230438232421704 68.4035993787977503, -162.6284200032552008 68.3994327121310874, -162.6480438232421761 68.4005672878689381, -162.6534200032551780 68.4047339545356010, -162.6563771565755019 68.4035993787977503, -162.6602895100911326 68.4005672878689381, -162.6730438232421818 68.3994327121310874, -162.6769561767577841 68.3964006212022610, -162.6897104899088333 68.3952660454644104, -162.6936228434244640 68.3922339545355982, -162.6980438232421591 68.3910993787977475, -162.7019561767577898 68.3880672878689353, -162.7313771565754905 68.3869327121310846, -162.7352895100911212 68.3839006212022582, -162.7438771565755076 68.3827660454644217, -162.7477895100911383 68.3797339545355953, -162.7605438232421591 68.3785993787977446, -162.7686228434244526 68.3714006212022696, -162.7772104899088390 68.3702660454644189, -162.7811228434244697 68.3672339545355925, -162.7874999999999943 68.3666666666666742, -162.7874999999999943 68.3500000000000085, -162.7686228434244526 68.3505672878689268, -162.7636372884114451 68.3544311523437642, -162.7619327121310562 68.3477895100911468, -162.7577660454643933 68.3424133300781307, -162.7589006212022298 68.3394561767578210, -162.7619327121310562 68.3355438232421903, -162.7630672878688927 68.3144561767578153, -162.7661980523003251 68.3104166666666686, -162.7619327121310562 68.3049133300781364, -162.7635067409939040 68.3008110894097342, -162.7701249864366275 68.2991126166449760, -162.7714006212022468 68.2894561767578239, -162.7744327121310448 68.2855438232421932, -162.7749999999999773 68.2833333333333456, -162.7746466742621294 68.2809088812934135, -162.7711866590711622 68.2774244520399378, -162.7704752604166458 68.2725429958767478, -162.7645836724175297 68.2666809082031278, -162.7607577853732437 68.2704800075954950, -162.7541666666666629 68.2708333333333428, -162.7548221164279312 68.2645443386501825, -162.7589887830945941 68.2585791693793453, -162.7576158311631787 68.2299126519097285, -162.7521223280164691 68.2256554497612910, -162.7466457790798415 68.2239644368489593, -162.7450208875867759 68.2135355631510549, -162.7376654730902601 68.2112643771701386, -162.7214887830945997 68.1937889946831604, -162.7200208875867986 68.1843688964843864, -162.7145443386501427 68.1826778835720546, -162.7104556613498119 68.1798221164279568, -162.6881869845919937 68.1784261067708428, -162.6874999999999716 68.1500000000000057, -162.6883507622612797 68.1466840955946225, -162.6964006212022298 68.1446183946397639, -162.6952660454643933 68.1227895100911525, -162.6922339545355669 68.1188771565755218, -162.6916666666666345 68.1125000000000114, -162.6987494574652544 68.1117775811089672, -162.7006429036458144 68.1076995849609546, -162.6915167914496294 68.0970426771376083, -162.6829111735025890 68.0944517347547986, -162.6840857611761919 68.0798356797960338, -162.7489451090494583 68.0785037570529710, -162.7506429036458258 68.0728649563259722, -162.7633117675781023 68.0590074327257213, -162.8146348741319400 68.0576905992296162, -162.8186984592013573 68.0548094007704236, -162.8367251925998005 68.0534274631076528, -162.8374999999999773 68.0458333333333343, -162.8381910536024009 68.0216296725803176, -162.8521223280164634 68.0201778835720745, -162.8603776719834855 68.0131554497612996, -162.8839887830945941 68.0116753472222371, -162.8826778835720290 67.9812110053168652, -162.8798221164279312 67.9771223280165202, -162.8781836615668226 67.9718163384331859, -162.8728776719835025 67.9701778835720631, -162.8687889946831433 67.9673221164279653, -162.8666666666666458 67.9666666666666828, -162.8666666666666458 67.9625000000000199, -162.8857577853732437 67.9621466742621720, -162.8892422146267336 67.9586866590712049, -162.8940911187065694 67.9579800075955092, -162.8979173448350650 67.9541805691189467, -162.9059088812933851 67.9621466742621720, -162.9267130533854129 67.9628533257378677, -162.9309088812933908 67.9586866590712049, -162.9357577853732550 67.9579800075955092, -162.9392422146267165 67.9545199924045278, -162.9416666666666629 67.9541666666666799, -162.9410993787977304 67.9605438232422046, -162.9380672878689040 67.9644561767578352, -162.9369327121310675 67.9779517279731067, -162.9438771565754962 67.9797339545356039, -162.9477895100911269 67.9827660454644302, -162.9605438232421761 67.9839006212022809, -162.9644561767578068 67.9869327121310931, -162.9840799967447822 67.9880672878689438, -162.9894561767577841 67.9839006212022809, -163.0230438232421761 67.9827660454644302, -163.0269561767578068 67.9797339545356039, -163.0291666666666401 67.9791666666666714, -163.0357577853732494 67.9788133409288378, -163.0392422146267108 67.9753533257378706, -163.0440911187065751 67.9746466742621749, -163.0475755479600650 67.9711866590712077, -163.0565911187065922 67.9704800075955120, -163.0600755479600537 67.9670199924045306, -163.0732577853732437 67.9663133409288349, -163.0767422146267336 67.9628533257378677, -163.0899244520399236 67.9621466742621720, -163.0975755479600480 67.9545199924045278, -163.1065911187065751 67.9538133409288463, -163.1163089328341869 67.9441218058268390, -163.1170199924044937 67.9392422146267592, -163.1204800075954608 67.9357577853732835, -163.1211866590711566 67.9309088812934192, -163.1246466742621237 67.9274244520399577, -163.1249999999999716 67.8958333333333570, -163.1246419270833314 67.8933764987521897, -163.1190911187065922 67.8878533257378649, -163.1142422146267279 67.8871466742621692, -163.1065911187065751 67.8795199924045392, -163.1041666666666572 67.8791666666666913, -163.0923804389105669 67.8784279717339558, -163.0910112169053718 67.8478776719835253, -163.0881554497612740 67.8437889946831802, -163.0874999999999773 67.8375000000000199, -163.0883507622612569 67.8341842651367415, -163.0938771565755019 67.8327660454644246, -163.1019561767577954 67.8255672878689495, -163.1063771565755189 67.8244327121310988, -163.1102895100911212 67.8214006212022724, -163.1162706163194400 67.8198655870225906, -163.1202660454644047 67.8147104899088760, -163.1217424180772468 67.8089569091797131, -163.1333333333333258 67.8083333333333513, -163.1357577853732437 67.8079800075955035, -163.1392422146267336 67.8045199924045363, -163.1440911187065694 67.8038133409288406, -163.1475755479600593 67.8003533257378592, -163.1565911187065865 67.7996466742621777, -163.1600755479600480 67.7961866590711963, -163.1649244520399122 67.7954800075955006, -163.1684088812934021 67.7920199924045335, -163.1708333333333201 67.7916666666666856, -163.1732577853732380 67.7920199924045335, -163.1767422146267279 67.7954800075955006, -163.1815911187065922 67.7961866590711963, -163.1850755479600537 67.7996466742621777, -163.1933795505099738 67.8003533257378592, -163.1979166666666572 67.7958479139540060, -163.2017422146267336 67.7996466742621777, -163.2190911187065865 67.8003533257378592, -163.2225755479600480 67.8038133409288406, -163.2308831108940694 67.8045199924045363, -163.2434088812933908 67.7920199924045335, -163.2458333333333087 67.7916666666666856, -163.2449824015299384 67.7883509318034072, -163.2369327121310505 67.7862850613064438, -163.2383076985676951 67.7800286187066092, -163.2449824015299384 67.7783157348632983, -163.2466842651367074 67.7716842651367415, -163.2500000000000000 67.7708333333333570, -163.2605438232421875 67.7702660454644246, -163.2644561767577898 67.7672339545356124, -163.2674133300781136 67.7660993787977617, -163.2727895100911439 67.7702660454644246, -163.2966834174262090 67.7714006212022753, -163.3034623887803605 67.7657472398546190, -163.3048751831054517 67.7550540500217267, -163.3118764241536383 67.7532574123806626, -163.3130672878689040 67.7311228434245010, -163.3170625474717781 67.7259677463107863, -163.3230438232421875 67.7244327121310903, -163.3352895100911439 67.7130672878689381, -163.3406599256727247 67.7116890801324018, -163.3422339545355726 67.7075866699218949, -163.3380672878689097 67.7022104899088788, -163.3374999999999773 67.7000000000000171, -163.3371466742621294 67.6725755479600934, -163.3336866590711622 67.6690911187066177, -163.3333333333333144 67.6666666666666856, -163.3311228434244526 67.6672339545356039, -163.3257466634114508 67.6714006212022809, -163.3061228434244754 67.6702660454644302, -163.3022104899088447 67.6672339545356039, -163.2842390272352304 67.6660290188259665, -163.2833333333333314 67.6625000000000227, -163.2836866590711793 67.6559088812934135, -163.2871466742621465 67.6524244520399520, -163.2874999999999943 67.6333333333333542, -163.3041666666666458 67.6333333333333542, -163.3107577853732550 67.6329800075955063, -163.3246458265516310 67.6191175672743299, -163.3253533257378365 67.5982871161566976, -163.3211866590711736 67.5940911187066149, -163.3204800075954779 67.5725755479600849, -163.3125135633680429 67.5645836724175552, -163.3187340630425126 67.5583319769965414, -163.3225755479600423 67.5621466742621664, -163.3308810763888630 67.5628533257378621, -163.3371443006727191 67.5566211276584312, -163.3378533257378251 67.5475755479600934, -163.3413133409288207 67.5440911187066177, -163.3420199924044880 67.5350755479600906, -163.3454800075954836 67.5315911187066149, -163.3458333333333314 67.5291666666666828, -163.3458333333333314 67.5250000000000199, -163.3467329237195997 67.5214945475260606, -163.3541666666666572 67.5208333333333428, -163.3535993787977247 67.5144561767578324, -163.3505672878688983 67.5105438232422017, -163.3492957221137090 67.5009194268120893, -163.3438771565755019 67.4964006212022696, -163.3259938557942519 67.4952014499240676, -163.3160993787977304 67.4842929416232806, -163.3172893948025148 67.4634875827365619, -163.3227895100911269 67.4589006212022753, -163.3272104899088504 67.4577660454644246, -163.3311228434244526 67.4547339545356124, -163.3438771565755019 67.4535993787977617, -163.3477895100911326 67.4505672878689353, -163.3535524156358463 67.4490883721245780, -163.3547809176974681 67.4217449612087876, -163.3605438232421818 67.4202660454644302, -163.3644561767578125 67.4172339545356039, -163.3772104899088333 67.4160993787977532, -163.3811228434244640 67.4130672878689410, -163.3897104899088504 67.4119327121310903, -163.3936228434244526 67.4089006212022781, -163.3991490681966070 67.4074824015299612, -163.3999999999999773 67.4041666666666828, -163.3993949042425982 67.3769939846462762, -163.3952282375759353 67.3713740030924697, -163.3964384290906935 67.3353273179796190, -163.4131050957573734 67.3175486246745010, -163.4116809421115306 67.3090504964192888, -163.3978273179795906 67.3077282375759722, -163.3938393486870666 67.3047717624240533, -163.3853273179796020 67.3035615709093094, -163.3812499999999943 67.3005388047960196, -163.3755406697590900 67.3047717624240533, -163.3394939846462535 67.3035615709093094, -163.3355060153537295 67.3006050957573905, -163.3311606513129277 67.2993949042426323, -163.3271726820203753 67.2964384290907276, -163.3186606513129107 67.2952282375759694, -163.3105060153537238 67.2881050957573876, -163.2394939846462592 67.2868949042426436, -163.2355060153537067 67.2839384290907248, -163.2311606513129334 67.2827282375759665, -163.2271726820203810 67.2797717624240619, -163.2208333333333314 67.2791666666666828, -163.2183764987521499 67.2788085937500142, -163.2128533257378251 67.2732577853732749, -163.2121419270833087 67.2683764987521897, -163.2065911187065694 67.2628533257378649, -163.2041666666666515 67.2625000000000171, -163.2017422146267336 67.2628533257378649, -163.1933822631835938 67.2711866590711907, -163.1767422146267279 67.2704800075954950, -163.1690911187065751 67.2628533257378649, -163.1600755479600480 67.2621466742621692, -163.1482577853732607 67.2503533257378621, -163.1458333333333144 67.2500000000000142, -163.1449825710720347 67.2466842651367358, -163.1394561767577898 67.2452660454644189, -163.1355438232421591 67.2422339545356067, -163.1311228434244640 67.2410993787977560, -163.1272104899088333 67.2380672878689438, -163.0727895100911269 67.2369327121310931, -163.0647104899088333 67.2297339545356039, -163.0519561767577841 67.2285993787977532, -163.0426038953992816 67.2202660454644274, -163.0311228434244697 67.2214006212022781, -163.0272104899088390 67.2244327121310903, -163.0227895100911439 67.2255672878689410, -163.0174133300781136 67.2297339545356039, -163.0144561767577898 67.2285993787977532, -163.0105438232421591 67.2255672878689410, -163.0083333333333258 67.2250000000000085, -163.0079800075954779 67.2225755479600906, -162.9961866590711566 67.2107577853732749, -162.9954800075954608 67.2059088812934107, -162.9836866590711679 67.1940911187066092, -162.9833333333333201 67.1916666666666771, -162.9809088812933737 67.1920199924045249, -162.9774244520399122 67.1954800075955063, -162.9517422146267336 67.1961866590712020, -162.9440911187065808 67.2038133409288321, -162.9333333333333087 67.2041666666666799, -162.9225755479600650 67.2038133409288321, -162.9190911187065751 67.2003533257378649, -162.9142422146267108 67.1996466742621692, -162.9107577853732494 67.1961866590712020, -162.8475755479600480 67.1954800075955063, -162.8440911187065865 67.1920199924045249, -162.8416666666666401 67.1916666666666771, -162.8406185574001483 67.1881859673394217, -162.8103651258680316 67.1868572658962790, -162.8063015407985858 67.1839760674370865, -162.7978651258680429 67.1826905992296162, -162.7896348741319343 67.1756427341037465, -162.7811984592013630 67.1743572658962762, -162.7771348741319173 67.1714760674370837, -162.7593712700737569 67.1701143052843008, -162.7573689778645587 67.1634641859266566, -162.7520317925346944 67.1618572658962876, -162.7479682074652487 67.1589760674370808, -162.7436984592013687 67.1576905992296105, -162.7396348741319230 67.1548095703125085, -162.7311984592013800 67.1535237630208428, -162.7229682074652715 67.1464762369791828, -162.7091047498914804 67.1450619167751910, -162.7075127495659501 67.1294559054904596, -162.7020833333333201 67.1256062825520985, -162.6979682074652658 67.1285237630208513, -162.6936984592013573 67.1298095703125171, -162.6839670817057026 67.1381429036458428, -162.6728651258680429 67.1368570963541771, -162.6646348741319343 67.1298095703125171, -162.6478651258680372 67.1285237630208513, -162.6438015407985915 67.1256429036458542, -162.6340491400824533 67.1241566975911610, -162.6333333333333258 67.1125000000000114, -162.6257198757595290 67.1132765028211935, -162.6249999999999716 67.1250000000000142, -162.6102895100911212 67.1244327121310960, -162.6063771565754905 67.1214006212022696, -162.5769561767577898 67.1202660454644189, -162.5715799967447595 67.1160993787977560, -162.5436228434244583 67.1172339545356067, -162.5382466634114280 67.1214006212022696, -162.5227895100911155 67.1202660454644189, -162.5174133300781136 67.1160993787977560, -162.5061228434244640 67.1172339545356067, -162.5022104899088333 67.1202660454644189, -162.4977895100911383 67.1214006212022696, -162.4938771565755076 67.1244327121310960, -162.4686228434244697 67.1255672878689325, -162.4632466634114394 67.1297339545356095, -162.4508965386284558 67.1284929063585167, -162.4499999999999886 67.1250000000000142, -162.4505672878688927 67.1227895100911667, -162.4547339545355840 67.1174133300781364, -162.4535993787977191 67.1061228434244867, -162.4505672878688927 67.1022104899088703, -162.4493442111544823 67.0884599473741474, -162.4438771565754962 67.0839006212022753, -162.4352895100911383 67.0827660454644246, -162.4313771565755076 67.0797339545355982, -162.4255984836154312 67.0782511393229299, -162.4249999999999829 67.0583333333333513, -162.4572784423828011 67.0576704237196282, -162.4593760172525947 67.0507035997178917, -162.4822906494140398 67.0492964002821310, -162.4843634711371294 67.0424116346571282, -162.4989698621961622 67.0409216986762289, -162.5009643554687386 67.0342976888020985, -162.5073689778645587 67.0323689778645928, -162.5092976888020644 67.0259643554687585, -162.5159216986762090 67.0239698621961963, -162.5174435085720290 67.0090511745876825, -162.5402187771267108 67.0076904296875142, -162.5427195231119697 67.0159969753689353, -162.5902187771267222 67.0173095703125057, -162.5926523844400720 67.0092258029514056, -162.5990844726562443 67.0076904296875142, -162.6007720947265511 67.0242353651258753, -162.6153154161241048 67.0256429036458456, -162.6173095703124716 67.0181145562066121, -162.6092556423610915 67.0156897650824845, -162.6076904296874943 67.0097812228732721, -162.6157023111979072 67.0073689778646013, -162.6176435682508554 67.0009223090277857, -162.6235521104600537 66.9993570963541742, -162.6260481092664634 67.0076473659939325, -162.6575544569227247 67.0090311686197992, -162.6590783013237740 67.0239698621961963, -162.6657023111978901 67.0259643554687585, -162.6676968044704665 67.0325883653428889, -162.6825619167751427 67.0341047498915117, -162.6841122097439154 67.0493021647135521, -162.6999999999999886 67.0500000000000114, -162.7041666666666515 67.0500000000000114, -162.7905846489800297 67.0493370903863024, -162.7927480061848655 67.0423312717013999, -162.9325917561848769 67.0409990098741417, -162.9340674506293283 67.0174526638455035, -162.9485110812716755 67.0160112169053974, -162.9510738796657847 67.0243106418185874, -162.9905927870008497 67.0256893581814381, -162.9927446153428718 67.0326578776041799, -163.0572553846571111 67.0340087890625114, -163.0594126383463447 67.0409952799479356, -163.1405873616536439 67.0423380533854356, -163.1427446153428775 67.0493245442708456, -163.2072553846571168 67.0506754557291771, -163.2094131469726506 67.0576636420356067, -163.2989201863606752 67.0590030246310960, -163.3010779486762090 67.0659912109375114, -163.3655887179904482 67.0673421223958428, -163.3677424112955521 67.0743170844184107, -163.4155909220377509 67.0756829155816092, -163.4177464803059650 67.0826636420355982, -163.5072535196939896 67.0840030246310874, -163.5094138251410527 67.0909990098741389, -163.6155861748589189 67.0923343234592124, -163.6177437676323621 67.0993211534288321, -163.6739228990342667 67.1006788465711992, -163.6760708279079779 67.1076344807942888, -163.7072625054253479 67.1090321858724081, -163.7093985663519788 67.1159498426649463, -163.7322681003146556 67.1173834906684164, -163.7343163384331604 67.1240169949001881, -163.7409910413953753 67.1260779486762260, -163.7416666666666458 67.1583333333333456, -163.7416666666666458 67.1625000000000085, -163.7422473483615306 67.2074130588107721, -163.7493764241536383 67.2092425876193715, -163.7506235758463333 67.2324240790473198, -163.7577097574869640 67.2342425876193772, -163.7589569091796875 67.2574240790473254, -163.7660054524739621 67.2592329237196367, -163.7673278808593693 67.2741004096137374, -163.7743387858072879 67.2758995903863024, -163.7756612141926951 67.2907670762804031, -163.7824824015299328 67.2925175984700701, -163.7841842651367017 67.2991490681966269, -163.7908157348632585 67.3008509318033958, -163.7925175984700559 67.3074824015299669, -163.7993387858072936 67.3092329237196338, -163.8006612141927008 67.3241004096137345, -163.8074824015299384 67.3258509318034015, -163.8091842651367074 67.3324824015299583, -163.8160430908203011 67.3342425876193715, -163.8166666666666629 67.3458333333333456, -163.8166666666666629 67.3500000000000227, -163.8241004096137203 67.3506612141927263, -163.8258509318033873 67.3574824015299640, -163.8326721191406250 67.3592329237196310, -163.8339945475260322 67.3741004096137317, -163.8408157348632699 67.3758509318033987, -163.8425175984700388 67.3824824015299697, -163.8493387858072765 67.3842329237196367, -163.8506612141927121 67.3991004096137374, -163.8574824015299498 67.4008509318034044, -163.8591842651367188 67.4074824015299612, -163.8658157348632756 67.4091842651367301, -163.8675175984700445 67.4158157348633011, -163.8741490681966013 67.4175175984700701, -163.8758509318033703 67.4241490681966269, -163.8824824015299271 67.4258509318033958, -163.8841842651366960 67.4324824015299669, -163.8908157348632812 67.4341842651367358, -163.8925662570529482 67.4410054524739735, -163.9074337429470347 67.4423278808593949, -163.9091842651367017 67.4491490681966326, -163.9158157348632585 67.4508509318034015, -163.9175175984700559 67.4574824015299583, -163.9241490681966127 67.4591842651367415, -163.9258509318033816 67.4658157348632983, -163.9326721191406193 67.4675662570529653, -163.9333333333333371 67.4750000000000227, -163.9406365288628251 67.4757449679904653, -163.9426310221354015 67.4823691474067004, -163.9490356445312216 67.4842975192599965, -163.9509643554687273 67.4907024807400404, -163.9573689778645473 67.4926308525933365, -163.9592976888020530 67.4990358140733662, -163.9657023111979015 67.5009641859266623, -163.9676310221353788 67.5073691474067061, -163.9740356445312273 67.5092975192600022, -163.9759643554687329 67.5157024807400319, -163.9823689778645530 67.5176308525933280, -163.9843634711371294 67.5242550320095631, -163.9989698621961622 67.5257449679904624, -164.0009643554687386 67.5323691474066976, -164.0073689778645587 67.5342975192599937, -164.0092976888020644 67.5407024807400376, -164.0157023111978845 67.5426308525933337, -164.0176310221353901 67.5490358140733633, -164.0240356445312386 67.5509641859266594, -164.0260301378037866 67.5575883653428946, -164.0406365288628194 67.5590783013238081, -164.0426310221353958 67.5657024807400290, -164.0490356445312159 67.5676308525933393, -164.0510301378037923 67.5742550320095603, -164.0656365288628251 67.5757449679904738, -164.0676310221354015 67.5823691474066948, -164.0740356445312216 67.5842975192600051, -164.0760301378037980 67.5909216986762260, -164.0906365288628308 67.5924116346571395, -164.0927093505859204 67.5992964002821282, -164.1156239827473655 67.6007035997179031, -164.1176968044704552 67.6075883653429059, -164.1323031955294880 67.6090783013238052, -164.1343634711371351 67.6159216986762317, -164.1492285834418112 67.6174380832248403, -164.1499999999999773 67.6250000000000142, -164.1657311333550240 67.6256206936306654, -164.1673233628639537 67.6316557570920338, -164.1709787518346673 67.6328458662276830, -164.1990644666883554 67.6339540269639912, -164.2009311252169823 67.6410296969943801, -164.2240688747829722 67.6423036363389940, -164.2259355333115991 67.6493793063693687, -164.2573978000216925 67.6506206936306569, -164.2591221588016310 67.6571565696514625, -164.2607574462890625 67.6575546264648438, -164.2616948096438989 67.6578083586400396, -164.2907311333550240 67.6589540269639969, -164.2926045735677008 67.6660551283094804, -164.3323954264322708 67.6672782050238908, -164.3342644585503365 67.6743630303277115, -164.3574022081162980 67.6756369696723254, -164.3592688666449533 67.6827126397027143, -164.3907311333550183 67.6839540269639883, -164.3926021999782847 67.6910459730360401, -164.4240644666883497 67.6922873602973283, -164.4259311252170050 67.6993630303277030, -164.4490688747829665 67.7006369696723311, -164.4509355333116218 67.7077126397027058, -164.4823978000216869 67.7089540269639940, -164.4842644585503137 67.7160296969943829, -164.5074520534938927 67.7173063490126026, -164.5089131673177008 67.7237477620442831, -164.5017269781722291 67.7256435999799749, -164.5036163330078125 67.7257537841796875, -164.5479888916015625 67.7386322021484375, -164.5502204739463252 67.7395050829368017, -164.5509155273437329 67.7326905992296133, -164.5589762369791345 67.7346145629882983, -164.5573625352647298 67.7409155951606152, -164.5545579838560570 67.7412016947954072, -164.5758781439687084 67.7495410501460924, -164.5749932183159672 67.7450627644856951, -164.5763888888888573 67.7416666666666885, -164.5825052897135095 67.7423794216580006, -164.5838012695312216 67.7489386664496749, -164.5775142383214416 67.7501810065124062, -164.6226348876953125 67.7678298950195312, -164.6609649658203125 67.7852935791015625, -164.7165462822032111 67.8051102324237718, -164.7162899441188983 67.8046990288628706, -164.7180555555555372 67.8000000000000256, -164.7325676812065751 67.8006898668077440, -164.7332621256510379 67.8075046115451556, -164.7254888321730846 67.8082968934296133, -164.7415771484375000 67.8140182495117188, -164.7423623379930291 67.8148571522831105, -164.7410237630208201 67.8092368231879590, -164.7568854437933794 67.8076905992296162, -164.7593634711371351 67.8159216986762345, -164.7739698621961679 67.8174116346571338, -164.7760426839192576 67.8242964002821367, -164.7989573160807026 67.8257035997178974, -164.8010301378037923 67.8325883653429003, -164.8156365288628251 67.8340783013237996, -164.8177093505859148 67.8409630669488024, -164.8406239827473883 67.8423702663845631, -164.8426968044704779 67.8492550320095660, -164.8573031955295107 67.8507449679904653, -164.8583333333333201 67.8541666666666856, -164.8583333333333201 67.8583333333333485, -164.8824161105685562 67.8589262220594804, -164.8842468261718466 67.8660591973198990, -164.9157531738281079 67.8672741360134779, -164.9175662570529255 67.8743387858073106, -164.9324337429470404 67.8756612141927320, -164.9342424180772468 67.8827097574869924, -164.9574242485894047 67.8839569091797017, -164.9592424180772525 67.8910430908203324, -164.9824242485893819 67.8922902425130417, -164.9842424180772298 67.8993764241536724, -165.0074242485893876 67.9006235758463674, -165.0092329237195941 67.9076721191406421, -165.0241004096137090 67.9089945475260635, -165.0259090847439154 67.9160430908203381, -165.0490909152560732 67.9172902425130474, -165.0509090847439211 67.9243764241536638, -165.0740909152560505 67.9256235758463731, -165.0758995903862569 67.9326721191406477, -165.0907670762803718 67.9339945475260549, -165.0923280598030942 67.9400768948618037, -165.0961032076319555 67.9411950520859023, -165.1240865071614508 67.9422741360134808, -165.1259134928385208 67.9493925306532276, -165.1574200100368728 67.9506074693468065, -165.1592329237195997 67.9576721191406392, -165.1741004096137146 67.9589945475260606, -165.1758995903862797 67.9660054524739792, -165.1833333333333087 67.9666666666666828, -165.1875000000000000 67.9666666666666828, -165.1990907457139599 67.9672902425130303, -165.2008509318033589 67.9741492377387146, -165.2074824015299441 67.9758507622612882, -165.2092329237196111 67.9826721191406307, -165.2241004096136976 67.9839945475260521, -165.2259092542860230 67.9910430908203125, -165.2490907457139713 67.9922902425130218, -165.2509092542860003 67.9993764241536525, -165.2740907457139485 68.0006235758463617, -165.2759092542860060 68.0077097574869782, -165.2990907457139542 68.0089569091796875, -165.3009092542860117 68.0160430908203182, -165.3240907457139599 68.0172902425130275, -165.3259092542860174 68.0243764241536439, -165.3490907457139656 68.0256235758463532, -165.3509156968858349 68.0327348497178832, -165.3907509697808109 68.0339318169487939, -165.3925799899630817 68.0410590277777771, -165.4240866767035527 68.0422743055555657, -165.4259133232964416 68.0493923611111171, -165.4574200100368842 68.0506076388888914, -165.4592503865559650 68.0577406141493100, -165.5074162801106752 68.0589260525173643, -165.5092503865559763 68.0660739474826499, -165.5574162801106581 68.0672593858507042, -165.5592503865559877 68.0744072808159757, -165.6074162801106695 68.0755927191840300, -165.6092503865559706 68.0827406141493157, -165.6574162801106809 68.0839260525173700, -165.6592522515190922 68.0910807291666771, -165.7240810818142336 68.0922526041666742, -165.7259206136067746 68.0994205050998289, -165.8240793863932083 68.1005794949001739, -165.8259170532226392 68.1077406141493071, -165.8740829467773210 68.1089260525173614, -165.8758509318033703 68.1158159044053946, -165.8791666666666629 68.1166666666666742, -165.9072794596353901 68.1173326280381985, -165.9093760172525833 68.1242964002821196, -165.9322906494140284 68.1257035997178946, -165.9343814425998005 68.1326473659939325, -165.9656185574001483 68.1340193006727475, -165.9676968044704495 68.1409216986762232, -165.9825619167751540 68.1424380832248318, -165.9840783013237626 68.1573031955295221, -165.9907023111978788 68.1592976888020843, -165.9926968044704552 68.1659216986762146, -166.0075619167751597 68.1674380832248374, -166.0091047498914634 68.1825619167751853, -166.0239698621961679 68.1840783013237939, -166.0259643554687159 68.1907023111979242, -166.0323689778645644 68.1926310221354299, -166.0343634711371408 68.1992550320095603, -166.0489698621961452 68.2007449679904596, -166.0510301378037923 68.2075883653428860, -166.0656365288628251 68.2090783013237854, -166.0676968044704722 68.2159216986762260, -166.0823031955295050 68.2174116346571253, -166.0843634711371237 68.2242550320095518, -166.0989698621961566 68.2257449679904511, -166.1010426839192462 68.2326297336154539, -166.1239573160807197 68.2340369330512146, -166.1260301378038093 68.2409216986762175, -166.1406365288628137 68.2424116346571310, -166.1427093505859034 68.2492964002821196, -166.1656239827473769 68.2507035997178946, -166.1677093505859091 68.2576297336154596, -166.1906239827473826 68.2590369330512203, -166.1927147759331262 68.2659806993272582, -166.2239518907334741 68.2673526340060874, -166.2260481092664577 68.2743140326605982, -166.2572852240668055 68.2756859673394132, -166.2593814425998175 68.2826473659939239, -166.2906185574001654 68.2840193006727532, -166.2927174886067405 68.2909901936849053, -166.3322825113932026 68.2923431396484375, -166.3343841552734261 68.2993235270182311, -166.3739491780598883 68.3006764729817775, -166.3760528564452841 68.3076633029513971, -166.4239471435546704 68.3090033637152914, -166.4260538736978958 68.3160007052951528, -166.4822794596354072 68.3173326280382014, -166.4843872070312329 68.3243340386284785, -166.5406127929687443 68.3256659613715414, -166.5427222357855612 68.3326727973090300, -166.6156110975477134 68.3339938693576414, -166.6177242702907790 68.3410129123263914, -166.7322757297091869 68.3423204210069457, -166.7343814425998119 68.3493147108290060, -166.7652187771267052 68.3506429036458343, -166.7676981608072708 68.3424068874783046, -166.7818854437933851 68.3410237630208428, -166.7843102349175126 68.3490776909722229, -166.7902187771267108 68.3506429036458343, -166.7927178276909501 68.3423424614800439, -166.8320522732204552 68.3410237630208428, -166.8339762369791401 68.3490844726562585, -166.8176968044704722 68.3507449679904653, -166.8156158447265511 68.3576568603515682, -166.7760508219400890 68.3590098063151146, -166.7739471435546648 68.3659966362847342, -166.7260528564453068 68.3673366970486143, -166.7239471435546818 68.3743299696180600, -166.6760528564452954 68.3756700303819542, -166.6739461263020701 68.3826673719618157, -166.6177205403645587 68.3839992947048643, -166.6156127929687329 68.3910007052951414, -166.5593872070312216 68.3923326280382042, -166.5572794596353958 68.3993340386284814, -166.5010538736978845 68.4006659613715300, -166.4989471435546591 68.4076633029513914, -166.4510528564453011 68.4090033637152857, -166.4489491780598769 68.4159901936849053, -166.4093841552734148 68.4173431396484517, -166.4072852240668112 68.4243140326606039, -166.3916666666666515 68.4250000000000114, -166.3874999999999886 68.4250000000000114, -166.3760426839192519 68.4257035997178917, -166.3739573160807197 68.4326297336154568, -166.3510426839192462 68.4340369330512175, -166.3489698621961566 68.4409216986762203, -166.3343634711371237 68.4424116346571196, -166.3323689778645758 68.4490356445312642, -166.3259643554687273 68.4509643554687557, -166.3239698621961509 68.4575883653428860, -166.3090057373046591 68.4591149224175410, -166.3083333333333087 68.4875000000000114, -166.3083333333333087 68.4916666666666742, -166.3075883653428662 68.4989698621961907, -166.3007035997178491 68.5010426839192803, -166.2992801242404255 68.5242234971788235, -166.2843634711371408 68.5257449679904624, -166.2823689778645644 68.5323689778645928, -166.2759643554687159 68.5342976888020985, -166.2739698621961679 68.5409216986762289, -166.2593634711371351 68.5424116346571282, -166.2573689778645587 68.5490356445312585, -166.2509643554687386 68.5509643554687642, -166.2490356445312329 68.5573689778645985, -166.2426310221353845 68.5592976888020900, -166.2407023111979072 68.5657023111979242, -166.2342976888020587 68.5676310221354299, -166.2323689778645530 68.5740356445312642, -166.2259223090277658 68.5759769015842124, -166.2243570963541401 68.5818854437934107, -166.2326575385199305 68.5843844943576499, -166.2339762369791458 68.6235521104600821, -166.2257019042968409 68.6260430230034757, -166.2243570963541401 68.6485521104600735, -166.2324110243055202 68.6509769015842153, -166.2339762369791458 68.6568854437934135, -166.2259643554687329 68.6592976888020985, -166.2240356445312273 68.6657023111979328, -166.2176310221354072 68.6676310221354242, -166.2157023111979015 68.6740356445312585, -166.2092976888020530 68.6759643554687642, -166.2073689778645473 68.6823689778645985, -166.2006852891709912 68.6843814425998431, -166.1993570963541345 68.7152187771267506, -166.2074110243055429 68.7176435682508782, -166.2089762369791401 68.7235521104600764, -166.2007449679904312 68.7260301378038321, -166.1999999999999886 68.7333333333333485, -166.1999999999999886 68.7375000000000114, -166.1993211534287980 68.7655897352430685, -166.1910112169053662 68.7681555853949789, -166.1923648410373175 68.7989288330078210, -166.1992411295572651 68.8010521782769189, -166.2007588704426837 68.8156144883897696, -166.2076165093315865 68.8177320692274463, -166.2090501573350423 68.8406012641059135, -166.2159077962239451 68.8427188449435903, -166.2174255371093636 68.8572811550564410, -166.2256554497612626 68.8598222520616474, -166.2241797553168112 68.8839887830946225, -166.1094143337673472 68.8826666937934107, -166.1072519938150833 68.8756652832031335, -165.9760813395182026 68.8743347167968807, -165.9739179823133384 68.8673295762803974, -165.8010820176866105 68.8660037570529653, -165.7985110812716698 68.8576778835720660, -165.7926625569661212 68.8592749701605982, -165.7901777479383441 68.8673221164279568, -165.7677324083116162 68.8659515380859517, -165.7655887179904255 68.8590087890625142, -165.7333333333333201 68.8583333333333485, -165.7291666666666572 68.8583333333333485, -165.3092566596137090 68.8589019775390767, -165.3074118720160470 68.8660909016927150, -165.1675881279839189 68.8672424316406335, -165.1657472398545963 68.8744160970052235, -165.0925862630208201 68.8755839029948049, -165.0907450358072879 68.8827575683593807, -164.9509216308593693 68.8839090983072992, -164.9490783691406079 68.8910909016927206, -164.8791666666666629 68.8916666666666799, -164.8749999999999716 68.8916666666666799, -164.8592468261718693 68.8922743055555742, -164.8574117024739394 68.8994242350260606, -164.7175882975260208 68.9005757649739650, -164.7157484266492986 68.9077443440755388, -164.6592515733506730 68.9089223225911525, -164.6574123806423415 68.9160881890191064, -164.6041666666666572 68.9166666666666856, -164.5999999999999943 68.9166666666666856, -164.4342559814452898 68.9172373453776146, -164.4324117024739280 68.9244245741102617, -164.2842549641926837 68.9255754258897753, -164.2824137369791515 68.9327494303385606, -164.2092529296874659 68.9339172363281421, -164.2074161105685448 68.9410739474826499, -164.1592505560980726 68.9422593858507042, -164.1574198404947822 68.9493923611111228, -164.1259134928385208 68.9506076388888971, -164.1240909152560619 68.9577097574869953, -164.1009090847439040 68.9589569091797046, -164.0990909152560562 68.9660430908203210, -164.0759090847438983 68.9672902425130303, -164.0740865071614394 68.9743923611111285, -164.0583333333333087 68.9750000000000085, -164.0541666666666458 68.9750000000000085, -164.0259180704752566 68.9755889892578296, -164.0240819295247263 68.9827443440755275, -163.9675847371419195 68.9839223225911553, -163.9657533433702099 68.9910590277777942, -163.9342466566297674 68.9922743055555685, -163.9324240790473084 68.9993764241536525, -163.9092425876193602 69.0006235758463617, -163.9074337429470347 69.0076721191406364, -163.8925662570529482 69.0089945475260578, -163.8907670762803832 69.0160054524739763, -163.8758995903862683 69.0173278808593835, -163.8741004096137033 69.0243387858073021, -163.8592329237196168 69.0256612141927235, -163.8574240790472913 69.0327097574869981, -163.8342425876193431 69.0339569091796932, -163.8324240790473141 69.0410430908203239, -163.8092425876193374 69.0422902425130331, -163.8074240790473084 69.0493764241536638, -163.7842425876193602 69.0506235758463731, -163.7824176364474624 69.0577348497178889, -163.7425823635525148 69.0589318169487996, -163.7407509697808052 69.0660681830512289, -163.7009156968858292 69.0672651502821253, -163.6991004096136919 69.0743387858072992, -163.6842329237196054 69.0756612141927206, -163.6824337429470404 69.0826721191406392, -163.6675662570529539 69.0839945475260606, -163.6657670762803605 69.0910054524739650, -163.6508995903862740 69.0923278808593864, -163.6490829467773267 69.0994072808159814, -163.6250000000000000 69.1000000000000085, -163.6250000000000000 69.1041666666666856, -163.6241004096137033 69.1076721191406449, -163.6092329237196168 69.1089945475260521, -163.6074240790472913 69.1160430908203267, -163.5842425876193431 69.1172902425130360, -163.5824337429470461 69.1243387858073106, -163.5675662570529312 69.1256612141927178, -163.5657670762803662 69.1326721191406364, -163.5508995903862797 69.1339945475260578, -163.5491004096137146 69.1410054524739763, -163.5342329237195997 69.1423278808593835, -163.5324337429470347 69.1493387858073021, -163.5175662570529482 69.1506612141927235, -163.5158157348632812 69.1574824015299612, -163.5091842651366960 69.1591842651367301, -163.5074824015299271 69.1658157348633011, -163.5008509318033703 69.1675175984700701, -163.4991490681966013 69.1741490681966269, -163.4925175984700445 69.1758509318033958, -163.4907670762803775 69.1826721191406335, -163.4758995903862626 69.1839945475260549, -163.4741004096136976 69.1910054524739735, -163.4592329237196111 69.1923278808593949, -163.4574824015299441 69.1991490681966326, -163.4508509318033589 69.2008509318034015, -163.4490907457139599 69.2077097574869953, -163.4259092542860117 69.2089569091797046, -163.4240907457139542 69.2160430908203210, -163.4009092542860060 69.2172902425130303, -163.3991004096137090 69.2243387858073049, -163.3842329237195941 69.2256612141927263, -163.3824337429470290 69.2326721191406449, -163.3675662570529425 69.2339945475260521, -163.3657670762803775 69.2410054524739706, -163.3508995903862626 69.2423278808593921, -163.3491004096136976 69.2493387858073106, -163.3342329237196111 69.2506612141927178, -163.3324824015299441 69.2574824015299555, -163.3258509318033589 69.2591842651367386, -163.3240866767035584 69.2660591973198905, -163.3083333333333087 69.2666666666666799, -163.3083333333333087 69.2708333333333428, -163.3074337429470404 69.2743387858073021, -163.2925662570529255 69.2756612141927235, -163.2907670762803605 69.2826721191406421, -163.2758995903862740 69.2839945475260492, -163.2741004096137090 69.2910054524739678, -163.2592329237195941 69.2923278808593892, -163.2574824015299271 69.2991490681966269, -163.2508509318033703 69.3008509318033958, -163.2491490681966013 69.3074824015299669, -163.2425175984700445 69.3091842651367358, -163.2407533433702156 69.3160591973198876, -163.2249999999999943 69.3166666666666771, -163.2256752861870552 69.3092544555664176, -163.2324635823567576 69.3074635823567888, -163.2342030843098826 69.3008697509765739, -163.2407969156900833 69.2991302490234489, -163.2425364176432083 69.2925364176432481, -163.2505798339843750 69.2904144287109460, -163.2488420274522412 69.2827534993489706, -163.2259318033854072 69.2839680989583542, -163.2241302490234318 69.2907969156901231, -163.2175364176432026 69.2925364176432481, -163.2157969156900776 69.2991302490234489, -163.2092030843098769 69.3008697509765739, -163.2074635823567519 69.3074635823567888, -163.2008697509765511 69.3092030843099138, -163.1990687052408759 69.3160296969943772, -163.1759312947590956 69.3173036363389912, -163.1741302490234204 69.3241302490234546, -163.1675364176432197 69.3258697509765796, -163.1657311333550240 69.3327126397027058, -163.1499999999999773 69.3333333333333428, -163.1493021647135322 69.3492211235894302, -163.1343634711371351 69.3507449679904653, -163.1323689778645587 69.3573691474067004, -163.1259643554687386 69.3592975192599965, -163.1240356445312329 69.3657024807400262, -163.1176310221354129 69.3676308525933365, -163.1156239827473939 69.3742964002821338, -163.0924146864149122 69.3757215711805770, -163.0912445068359204 69.3902850680881187, -163.1006429036458201 69.3931147257487169, -163.0992980957031193 69.4156234741211051, -163.0924116346571111 69.4176968044704950, -163.0909216986761976 69.4323031955295278, -163.0840783013237569 69.4343634711371749, -163.0825883653428718 69.4489698621961935, -163.0759643554687273 69.4509641859266651, -163.0740356445312216 69.4573691474066948, -163.0660237630208087 69.4597813924153797, -163.0676489935980840 69.4659167819553147, -163.0839762369791401 69.4675821940104328, -163.0826158311631673 69.4909183078342210, -163.0761847601996237 69.4914350721571310, -163.0735521104600423 69.4826905992296133, -163.0427147759331490 69.4840184529622604, -163.0407023111978901 69.4907024807400404, -163.0340783013237740 69.4926968044704978, -163.0325619167751654 69.5075619167751881, -163.0249999999999773 69.5083333333333542, -163.0249999999999773 69.5125000000000171, -163.0257198757595347 69.5242233276367330, -163.0406843397352361 69.5257498847113879, -163.0423095703124829 69.5318852742513229, -163.0342976888020701 69.5342975192599937, -163.0323031955294937 69.5409216986762289, -163.0176147460937273 69.5424199422200644, -163.0160237630208258 69.5490844726562614, -163.0325619167751654 69.5507714165581774, -163.0341098361544994 69.5659467909071338, -163.0572906494140568 69.5673702663845717, -163.0593814425998005 69.5743140326606095, -163.0906185574001483 69.5756859673394246, -163.0926968044704779 69.5825883653429003, -163.1075619167751540 69.5841047498915088, -163.1083333333333201 69.5916666666666828, -163.1010301378038037 69.5924116346571395, -163.0989573160807140 69.5992964002821282, -163.0760426839192689 69.6007035997179031, -163.0739573160807083 69.6076297336154681, -163.0507765028211793 69.6090532090929059, -163.0490844726562329 69.6256427341037494, -163.0260437011718579 69.6242996215820540, -163.0239698621961679 69.6174116346571310, -163.0166666666666515 69.6166666666666885, -163.0173804389105783 69.6325946384006187, -163.0406497531466812 69.6340530395507926, -163.0423221164279255 69.6401775783962904, -163.0342430962456319 69.6426723904080092, -163.0326778835720347 69.6490632798936815, -163.0492241753472058 69.6507875230577440, -163.0506554497612797 69.6657299465603472, -163.0343383789062273 69.6674302842882156, -163.0326778835720347 69.6735109117296219, -163.0409166124131843 69.6760550604926436, -163.0423221164279255 69.6907418145073905, -163.0264889187282904 69.6923221164279738, -163.0240169949001654 69.6843163384331774, -163.0176496717664918 69.6823503282335253, -163.0155893961588447 69.6756785074869924, -162.9598222520616275 69.6743445502387289, -162.9572811550564211 69.6825746324327469, -162.9427188449435562 69.6840920342339558, -162.9406144883897412 69.6909079657660726, -162.9257113986544994 69.6924609714084369, -162.9249999999999829 69.7083333333333570, -162.9406185574001711 69.7090193006727645, -162.9427093505859148 69.7159630669488024, -162.9656239827473883 69.7173702663845631, -162.9677205403645530 69.7243340386284842, -163.0242479112413037 69.7256730821397781, -163.0254221598307254 69.7402850680881272, -163.0176310221353901 69.7426308525933365, -163.0157023111979129 69.7490358140733662, -163.0090783013237683 69.7510301378038378, -163.0075568305121294 69.7659467909071367, -162.9843760172526004 69.7673702663845745, -162.9822804768880076 69.7743298000759751, -162.9583333333333144 69.7750000000000199, -162.9575883653428718 69.7823031955295363, -162.9509643554687273 69.7842975192600079, -162.9489573160807083 69.7909630669488052, -162.9260426839192633 69.7923702663845660, -162.9239573160807026 69.7992964002821310, -162.9010426839192576 69.8007035997179059, -162.8989491780598655 69.8076570298936758, -162.8593841552734318 69.8090096367730268, -162.8572852240668283 69.8159806993272696, -162.8257788764105669 69.8173645019531506, -162.8242285834418226 69.8325619167751910, -162.8093634711371465 69.8340783013237996, -162.8072906494140568 69.8409630669488024, -162.7843760172525833 69.8423702663845631, -162.7823031955294937 69.8492550320095660, -162.7676968044704608 69.8507449679904653, -162.7656365288628422 69.8575883653429059, -162.7510301378038093 69.8590783013238052, -162.7489573160807197 69.8659630669488081, -162.7260426839192462 69.8673702663845688, -162.7240356445312273 69.8740358140733662, -162.7176310221354072 69.8759641859266623, -162.7156158447265568 69.8826570298936787, -162.6760508219400947 69.8840096367730297, -162.6740356445312443 69.8907024807400319, -162.6676310221353958 69.8926308525933422, -162.6656138102213447 69.8993298000759751, -162.6416666666666515 69.9000000000000199, -162.6408952501084855 69.9075619167751796, -162.6260301378038093 69.9090783013237882, -162.6239471435546591 69.9159966362847314, -162.5760528564453011 69.9173366970486114, -162.5739698621961509 69.9242550320095546, -162.5593634711371465 69.9257449679904539, -162.5573031955294994 69.9325883653428804, -162.5426968044704665 69.9340783013237939, -162.5407023111978901 69.9407023111979242, -162.5342976888020701 69.9426310221354157, -162.5323031955294937 69.9492550320095461, -162.5176968044704608 69.9507449679904596, -162.5156365288628137 69.9575883653428860, -162.5010301378038093 69.9590783013237854, -162.4990356445312329 69.9657023111979157, -162.4958333333333087 69.9666666666666828, -162.4916666666666458 69.9666666666666828, -162.4908952501084798 69.9742285834418425, -162.4760301378038037 69.9757449679904511, -162.4740356445312273 69.9823689778645814, -162.4676310221354072 69.9842976888020871, -162.4657023111979015 69.9907023111979214, -162.4576904296874886 69.9931145562066064, -162.4590735541449362 70.0073018391927064, -162.4673095703124659 70.0097812228732721, -162.4659813774956376 70.0406185574001796, -162.4592976888020530 70.0426310221354242, -162.4568854437933965 70.0506429036458371, -162.4426981608072822 70.0492597791883753, -162.4402187771267165 70.0410237630208314, -162.4260314941406023 70.0424068874783075, -162.4239444308810505 70.0493394639756985, -162.3507524278428491 70.0506659613715357, -162.3495778401692462 70.0652852376302064, -162.3589762369791458 70.0681145562066092, -162.3574110243055202 70.0740230984158075, -162.3509643554687329 70.0759643554687557, -162.3489437527126427 70.0826748318142450, -162.3083333333333371 70.0833333333333428, -162.3091057671441035 70.0905921088324675, -162.3156646728515682 70.0926686604817775, -162.3176686604817576 70.0989980061848996, -162.3256679958767279 70.1015306260850792, -162.3239718967013800 70.1075558132595518, -162.3094078911675240 70.1091057671441007, -162.3072452121310789 70.1159366183810846, -162.2844214545355896 70.1173967149522639, -162.2822452121310732 70.1242699517144104, -162.2594214545355840 70.1257300482855896, -162.2572452121310675 70.1326032850477503, -162.2344214545355783 70.1340633816189296, -162.2322452121310619 70.1409366183810761, -162.2094214545355726 70.1423967149522696, -162.2068027072482437 70.1506679958767450, -162.2010148790147639 70.1490386962890682, -162.1984693739149179 70.1409986707899407, -162.1844092475043340 70.1424346923828210, -162.1822587754991218 70.1492275661892393, -162.1677412245008725 70.1507724338107721, -162.1656646728515625 70.1573313395182367, -162.1593353271484261 70.1593353271484403, -162.1573313395182367 70.1656646728515625, -162.1510019938151004 70.1676686604817803, -162.1489254421657904 70.1742275661892450, -162.1344078911675410 70.1757724338107636, -162.1322396172417371 70.1826212565104299, -162.1010937160915830 70.1840454101562585, -162.0989980061848996 70.1906646728515682, -162.0926686604817633 70.1926686604817718, -162.0905921088324533 70.1992275661892364, -162.0760745578342039 70.2007724338107693, -162.0739254421658018 70.2075608995225764, -162.0594078911675240 70.2091057671441092, -162.0572587754991218 70.2158942328559021, -162.0427412245008725 70.2174391004774350, -162.0405785454643990 70.2242699517144189, -162.0177547878689097 70.2257300482855982, -162.0155921088324646 70.2325608995225821, -162.0010745578341869 70.2341057671441007, -161.9989980061848769 70.2406646728515653, -161.9926686604817689 70.2426686604817831, -161.9906646728515511 70.2489980061849053, -161.9843353271484432 70.2510019938151089, -161.9823313395182254 70.2573313395182311, -161.9760019938150890 70.2593353271484489, -161.9739254421657790 70.2658942328559135, -161.9594078911675297 70.2674391004774321, -161.9573313395182197 70.2739980061848968, -161.9510019938150833 70.2760019938151146, -161.9489980061848939 70.2823313395182367, -161.9426686604817576 70.2843353271484403, -161.9405921088324476 70.2908942328559050, -161.9260745578341982 70.2924391004774378, -161.9239254421657961 70.2992275661892450, -161.9094078911675183 70.3007724338107778, -161.9068027072482607 70.3090013292100764, -161.9010148790147525 70.3073720296223996, -161.8984693739149350 70.2993320041232721, -161.8926815456814268 70.3009613037109489, -161.8901360405815808 70.3090013292100764, -161.8340550740559820 70.3076327853732721, -161.8337858412000685 70.3008385552300439, -161.8506679958767336 70.2990420871310846, -161.8489952935112797 70.2923828125000085, -161.8177603827582516 70.2909545898437500, -161.8156646728515398 70.2843353271484403, -161.8076653374565979 70.2818027072482749, -161.8093614366319457 70.2757775200737882, -161.8256679958767279 70.2740420871310789, -161.8240117390950559 70.2674482557508782, -161.8094078911675240 70.2658942328559135, -161.8068027072482664 70.2576653374566007, -161.7743320041232664 70.2591237386067746, -161.7759184095594378 70.2656382242838617, -161.7840013292100707 70.2681972927517364, -161.7823871188693374 70.2840013292100707, -161.7760258992512945 70.2824069552951443, -161.7739118787977191 70.2757300482855953, -161.7510881212022582 70.2742699517144160, -161.7488979763454893 70.2673526340060874, -161.6761020236544937 70.2659806993272582, -161.6739980061848883 70.2593353271484489, -161.6676686604817519 70.2573313395182311, -161.6656646728515625 70.2510019938151089, -161.6576653374565922 70.2484693739149435, -161.6593853420681342 70.2423594156901174, -161.7155665079752396 70.2409752739800410, -161.7181972927517393 70.2326653374566092, -161.7572362263997263 70.2340348985460139, -161.7594368828667371 70.2409854465060874, -161.8572297837999088 70.2423478868272610, -161.8598639594183908 70.2506679958767393, -161.8840013292100650 70.2491621229383725, -161.8825349595811645 70.2340460883246607, -161.8427636040581490 70.2326310899522639, -161.8405665079752396 70.2256913926866417, -161.7844334920247320 70.2243086073133753, -161.7823313395182083 70.2176686604817775, -161.7743320041232664 70.2151360405815979, -161.7764350043402715 70.2076653374566035, -161.8322330050998232 70.2090247260199760, -161.8348639594183851 70.2173346625434078, -161.8590013292100593 70.2158287896050410, -161.8575703938801951 70.2010776095920193, -161.8510019938150890 70.1989980061849081, -161.8489118787977361 70.1923967149522667, -161.8243320041232494 70.1908243815104242, -161.8257503933376711 70.1673594156901146, -161.8992779201931285 70.1659735785590328, -161.8995344373914804 70.1595052083333428, -161.8926686604817746 70.1573313395182367, -161.8901360405815808 70.1493320041232664, -161.8594273885091184 70.1507110595703125, -161.8572363959418396 70.1576310899522610, -161.8177636040581433 70.1590355767144160, -161.8156646728515398 70.1656646728515625, -161.8093353271484318 70.1676686604817803, -161.8073313395182140 70.1739980061849025, -161.8010019938151061 70.1760019938151061, -161.7989118787977247 70.1826032850477475, -161.7760881212022355 70.1840633816189268, -161.7739030626084968 70.1909644232855925, -161.7340543958875685 70.1923821343316092, -161.7334535386827099 70.2075415717230982, -161.7572452121310675 70.2090633816189325, -161.7593353271484204 70.2156646728515739, -161.7673346625433908 70.2181972927517393, -161.7652316623263857 70.2256679958767478, -161.7427552964952042 70.2242719862196196, -161.7405921088324590 70.2174391004774350, -161.7260745578341812 70.2158942328559021, -161.7239980061848996 70.2093353271484375, -161.7208333333333314 70.2083333333333428, -161.7166666666666686 70.2083333333333428, -161.7092544555663949 70.2090087890625085, -161.7073920355902601 70.2160678439670249, -161.6426079644097058 70.2172654893663264, -161.6407455444335710 70.2243245442708428, -161.6258338080511976 70.2256832546658103, -161.6245622422960082 70.2321187337239650, -161.6339131673177008 70.2345855712890739, -161.6324610392252623 70.2409868028428832, -161.6175877888997263 70.2423421223958400, -161.6154144287109204 70.2505798339843750, -161.5592739529079722 70.2493984646267364, -161.5570810953775833 70.2410868326822992, -161.4592763264973883 70.2422587076822964, -161.4570810953775890 70.2505798339843750, -161.4342649671766310 70.2493648952908103, -161.4323939005533646 70.2422726101345489, -161.4083333333333314 70.2416666666666742, -161.4075503879123232 70.2493418375651117, -161.3010572645399066 70.2506547715928917, -161.2989473130967610 70.2576633029513999, -161.2510526869031935 70.2590033637152800, -161.2489573160807197 70.2659630669487854, -161.2260426839192462 70.2673702663845603, -161.2239520602755931 70.2743140326605982, -161.1927146063910357 70.2756859673394132, -161.1906158447265511 70.2826568603515653, -161.1510508219400890 70.2840098063151117, -161.1489491780598939 70.2909901936849053, -161.1093841552734318 70.2923431396484375, -161.1073031955295107 70.2992550320095546, -161.0926968044704779 70.3007449679904539, -161.0906239827473883 70.3076297336154568, -161.0677093505859148 70.3090369330512175, -161.0656239827473826 70.3159630669487967, -161.0427093505859091 70.3173702663845575, -161.0406365288628194 70.3242550320095603, -161.0260301378038150 70.3257449679904596, -161.0235521104600593 70.3339762369791686, -161.0176435682508611 70.3324110243055571, -161.0156365288628422 70.3257449679904596, -161.0007714165581376 70.3242285834418510, -160.9999999999999716 70.3166666666666771, -160.9958333333333087 70.3166666666666771, -160.9833333333333201 70.3166666666666771))" exactextractr/src/exactextract/test/resources/antarctica.wkt0000644000176200001440000006320214776002150024257 0ustar liggesusers"MULTIPOLYGON (((-59.572094692611529 -80.040178725096297,-59.865849371974718 -80.549656671061854,-60.1596557277702 -81.000326837079314,-62.255393439367083 -80.863177585776654,-64.488125372969762 -80.921933689292558,-65.741666429289836 -80.588827406739142,-65.741666429289836 -80.549656671061854,-66.29003089055513 -80.25577280061799,-64.037687750897646 -80.294943536295179,-61.883245612217138 -80.392870375488286,-61.138975796133451 -79.9813709451481,-60.610119188058405 -79.628679294756125,-59.572094692611529 -80.040178725096297)),((-159.208183560197654 -79.497059421708727,-161.127601284814659 -79.634208673011301,-162.439846768218388 -79.281465346187019,-163.027407803376974 -78.928773695794959,-163.066604377270323 -78.869965915846763,-163.712895677728739 -78.595667413241529,-163.712895677728739 -78.595666605797277,-163.105800951163815 -78.223337911134294,-161.245113491846382 -78.380175883140168,-160.246208055644502 -78.693645121422662,-159.482404548154477 -79.046337579258989,-159.208183560197654 -79.497059421708727)),((-45.154757656421026 -78.047069600586738,-43.920827806155756 -78.478102722333261,-43.489949713706096 -79.085559991368456,-43.372437506674373 -79.516644789547271,-43.333266770997085 -80.026122735512928,-44.880536668464288 -80.339643650227714,-46.506173875502014 -80.594356784994318,-48.386420864441789 -80.829484551922349,-50.482106899606464 -81.025441583173119,-52.851988084511788 -80.966685479657301,-54.164259406131606 -80.633527520671578,-53.98799109558405 -80.222028090331406,-51.8531343247422 -79.947729587726087,-50.991326463410587 -79.614623305172657,-50.364594692574741 -79.183486830561634,-49.914131232286508 -78.811209004886692,-49.306958991073117 -78.458569030926924,-48.660616014182523 -78.047017924154446,-48.660616014182523 -78.047018731598683,-48.1513964503784 -78.047069600586738,-46.662856818211026 -77.831475525065031,-45.154757656421026 -78.047069600586738)),((-121.211511393857123 -73.500990499006036,-119.918851278292053 -73.65772511814734,-118.724143032691913 -73.481353454735199,-119.292118700011955 -73.834096781559481,-120.232217163709976 -74.088809916326156,-121.622829956684257 -74.010468444971607,-122.621734585441899 -73.657777602023884,-122.621735392886137 -73.657776794579632,-122.406244670229029 -73.32461883559391,-121.211511393857123 -73.500990499006036)),((-125.559566406895314 -73.481353454735199,-124.031881877266855 -73.873267517236741,-124.619468750641531 -73.834096781559481,-125.912180542638907 -73.736118265934095,-127.28312964568191 -73.461768894340821,-127.283130453126162 -73.461768086896569,-126.558471843097209 -73.246225687807168,-125.559566406895314 -73.481353454735199)),((-98.981549648823901 -71.933334248999785,-97.884743211645031 -72.070535176734737,-96.787936774466232 -71.952971293270707,-96.200349901091442 -72.521205342752182,-96.983764614636215 -72.442863871397634,-98.19808325884685 -72.482034607074922,-99.432013109112205 -72.442863871397634,-100.783455166409212 -72.501619974913552,-101.80186845580134 -72.305662943662782,-102.330725063876386 -71.894164320766848,-102.330725063876386 -71.89416351332261,-101.703967454824436 -71.717791849910384,-100.4309185453141 -71.854992777645322,-98.981549648823901 -71.933334248999785)),((-68.451345994730417 -70.955822855766741,-68.333833787698694 -71.406493021784186,-68.510127936462425 -71.798407084285728,-68.784297247986984 -72.170735778948625,-69.959470994736421 -72.307885030251299,-71.075888637970138 -72.50384206150207,-72.388134121373781 -72.484256693663426,-71.898499925408288 -72.092342631161884,-73.073621995725432 -72.229491882464544,-74.190039638959064 -72.366692810199496,-74.953894822881381 -72.07275726332324,-75.01262508818121 -71.661257832983068,-73.915818651002326 -71.269344577925779,-73.915818651002326 -71.269343770481527,-73.230330776650561 -71.151779887017511,-72.074716559523551 -71.190950622694771,-71.780961880160362 -70.681472676729129,-71.72217993842844 -70.30919565849851,-71.741791144483187 -69.505782165656797,-71.173815477163146 -69.035474955368414,-70.253251512315728 -68.878740336227196,-69.72444658067306 -69.251017354457815,-69.489422166609586 -69.623346049120727,-69.058518235943808 -70.074016215138187,-68.725541144471066 -70.505152689749281,-68.451345994730417 -70.955822855766741)),((-58.614142829000912 -64.152467130133147,-59.045072597882864 -64.368009529222633,-59.789342413966551 -64.211223233649051,-60.611927863188697 -64.309201749274436,-61.297415737540376 -64.544329516202566,-62.022100185785433 -64.799094327401363,-62.511760219966902 -65.093029874277519,-62.648857794837369 -65.484942321890657,-62.590127529537739 -65.857219340121361,-62.120078701410705 -66.190325622674706,-62.805566575762384 -66.425505066034958,-63.745690070232371 -66.503846537389592,-64.294106207929957 -66.837004496375215,-64.881693081304633 -67.150473734657723,-65.508424852140564 -67.581610209268916,-65.665081956633287 -67.95388722749945,-65.31254533553809 -68.365334981407415,-64.783714565679333 -68.678907572554493,-63.961103278241097 -68.913983663050175,-63.197299770751044 -69.227556254197253,-62.78595536970775 -69.619418640266503,-62.570516323482906 -69.991747334929499,-62.276735805903542 -70.383661397431041,-61.806661139560589 -70.716767679984471,-61.512906460197399 -71.089044698215076,-61.375808885327132 -72.010073750953126,-61.081976691315532 -72.38235076918383,-61.003661058177187 -72.774264831685372,-60.69026933454316 -73.166178894187084,-60.827366909413428 -73.695242207991186,-61.375808885327132 -74.106741638331371,-61.96336992048569 -74.439847920884887,-63.295200771727963 -74.576997172187376,-63.745690070232371 -74.929740499011729,-64.352836473229587 -75.262846781565159,-65.860987311451765 -75.635123799795778,-67.192818162694124 -75.791910095369445,-68.446281704365759 -76.007452494458761,-69.797723761662837 -76.222994893548176,-70.600723843046239 -76.634494323888447,-72.206775682245365 -76.673665059565721,-73.969536302369676 -76.634494323888447,-75.555976935514025 -76.712887471675202,-77.240370246067613 -76.712887471675202,-76.926978522433586 -77.104801534176744,-75.399293992804928 -77.281069844724385,-74.28287634957141 -77.555420023761812,-73.656118740519446 -77.908111674153957,-74.772536383753078 -78.221632588868673,-76.496100429984011 -78.123654073243287,-77.925858120419264 -78.378418884442254,-77.984665900367474 -78.789918314782341,-78.02378495961247 -79.18183318472822,-76.848637051079123 -79.51493946728165,-76.633223843070397 -79.887216485512269,-75.360097418911749 -80.259545180175252,-73.244851854124619 -80.416331475748763,-71.442946336539222 -80.690629978353982,-70.013162807887767 -81.004150893068783,-68.191646084247537 -81.317671807783569,-65.704278530526665 -81.47445810335725,-63.256030036050703 -81.748756605962484,-61.55202551944231 -82.042692152838541,-59.691415574773458 -82.375850111824349,-58.712121344626212 -82.846105645680353,-58.222487148660917 -83.21843434034335,-57.008116828017819 -82.865691013519083,-55.362894253141548 -82.571755466642827,-53.619770677288244 -82.258234551928041,-51.543644171746024 -82.003521417161352,-49.761349860215461 -81.729171238123755,-47.27393063006221 -81.709585870285295,-44.825707973802508 -81.84673512158777,-42.808363409992381 -82.081914564948107,-42.162020433101787 -81.650829766769291,-40.771433478343596 -81.356894219893221,-38.244817674297053 -81.337308852054591,-36.266669684380219 -81.121714776532983,-34.386396857224355 -80.906172377443482,-32.31029618989831 -80.769023126140738,-30.097097947701997 -80.592651462728696,-28.549802212018704 -80.337938327962007,-29.254901292425131 -79.985195001137654,-29.685805223090966 -79.632503350745679,-29.685805223090966 -79.260226332514975,-31.62480831554663 -79.299397068192235,-33.681323615033961 -79.456131687333453,-35.639912075328255 -79.456131687333453,-35.914107225069017 -79.083854669102919,-35.777009650198721 -78.33924814876498,-35.326546189910431 -78.123654073243287,-33.896762661258862 -77.888526306315242,-32.212369350705302 -77.653450215819575,-30.99805070649461 -77.359514668943319,-29.783732062284059 -77.065579122067263,-28.882779303491361 -76.673665059565721,-27.511751878355653 -76.497345072585787,-26.16033565927475 -76.36014414485075,-25.474821946706868 -76.281802673496287,-23.927552049239779 -76.242580261386735,-22.45859778491095 -76.105431010084246,-21.22469377286177 -75.909473978833475,-20.010375128651077 -75.67434621190543,-18.91354285325616 -75.439218444977286,-17.522981736714172 -75.125697530262499,-16.641588507544014 -74.792539571276876,-15.701490851290259 -74.498604024400635,-15.407710333710867 -74.106741638331371,-16.465320196996373 -73.871613871403412,-16.112783575901261 -73.460114441063155,-15.446855231171952 -73.146541849916076,-14.408804897508986 -72.950584818665305,-13.311972622113984 -72.715457051737332,-12.293507656289563 -72.401936137022545,-11.510067104528588 -72.010073750953126,-11.020432908563038 -71.539766540664829,-10.295774298534155 -71.265416361627302,-9.101015183946089 -71.324224141575513,-8.611380987980596 -71.657330424128844,-7.416621873392415 -71.696501159806033,-7.377451137715241 -71.324224141575513,-6.868231573911117 -70.932310079073972,-5.790984666354774 -71.030288594699272,-5.53637488445267 -71.402617289362254,-4.341667446296867 -71.461373392878073,-3.048981492515594 -71.28505340589814,-1.795492112627784 -71.167437846001832,-0.659489101555522 -71.226245625950042,-0.228636847322065 -71.637745056290299,0.868195428072937 -71.304638773736784,1.886686232113533 -71.128267110324742,3.022637566753417 -70.991117859022069,4.139055209987049 -70.853916931287046,5.157546014027673 -70.618789164359086,6.273911980828927 -70.462054545217796,7.135719842160626 -70.246512146128381,7.742866245157842 -69.893768819304029,8.487110223025326 -70.148533630502996,9.525134718472202 -70.011332702768215,10.249845004933434 -70.481639913056512,10.81782067225339 -70.834331563448501,11.953823683325652 -70.63837453219773,12.404287143613942 -70.246512146128381,13.422777947654396 -69.972161967090955,14.734997592842006 -70.030918070606774,15.126756626046586 -70.403246765269756,15.949342075268646 -70.030918070606774,17.02658898282516 -69.913354187142744,18.201711053142333 -69.874183451465484,19.259372592860046 -69.893768819304029,20.375738559661471 -70.011332702768215,21.452985467217815 -70.070140482716255,21.923034295344735 -70.403246765269756,22.569403110451447 -70.697182312145827,23.666183709414213 -70.520810648733701,24.841357456163593 -70.481639913056512,25.977308790803647 -70.481639913056512,27.093726434037279 -70.462054545217796,28.092580193806867 -70.32485361748293,29.150241733524581 -70.207289734018985,30.031583286262531 -69.932939554981289,30.971732618948607 -69.756619568001454,31.990171746556854 -69.658641052376069,32.754052768695288 -69.384290873338458,33.302443068176757 -68.835642191695712,33.8704187354966 -68.50258758557456,34.908494907375854 -68.659270528283486,35.300202264148226 -69.012013855107938,36.162010125479782 -69.247141622035969,37.200034620926573 -69.168748474249043,37.905107863116797 -69.521440124641202,38.649403517416914 -69.776204935840184,39.667894321457339 -69.541077168912039,40.020430942552565 -69.109940694300946,40.921357863129089 -68.933620707321182,41.959434035008229 -68.600514424767667,42.938702426939102 -68.463313497032715,44.113876173688624 -68.267408142214236,44.897290887233424 -68.051865743124921,45.719928012887834 -67.816737976196777,46.503342726432635 -67.601195577107461,47.443440382686305 -67.718759460571476,48.344418979695121 -67.366067810179416,48.990736118369597 -67.091717631141904,49.930885451055673 -67.111302998980449,50.753470900277733 -66.876175232052404,50.949324578663919 -66.523483581660429,51.791547072157044 -66.249133402622903,52.614132521378934 -66.053176371372132,53.613037957580815 -65.89639007579855,54.533550245996054 -65.818048604444002,55.414943475166211 -65.876804707959906,56.355041131419881 -65.974783223585376,57.158092889235689 -66.249133402622903,57.255968051996462 -66.680218200801633,58.137361281166619 -67.013324483355149,58.744507684163949 -67.287674662392675,59.939318475184251 -67.40523854585669,60.605220981697443 -67.679588724894217,61.427806430919333 -67.95388722749945,62.387489455011661 -68.012695007447547,63.190489536395233 -67.816737976196777,64.052349074159025 -67.40523854585669,64.992446730412922 -67.620729268513713,65.971715122343966 -67.738344828410035,66.911864455029757 -67.85590871187415,67.891132846960915 -67.934301859660806,68.890038283162909 -67.934301859660806,69.712623732384742 -68.972791442998371,69.673452996707482 -69.227556254197253,69.555940789675816 -69.678226420214713,68.596257765583488 -69.932939554981289,67.812739699174159 -70.305268249644286,67.949888950476662 -70.697182312145827,69.066306593710266 -70.67754526787499,68.929157342407763 -71.069459330376532,68.41998945503596 -71.441788025039529,67.949888950476662 -71.853287455379615,68.713769972615154 -72.166808370094415,69.869306675093952 -72.264786885719801,71.024895054004588 -72.088415222307759,71.573285353486057 -71.696501159806033,71.906288283174916 -71.324224141575513,72.454626906224036 -71.010703226860628,73.081410353492089 -70.716767679984471,73.336020135394193 -70.364024353160204,73.864876743469239 -69.874183451465484,74.491556837872707 -69.776204935840184,75.627559848944969 -69.73703420016281,76.62646528514685 -69.619418640266503,77.644904412755267 -69.462684021125298,78.13453860872059 -69.070769958623757,78.428370802732246 -68.698441263960675,79.113858677083925 -68.326215922162433,80.093127069014855 -68.071502787395758,80.935349562507753 -67.875545756144987,81.483791538421457 -67.542387797159265,82.05176720574147 -67.366067810179416,82.776425815770409 -67.209281514605919,83.775331251972403 -67.30726003023122,84.676206496116635 -67.209281514605919,85.65552656447997 -67.091717631141904,86.752358839874859 -67.150473734657723,87.477017449903798 -66.876175232052404,87.986288690140242 -66.20991099051335,88.358410679073955 -66.484261169550862,88.828407830768555 -66.95456837983923,89.670630324261566 -67.150473734657723,90.6303650247863 -67.228866882444464,91.590099725310807 -67.111302998980449,92.608538852919054 -67.189696146767204,93.548636509172951 -67.209281514605919,94.175419956441004 -67.111302998980449,95.017590773501666 -67.170110778928645,95.781471795640272 -67.385653178017975,96.682398716216795 -67.248503926715486,97.759645623773139 -67.248503926715486,98.680209588620556 -67.111302998980449,99.718182407635055 -67.248503926715486,100.384188267012775 -66.915345967729678,100.893356154384691 -66.582239685176248,101.578895705168549 -66.307889506138636,102.832410923272647 -65.56328379324512,103.478676385514774 -65.700484720979972,104.242557407653095 -65.974783223585376,104.90845991416623 -66.327526550409658,106.181560500108759 -66.934931335568393,107.160880568472095 -66.95456837983923,108.081392856887163 -66.95456837983923,109.158639764443677 -66.837004496375215,110.235834995567842 -66.699803568640363,111.058472121222081 -66.425505066034958,111.743959995573931 -66.131569519158887,112.860377638807478 -66.092347107049321,113.604673293107368 -65.876804707959906,114.388088006652055 -66.072761739210677,114.897307570456263 -66.386282653925477,115.602380812646544 -66.699803568640363,116.699161411609424 -66.660632832962989,117.384700962393225 -66.915345967729678,118.579460076981292 -67.170110778928645,119.832923618653098 -67.268089294553945,120.870999790532181 -67.189696146767204,121.654414504077096 -66.876175232052404,122.320368687022352 -66.562654317337689,123.221295607598933 -66.484261169550862,124.122274204607635 -66.6214620972859,125.160247023622247 -66.719388936478907,126.10039635630838 -66.562654317337689,127.001426629749318 -66.562654317337689,127.88276818248724 -66.660632832962989,128.803280470902422 -66.75861134858846,129.704259067911181 -66.582239685176248,130.78145429903546 -66.425505066034958,131.799945103075885 -66.386282653925477,132.935896437716139 -66.386282653925477,133.856460402563386 -66.288304138300091,134.757387323139909 -66.209962666945628,135.031582472880729 -65.720070088818616,135.070753208557818 -65.30857065847843,135.697484979393579 -65.582869161083664,135.873804966373513 -66.033591003533417,136.206704543197787 -66.445090433873673,136.61804894424111 -66.778196716427018,137.460271437733951 -66.95456837983923,138.596222772374148 -66.895760599891133,139.908442417561474 -66.876175232052404,140.80942101457029 -66.817367452104378,142.121692336190193 -66.817367452104378,143.061841668876156 -66.797782084265663,144.374061314063709 -66.837004496375215,145.490427280865021 -66.915345967729678,146.195552199487821 -67.228866882444464,145.999698521101521 -67.601195577107461,146.646067336208233 -67.895131123983703,147.723262567332341 -68.130258890911662,148.839628534133709 -68.385023702110544,150.132314487914897 -68.561292012658186,151.483704868779597 -68.718129984663975,152.502247349252485 -68.874812927372986,153.638198683892568 -68.894501648076101,154.28456749899928 -68.561292012658186,155.165857375304853 -68.835642191695712,155.929790073875466 -69.14921478284279,156.811131626613388 -69.384290873338458,158.025527785472406 -69.482269388963942,159.181012811518741 -69.599833272427958,159.670698683916527 -69.991747334929499,160.806650018556496 -70.226875101857544,161.570479364262809 -70.579618428681812,162.686897007496356 -70.736353047823201,163.842433709974927 -70.716767679984471,164.919680617531213 -70.77552378350029,166.11443973211945 -70.755938415661745,167.309095493842989 -70.834331563448501,168.425616489941177 -70.971480814751061,169.46358930895596 -71.206660258111398,170.501665480835044 -71.402617289362254,171.206790399457617 -71.696501159806033,171.089226515993772 -72.088415222307759,170.560421584350735 -72.441158549132112,170.109958124062388 -72.891828715149387,169.757369826535154 -73.244520365541547,169.287320998408319 -73.656019795881633,167.975101353220765 -73.81280609145513,167.387488641629744 -74.16549774184719,166.094802687848443 -74.381040140936605,165.644390903992445 -74.772954203438147,164.958851353208587 -75.145282898101229,164.234192743179705 -75.45880381281593,163.822796665703919 -75.870303243156201,163.568238560234278 -76.242580261386735,163.470260044608978 -76.693302103836558,163.489897088879758 -77.065579122067263,164.057872756199771 -77.457441508136426,164.273363478856965 -77.829770202799324,164.743463983416149 -78.182513529623776,166.60412560451735 -78.319611104494058,166.995781284857429 -78.750747579105251,165.193875767272033 -78.907483005690693,163.666217075859578 -79.123025404780023,161.766384719081117 -79.162247816889675,160.924162225588333 -79.73048186637098,160.747893915040748 -80.200737400227155,160.31696414615871 -80.573066094889967,159.78821089094842 -80.945394789553049,161.120015903974405 -81.278501072106479,161.629287144210906 -81.690000502446566,162.490991652678048 -82.06227752067727,163.705336135104773 -82.395435479662893,165.095948928078855 -82.708956394377779,166.60412560451735 -83.022477309092579,168.895665318067955 -83.335998223807366,169.404781529007579 -83.825890801934378,172.283933954149376 -84.041433201023708,172.477048781624177 -84.117914320815672,173.224083286835395 -84.413710219254412,175.985671828513119 -84.158997084487638,178.277211542064066 -84.472517999202438,180.0 -84.71338,180 -90,-180.0 -90.0,-180.0 -84.71338,-179.942499356178928 -84.721443373552489,-179.058677334691197 -84.139411716649093,-177.256771817105744 -84.452932631363879,-177.140806673265786 -84.417941227148319,-176.084672818077593 -84.099259128758419,-175.947234613627757 -84.110448710216616,-175.829882168662522 -84.117914320815672,-174.382502814815695 -84.534323012223567,-173.116559414745467 -84.117914320815672,-172.889105598012804 -84.061018568862337,-169.951222907571434 -83.884646905450126,-168.999988980158633 -84.117914320815672,-168.530198534193232 -84.237390232274478,-167.022099372403318 -84.570496514827909,-164.182143521155069 -84.825209649594584,-161.929774543281383 -85.138730564309384,-158.071379564424944 -85.373910007669707,-155.192252977499294 -85.09955982863211,-150.942098965438021 -85.295516859882881,-148.533072883071497 -85.609037774597667,-145.888918226332976 -85.31510222772161,-143.107718478600447 -85.040752048683913,-142.892279432375631 -84.570496514827909,-146.829068366463304 -84.531274102718342,-150.060731574483952 -84.296146335790382,-150.902928229760732 -83.904232273288841,-153.586201138300197 -83.688689874199355,-153.409906989536466 -83.238019708182065,-153.037759162386408 -82.826520277841809,-152.665637173452751 -82.454191583178812,-152.861516690055055 -82.042692152838541,-154.526298794553895 -81.768393650233321,-155.290179816692387 -81.415650323409039,-156.837449714159504 -81.102129408694253,-154.408786587522229 -81.16093718864245,-152.097661506132823 -81.004150893068783,-150.648292609642624 -81.337308852054591,-148.865998298112061 -81.043373305178335,-147.220749885019501 -80.671044610515438,-146.417748996191847 -80.337938327962007,-146.770286424731182 -79.926438897621921,-148.062946540296366 -79.652088718584224,-149.53190080462511 -79.358204848140446,-151.588416104112412 -79.299397068192235,-153.390321621697808 -79.162247816889675,-155.329376390585765 -79.064269301264204,-155.97566769104418 -78.691939799157041,-157.268301968393047 -78.378418884442254,-158.051768358370111 -78.025675557617902,-158.365134243787963 -76.889207458654951,-157.875474209606352 -76.987237650712615,-156.974573127245947 -77.300758565427515,-155.329376390585765 -77.202728373369752,-153.742832404576745 -77.065579122067263,-152.92024695535477 -77.496663920245993,-151.333780483994303 -77.398737081052801,-150.001949632751945 -77.183143005531193,-148.748486091080338 -76.908844502925973,-147.612483080008076 -76.575738220372529,-146.104408948989999 -76.477759704747058,-146.143528008234995 -76.105431010084246,-146.496091274990476 -75.733153991853541,-146.202309949967002 -75.380410665029189,-144.909623996185758 -75.204039001616962,-144.322037122811082 -75.53719696060277,-142.794352593182623 -75.341239929352,-141.638764214271617 -75.086475118152947,-140.209006523836166 -75.066889750314388,-138.857590304755348 -74.968911234688917,-137.506199923890449 -74.733783467760958,-136.428901339901927 -74.518241068671642,-135.214582695691291 -74.302698669582142,-134.431193820362608 -74.361454773097961,-133.745654269578552 -74.439847920884887,-132.257167928732059 -74.302698669582142,-130.925311239273583 -74.47901865656199,-129.554283814137762 -74.459433288723432,-128.24203833073426 -74.322284037420701,-126.890622111653244 -74.420262553046172,-125.402082479485784 -74.518241068671642,-124.011495524727607 -74.47901865656199,-122.562152466453611 -74.498604024400635,-121.073612834286237 -74.518241068671642,-119.702559570934227 -74.47901865656199,-118.684145474097932 -74.185083109685834,-117.469800991671207 -74.02834849054463,-116.216311611783411 -74.243890889633946,-115.021552497195415 -74.06751922622189,-113.944331427855076 -73.71482757582983,-113.297988450964482 -74.02834849054463,-112.94545182986937 -74.381040140936605,-112.299083014762587 -74.714198099922413,-111.261058519315625 -74.420262553046172,-110.066325242943734 -74.792539571276876,-108.714909023862731 -74.910103454740891,-107.559346483168113 -75.184453633778418,-106.149148322355018 -75.125697530262499,-104.876073574628663 -74.949325866850458,-103.367948574622659 -74.988496602527647,-102.016506517325652 -75.125697530262499,-100.645530768622308 -75.302017517242433,-100.116699998763266 -74.870932719063532,-100.763042975653946 -74.537826436510187,-101.252703009835528 -74.185083109685834,-102.545337287184509 -74.106741638331371,-103.113312954504536 -73.734412943668389,-103.328752000729281 -73.362084249005562,-103.681288621824393 -72.617530212544153,-102.917485114334355 -72.754679463846813,-101.605239630930726 -72.813435567362632,-100.312527838933448 -72.754679463846813,-99.137379930400101 -72.911414082988102,-98.118889126359477 -73.205349629864173,-97.688036872126105 -73.558041280256333,-96.336594814828914 -73.616849060204359,-95.043960537479848 -73.47969980890187,-93.672907274128107 -73.283742777650929,-92.439003262078955 -73.166178894187084,-91.420564134470709 -73.401306661115129,-90.088733283228436 -73.322913513328203,-89.226951260112941 -72.558722432595957,-88.42395117872951 -73.009392598613402,-87.268336961602614 -73.185764262025629,-86.01482174349843 -73.087785746400158,-85.192236294276569 -73.47969980890187,-83.879990810872755 -73.518870544578974,-82.66564632844603 -73.636434428043088,-81.470913052074138 -73.851976827132404,-80.687446662096988 -73.47969980890187,-80.295790981756994 -73.126956482077432,-79.296885545555 -73.518870544578974,-77.925858120419264 -73.420892028953588,-76.907367316378753 -73.636434428043088,-76.221879442027074 -73.969540710596419,-74.890048590784801 -73.871613871403412,-73.852024095337924 -73.656019795881633,-72.833533291297414 -73.401306661115129,-71.619214647086864 -73.264157409812384,-70.209042324489957 -73.146541849916076,-68.935915900331224 -73.009392598613402,-67.956621670184091 -72.793850199524087,-67.36906063502559 -72.480329284809301,-67.134036220962031 -72.049244486630386,-67.251548427993669 -71.637745056290299,-67.564940151627894 -71.245830993788758,-67.917476772723006 -70.853916931287046,-68.230842658140915 -70.462054545217796,-68.485452440043019 -70.109311218393515,-68.544208543558938 -69.717397155891973,-68.446281704365759 -69.325534769822724,-67.976232876238896 -68.953206075159727,-67.584499681250321 -68.54170664481947,-67.427842576757513 -68.149844258750221,-67.623670416927695 -67.718759460571476,-67.741182623959332 -67.326845398069935,-67.251548427993669 -66.876175232052404,-66.703183966728574 -66.582239685176248,-66.056815151621862 -66.209962666945628,-65.371327277270098 -65.89639007579855,-64.568275519454403 -65.602506205354672,-64.176542324465828 -65.171423022064445,-63.62815202498453 -64.897072843026749,-63.001394415932566 -64.642308031827866,-62.041685553623978 -64.583551928311948,-61.414927944572014 -64.270031013597162,-60.709854702381705 -64.074073982346391,-59.887269253159559 -63.956510098882369,-59.16258480491453 -63.701745287683572,-58.594557461162282 -63.388224372968601,-57.811142747617509 -63.270660489504579,-57.223581712458838 -63.525425300703638,-57.595729539608868 -63.858531583257069,-58.614142829000912 -64.152467130133147)))" exactextractr/src/exactextract/test/test_raster_area.cpp0000644000176200001440000000163514776002150023442 0ustar liggesusers#include #include "catch.hpp" #include "grid.h" #include "matrix.h" #include "raster_area.h" using namespace exactextract; TEST_CASE("Cartesian area raster returns pixel area") { double dx = 1.0 / 3; double dy = 1.0 / 4; Grid g{{0, 0, 10, 10}, dx, dy}; CartesianAreaRaster areas(g); CHECK( areas(4, 3) == dx * dy ); } TEST_CASE("Spherical area raster returns pixel area") { double dx = 1.0; double dy = 1.0; Grid g{{0, 45, 10, 55}, dx, dy}; SphericalAreaRaster areas(g); // Compare to result from PostGIS, which is more accurate because // it is performing a geodesic calculation using geographiclib. // SELECT ST_Area('POLYGON ((3 50, 4 50, 4 51, 3 51, 3 50))'::geography); constexpr double postgis_area = 7892061583.206543; CHECK( std::abs((areas(4, 3) - postgis_area) / postgis_area) < 0.002 ); } exactextractr/src/exactextract/test/test_raster_cell_intersection.cpp0000644000176200001440000005266014776002150026243 0ustar liggesusers#include #include "catch.hpp" #include "geos_utils.h" #include "raster_cell_intersection.h" using namespace exactextract; void check_cell_intersections(Raster & rci, const std::vector> & v) { const Matrix& actual = rci.data(); Matrix expected{v}; REQUIRE( expected.rows() == actual.rows() ); REQUIRE( expected.cols() == actual.cols() ); bool cell_values_match = true; for (size_t i = 0; i < expected.rows(); i++) { for (size_t j = 0; j < expected.cols(); j++) { cell_values_match = cell_values_match && actual(i, j) == expected(i, j); } } if (!cell_values_match) { std::cerr << "Expected: " << std::endl; std::cerr << expected; std::cerr << "Actual: " << std::endl; std::cerr << actual; } for (size_t i = 0; i < expected.rows(); i++) { for (size_t j = 0; j < expected.cols(); j++) { CHECK ( actual(i, j) == expected(i, j) ); } } } static GEOSContextHandle_t init_geos() { static GEOSContextHandle_t context = nullptr; if (context == nullptr) { context = initGEOS_r(nullptr, nullptr); } return context; } TEST_CASE("Basic rectangle", "[raster-cell-intersection]" ) { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 3, 3}, 1, 1}; // 3x3 grid auto g = GEOSGeom_read_r(context, "POLYGON ((0.5 0.5, 2.5 0.5, 2.5 2.5, 0.5 2.5, 0.5 0.5))"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, { {0.25, 0.5, 0.25}, {0.50, 1.0, 0.50}, {0.25, 0.5, 0.25} }); // check alternate interface for rectangles auto rci2 = raster_cell_intersection(ex, geos_get_box(context, g.get())); CHECK( rci == rci2 ); } TEST_CASE("Basic rectangular line", "[raster-cell-intersection]" ) { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 3, 3}, 1, 1}; // 3x3 grid auto g = GEOSGeom_read_r(context, "LINESTRING (0.5 0.5, 2.5 0.5, 2.5 2.5, 0.5 2.5, 0.5 0.5)"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, { {1.00, 1.00, 1.00}, {1.00, 0, 1.00}, {1.00, 1.00, 1.00} }); } TEST_CASE("Basic rectangle with GeometryCollection", "[raster-cell-intersection]" ) { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 3, 3}, 1, 1}; // 3x3 grid auto g = GEOSGeom_read_r(context, "GEOMETRYCOLLECTION (" "MULTIPOLYGON (((0.5 0.5, 1.5 0.5, 1.5 1.5, 0.5 1.5, 0.5 0.5))," " ((1.5 1.5, 2.5 1.5, 2.5 2.5, 1.5 2.5, 1.5 1.5)))," "POLYGON ((0.5 1.5, 1.5 1.5, 1.5 2.5, 0.5 2.5, 0.5 1.5))," "POLYGON ((1.5 0.5, 2.5 0.5, 2.5 1.5, 1.5 1.5, 1.5 0.5)))"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, { {0.25, 0.5, 0.25}, {0.50, 1.0, 0.50}, {0.25, 0.5, 0.25} }); } TEST_CASE("Unsupported geometry", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 3, 3}, 1, 1}; // 3x3 grid auto g = GEOSGeom_read_r(context, "POINT (0 0)"); CHECK_THROWS_WITH(raster_cell_intersection(ex, context, g.get()), "Unsupported geometry type."); } TEST_CASE("Basic non-rectangle", "[raster-cell-intersection]" ) { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 3, 3}, 1, 1}; // 3x3 grid auto g = GEOSGeom_read_r(context, "POLYGON ((0.5 0.5, 2.5 0.5, 2.5 2.0, 2.0 2.0, 2.0 2.5, 0.5 2.5, 0.5 0.5))"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, { {0.25, 0.5, 0.00}, {0.50, 1.0, 0.50}, {0.25, 0.5, 0.25} }); } TEST_CASE("Small polygon optimization", "[raster-cell-intersection]") { auto context = init_geos(); Grid ex{{0, 0, 3, 3}, 1, 1}; // 3x3 grid // small polygon entirely contained in a single cell auto g = GEOSGeom_read_r(context, "POLYGON ((0.5 0.5, 0.6 0.5, 0.6 0.6, 0.5 0.5))"); double g_area; CHECK( GEOSArea_r(context, g.get(), &g_area) ); auto rci = raster_cell_intersection(ex, context, g.get()); CHECK( rci.rows() == 1 ); CHECK( rci.cols() == 1 ); CHECK( rci(0, 0) == static_cast(g_area) ); CHECK( rci.xmin() == 0 ); CHECK( rci.xmax() == 1 ); CHECK( rci.ymin() == 0 ); CHECK( rci.ymax() == 1 ); } TEST_CASE("Small line optimization", "[raster-cell-intersection]") { auto context = init_geos(); Grid ex{{0, 0, 3, 3}, 1, 1}; // 3x3 grid // small line entirely contained in a single cell auto g = GEOSGeom_read_r(context, "LINESTRING (0.5 0.5, 0.6 0.5, 0.6 0.6, 0.5 0.5)"); double g_length; CHECK( GEOSLength_r(context, g.get(), &g_length) ); auto rci = raster_cell_intersection(ex, context, g.get()); CHECK( rci.rows() == 1 ); CHECK( rci.cols() == 1 ); CHECK( rci(0, 0) == static_cast(g_length) ); CHECK( rci.xmin() == 0 ); CHECK( rci.xmax() == 1 ); CHECK( rci.ymin() == 0 ); CHECK( rci.ymax() == 1 ); } TEST_CASE("Basic line", "[raster-cell-intersection]") { auto context = init_geos(); Grid ex{{0, 0, 3, 3, }, 1, 1}; //3x3 grid auto g = GEOSGeom_read_r(context, "LINESTRING (0.5 0.5, 2.5 0.5, 2.5 2.5, 0.5 2.5)"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, { {0.50, 1.00, 1.00}, { 0, 0, 1.00}, {0.50, 1.00, 1.00} }); } TEST_CASE("Geometry extent larger than raster", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); // Process a geometry using four 3x3 tiles // +-----+-----+ // | 1 | 2 | // +-----+-----+ // | 3 | 4 | // +-----+-----+ Box b3 {0, 0, 3, 3}; Box b2 = b3.translate(3, 3); Box b1 = b3.translate(0, 3); Box b4 = b3.translate(3, 0); Grid g1 {b1, 1, 1}; Grid g2 {b2, 1, 1}; Grid g3 {b3, 1, 1}; Grid g4 {b4, 1, 1}; auto g = GEOSGeom_read_r(context, "POLYGON ((0.5 0.5, 4.5 0.5, 4.5 5.5, 0.5 5.5, 0.5 0.5))"); Raster ll = raster_cell_intersection(g3, context, g.get()); check_cell_intersections(ll, { {0.50, 1.0, 1.0}, {0.50, 1.0, 1.0}, {0.25, 0.5, 0.5} }); Raster lr = raster_cell_intersection(g4, context, g.get()); check_cell_intersections(lr, { {1.00, 0.50}, {1.00, 0.50}, {0.50, 0.25} }); Raster ur = raster_cell_intersection(g2, context, g.get()); check_cell_intersections(ur, { {0.50, 0.25}, {1.00, 0.50}, {1.00, 0.50} }); Raster ul = raster_cell_intersection(g1, context, g.get()); check_cell_intersections(ul, { {0.25, 0.5, 0.5}, {0.50, 1.0, 1.0}, {0.50, 1.0, 1.0} }); } TEST_CASE("Geometry entirely outside raster", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); Grid ex{{-3, -3, 0, 0}, 1, 1}; // 3x3 grid auto g = GEOSGeom_read_r(context, "POLYGON ((1.5 0.5, 2.5 1.5, 1.5 2.5, 0.5 1.5, 1.5 0.5))"); Raster rci = raster_cell_intersection(ex, context, g.get()); CHECK ( rci.rows() == 0 ); CHECK ( rci.cols() == 0 ); // check alternate pathway for rectangles auto rci_rect = raster_cell_intersection(ex, geos_get_box(context, g.get())); CHECK ( rci_rect.rows() == 0 ); CHECK ( rci_rect.cols() == 0 ); } TEST_CASE("Invalid geometry with detached inner ring outside raster", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 3, 3}, 1, 1}; // 3x3 grid auto g = GEOSGeom_read_r(context, "POLYGON ((1.5 0.5, 2.5 1.5, 1.5 2.5, 0.5 1.5, 1.5 0.5), (100 100, 100 101, 101 101, 100 100))"); Raster rci = raster_cell_intersection(ex, context, g.get()); CHECK ( rci.rows() == 3 ); CHECK ( rci.cols() == 3 ); } TEST_CASE("Diagonals", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 3, 3}, 1, 1}; // 3x3 grid auto g = GEOSGeom_read_r(context, "POLYGON ((1.5 0.5, 2.5 1.5, 1.5 2.5, 0.5 1.5, 1.5 0.5))"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, { {0.00, 0.25, 0.00}, {0.25, 1.00, 0.25}, {0.00, 0.25, 0.00}, }); } TEST_CASE("Starting on cell boundary", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); // Situation found in Canada using 0.5-degree global grid Grid ex{{0, 0, 2, 2}, 1, 1}; // 2x2 grid auto g = GEOSGeom_read_r(context, "POLYGON ((1 1.5, 1.5 1.5, 1.5 0.5, 0.5 0.5, 0.5 1.5, 1 1.5))"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, { {0.25, 0.25}, {0.25, 0.25}, }); } TEST_CASE("Bouncing off boundary", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); // Situation found in Trinidad and Tobago using 0.5-degree global grid Grid ex{{0, -1, 2, 2}, 1, 1}; // 3x2 grid auto g = GEOSGeom_read_r(context, "POLYGON ((0.5 1.5, 0.5 0.5, 0.5 0, 1.5 0.5, 1.5 1.5, 0.5 1.5))"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, { {0.25, 0.25}, {0.4375, 0.3125} }); } TEST_CASE("Bouncing off boundary (2)", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 2, 2}, 1, 1}; auto g = GEOSGeom_read_r(context, "POLYGON ((0.5 0.5, 1.5 0.5, 1.5 1.5, 0.5 1.5, 1 1.2, 0.5 0.5))"); CHECK_NOTHROW( RasterCellIntersection(ex, context, g.get()) ); } TEST_CASE("Follows grid boundary", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); // Occurs on the Libya-Egypt border, for example Grid ex{{0, 0, 3, 3}, 1, 1}; auto g = GEOSGeom_read_r(context, "POLYGON ((0.5 0.5, 2 0.5, 2 1.5, 2 2.5, 0.5 2.5, 0.5 0.5))"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, { {0.25, 0.5}, {0.50, 1.0}, {0.25, 0.5}, }); } TEST_CASE("Starts on vertical boundary, moving up", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 4, 3}, 1, 1}; // 4x3 grid auto g = GEOSGeom_read_r(context, "POLYGON ((3 0.5, 3 2.5, 0.5 2.5, 0.5 0.5, 3 0.5))"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, { {0.25, 0.5, 0.5}, {0.50, 1.0, 1.0}, {0.25, 0.5, 0.5}, }); } TEST_CASE("Starts on vertical boundary, moving down", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 4, 3}, 1, 1}; // 4x3 grid auto g = GEOSGeom_read_r(context, "POLYGON ((0.5 2.5, 0.5 0.5, 3 0.5, 3 2.5, 0.5 2.5))"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, { {0.25, 0.5, 0.5}, {0.50, 1.0, 1.0}, {0.25, 0.5, 0.5}, }); } TEST_CASE("Starts on vertical boundary, moving down at rightmost extent of grid", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 3, 3}, 1, 1}; // 3x3 grid auto g = GEOSGeom_read_r(context, "POLYGON ((3 2.5, 3 0.5, 0.5 0.5, 0.5 2.5, 3 2.5))"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, { {0.25, 0.5, 0.5}, {0.50, 1.0, 1.0}, {0.25, 0.5, 0.5}, }); } TEST_CASE("Starts on horizontal boundary, moving right", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 3, 4}, 1, 1}; // 3x4 grid auto g = GEOSGeom_read_r(context, "POLYGON ((0.5 1, 2.5 1, 2.5 3.5, 0.5 3.5, 0.5 1))"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, { {0.25, 0.5, 0.25}, {0.50, 1.0, 0.50}, {0.50, 1.0, 0.50} }); } TEST_CASE("Starts on horizontal boundary, moving left", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 3, 4}, 1, 1}; // 3x4 grid auto g = GEOSGeom_read_r(context, "POLYGON ((2.5 3, 0.5 3, 0.5 3.5, 0.25 3.5, 0.25 0.5, 2.5 0.5, 2.5 3))"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, { {0.125, 0.00, 0.00}, {0.750, 1.00, 0.50}, {0.750, 1.00, 0.50}, {0.375, 0.50, 0.25}, }); } TEST_CASE("Regression test - Fiji", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); // Just make sure this polygon doesn't throw an exception. It caused some problems where the // rightmost edge was interpreted to be exactly on a cell wall. Grid ex{{-180.5, -90.5, 180.5, 90.5}, 0.5, 0.5}; auto g = GEOSGeom_read_r(context, "MULTIPOLYGON (((178.3736000000001 -17.33992000000002, 178.71806000000007 -17.62845999999996, 178.5527099999999 -18.150590000000008, 177.93266000000008 -18.287990000000036, 177.38145999999992 -18.164319999999975, 177.28504000000007 -17.72464999999997, 177.67087 -17.381139999999974, 178.12557000000007 -17.50480999999995, 178.3736000000001 -17.33992000000002)), ((179.36414266196417 -16.801354076946836, 178.7250593629972 -17.012041674368007, 178.5968385951172 -16.63915000000003, 179.0966093629972 -16.43398427754741, 179.4135093629972 -16.379054277547382, 180.00000000000003 -16.06713266364241, 180.00000000000003 -16.555216566639146, 179.36414266196417 -16.801354076946836)), ((-179.91736938476527 -16.501783135649347, -179.99999999999997 -16.555216566639146, -179.99999999999997 -16.06713266364241, -179.79332010904858 -16.020882256741217, -179.91736938476527 -16.501783135649347)))"); CHECK_NOTHROW( RasterCellIntersection(ex, context, g.get()) ); } TEST_CASE("Small polygon", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 10, 10}, 10, 10}; // Single cell auto g = GEOSGeom_read_r(context, "POLYGON ((3 3, 4 3, 4 4, 3 4, 3 3))"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, {{0.01}}); } TEST_CASE("Fill handled correctly", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 3, 5}, 1, 1}; // 3x5 grid auto g = GEOSGeom_read_r(context, "POLYGON ((0.5 0.2, 2.2 0.2, 2.2 0.4, 0.7 0.4, 0.7 2.2, 2.2 2.2, 2.2 0.6, 2.4 0.6, 2.4 4.8, 0.5 4.8, 0.5 0.2))"); Raster rci = raster_cell_intersection(ex, context, g.get()); check_cell_intersections(rci, { {0.40, 0.80, 0.32}, {0.50, 1.00, 0.40}, {0.44, 0.80, 0.36}, {0.20, 0.00, 0.20}, {0.22, 0.20, 0.12}, }); } TEST_CASE("Result indexing is correct", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); Grid ex{{-20, -15, 40, 30}, 0.5, 1}; auto g = GEOSGeom_read_r(context, "POLYGON ((0.25 0.20, 2.75 0.20, 2.75 4.5, 0.25 4.5, 0.25 0.20))"); Raster rci = raster_cell_intersection(ex, context, g.get()); size_t n_rows = rci.rows(); size_t n_cols = rci.cols(); CHECK( n_rows == 5 ); CHECK( n_cols == 6 ); CHECK( rci.grid().col_offset(ex) == 40 ); CHECK( rci.grid().row_offset(ex) == 25 ); Matrix expected{{ {0.25, 0.50, 0.50, 0.50, 0.50, 0.25}, {0.50, 1.00, 1.00, 1.00, 1.00, 0.50}, {0.50, 1.00, 1.00, 1.00, 1.00, 0.50}, {0.50, 1.00, 1.00, 1.00, 1.00, 0.50}, {0.40, 0.80, 0.80, 0.80, 0.80, 0.40} }}; CHECK( rci.data() == expected ); } TEST_CASE("Robustness regression test #1", "[raster-cell-intersection]") { // This test exercises some challenging behavior where a polygon follows // ymin, but the grid resolution is such that ymin < (ymax - ny*dy) GEOSContextHandle_t context = init_geos(); Grid ex{{-180, -90, 180, 90}, 1.0/6, 1.0/6}; auto g = GEOSGeom_read_r(context, #include "resources/antarctica.wkt" ); CHECK_NOTHROW( RasterCellIntersection(ex, context, g.get()) ); } TEST_CASE("Robustness regression test #2", "[raster-cell-intersection]") { // This test exercises some challenging behavior where a polygon follows // xmax, but the grid resolution is such that xmax < (xmin + nx*m_dx) GEOSContextHandle_t context = init_geos(); Grid ex{{-180, -90, 180, 90}, 1.0/6, 1.0/6}; auto g = GEOSGeom_read_r(context, #include "resources/russia.wkt" ); CHECK_NOTHROW( RasterCellIntersection(ex, context, g.get()) ); } TEST_CASE("Robustness regression test #3", "[raster-cell-intersection]") { // The situation in this case was causing some kind of infinite loop, ultimately exhausting memory GEOSContextHandle_t context = init_geos(); Grid ex{{179.96666666664618, -16.541666666669137, 179.99999999997954,-16.475000000002474}, 0.0083333333333328596, 0.0083333333333328596}; auto g = GEOSGeom_read_r(context, "POLYGON ((179.9715827094184135 -16.5409617106119526, 180.0000000000000000 -16.5326999999999984, 179.9872884114583655 -16.5342697143554425, 179.9715827094184135 -16.5409617106119526))"); CHECK_NOTHROW( raster_cell_intersection(ex, context, g.get()) ); } TEST_CASE("Robustness regression test #4", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); Grid ex{{-166.84166666666667, 66.991666666666674, -152.625, 71.358333333333334}, 0.0083333333333333332, 0.0083333333333333332}; auto g = GEOSGeom_read_r(context, #include "resources/regression4.wkt" ); CHECK_NOTHROW( raster_cell_intersection(ex, context, g.get()) ); } TEST_CASE("Robustness regression test #5", "[raster-cell-intersection]") { GEOSContextHandle_t context = init_geos(); Grid ex{{0, 0, 10, 10}, 1, 1}; auto g = GEOSGeom_read_r(context, "POINT (2 2)"); g.reset(GEOSBuffer_r(context, g.get(), 1, 30)); CHECK_NOTHROW( raster_cell_intersection(ex, context, g.get()) ); } TEST_CASE("Robustness regression test #6", "[raster-cell-intersection]") { auto context = init_geos(); Grid ex{{145.925, -35.525, 147.375, -33.475}, 0.05, 0.05}; auto g = GEOSGeom_read_r(context, #include "resources/regression6.wkt" ); auto result = raster_cell_intersection(ex, context, g.get()); decltype(result)::value_type tot = 0; for (size_t i = 0; i < result.rows(); i++) { for (size_t j = 0; j < result.cols(); j++) { tot += result(i, j); if (result(i, j) < 0 || result(i, j) > 1) { FAIL(); } } } CHECK( tot == 823.0 ); } TEST_CASE("Robustness regression test #7", "[raster-cell-intersection]") { auto context = init_geos(); Grid ex{{487800, 492800, 5813800, 5818800}, 100, 100}; auto g = GEOSGeom_read_r(context, "POLYGON ((492094.9283999996 5816959.8553, 492374.9335527361 5816811.352641133, 492374.9335527363 5816811.352641133, 492094.9283999996 5816959.8553))"); double total_area; CHECK( GEOSArea_r(context, g.get(), &total_area) == 1); double cell_area = ex.dx() * ex.dy(); double max_possible_frac = total_area / cell_area; auto result = raster_cell_intersection(ex, context, g.get()); for (auto& frac : result) { CHECK( (frac >= 0 && frac <= max_possible_frac) ); } } TEST_CASE("Processing region is empty when there are no polygons") { Box raster_extent{0, 0, 10, 10}; std::vector component_boxes; CHECK( processing_region(raster_extent, component_boxes).empty() ); } TEST_CASE("Processing region is empty when all polygons are outside of it") { Box raster_extent{40, 40, 50, 50}; std::vector component_boxes; component_boxes.emplace_back(60, 60, 70, 70); component_boxes.emplace_back(20, 20, 30, 30); CHECK( processing_region(raster_extent, component_boxes).empty() ); } TEST_CASE("Processing region incorporates overlapping area of all component boxes") { Box raster_extent{40, 40, 50, 50}; std::vector component_boxes; component_boxes.emplace_back(41, 42, 43, 44); CHECK( processing_region(raster_extent, component_boxes) == component_boxes[0] ); component_boxes.emplace_back(30, 30, 45, 46); CHECK( processing_region(raster_extent, component_boxes) == Box{40, 40, 45, 46}); component_boxes.emplace_back(47, 0, 48, 100); CHECK( processing_region(raster_extent, component_boxes) == Box{40, 40, 48, 50} ); component_boxes.emplace_back(49, 49, 100, 100); CHECK( processing_region(raster_extent, component_boxes) == raster_extent ); component_boxes.emplace_back(30, 30, 80, 80); CHECK( processing_region(raster_extent, component_boxes) == raster_extent ); } exactextractr/src/exactextract/test/test_cell.cpp0000644000176200001440000000147214776002150022070 0ustar liggesusers#include "catch.hpp" #include "cell.h" using namespace exactextract; TEST_CASE("Test multi-traversal area calculations", "[cell]" ) { Cell c{0, 0, 20, 20}; // Grab lower-left hand corner c.take({5, 0}); c.take({5, 5}); c.take({0, 5}); c.force_exit(); CHECK( c.last_traversal().traversed() ); CHECK( c.covered_fraction() == 25/400.0 ); // Grab right-hand side c.take({13, 20}); c.take({13, 0}); c.force_exit(); CHECK( c.last_traversal().traversed() ); CHECK( c.covered_fraction() == (25 + 140)/400.0 ); // Carve out a piece from right-hand side c.take({20, 5}); c.take({18, 5}); c.take({18, 10}); c.take({20, 10}); c.force_exit(); CHECK( c.last_traversal().traversed() ); CHECK( c.covered_fraction() == (25 + 140 - 10)/400.0); } exactextractr/src/exactextract/test/test_stats.cpp0000644000176200001440000004572214776002150022315 0ustar liggesusers#include #include #include "catch.hpp" #include "grid.h" #include "raster_cell_intersection.h" #include "raster_stats.h" #include "variance.h" #include "weighted_quantiles.h" #include "geos_utils.h" using Catch::Detail::Approx; namespace exactextract { static GEOSContextHandle_t init_geos() { static GEOSContextHandle_t context = nullptr; if (context == nullptr) { context = initGEOS_r(nullptr, nullptr); } return context; } template void fill_by_row(Raster & r, T start, T dt) { T val = start; for (size_t i = 0; i < r.rows(); i++) { for (size_t j = 0; j < r.cols(); j++) { r(i, j) = val; val += dt; } } } template T nodata_test_value(); template<> float nodata_test_value() { return NAN; } template<> double nodata_test_value() { return -3.2e38; } template<> int nodata_test_value() { return -999; } TEMPLATE_TEST_CASE("Unweighted stats", "[stats]", float, double, int) { GEOSContextHandle_t context = init_geos(); Box extent{-1, -1, 4, 4}; Grid ex{extent, 1, 1}; // 4x5 grid auto g = GEOSGeom_read_r(context, "POLYGON ((0.5 0.5, 2.5 0.5, 2.5 2.5, 0.5 2.5, 0.5 0.5))"); Raster areas = raster_cell_intersection(ex, context, g.get()); auto NA = nodata_test_value(); Raster values{Matrix{{ {1, 1, 1, 1, 1}, {1, 1, 2, 3, 1}, {1, 4, 5, 6, 1}, {1, 0, NA, 7, 1}, {1, 1, 1, 1, 1} }}, extent}; values.set_nodata(NA); RasterStats stats{true}; stats.process(areas, values); CHECK( stats.count() == (0.25 + 0.5 + 0.25) + (0.50 + 1.0 + 0.50) + (0.25 + 0.0 + 0.25) ); CHECK( stats.sum() == (1*0.25 + 2*0.5 + 3*0.25) + (4*0.50 + 5*1.0 + 6*0.50) + (0*0.25 + 0*0.5 + 7*0.25) ); CHECK( stats.mean() == 13.75f / 3.5f ); CHECK( stats.mode() == 5 ); CHECK( stats.minority() == 0 ); CHECK( stats.min() == 0 ); CHECK( stats.max() == 7 ); CHECK( stats.variety() == 8 ); } TEMPLATE_TEST_CASE("Weighted multiresolution stats", "[stats]", float, double, int) { GEOSContextHandle_t context = init_geos(); Box extent { 0, 0, 8, 6 }; Grid ex1{extent, 1, 1}; Grid ex2{extent, 2, 2}; auto g = GEOSGeom_read_r(context, "POLYGON ((3.5 1.5, 6.5 1.5, 6.5 2.5, 3.5 2.5, 3.5 1.5))"); Raster areas = raster_cell_intersection(ex1.common_grid(ex2), context, g.get()); Raster values{extent, 6, 8}; Raster weights{extent, 3, 4}; fill_by_row(values, 1, 1); fill_by_row(weights, 5, 5); RasterStats stats(false); stats.process(areas, values, weights); std::valarray cov_values = { 28, 29, 30, 31, 36, 37, 38, 39 }; std::valarray cov_weights = { 30, 35, 35, 40, 50, 55, 55, 60 }; std::valarray cov_fracs = { 0.25, 0.5, 0.5, 0.25, 0.25, 0.5, 0.5, 0.25 }; CHECK( stats.weighted_mean() == Approx( (cov_values * cov_weights * cov_fracs).sum() / (cov_weights * cov_fracs).sum() )); CHECK( stats.mean() == Approx( (cov_values * cov_fracs).sum() / cov_fracs.sum() )); CHECK( stats.weighted_fraction() == Approx( (cov_values*cov_weights*cov_fracs).sum() / (cov_values*cov_fracs).sum() )); } TEMPLATE_TEST_CASE("Missing data handling", "[stats]", float, double, int) { GEOSContextHandle_t context = init_geos(); Box extent{0, 0, 2, 2}; Grid ex{extent, 1, 1}; // 2x2 grid auto g = GEOSGeom_read_r(context, "POLYGON ((0.5 0.5, 1.5 0.5, 1.5 1.5, 0.5 1.5, 0.5 0.5))"); Raster areas = raster_cell_intersection(ex, context, g.get()); // Polygon covers 25% of each of the four cells for (size_t i = 0; i < areas.rows(); i++) { for (size_t j = 0; j < areas.cols(); j++) { CHECK( areas(i, j) == 0.25f ); } } auto NA = nodata_test_value(); Raster all_values_missing{Matrix{{ {NA, NA}, {NA, NA} }}, extent}; all_values_missing.set_nodata(NA); Raster all_values_defined{Matrix{{ {1, 2}, {3, 4}, }}, extent}; all_values_defined.set_nodata(NA); Raster some_values_defined{Matrix{{ {1, 2}, {NA, NA} }}, extent}; some_values_defined.set_nodata(NA); SECTION("All values missing, no weights provided") { // Example application: land cover on an island not covered by dataset RasterStats stats(false); stats.process(areas, all_values_missing); CHECK( stats.count() == 0 ); CHECK( stats.sum() == 0 ); CHECK( !stats.min().has_value() ); CHECK( !stats.max().has_value() ); CHECK( std::isnan(stats.mean()) ); CHECK( std::isnan(stats.variance()) ); CHECK( std::isnan(stats.stdev()) ); CHECK( std::isnan(stats.weighted_variance()) ); CHECK( std::isnan(stats.weighted_stdev()) ); CHECK( std::isnan(stats.coefficient_of_variation()) ); CHECK( !stats.mode().has_value() ); CHECK( !stats.minority().has_value() ); CHECK( stats.variety() == 0 ); CHECK( stats.weighted_count() == stats.count() ); CHECK( stats.weighted_sum() == stats.sum() ); CHECK( std::isnan(stats.weighted_mean()) ); } SECTION("All values defined, no weights provided") { // Example application: precipitation over polygon in the middle of continent RasterStats stats{true}; stats.process(areas, all_values_defined); CHECK( stats.count() == 1.0f ); CHECK( stats.sum() == 2.5f ); CHECK( stats.min() == 1.0f ); CHECK( stats.max() == 4.0f ); CHECK( stats.mean() == 2.5f ); CHECK( stats.mode() == 4.0f ); CHECK( stats.minority() == 1.0f ); CHECK( stats.variance() == 1.25f ); CHECK( stats.stdev() == 1.118034f ); CHECK( stats.weighted_variance() == stats.variance() ); CHECK( stats.weighted_stdev() == stats.stdev() ); CHECK( stats.coefficient_of_variation() == 0.4472136f ); CHECK( stats.weighted_count() == stats.count() ); CHECK( stats.weighted_sum() == stats.sum() ); CHECK( stats.weighted_mean() == stats.mean() ); } SECTION("Some values defined, no weights provided") { // Example application: precipitation at edge of continent RasterStats stats{true}; stats.process(areas, some_values_defined); CHECK( stats.count() == 0.5f ); CHECK( stats.sum() == 0.75f ); CHECK( stats.min() == 1.0f ); CHECK( stats.max() == 2.0f ); CHECK( stats.mean() == 1.5f ); CHECK( stats.mode() == 2.0f ); CHECK( stats.minority() == 1.0f ); CHECK( stats.variance() == 0.25f ); CHECK( stats.stdev() == 0.5f ); CHECK( stats.weighted_variance() == stats.variance() ); CHECK( stats.weighted_stdev() == stats.stdev() ); CHECK( stats.coefficient_of_variation() == Approx(0.333333f) ); CHECK( stats.weighted_count() == stats.count() ); CHECK( stats.weighted_sum() == stats.sum() ); CHECK( stats.weighted_mean() == stats.mean() ); } SECTION("No values defined, all weights defined") { // Example: population-weighted temperature in dataset covered by pop but without temperature data RasterStats stats{true}; stats.process(areas, all_values_missing, all_values_defined); CHECK( stats.count() == 0 ); CHECK( stats.sum() == 0 ); CHECK( !stats.min().has_value() ); CHECK( !stats.max().has_value() ); CHECK( std::isnan(stats.mean()) ); CHECK( std::isnan(stats.variance()) ); CHECK( std::isnan(stats.stdev()) ); CHECK( std::isnan(stats.weighted_variance()) ); CHECK( std::isnan(stats.weighted_stdev()) ); CHECK( std::isnan(stats.coefficient_of_variation()) ); CHECK( stats.weighted_count() == stats.count() ); CHECK( stats.weighted_sum() == stats.sum() ); CHECK( std::isnan(stats.weighted_mean()) ); } SECTION("No values defined, no weights defined") { RasterStats stats{true}; stats.process(areas, all_values_missing, all_values_missing); CHECK( stats.count() == 0 ); CHECK( stats.sum() == 0 ); CHECK( !stats.min().has_value() ); CHECK( !stats.max().has_value() ); CHECK( std::isnan(stats.mean()) ); CHECK( std::isnan(stats.variance()) ); CHECK( std::isnan(stats.stdev()) ); CHECK( std::isnan(stats.weighted_variance()) ); CHECK( std::isnan(stats.weighted_stdev()) ); CHECK( std::isnan(stats.coefficient_of_variation()) ); CHECK( stats.weighted_count() == 0 ); CHECK( stats.weighted_sum() == 0 ); CHECK( std::isnan(stats.weighted_mean()) ); } SECTION("All values defined, no weights defined") { // Example: population-weighted temperature in polygon covered by temperature but without pop data RasterStats stats{true}; stats.process(areas, all_values_defined, all_values_missing); CHECK( stats.count() == 1.0f ); CHECK( stats.sum() == 2.5f ); CHECK( stats.min() == 1.0f ); CHECK( stats.max() == 4.0f ); CHECK( stats.mean() == 2.5f ); CHECK( stats.variance() == 1.25f ); CHECK( stats.stdev() == 1.118034f ); CHECK( stats.coefficient_of_variation() == 0.4472136f ); CHECK( std::isnan(stats.weighted_count()) ); CHECK( std::isnan(stats.weighted_sum()) ); CHECK( std::isnan(stats.weighted_mean()) ); CHECK( std::isnan(stats.weighted_variance()) ); CHECK( std::isnan(stats.weighted_stdev()) ); } SECTION("All values defined, some weights defined") { RasterStats stats{true}; stats.process(areas, all_values_defined, some_values_defined); CHECK( stats.count() == 1.0f ); CHECK( stats.sum() == 2.5f ); CHECK( stats.min() == 1.0f ); CHECK( stats.max() == 4.0f ); CHECK( stats.mean() == 2.5f ); CHECK( stats.variance() == 1.25f ); CHECK( stats.stdev() == 1.118034f ); CHECK( stats.coefficient_of_variation() == 0.4472136f ); CHECK( std::isnan(stats.weighted_count()) ); CHECK( std::isnan(stats.weighted_sum()) ); CHECK( std::isnan(stats.weighted_mean()) ); CHECK( std::isnan(stats.weighted_variance()) ); CHECK( std::isnan(stats.weighted_stdev()) ); } } TEST_CASE("Stat subsets calculated for a specific category") { std::vector landcov = {1, 1, 1, 2, 2, 2}; std::vector coverage = {0.5, 0.4, 0, 0.3, 0.3, 0.2}; std::vector weight = {0.3, 0.4, 1, 4.0, 3.0, 0}; RasterStats stats{true}; for (std::size_t i = 0; i < landcov.size(); i++) { stats.process_value(landcov[i], coverage[i], weight[i]); } CHECK( stats.count(1).value() == Approx(0.5 + 0.4) ); CHECK( stats.count(2).value() == Approx(0.3 + 0.3 + 0.2) ); CHECK( !stats.count(3).has_value() ); CHECK( stats.frac(1).value() == Approx(stats.count(1).value() / stats.count()) ); CHECK( stats.frac(2).value() == Approx(stats.count(2).value() / stats.count()) ); CHECK( !stats.frac(3).has_value() ); CHECK( stats.weighted_count(1).value() == Approx(0.5*0.3 + 0.4*0.4) ); CHECK( stats.weighted_count(2).value() == Approx(0.3*4.0 + 0.3*3.0) ); CHECK( !stats.weighted_count(3).has_value() ); CHECK( stats.weighted_frac(1).value() == Approx(stats.weighted_count(1).value() / stats.weighted_count()) ); CHECK( stats.weighted_frac(2).value() == Approx(stats.weighted_count(2).value() / stats.weighted_count()) ); CHECK( !stats.weighted_frac(3).has_value() ); } TEST_CASE("Iterator provides access to seen values") { std::vector values = { 1, 3, 2}; std::vector cov = {1.0, 2.0, 0.0}; RasterStats stats{true}; for (std::size_t i = 0; i < values.size(); i++) { stats.process_value(values[i], cov[i], 1.0); } std::vector found(stats.begin(), stats.end()); std::sort(found.begin(), found.end()); CHECK( found.size() == 3 ); CHECK( found[0] == 1 ); CHECK( found[1] == 2 ); CHECK( found[2] == 3 ); } TEST_CASE("Unweighted stats consider all values when part of polygon is inside value raster but outside weighting raster") { GEOSContextHandle_t context = init_geos(); RasterStats weighted_stats{true}; RasterStats unweighted_stats{true}; Grid values_grid{{0, 0, 5, 5}, 1, 1}; // 5x5 grid Grid weights_grid{{0, 2, 5, 5}, 1, 1}; // 3x3 grid auto g = GEOSGeom_read_r(context, "POLYGON ((0.5 0.5, 3.5 0.5, 3.5 3.5, 0.5 3.5, 0.5 0.5))"); auto common_grid = values_grid.common_grid(weights_grid); Raster areas = raster_cell_intersection(common_grid, context, g.get()); Raster values(values_grid); Raster weights(weights_grid); fill_by_row(values, 1.0, 1.0); fill_by_row(weights, 0.1, 0.05); weighted_stats.process(areas, values, weights); unweighted_stats.process(areas, values); CHECK( weighted_stats.count() == unweighted_stats.count() ); CHECK( weighted_stats.max() == unweighted_stats.max() ); CHECK( weighted_stats.mean() == unweighted_stats.mean() ); CHECK( weighted_stats.min() == unweighted_stats.min() ); CHECK( weighted_stats.sum() == unweighted_stats.sum() ); } TEST_CASE("Variance calculations are correct for equally-weighted observations") { std::vector values{3.4, 2.9, 1.7, 8.8, -12.7, 100.4, 8.4, 11.3}; WestVariance wv; for (const auto& x : values) { wv.process(x, 3.0); } CHECK( wv.stdev() == Approx(32.80967) ); // CHECK( wv.variance() == Approx(1076.474) ); CHECK( wv.coefficent_of_variation() == Approx(2.113344) ); } TEST_CASE("Variance calculations are correct for unequally-weighted observations") { std::vector values{3.4, 2.9, 1.7, 8.8, -12.7, 100.4, 8.4, 11.3, 50}; std::vector weights{1.0, 0.1, 1.0, 0.2, 0.44, 0.3, 0.3, 0.83, 0}; WestVariance wv; for (size_t i = 0; i < values.size(); i++) { wv.process(values[i], weights[i]); } CHECK( wv.stdev() == Approx(25.90092) ); // output from Weighted.Desc.Stat::w.sd in R CHECK( wv.variance() == Approx(670.8578) ); // output from Weighted.Desc.Stat::w.var in R CHECK( wv.coefficent_of_variation() == Approx(2.478301) ); // output from Weighted.Desc.Stat::w.sd / Weighted.Desc.Stat::w.mean } TEST_CASE("Variance calculations are correct for unequally-weighted observations, initial zeros") { std::vector values{1, 2, 3, 4, 5, 6, 7, 8, 9}; std::vector weights{0, 0, 0, 0, 0, 0, 0.25, 0.5, 0.25}; WestVariance wv; for (size_t i = 0; i < values.size(); i++) { wv.process(values[i], weights[i]); } CHECK( wv.stdev() == Approx(0.7071068) ); // output from Weighted.Desc.Stat::w.sd in R CHECK( wv.variance() == Approx(0.5) ); // output from Weighted.Desc.Stat::w.var in R CHECK( wv.coefficent_of_variation() == Approx(0.7071068 / 8) ); // output from Weighted.Desc.Stat::w.sd / Weighted.Desc.Stat::w.mean } TEST_CASE("Weighted quantile calculations are correct for equally-weighted inputs") { std::vector values{3.4, 2.9, 1.7, 8.8, -12.7, 100.4, 8.4, 11.3}; WeightedQuantiles wq; for (const auto &x : values) { wq.process(x, 1.7); } // check values against output of stats::quantile in R CHECK( wq.quantile(0) == -12.7 ); CHECK( wq.quantile(0.25) == Approx(2.6) ); CHECK( wq.quantile(0.50) == Approx(5.9) ); CHECK( wq.quantile(0.75) == Approx(9.425) ); CHECK( wq.quantile(1.0) == Approx(100.4) ); } TEST_CASE("Weighted quantile calculations are correct for unequally-weighted inputs") { std::vector values{3.4, 2.9, 1.7, 8.8, -12.7, 100.4, 8.4, 11.3, 50}; std::vector weights{1.0, 0.1, 1.0, 0.2, 0.44, 0.3, 0.3, 0.83, 0}; WeightedQuantiles wq; for (auto i = 0; i < values.size(); i++) { wq.process(values[i], weights[i]); } // check values against output of wsim.distributions::stack_weighted_quantile in R // https://gitlab.com/isciences/wsim/wsim CHECK( wq.quantile(0.00) == Approx(-12.7) ); CHECK( wq.quantile(0.25) == Approx(2.336667) ); CHECK( wq.quantile(0.50) == Approx(4.496774) ); CHECK( wq.quantile(0.75) == Approx(9.382437) ); CHECK( wq.quantile(1.00) == Approx(100.4) ); } TEST_CASE("Weighted quantile errors on invalid weights") { CHECK_THROWS( WeightedQuantiles().process(5, -1 ) ); CHECK_THROWS( WeightedQuantiles().process(3, std::numeric_limits::quiet_NaN() )); CHECK_THROWS( WeightedQuantiles().process(3, std::numeric_limits::infinity() )); } TEST_CASE("Weighted quantile errors on invalid quantiles") { WeightedQuantiles wq; for (auto i = 0; i < 10; i++) { wq.process(i, 1); } CHECK_THROWS( wq.quantile(1.1) ); CHECK_THROWS( wq.quantile(-0.1) ); CHECK_THROWS( wq.quantile(std::numeric_limits::quiet_NaN()) ); } TEST_CASE("Weighted quantiles are appropriately refreshed") { WeightedQuantiles wq; wq.process(1, 1); wq.process(2, 1), wq.process(3, 1); CHECK( wq.quantile(0.5) == 2 ); wq.process(4, 1); CHECK( wq.quantile(0.5) == 2.5 ); } } exactextractr/src/exactextract/test/test_box.cpp0000644000176200001440000001360214776002150021737 0ustar liggesusers#include "catch.hpp" #include "box.h" using namespace exactextract; TEST_CASE("Box dimensions are calculated correctly", "[box]" ) { Box b{5, 7, 7, 10}; CHECK( b.width() == 2 ); CHECK( b.height() == 3 ); CHECK( b.area() == 6 ); } TEST_CASE("Coordinate sides are correctly identified", "[box]") { Box b{0, 0, 13, 17}; CHECK(Side::LEFT == b.side({0, 5})); CHECK(Side::RIGHT == b.side({13, 5})); CHECK(Side::TOP == b.side({5, 17})); CHECK(Side::BOTTOM == b.side({5, 0})); CHECK(Side::NONE == b.side({5, 5})); } TEST_CASE("Boxes with empty or negative dimensions are considered empty", "[box]") { CHECK( !Box{0, 0, 1, 1}.empty() ); // normal box CHECK( Box{0, 0, 0, 1}.empty() ); // no width CHECK( Box{0, 0, 1, 0}.empty() ); // no height CHECK( Box{0, 0, 0, 0}.empty() ); // no width or height CHECK( Box{0, 0, -1, 1}.empty() ); // negative width CHECK( Box{0, 0, 1, -1}.empty() ); // negative height CHECK( Box{0, 0, -1, -1}.empty() ); // negative width and height } TEST_CASE("Box intersection test is correct", "[box]") { Box a = {0, 0, 10, 10}; Box b = {20, 20, 30, 30}; Box c = {5, 5, 25, 25 }; Box d = {1, 1, 2, 2}; CHECK( !a.intersects(b) ); CHECK( !b.intersects(a) ); CHECK( a.intersects(c) ); CHECK( c.intersects(a) ); CHECK( b.intersects(c) ); CHECK( c.intersects(b) ); CHECK( a.intersects(d) ); CHECK( d.intersects(a) ); CHECK( !b.intersects(d) ); CHECK( !d.intersects(b) ); } TEST_CASE("Box intersection calculations are correct", "[box]") { Box a = {0, 1, 9, 10}; Box b = {21, 22, 27, 29}; Box c = {5, 4, 25, 24 }; Box d = {1, 2, 3, 4}; CHECK( a.intersection(a) == a ); CHECK( a.intersection(b).empty() ); CHECK( a.intersection(c) == Box{5, 4, 9, 10} ); CHECK( a.intersection(d) == d ); CHECK( b.intersection(a).empty() ); CHECK( b.intersection(b) == b ); CHECK( b.intersection(c) == Box{21, 22, 25, 24} ); CHECK( b.intersection(d).empty() ); CHECK( c.intersection(a) == a.intersection(c) ); CHECK( c.intersection(b) == b.intersection(c) ); CHECK( c.intersection(c) == c ); CHECK( c.intersection(d).empty() ); CHECK( d.intersection(a) == d ); CHECK( d.intersection(b).empty() ); CHECK( d.intersection(c).empty() ); CHECK( d.intersection(d) == d ); } TEST_CASE("Boxes can be expanded to include other boxes", "[box]") { Box a = {0, 1, 9, 10}; Box b = {21, 22, 27, 29}; Box c = {5, 4, 25, 24 }; Box d = {1, 2, 3, 4}; Box e = {0, 0, -1, -1}; // empty CHECK( a.expand_to_include(a) == a ); CHECK( a.expand_to_include(b) == Box{0, 1, 27, 29} ); CHECK( a.expand_to_include(c) == Box{0, 1, 25, 24} ); CHECK( a.expand_to_include(d) == a ); CHECK( a.expand_to_include(e) == a ); CHECK( b.expand_to_include(a) == a.expand_to_include(b) ); CHECK( b.expand_to_include(b) == b ); CHECK( b.expand_to_include(c) == Box{5, 4, 27, 29} ); CHECK( b.expand_to_include(d) == Box{1, 2, 27, 29} ); CHECK( b.expand_to_include(e) == b ); CHECK( c.expand_to_include(a) == a.expand_to_include(c) ); CHECK( c.expand_to_include(b) == b.expand_to_include(c) ); CHECK( c.expand_to_include(c) == c ); CHECK( c.expand_to_include(d) == Box{1, 2, 25, 24} ); CHECK( c.expand_to_include(e) == c ); CHECK( d.expand_to_include(a) == a.expand_to_include(d) ); CHECK( d.expand_to_include(b) == b.expand_to_include(d) ); CHECK( d.expand_to_include(c) == c.expand_to_include(d) ); CHECK( d.expand_to_include(d) == d ); CHECK( d.expand_to_include(e) == d ); CHECK( e.expand_to_include(a) == a); CHECK( e.expand_to_include(b) == b); CHECK( e.expand_to_include(c) == c); CHECK( e.expand_to_include(d) == d); CHECK( e.expand_to_include(e).empty() ); } TEST_CASE("Crossing is correctly determined", "[box]") { Box b{0, 0, 1, 1}; double tol = 1e-14; // vertical up Crossing crx = b.crossing({0.4, 0.6}, {0.4, 11}); CHECK( crx.side() == Side::TOP ); CHECK( crx.coord().equals({0.4, 1}, tol) ); // vertical down crx = b.crossing({0.4, 0.6}, {0.4, -11}); CHECK( crx.side() == Side::BOTTOM ); CHECK( crx.coord().equals({0.4, 0}, tol) ); // horizontal left crx = b.crossing({0.4, 0.6}, {-11, 0.6}); CHECK( crx.side() == Side::LEFT ); CHECK( crx.coord().equals({0, 0.6}, tol) ); // horizontal right crx = b.crossing({0.4, 0.6}, {11, 0.6}); CHECK( crx.side() == Side::RIGHT ); CHECK( crx.coord().equals({1, 0.6}, tol) ); // upper-right (1) crx = b.crossing({0.4, 0.6}, {1.2, 0.8}); CHECK( crx.side() == Side::RIGHT ); CHECK( crx.coord().equals({1, 0.75}, tol) ); // upper-right (2) crx = b.crossing({0.4, 0.6}, {0.7, 1.2}); CHECK( crx.side() == Side::TOP ); CHECK( crx.coord().equals({0.6, 1}, tol) ); // upper-left (1) crx = b.crossing({0.4, 0.6}, {0.1, 1.2}); CHECK( crx.side() == Side::TOP ); CHECK( crx.coord().equals({0.2, 1}, tol) ); // upper-left (2) crx = b.crossing({0.4, 0.6}, {-0.4, 1.2}); CHECK( crx.side() == Side::LEFT ); CHECK( crx.coord().equals({0, 0.9}, tol) ); // upper-left (2) crx = b.crossing({0.4, 0.6}, {-0.4, 1.2}); CHECK( crx.side() == Side::LEFT ); CHECK( crx.coord().equals({0, 0.9}, tol) ); // lower-left (1) crx = b.crossing({0.4, 0.6}, {-0.4, 0.2}); CHECK( crx.side() == Side::LEFT ); CHECK( crx.coord().equals({0, 0.4}, tol) ); // lower-left (2) crx = b.crossing({0.4, 0.6}, {0.1, -0.3}); CHECK( crx.side() == Side::BOTTOM ); CHECK( crx.coord().equals({0.2, 0}, tol) ); // lower-right (1) crx = b.crossing({0.4, 0.6}, {0.7, -0.3}); CHECK( crx.side() == Side::BOTTOM ); CHECK( crx.coord().equals({0.6, 0}, tol) ); // lower-right (2) crx = b.crossing({0.4, 0.6}, {1.6, 0}); CHECK( crx.side() == Side::RIGHT ); CHECK( crx.coord().equals({1, 0.3}, tol) ); } exactextractr/src/exactextract/test/test_geos_utils.cpp0000644000176200001440000000312014776002150023316 0ustar liggesusers#include "catch.hpp" #include "geos_utils.h" using namespace exactextract; TEST_CASE("Box-polygon conversion", "[geos]") { GEOSContextHandle_t context = initGEOS_r(nullptr, nullptr); Box b { 3, 2, 7, 9 }; auto actual = geos_make_box_polygon(context, b); auto expected = GEOSGeom_read_r(context, "POLYGON ((3 2, 7 2, 7 9, 3 9, 3 2))"); CHECK( GEOSEqualsExact_r(context, actual.get(), expected.get(), 0.0) ); CHECK( b == geos_get_box(context, expected.get()) ); finishGEOS_r(context); } TEST_CASE("Orientation testing", "[geos]") { GEOSContextHandle_t context = initGEOS_r(nullptr, nullptr); auto ccw = GEOSGeom_read_r(context, "LINEARRING (0 0, 1 0, 1 1, 0 1, 0 0)"); auto cw = GEOSGeom_read_r(context, "LINEARRING (0 0, 0 1, 1 1, 1 0, 0 0)"); auto too_short = GEOSGeom_read_r(context, "LINESTRING (0 0, 1 1, 1 3)"); CHECK( geos_is_ccw(context, GEOSGeom_getCoordSeq_r(context, ccw.get())) ); CHECK( !geos_is_ccw(context, GEOSGeom_getCoordSeq_r(context, cw.get())) ); CHECK_THROWS( geos_is_ccw(context, GEOSGeom_getCoordSeq_r(context, too_short.get())) ); finishGEOS_r(context); } TEST_CASE("Segment intersection", "[geos]") { GEOSContextHandle_t context = initGEOS_r(nullptr, nullptr); Coordinate result; bool intersected; intersected = segment_intersection(context, {0, 0}, {1, 2}, {0, 1}, {2, 1}, result); CHECK( intersected ); CHECK( result == Coordinate{0.5, 1.0} ); intersected = segment_intersection(context, {0, 0}, {1, 1}, {2, 2}, {3, 3}, result); CHECK( !intersected ); finishGEOS_r(context); } exactextractr/src/exactextract/test/test_perimeter_distance.cpp0000644000176200001440000000152114776002150025012 0ustar liggesusers#include "catch.hpp" #include "perimeter_distance.h" using namespace exactextract; TEST_CASE( "Perimeter distance from origin", "[perimeter-distance]" ) { Box b{0, 0, 13, 17}; CHECK( perimeter_distance(b, {0, 0 }) == 0 ); CHECK( perimeter_distance(b, {0, 5 }) == 5 ); CHECK( perimeter_distance(b, {0, 17}) == 17 ); CHECK( perimeter_distance(b, {6, 17}) == 23 ); CHECK( perimeter_distance(b, {13, 17}) == 30 ); CHECK( perimeter_distance(b, {13, 14}) == 33 ); CHECK( perimeter_distance(b, {13, 0 }) == 47 ); CHECK( perimeter_distance(b, {5, 0 }) == 55 ); } TEST_CASE( "CCW perimeter distance between two measures", "[perimeter-distance]" ) { CHECK( perimeter_distance_ccw(3, 2, 100) == 1 ); CHECK( perimeter_distance_ccw(0.5, 3.5, 4) == 1 ); CHECK( perimeter_distance_ccw(1, 1, 100) == 0 ); } exactextractr/src/exactextract/test/test_traversal_areas.cpp0000644000176200001440000000405614776002150024330 0ustar liggesusers#include "catch.hpp" #include "traversal_areas.h" using namespace exactextract; using TraversalVector = std::vector*>; TEST_CASE("Exit from same side as entry", "[traversal-areas]" ) { Box b{0, 0, 10, 10}; std::vector traversal { {7, 0}, {7, 1}, {6, 1}, {6, 0} }; TraversalVector traversals { &traversal }; CHECK( left_hand_area(b, traversals) == 1 ); std::reverse(traversal.begin(), traversal.end()); CHECK( left_hand_area(b, traversals) == 99 ); } TEST_CASE("Enter bottom, exit left", "[traversal-areas]") { Box b{0, 0, 10, 10}; std::vector traversal { {5, 0}, {5, 5}, {0, 5} }; TraversalVector traversals { &traversal }; CHECK( left_hand_area(b, traversals) == 25 ); } TEST_CASE("Enter bottom, exit top", "[traversal-areas]") { Box b{0, 0, 10, 10}; std::vector traversal { {4, 0}, {4, 10} }; TraversalVector traversals { &traversal }; CHECK( left_hand_area(b, traversals) == 40 ); } TEST_CASE("Multiple traversals (basic)", "[traversal-areas]") { Box b{0, 0, 10, 10}; std::vector t1 = { {2, 10}, {2, 0} }; std::vector t2 = { {4, 0}, {4, 10} }; TraversalVector traversals{ &t1, &t2 }; CHECK( left_hand_area(b, traversals) == 20 ); } TEST_CASE("Multiple traversals", "[traversal-areas]") { Box b{0, 0, 10, 10}; std::vector t1 = { { 2, 0}, { 2, 2}, {0, 2} }; // 2x2 = 4 std::vector t2 = { { 3, 10}, { 3, 0} }; std::vector t3 = { { 5, 0}, { 5, 10} }; // 2x10 = 20 std::vector t4 = { { 8, 10}, {10, 8} }; // 2x2/2 = 2 std::vector t5 = { {10, 6}, { 8, 6}, {8, 3}, {10, 3} }; // 2x3 = 6 std::vector t6 = { {10, 4}, { 9, 4}, {9, 5}, {10, 5} }; // 1x1 = 1 (subtracted) std::vector t7 = { {10, 3}, { 8, 3}, {8, 0} }; // 2x3 = 6 TraversalVector traversals{ &t1, &t2, &t3, &t4, &t5, &t6, &t7 }; CHECK( left_hand_area(b, traversals) == 4 + 20 + 2 + 6 - 1 + 6 ); } exactextractr/src/exactextract/test/test_raster_iterator.cpp0000644000176200001440000000212114776002150024352 0ustar liggesusers#include #include "catch.hpp" #include "grid.h" #include "matrix.h" #include "raster.h" using namespace exactextract; template void fill_sequential(Raster & r) { for (size_t i = 0; i < r.rows(); i++) { for (size_t j = 0; j < r.cols(); j++) { r(i, j) = i*r.cols() + j; } } } TEST_CASE("Iterator comparison") { Raster r{{-180, -90, 180, 90}, 180, 360}; fill_sequential(r); Raster r2{{-180, -90, 180, 90}, 180, 360}; auto a = r.begin(); auto b = r.begin(); CHECK( a == b ); CHECK( !(a != b )); auto c = r2.begin(); CHECK( a != c ); CHECK( !(c == a) ); ++a; CHECK( a != b ); CHECK( !(a == b)); } TEST_CASE("Iterator operates rowwise") { Raster r{{0, 0, 8, 10}, 10, 8}; fill_sequential(r); auto it = r.begin(); CHECK( *it == 0 ); ++it; CHECK( *it == 1 ); std::vector values(r.begin(), r.end()); std::vector expected(r.size()); std::iota(expected.begin(), expected.end(), 0); CHECK( values == expected ); } exactextractr/src/exactextract/test/test_grid.cpp0000644000176200001440000002735314776002150022104 0ustar liggesusers#include "catch.hpp" #include "grid.h" #include using namespace exactextract; const Box global{-180, -90, 180, 90}; TEST_CASE("Infinite grid dimensions calculated correctly", "[grid]") { Grid grid{global, 0.5, 0.5}; CHECK( grid.rows() == 2+360); CHECK( grid.cols() == 2+720); } TEST_CASE("Infinite grid dimension robustness", "[grid]") { Grid grid{{8.5, 1.6, 16.2, 13.1}, 0.1, 0.1}; CHECK(grid.cols() == 2+77); CHECK(grid.rows() == 2+115); } TEST_CASE("Bounded grid dimensions calculated correctly", "[grid]") { Grid grid{global, 0.5, 0.5}; CHECK( grid.rows() == 360); CHECK( grid.cols() == 720); } TEST_CASE("Bounded grid dimension robustness", "[grid]") { Grid grid{{8.5, 1.6, 16.2, 13.1}, 0.1, 0.1}; CHECK(grid.cols() == 77); CHECK(grid.rows() == 115); } TEST_CASE("Infinite grid index lookups are correct", "[grid]") { Grid grid{global, 1.0, 0.5}; CHECK( grid.get_row(90) == 1 ); CHECK( grid.get_row(-89.50000001) == 360 ); CHECK( grid.get_row(-89.5) == 360 ); CHECK( grid.get_row(-90) == 360 ); CHECK( grid.get_row(-90.00000001) == 361 ); CHECK( grid.get_row( 90.00000001) == 0 ); CHECK( grid.get_column(-180) == 1 ); CHECK( grid.get_column(-179.000001) == 1 ); CHECK( grid.get_column(-179) == 2 ); CHECK( grid.get_column(179) == 360 ); CHECK( grid.get_column(180) == 360 ); CHECK( grid.get_column(-180.0000001) == 0 ); CHECK( grid.get_column( 180.0000001) == 361 ); } TEST_CASE("Bounded grid index lookups are correct", "[grid]") { Grid grid{global, 1.0, 0.5}; CHECK( grid.get_row(90) == 0 ); CHECK( grid.get_row(-89.50000001) == 359 ); CHECK( grid.get_row(-89.5) == 359 ); CHECK( grid.get_row(-90) == 359 ); CHECK_THROWS( grid.get_row(-90.00000001) ); CHECK_THROWS( grid.get_row( 90.00000001) ); CHECK( grid.get_column(-180) == 0 ); CHECK( grid.get_column(-179.000001) == 0 ); CHECK( grid.get_column(-179) == 1 ); CHECK( grid.get_column(179) == 359 ); CHECK( grid.get_column(180) == 359 ); CHECK_THROWS( grid.get_column(-180.0000001) ); CHECK_THROWS( grid.get_column( 180.0000001) ); } TEST_CASE("Infinite grid shrink works correctly", "[grid]") { Grid grid1{global, 1, 0.5}; Grid grid2 = grid1.shrink_to_fit({-44.3, -21.4, 18.3, 88.2}); CHECK( grid2.xmin() == -45 ); CHECK( grid2.xmax() == 19 ); CHECK( grid2.ymin() == -21.5 ); CHECK( grid2.ymax() == 88.5 ); CHECK( grid2.dx() == grid1.dx() ); CHECK( grid2.dy() == grid1.dy() ); } TEST_CASE("Bounded grid shrink works correctly", "[grid]") { Grid grid1{global, 1, 0.5}; Grid grid2 = grid1.shrink_to_fit({-44.3, -21.4, 18.3, 88.2}); CHECK( grid2.xmin() == -45 ); CHECK( grid2.xmax() == 19 ); CHECK( grid2.ymin() == -21.5 ); CHECK( grid2.ymax() == 88.5 ); CHECK( grid2.dx() == grid1.dx() ); CHECK( grid2.dy() == grid1.dy() ); } TEST_CASE("Repeated shrink has no effect", "[grid]") { Grid grid{{-180.5, -90, 180, 90}, 0.1, 0.1}; Box reduced{ 8.532812500000006, 1.6762207031249972, 16.183398437500017, 13.078515624999994 }; Grid grid2 = grid.shrink_to_fit(reduced); Grid grid3 = grid2.shrink_to_fit(reduced); CHECK( grid2.rows() == grid3.rows() ); CHECK( grid2.cols() == grid3.cols() ); } TEST_CASE("Shrink robustness", "[grid]") { Grid grid{{-180.5, -90, 180, 90}, 0.5, 0.5}; Box reduced{ -1.0000000000000142, 8.141666666665664, 0.08749999999993818, 9.904166666665645}; Grid grid2 = grid.shrink_to_fit(reduced); CHECK(reduced.xmin >= grid2.xmin()); CHECK(reduced.xmax <= grid2.xmax()); CHECK(reduced.ymin >= grid2.ymin()); CHECK(reduced.ymax <= grid2.ymax()); } TEST_CASE("Shrink robustness (2)", "[grid]") { Grid grid{{-180.5, -90.5, 180.5, 90.5}, 0.25, 0.25}; Box reduced{ 129.75833333333242, -1.2541666666666238, 129.7624999999993, -1.2499999999999964 }; Grid grid2 = grid.shrink_to_fit(reduced); CHECK(reduced.xmin >= grid2.xmin()); CHECK(reduced.xmax <= grid2.xmax()); CHECK(reduced.ymin >= grid2.ymin()); CHECK(reduced.ymax <= grid2.ymax()); } TEST_CASE("Cropping", "[grid]") { Grid grid{{0, 0, 10, 10}, 0.5, 0.5}; // Cropping with a box larger than the grid's extent has no effect CHECK( grid.crop({-100, -100, 100, 100}) == grid ); // Nor does cropping a grid with its own box CHECK( grid.crop(grid.extent()) == grid ); // Extent of cropped grid is no larger than necessary to contain box CHECK( grid.crop({1.8, 2.2, 6.4, 7.5}) == Grid{{1.5, 2.0, 6.5, 7.5}, 0.5, 0.5} ); // But it doesn't expand to cover the box, where the box is larger than the grid's extent CHECK( grid.crop({1.8, -2, 11, 7.5}) == Grid{{1.5, 0, 10, 7.5}, 0.5, 0.5} ); // The cropping code hits a special case when the new xmax/ymin falls exactly on a cell boundary CHECK( grid.crop({2, 2, 8, 8}) == Grid{{2, 2, 8, 8}, 0.5, 0.5} ); // Cropping to a box outside the extent of the grid produces an empty grid CHECK( grid.crop({200, 200, 300, 300}) == Grid::make_empty() ); CHECK( grid.crop({100, 100, 200, 100}) == Grid::make_empty() ); } TEST_CASE("Cropping robustness", "[grid]") { Grid grid{{-180, -90, 180, 90}, 0.0083333333333333332, 0.0083333333333333332}; Box b = { 178.60767788357205, 70.782677883572063, 180, 71.542309400770421 }; auto cropped = grid.crop(b); CHECK( grid.extent().contains(cropped.extent()) ); } TEST_CASE("Cropping robustness (2)") { Grid grid{{-180, -90, 180, 90}, 0.5, 0.5}; Box b{179.749999999999972, -18.5833333333333321, 179.999999999999972, -18.5}; auto cropped = grid.crop(b); CHECK( grid.extent().contains(cropped.extent()) ); } TEST_CASE("Grid compatibility tests", "[grid]") { constexpr double tol = 1e-6; Grid half_degree_global{global, 0.5, 0.5}; Grid one_degree_global{global, 1, 1}; Grid quarter_degree_partial{{-180, -60, 90, 83}, 0.25, 0.25}; Grid nldas{{-125.0, 0.25, -67, 53}, 0.125, 0.125}; Grid tenth_degree_global{global, 0.1, 0.1}; Grid half_degree_offset{{-180.25, -90, -100.25, 50}, 0.5, 0.5}; CHECK( half_degree_global.compatible_with(one_degree_global, tol) ); CHECK( quarter_degree_partial.compatible_with(one_degree_global, tol) ); CHECK( one_degree_global.compatible_with(nldas, tol) ); CHECK( half_degree_global.compatible_with(tenth_degree_global, tol) ); CHECK( !quarter_degree_partial.compatible_with(tenth_degree_global, tol) ); CHECK( !tenth_degree_global.compatible_with(nldas, tol) ); CHECK( !half_degree_global.compatible_with(half_degree_offset, tol) ); } TEST_CASE("Grid compatibility, with tolerance", "[grid]") { constexpr double tol = 1e-6; Grid a{{60.525000000000006, 29.308333333333334, 75.166666666666671, 38.491666666666667}, 0.0083333333333333332, 0.0083333333333333332}; Grid b{{60.5, 29, 75.5, 38.5}, 0.5, 0.5}; CHECK ( a.compatible_with(b, tol) ); CHECK ( b.compatible_with(a, tol) ); } TEST_CASE("Grid compatibility with reduced tolerance") { constexpr double tol = 1e-3; // Examples below are taken from test data used in vignettes for exactextractr // The grids are considered compatible before they are pre-cropped to the extent of the input polygons, // but not afterwards. Grid a{{-25.8583333333334, 37.6999999999999, -25.1333333333334, 37.9083333333333}, 1.0/120, 1.0/120}; Grid b{{-25.8550000000072, 37.7029166667142, -25.1345833334558, 37.9095833333478}, 1.0/4800, 1.0/4800}; CHECK ( a.compatible_with(b, tol) ); CHECK ( b.compatible_with(a, tol) ); } TEST_CASE("Grid compatibility (empty grid)", "[grid]") { constexpr double tol = 0.0; Grid half_degree_global{global, 0.5, 0.5}; CHECK( half_degree_global.compatible_with(Grid::make_empty(), tol) ); CHECK( Grid::make_empty().compatible_with(half_degree_global, tol) ); CHECK( Grid::make_empty().compatible_with(Grid::make_empty(), tol) ); } TEST_CASE("Common extent calculation", "[grid]") { Grid half_degree_global{global, 0.5, 0.5}; Grid nldas{{-125.0, 0.25, -67, 53}, 0.125, 0.125}; CHECK( nldas.common_grid(half_degree_global) == Grid{global, 0.125, 0.125} ); CHECK( nldas.overlapping_grid(half_degree_global) == nldas ); } TEST_CASE("Common extent calculation (empty grid)", "[grid]") { Grid half_degree_global{global, 0.5, 0.5}; Grid empty = Grid::make_empty(); CHECK( half_degree_global.common_grid(empty) == half_degree_global ); CHECK( half_degree_global.overlapping_grid(empty) == empty ); } TEST_CASE("Cell center calculations", "[grid]") { Grid g1{global, 0.5, 0.25}; Grid g2{global, 0.5, 0.25}; CHECK ( g1.x_for_col(0) == -179.75 ); CHECK ( g2.x_for_col(1) == -179.75 ); CHECK ( g1.y_for_row(0) == 89.875 ); CHECK ( g2.y_for_row(1) == 89.875 ); } TEST_CASE("Offset calculations", "[grid]") { Grid g1{global, 0.5, 0.25}; Grid g2{{-170, -90, 180, 88.5}, 0.5, 0.25}; // Symmetrical; we're expected to already know which grid is positively offset from the other CHECK( g1.row_offset(g2) == 6 ); CHECK( g2.row_offset(g1) == 6 ); CHECK ( g1.col_offset(g2) == 20 ); CHECK ( g2.col_offset(g1) == 20 ); } TEST_CASE("Infinite grid offset calculations", "[grid]") { Grid g1{global, 0.5, 0.25}; Grid g2{{-170, -90, 180, 88.5}, 0.5, 0.25}; // Symmetrical; we're expected to already know which grid is positively offset from the other CHECK( g1.row_offset(g2) == 6 ); CHECK( g2.row_offset(g1) == 6 ); CHECK ( g1.col_offset(g2) == 20 ); CHECK ( g2.col_offset(g1) == 20 ); } template static size_t total_subgrid_size(const T& grids) { return std::accumulate( grids.begin(), grids.end(), static_cast(0), [](size_t sum, const Grid & g) { return sum + g.size(); }); } TEST_CASE("Grid subdivision", "[grid]") { Grid g{{-180, -89.75, 180, 90}, 0.25, 0.25}; // A row in a global 0.25-degree grid has 1440 cells. // With a maximum of 1000 cells per subgrid, we need two subgrids to cover each row. auto grids = subdivide(g, 1000); CHECK(grids.size() == 2*g.rows()); // The first subgrid is the maximum size, and the second subgrid gets the leftovers. // Technically, you could combine the leftovers from two rows into a subgrid of size 880, // and have three subgrids per two rows, but we're not that clever. CHECK(grids[0].size() == 1000); CHECK(grids[1].size() == 440); CHECK(total_subgrid_size(grids) == g.size()); // If we up the maximum to 3000 cells per subgrid, we can cover two rows per subgrid grids = subdivide(g, 3000); CHECK(grids.size() == std::ceil(0.5*g.rows())); CHECK(grids[0].size() == 2880); CHECK(grids[1].size() == 2880); CHECK(grids[grids.size()-1].size() == 1440); // leftover single row at the end CHECK(total_subgrid_size(grids) == g.size()); } TEST_CASE("Empty grid subdivision", "[grid]") { auto g = Grid::make_empty(); auto grids = subdivide(g, 100); } exactextractr/src/exactextract/test/test_raster.cpp0000644000176200001440000002300114776002150022441 0ustar liggesusers#include #include "catch.hpp" #include "grid.h" #include "matrix.h" #include "raster.h" using namespace exactextract; template void fill_with_squares(Raster & r) { for (size_t i = 0; i < r.rows(); i++) { for (size_t j = 0; j < r.cols(); j++) { r(i, j) = i*j; } } } template void fill_sequential(Raster & r) { for (size_t i = 0; i < r.rows(); i++) { for (size_t j = 0; j < r.cols(); j++) { r(i, j) = i*r.cols() + j; } } } template static void print(const AbstractRaster & r) { for (size_t i = 0; i < r.rows(); i++) { for (size_t j = 0; j < r.cols(); j++) { std::cout << r(i, j) << '\t'; } std::cout << std::endl; } } TEST_CASE("Constructing a Raster" ) { Raster r{{-180, -90, 180, 90}, 180, 360}; fill_with_squares(r); CHECK ( r.rows() == 180 ); CHECK ( r.cols() == 360 ); CHECK ( r.xres() == 1.0 ); CHECK ( r.yres() == 1.0 ); CHECK ( r.xmin() == -180 ); CHECK ( r.xmax() == 180 ); CHECK ( r.ymin() == -90 ); CHECK ( r.ymax() == 90 ); bool all_equal = true; for (size_t i = 0; i < r.rows(); i++) { for (size_t j = 0; j < r.cols(); j++) { if (r(i, j) != i*j) all_equal = false; } } CHECK (all_equal); }; TEST_CASE("Rasters are unequal when their values differ") { Raster r1{{0, 0, 1, 1}, 10, 10}; Raster r2{{0, 0, 1, 1}, 10, 10}; fill_with_squares(r1); fill_with_squares(r2); CHECK( r1 == r2 ); CHECK( !(r1 != r2) ); r2(1, 3) = -5; CHECK( !(r1 == r2) ); CHECK( r1 != r2 ); } TEST_CASE("Can access raster cells by value or by reference") { Raster r{{0, 0, 1, 1}, 10, 10}; fill_sequential(r); float orig = r(2, 2); float& cell = r(2, 2); cell *= 2; CHECK( r(2, 2) == orig*2 ); } TEST_CASE("Rasters are unequal when their extents differ") { Raster r1{{0, 0, 1, 1}, 10, 10}; Raster r2{{0, 0, 1, 10}, 10, 10}; fill_with_squares(r1); fill_with_squares(r2); CHECK( r1 != r2); } TEST_CASE("Rasters are equal if their NODATA values differ, only long as the NODATA value is never used (1)") { Raster r1{{0, 0, 1, 1}, 10, 10}; Raster r2{{0, 0, 1, 1}, 10, 10}; r1.set_nodata(999); fill_with_squares(r1); fill_with_squares(r2); CHECK( r1.has_nodata() ); CHECK( !r2.has_nodata() ); CHECK( r1 == r2); r1.set_nodata(25); CHECK( r1 != r2 ); r2.set_nodata(25); CHECK( r1 == r2); } TEST_CASE("Rasters are equal if their NODATA values differ, only long as the NODATA value is never used (2)") { Raster r1{{0, 0, 1, 1}, 10, 10}; Raster r2{{0, 0, 1, 1}, 10, 10}; fill_with_squares(r1); fill_with_squares(r2); CHECK( r1 == r2); r2.set_nodata(25); CHECK( r1 != r2 ); r1.set_nodata(25); CHECK( r1 == r2 ); } TEST_CASE("Creating a scaled view") { Raster r{{0, 0, 10, 10}, 10, 10}; Grid ex{{0, 0, 10, 10}, 0.1, 0.1}; fill_with_squares(r); RasterView rv(r, ex); CHECK ( rv.xmin() == 0 ); CHECK ( rv.ymin() == 0 ); CHECK ( rv.xmax() == 10 ); CHECK ( rv.ymax() == 10 ); CHECK ( rv.rows() == 100 ); CHECK ( rv.cols() == 100 ); bool all_equal = true; for (size_t i = 0; i < rv.rows(); i++) { for (size_t j = 0; j < rv.cols(); j++) { if (rv(i, j) != (int (i/10))*(int (j/10))) all_equal = false; } } CHECK (all_equal); } TEST_CASE("Creating a shifted view") { Raster r{{0, 0, 10, 10}, 10, 10}; Box clipped = {2, 3, 5, 8}; Grid ex{clipped, 1, 1}; fill_with_squares(r); RasterView rv(r, ex); CHECK ( rv.xmin() == 2 ); CHECK ( rv.ymin() == 3 ); CHECK ( rv.xmax() == 5 ); CHECK ( rv.ymax() == 8 ); CHECK ( rv.rows() == 5 ); CHECK ( rv.cols() == 3 ); CHECK ( rv.xres() == 1 ); CHECK ( rv.yres() == 1 ); Matrix expected_values{{ { 4, 6, 8 }, { 6, 9, 12}, { 8, 12, 16}, { 10, 15, 20}, { 12, 18, 24} }}; Raster expected{std::move(expected_values), clipped}; CHECK (rv == expected); } TEST_CASE("Creating a scaled and shifted view") { Raster r{{0, 0, 10, 10}, 10, 10}; Box clipped = {2.5, 3, 5, 8.5}; Grid ex{clipped, 0.5, 0.5}; fill_with_squares(r); RasterView rv(r, ex); CHECK ( rv.xmin() == 2.5 ); CHECK ( rv.ymin() == 3.0 ); CHECK ( rv.xmax() == 5.0 ); CHECK ( rv.ymax() == 8.5 ); CHECK ( rv.rows() == 11 ); CHECK ( rv.cols() == 5 ); CHECK ( rv.xres() == 0.5 ); CHECK ( rv.yres() == 0.5 ); Matrix expected_values{{ { 2, 3, 3, 4, 4 }, { 4, 6, 6, 8, 8 }, { 4, 6, 6, 8, 8 }, { 6, 9, 9, 12, 12 }, { 6, 9, 9, 12, 12 }, { 8, 12, 12, 16, 16 }, { 8, 12, 12, 16, 16 }, { 10, 15, 15, 20, 20 }, { 10, 15, 15, 20, 20 }, { 12, 18, 18, 24, 24 }, { 12, 18, 18, 24, 24 } }}; Raster expected{std::move(expected_values), clipped}; CHECK (rv == expected); } TEST_CASE("Creating a scaled and shifted view (greater extent)") { Raster r{{0, 0, 10, 10}, 10, 10}; Box expanded { 2.5, 8.5, 4, 11 }; Grid ex{expanded, 0.5, 0.5}; fill_with_squares(r); RasterView rv(r, ex); CHECK ( rv.xmin() == 2.5 ); CHECK ( rv.ymin() == 8.5 ); CHECK ( rv.xmax() == 4.0 ); CHECK ( rv.ymax() == 11 ); CHECK ( rv.rows() == 5 ); CHECK ( rv.cols() == 3 ); CHECK ( rv.xres() == 0.5 ); CHECK ( rv.yres() == 0.5 ); float nan = std::numeric_limits::quiet_NaN(); Matrix expected_values{{ { nan, nan, nan }, { nan, nan, nan }, { 0, 0, 0 }, { 0, 0, 0 }, { 2, 3, 3 } }}; Raster expected{std::move(expected_values), expanded}; CHECK (rv == expected ); } TEST_CASE("Creating a shifted view (robustness)") { Box rast_box{-130.76666666666947, -25.083333333335318, -124.77500000000313, -23.916666666668718}; Box view_box{-130.75833333333614, -25.083333333335318, -124.77500000000313, -23.916666666668718}; // Extent of view starts one cell of the right of the original raster, so column N in the view should // correspond to column N+1 of the original. double res = 0.0083333333333328596; Raster rast{Grid{rast_box, res, res}}; fill_sequential(rast); RasterView rv{rast, Grid{view_box, res, res}}; CHECK ( rv(5, 5) == rast(5, 6) ); } TEST_CASE("Creating a scaled view (robustness)") { Box rast_box{-180, -90, 180, 90}; double rast_res = 0.5 - 1e-12; double view_res = 2.5 / 60; Raster rast{Grid{rast_box, rast_res, rast_res}}; fill_sequential(rast); RasterView rv{rast, Grid{rast_box, view_res, view_res}}; CHECK ( rv.rows() == 4320 ); size_t i = 2015; for (size_t j = 0; j < rv.cols(); j++) { CHECK( rv(i, j) == rast(i / 12, j / 12) ); } } TEST_CASE("Empty view") { Box rast_box{10, 0, 10, 10}; double res = 1.0; Raster rast{Grid{rast_box, res, res}}; RasterView rv{rast, Grid::make_empty()}; CHECK ( rv.rows() == 0 ); CHECK ( rv.cols() == 0 ); } TEST_CASE("View on empty raster") { Raster rast{Grid::make_empty()}; Box rast_box{-10, -10, 10, 10}; double res = 0.5; Grid view_grid{rast_box, res, res}; RasterView rv{rast, view_grid}; CHECK ( 24 < rv.rows() ); CHECK ( 24 < rv.cols() ); CHECK ( std::isnan(rv(24, 24)) ); } TEST_CASE("Expanded view") { Box rast_box{5, 10, 20, 20}; Box view_box{0, 0, 30, 30}; double res = 1; Raster rast{Grid{rast_box, res, res}}; fill_sequential(rast); RasterView rv{rast, Grid{view_box, res, res}}; // Check 3 points outside UL corner CHECK( std::isnan(rv(9, 4)) ); CHECK( std::isnan(rv(10, 4)) ); CHECK( std::isnan(rv(9, 5)) ); // Check point just inside UL corner CHECK( rv(10, 5) == rast(0, 0) ); // Check 3 points outside UR corner CHECK( std::isnan(rv(9, 19)) ); CHECK( std::isnan(rv(10, 20)) ); CHECK( std::isnan(rv(9, 20)) ); // Check point just inside UR corner CHECK( rv(10, 19) == rast(0, 14) ); // Check 3 points just outside LL corner CHECK( std::isnan(rv(19, 4)) ); CHECK( std::isnan(rv(20, 4)) ); CHECK( std::isnan(rv(20, 5)) ); // Check point just inside LL corner CHECK( rv(19, 5) == rast(9, 0) ); // Check 3 points just outside LR corner CHECK( std::isnan(rv(20, 20)) ); CHECK( std::isnan(rv(20, 19)) ); CHECK( std::isnan(rv(19, 20)) ); // Check point just inside LR corner CHECK( rv(19, 19) == rast(9, 14) ); } TEST_CASE("Get method accesses value and tells us if it was defined") { float nan = std::numeric_limits::quiet_NaN(); Raster r{ Matrix{{ { 1, -999}, {nan, 7 }}}, Box{0, 0, 2, 2}}; r.set_nodata(-999); float f{0}; CHECK (r.get(0, 0, f)); CHECK (f == 1); CHECK (!r.get(0, 1, f)); CHECK (!r.get(1, 0, f)); CHECK (r.get(1, 1, f)); CHECK (f == 7); } exactextractr/src/exactextract/test/test_utils.cpp0000644000176200001440000000714114776002150022310 0ustar liggesusers#include "catch.hpp" #include "utils.h" using exactextract::parse_dataset_descriptor; using exactextract::parse_raster_descriptor; using exactextract::parse_stat_descriptor; TEST_CASE("Parsing feature descriptor: no layer specified") { auto descriptor = "countries.shp"; auto parsed = parse_dataset_descriptor(descriptor); CHECK( parsed.first == "countries.shp" ); CHECK( parsed.second == "0" ); } TEST_CASE("Parsing feature descriptor with layer") { auto descriptor = "PG:dbname=postgres port=5432[countries]"; auto parsed = parse_dataset_descriptor(descriptor); CHECK( parsed.first == "PG:dbname=postgres port=5432" ); CHECK( parsed.second == "countries" ); } TEST_CASE("Parsing raster descriptor: file with name and band") { auto descriptor = "pop:gpw_v4.tif[27]"; auto parsed = parse_raster_descriptor(descriptor); CHECK( std::get<0>(parsed) == "pop" ); CHECK( std::get<1>(parsed) == "gpw_v4.tif" ); CHECK( std::get<2>(parsed) == 27 ); } TEST_CASE("Parsing raster descriptor: file with no band") { auto descriptor = "land_area:gpw_v4_land.tif"; auto parsed = parse_raster_descriptor(descriptor); CHECK( std::get<0>(parsed) == "land_area" ); CHECK( std::get<1>(parsed) == "gpw_v4_land.tif" ); CHECK( std::get<2>(parsed) == 1 ); } TEST_CASE("Parsing raster descriptor: file with no name and no band") { auto descriptor = "gpw_v4_land.tif"; auto parsed = parse_raster_descriptor(descriptor); CHECK( std::get<0>(parsed) == "gpw_v4_land.tif" ); CHECK( std::get<1>(parsed) == "gpw_v4_land.tif" ); CHECK( std::get<2>(parsed) == 1 ); } TEST_CASE("Parsing raster descriptor: file with no name but a specific band") { auto descriptor = "gpw_v4_land.tif[8]"; auto parsed = parse_raster_descriptor(descriptor); CHECK( std::get<0>(parsed) == "gpw_v4_land.tif" ); CHECK( std::get<1>(parsed) == "gpw_v4_land.tif" ); CHECK( std::get<2>(parsed) == 8 ); } TEST_CASE("Parsing ugly raster descriptor") { auto descriptor = "gpw[3]:gpw_v4_land.tif"; auto parsed = parse_raster_descriptor(descriptor); CHECK( std::get<0>(parsed) == "gpw[3]" ); CHECK( std::get<1>(parsed) == "gpw_v4_land.tif" ); CHECK( std::get<2>(parsed) == 1 ); } TEST_CASE("Degenerate raster descriptor") { CHECK_THROWS_WITH( parse_raster_descriptor(""), "Empty descriptor." ); CHECK_THROWS_WITH( parse_raster_descriptor(":"), "Descriptor has no filename." ); } TEST_CASE("Parsing stat descriptor with no weighting") { auto descriptor = parse_stat_descriptor("sum(population)"); CHECK( descriptor.name == "population_sum" ); CHECK( descriptor.stat == "sum" ); CHECK( descriptor.values == "population" ); CHECK( descriptor.weights == "" ); } TEST_CASE("Parsing stat descriptor with weighting") { auto descriptor = parse_stat_descriptor("mean(deficit,population)"); CHECK( descriptor.name == "deficit_mean_population" ); CHECK( descriptor.stat == "mean" ); CHECK( descriptor.values == "deficit" ); CHECK( descriptor.weights == "population" ); } TEST_CASE("Parsing stat descriptor with name and weighting") { auto descriptor = parse_stat_descriptor("pop_weighted_mean_deficit=mean(deficit,population)"); CHECK( descriptor.name == "pop_weighted_mean_deficit" ); CHECK( descriptor.stat == "mean" ); CHECK( descriptor.values == "deficit" ); CHECK( descriptor.weights == "population" ); } TEST_CASE("Parsing degenerate stat descriptors") { CHECK_THROWS_WITH( parse_stat_descriptor(""), "Invalid stat descriptor." ); CHECK_THROWS_WITH( parse_stat_descriptor("sum()"), "Invalid stat descriptor." ); } exactextractr/src/exactextract/test/test_main.cpp0000644000176200001440000000005714776002150022073 0ustar liggesusers#define CATCH_CONFIG_MAIN #include "catch.hpp" exactextractr/src/exactextract/docs/0000755000176200001440000000000014776002150017353 5ustar liggesusersexactextractr/src/exactextract/docs/Doxyfile.in0000644000176200001440000031744614776002150021505 0ustar liggesusers# Doxyfile 1.8.11 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "exactextract" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@ # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: # Fortran. In the later case the parser tries to guess whether the code is fixed # or free formatted code, this is the default for Fortran type files), VHDL. For # instance to make doxygen treat .inc files as Fortran files (default is PHP), # and .f files as C (default is Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO, these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = @CMAKE_CURRENT_SOURCE_DIR@/src/ @CMAKE_CURRENT_SOURCE_DIR@/docs # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl, # *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. FILE_PATTERNS = *.cpp *.h # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = */test/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES # If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the # clang parser (see: http://clang.llvm.org/) for more accurate parsing at the # cost of reduced performance. This can be particularly helpful with template # rich C++ code for which doxygen's built-in parser lacks the necessary type # information. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse-libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories # specified with INPUT and INCLUDE_PATH. # This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. CLANG_OPTIONS = #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /