Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate functions in smaller topics + update ci #2431

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ on:
schedule:
- cron: "0 4 * * *"

name: R-CMD-check
name: R-CMD-check.yaml

permissions: read-all

jobs:
R-CMD-check:
Expand Down Expand Up @@ -54,3 +56,4 @@ jobs:
- uses: r-lib/actions/check-r-package@v2
with:
upload-snapshots: true
build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")'
19 changes: 15 additions & 4 deletions .github/workflows/test-coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ on:
pull_request:
branches: [main, master]

name: test-coverage
name: test-coverage.yaml

permissions: read-all

jobs:
test-coverage:
Expand All @@ -23,23 +25,32 @@ jobs:

- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::covr
extra-packages: any::covr, any::xml2
needs: coverage

- name: Test coverage
run: |
covr::codecov(
cov <- covr::package_coverage(
quiet = FALSE,
clean = FALSE,
install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package")
)
covr::to_cobertura(cov)
shell: Rscript {0}

- uses: codecov/codecov-action@v4
with:
fail_ci_if_error: ${{ github.event_name != 'pull_request' && true || false }}
file: ./cobertura.xml
plugin: noop
disable_search: true
token: ${{ secrets.CODECOV_TOKEN }}

- name: Show testthat output
if: always()
run: |
## --------------------------------------------------------------------
find ${{ runner.temp }}/package -name 'testthat.Rout*' -exec cat '{}' \; || true
find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true
shell: bash

- name: Upload test results
Expand Down
2 changes: 1 addition & 1 deletion CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ opening an issue or contacting one or more of the project maintainers.

This Code of Conduct is adapted from the Contributor Covenant
(http:contributor-covenant.org), version 1.0.0, available at
http://contributor-covenant.org/version/1/0/0/
https://www.contributor-covenant.org/version/1/0/0/code-of-conduct/
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ Collate:
'wkb.R'
'wkt.R'
'plot.R'
'geos_binary_pred.R'
'geom-measures.R'
'geom-predicates.R'
'geom-transformers.R'
Expand Down
2 changes: 1 addition & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,7 @@

# version 0.5-1

* add spatial indexes to most binary geometry operations; #394 and http://r-spatial.org/r/2017/06/22/spatial-index.html
* add spatial indexes to most binary geometry operations; #394 and https://r-spatial.org/r/2017/06/22/spatial-index.html

* drastically reduce memory footprint of `st_intersection` and similar; #394

Expand Down
4 changes: 2 additions & 2 deletions PROPOSAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Applicant: [Edzer Pebesma](https://github.com/edzer/), [Institute for Geoinforma

Supporting authors: Edzer Pebesma, Roger Bivand, Michael Sumner, Robert Hijmans, Virgilio Gómez-Rubio

[Simple features](https://en.wikipedia.org/wiki/Simple_Features) is an open ([OGC](https://www.ogc.org/standard/sfa/) and [ISO](https://www.iso.org/standard/40114.html)) interface standard for access and manipulation of spatial vector data (points, lines, polygons). It includes a standard [SQL schema](http://www.opengeospatial.org/standards/sfs) that supports storage, retrieval, query and update of feature collections via a SQL interface. All commonly used databases provide this interface. [GeoJSON](https://geojson.org/) is a standard for encoding simple features in JSON, and is used in JavaScript and MongoDB. Well-known-text ([WKT](https://en.wikipedia.org/wiki/Well-known_text)) is a text representation of simple features used often in linked data; well-known-binary ([WKB] (https://en.wikipedia.org/wiki/Well-known_text)) a standard binary representation used in databases. _Simple Feature Access_ defines coordinate reference systems, and makes it easy to move data from longitude-latitude to projections back and forth in a standardized way.
[Simple features](https://en.wikipedia.org/wiki/Simple_Features) is an open ([OGC](https://www.ogc.org/standard/sfa/) and [ISO](https://www.iso.org/standard/40114.html)) interface standard for access and manipulation of spatial vector data (points, lines, polygons). It includes a standard [SQL schema](https://www.ogc.org/standard/sfs/) that supports storage, retrieval, query and update of feature collections via a SQL interface. All commonly used databases provide this interface. [GeoJSON](https://geojson.org/) is a standard for encoding simple features in JSON, and is used in JavaScript and MongoDB. Well-known-text ([WKT](https://en.wikipedia.org/wiki/Well-known_text)) is a text representation of simple features used often in linked data; well-known-binary ([WKB] (https://en.wikipedia.org/wiki/Well-known_text)) a standard binary representation used in databases. _Simple Feature Access_ defines coordinate reference systems, and makes it easy to move data from longitude-latitude to projections back and forth in a standardized way.


[GDAL](https://gdal.org/) is an open source C++ library for reading and writing both raster and vector data with more than 225 drivers (supported file formats, data base connectors, web service interfaces). GDAL is used by practically all open source geospatial projects and by many industry products (including ESRI's ArcGIS, ERDAS, and FME). It provides coordinate transformations (built on top of PROJ.4) and geometric operations (e.g. polygon intersections, unions, buffers and distance). Standards for coordinate transformations change over time; such changes are typically adopted directly in GDAL/PROJ.4 but do not easily find their way into R-only packages such as `mapproj`.
Expand All @@ -16,7 +16,7 @@ Today, 221 CRAN packages depend on, import or link to `sp`, 259 when including _
Off-CRAN package [rgdal2](https://github.com/thk686/rgdal2) is an interface to GDAL 2.0, which uses raw pointers to interface features, but does not import any data in R, using GDAL to handle everything. CRAN Package [wkb](https://cran.r-project.org/package=wkb), contributed by Tibco Software, converts between WKB representations of several simple feature classes and corresponding classes in `sp`, and seems to be needed for Tibco software purposes.

<!---
[second edition](http://www.springer.com/statistics/life+sciences%2C+medicine+%26+health/book/978-1-4614-7617-7))
[second edition](https://link.springer.com/book/10.1007/978-1-4614-7618-4))
-->

## The problem
Expand Down
1 change: 1 addition & 0 deletions R/db.R
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,7 @@ setMethod("dbDataType", c("DBIObject", "sf"), function(dbObj, obj) {
#' @param con database connection
#' @param x inherits data.frame
#' @param classes classes inherited
#' @noRd
is_geometry_column <- function(con, x, classes = "") UseMethod("is_geometry_column")

#' @export
Expand Down
70 changes: 47 additions & 23 deletions R/geom-predicates.R
Original file line number Diff line number Diff line change
Expand Up @@ -116,26 +116,24 @@ st_relate = function(x, y, pattern = NA_character_, sparse = !is.na(pattern)) {
st_geos_binop("relate", x, y, sparse = FALSE)
}

#' Geometric binary predicates on pairs of simple feature geometry sets
#' Identify if `x` and `y` share any space
#'
#' Geometric binary predicates on pairs of simple feature geometry sets
#' @name geos_binary_pred
#'
#' @param x object of class \code{sf}, \code{sfc} or \code{sfg}
#' @param y object of class \code{sf}, \code{sfc} or \code{sfg}; if missing, \code{x} is used
#' @param sparse logical; should a sparse index list be returned (`TRUE`) or a dense logical matrix? See below.
#' @inheritDotParams s2::s2_options
#' @param prepared logical; prepare geometry for `x`, before looping over `y`? See Details.
#' @details If \code{prepared} is \code{TRUE}, and \code{x} contains POINT geometries and \code{y} contains polygons, then the polygon geometries are prepared, rather than the points.
#' @return If \code{sparse=FALSE}, \code{st_predicate} (with \code{predicate} e.g. "intersects") returns a dense logical matrix with element \code{i,j} equal to \code{TRUE} when \code{predicate(x[i], y[j])} (e.g., when geometry of feature i and j intersect); if \code{sparse=TRUE}, an object of class \code{\link{sgbp}} is returned, which is a sparse list representation of the same matrix, with list element \code{i} an integer vector with all indices \code{j} for which \code{predicate(x[i],y[j])} is \code{TRUE} (and hence a zero-length integer vector if none of them is \code{TRUE}). From the dense matrix, one can find out if one or more elements intersect by \code{apply(mat, 1, any)}, and from the sparse list by \code{lengths(lst) > 0}, see examples below.
#' @details For most predicates, a spatial index is built on argument \code{x}; see \url{https://r-spatial.org/r/2017/06/22/spatial-index.html}.
#' Specifically, \code{st_intersects}, \code{st_disjoint}, \code{st_touches} \code{st_crosses}, \code{st_within}, \code{st_contains}, \code{st_contains_properly}, \code{st_overlaps}, \code{st_equals}, \code{st_covers} and \code{st_covered_by} all build spatial indexes for more efficient geometry calculations. \code{st_relate}, \code{st_equals_exact}, and do not; \code{st_is_within_distance} uses a spatial index for geographic coordinates when \code{sf_use_s2()} is true.
#'
#' If \code{y} is missing, `st_predicate(x, x)` is effectively called, and a square matrix is returned with diagonal elements `st_predicate(x[i], x[i])`.
#' If \code{y} is missing, `st_<predicate>(x, x)` is effectively called, and a square matrix is returned with diagonal elements `st_predicate(x[i], x[i])`.
#'
#' Sparse geometry binary predicate (\code{\link{sgbp}}) lists have the following attributes: \code{region.id} with the \code{row.names} of \code{x} (if any, else \code{1:n}), \code{ncol} with the number of features in \code{y}, and \code{predicate} with the name of the predicate used.
#'
#' @note For intersection on pairs of simple feature geometries, use
#' the function \code{\link{st_intersection}} instead of \code{st_intersects}.
#' @family geometric binary predicates for two spatial objects
#'
#' @examples
#' pts = st_sfc(st_point(c(.5,.5)), st_point(c(1.5, 1.5)), st_point(c(2.5, 2.5)))
Expand All @@ -147,15 +145,7 @@ st_relate = function(x, y, pattern = NA_character_, sparse = !is.na(pattern)) {
#' lengths(lst) > 0
#' # which points fall inside the first polygon?
#' st_intersects(pol, pts)[[1]]
#' # remove duplicate geometries:
#' p1 = st_point(0:1)
#' p2 = st_point(2:1)
#' p = st_sf(a = letters[1:8], geom = st_sfc(p1, p1, p2, p1, p1, p2, p2, p1))
#' st_equals(p)
#' st_equals(p, remove_self = TRUE)
#' (u = st_equals(p, retain_unique = TRUE))
#' # retain the records with unique geometries:
#' p[-unlist(u),]
#' @export
st_intersects = function(x, y, sparse = TRUE, ...) UseMethod("st_intersects")

Expand Down Expand Up @@ -188,6 +178,11 @@ st_disjoint = function(x, y = x, sparse = TRUE, prepared = TRUE, ...) {
}

#' @name geos_binary_pred
#' @inheritParams st_intersects
#' @inheritDotParams s2::s2_options
#' @details If \code{prepared} is \code{TRUE}, and \code{x} contains POINT geometries and \code{y} contains polygons, then the polygon geometries are prepared, rather than the points.

#' @param prepared logical; prepare geometry for `x`, before looping over `y`? See Details.
#' @export
st_touches = function(x, y, sparse = TRUE, prepared = TRUE, ...)
st_geos_binop("touches", x, y, sparse = sparse, prepared = prepared, ...)
Expand All @@ -202,18 +197,23 @@ st_crosses = function(x, y, sparse = TRUE, prepared = TRUE, ...)
st_within = function(x, y, sparse = TRUE, prepared = TRUE, ...)
st_geos_binop("within", x, y, sparse = sparse, prepared = prepared, ...)

#' @name geos_binary_pred
#' Identify if y is within x
#'
#' * `st_contains()` is true if
#' * `st_contains_properly(x, y)` is true if `x` intersects `y`'s interior, but not its edges or exterior; `x` contains `x`, but `x` does not properly contain `x`.
#' @param model character; polygon/polyline model; one of
#' "open", "semi-open" or "closed"; see Details.
#' @details for \code{model}, see https://github.com/r-spatial/s2/issues/32
#' @inheritParams geos_binary_pred
#' @param ... passed on to [s2::s2_contains()]
#' @export
#' @family geometric binary predicates for two spatial objects
st_contains = function(x, y, sparse = TRUE, prepared = TRUE, ..., model = "open")
st_geos_binop("contains", x, y, sparse = sparse, prepared = prepared, ..., model = model)

#' @name geos_binary_pred
#' @rdname st_contains
#' @export
#' @details `st_contains_properly(A,B)` is true if A intersects B's interior, but not its edges or exterior; A contains A, but A does not properly contain A.
#'
#' @details
#' See also \link{st_relate} and \url{https://en.wikipedia.org/wiki/DE-9IM} for a more detailed description of the underlying algorithms.
st_contains_properly = function(x, y, sparse = TRUE, prepared = TRUE, ...) {
if (! prepared)
Expand All @@ -226,10 +226,32 @@ st_contains_properly = function(x, y, sparse = TRUE, prepared = TRUE, ...) {
st_overlaps = function(x, y, sparse = TRUE, prepared = TRUE, ...)
st_geos_binop("overlaps", x, y, sparse = sparse, prepared = prepared, ...)

#' @name geos_binary_pred
#' @param retain_unique logical; if `TRUE` (and `y` is missing) return only indexes of points larger than the current index; this can be used to select unique geometries, see examples. This argument can be used for all geometry predicates; see also \link{distinct.sf} to find records where geometries AND attributes are distinct.
#' @param remove_self logical; if `TRUE` (and `y` is missing) return only indexes of geometries different from the current index; this can be used to omit self-intersections; see examples. This argument can be used for all geometry predicates

#' Verify if geographies are equal
#'
#' * `st_equals()` validate if x and y are equal.
#' * `st_equals_exact()` returns true for two geometries of the same type and their vertices corresponding by index are equal up to a specified tolerance.
#'
#' @inheritParams geos_binary_pred
#' @param retain_unique logical; if `TRUE` (and `y` is missing) return only
#' indexes of points larger than the current index; this can be used to select
#' unique geometries, see examples. This argument can be used for all geometry predicates;
#' see also \link{distinct.sf} to find records where geometries AND attributes are distinct.
#' @param remove_self logical; if `TRUE` (and `y` is missing) return only indexes of geometries different from the current index; this can be used to omit self-intersections; see examples.
#' This argument can be used for all geometry predicates
#' @param ... passed on to [s2::s2_options()]
#' @export
#' @family geometric binary predicates for two spatial objects
#' @examples
#' # remove duplicate geometries:
#' p1 = st_point(0:1)
#' p2 = st_point(2:1)
#' p = st_sf(a = letters[1:8], geom = st_sfc(p1, p1, p2, p1, p1, p2, p2, p1))
#' st_equals(p)
#' st_equals(p, remove_self = TRUE)
#' (u = st_equals(p, retain_unique = TRUE))
#' # retain the records with unique geometries:
#' p[-unlist(u),]
st_equals = function(x, y, sparse = TRUE, prepared = FALSE, ...,
retain_unique = FALSE, remove_self = FALSE) {
if (prepared)
Expand All @@ -239,6 +261,9 @@ st_equals = function(x, y, sparse = TRUE, prepared = FALSE, ...,
}

#' @name geos_binary_pred
#' @param model character; polygon/polyline model; one of
#' `"open"`, `"semi-open"` or `"closed"`; see Details.
#' @details for \code{model}, see https://github.com/r-spatial/s2/issues/32
#' @export
st_covers = function(x, y, sparse = TRUE, prepared = TRUE, ..., model = "closed")
st_geos_binop("covers", x, y, sparse = sparse, prepared = prepared, ..., model = model)
Expand All @@ -250,10 +275,9 @@ st_covered_by = function(x, y = x, sparse = TRUE, prepared = TRUE, ..., model =
st_geos_binop("covered_by", x, y, sparse = sparse, prepared = prepared, ...)


#' @name geos_binary_pred
#' @rdname st_equals
#' @export
#' @param par numeric; parameter used for "equals_exact" (margin);
#' @details \code{st_equals_exact} returns true for two geometries of the same type and their vertices corresponding by index are equal up to a specified tolerance.
st_equals_exact = function(x, y, par, sparse = TRUE, prepared = FALSE, ...) {
if (prepared)
stop("prepared geometries not supported for st_equals_exact")
Expand Down
33 changes: 33 additions & 0 deletions R/geos_binary_pred.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#' Geometric binary predicates on pairs of simple feature geometry sets
#'
#' @description
#'
#' For most predicates, a spatial index is built on argument `x`;
#' see \url{https://r-spatial.org/r/2017/06/22/spatial-index.html}.
#'
#' If `prepared = TRUE`, `x` contains POINT geometries, and `y` contains polygons,
#' then the polygon geometries are prepared, rather than the points.
#' @name geos_binary_pred
#' @family geometric binary predicates for two spatial objects
#' @param remove_self logical; if `TRUE` (and `y` is missing) return only indexes of geometries different from the current index; this can be used to omit self-intersections; see examples.
#' This argument can be used for all geometry predicates
#' @examples
#' pts = st_sfc(st_point(c(.5,.5)), st_point(c(1.5, 1.5)), st_point(c(2.5, 2.5)))
#' pol = st_polygon(list(rbind(c(0,0), c(2,0), c(2,2), c(0,2), c(0,0))))
#' (lst = st_intersects(pts, pol))
#' (mat = st_intersects(pts, pol, sparse = FALSE))
#' # which points fall inside a polygon?
#' apply(mat, 1, any)
#' lengths(lst) > 0
#' # which points fall inside the first polygon?
#' st_intersects(pol, pts)[[1]]
#' # remove duplicate geometries:
#' p1 = st_point(0:1)
#' p2 = st_point(2:1)
#' p = st_sf(a = letters[1:8], geom = st_sfc(p1, p1, p2, p1, p1, p2, p2, p1))
#' st_equals(p)
#' st_equals(p, remove_self = TRUE)
#' (u = st_equals(p, retain_unique = TRUE))
#' # retain the records with unique geometries:
#' p[-unlist(u),]
NULL
18 changes: 10 additions & 8 deletions R/nearest.R
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#' get nearest points between pairs of geometries
#' Get nearest points between pairs of geometries
#'
#' get nearest points between pairs of geometries
#' @param x object of class \code{sfg}, \code{sfc} or \code{sf}
#' @param y object of class \code{sfg}, \code{sfc} or \code{sf}
#' @param pairwise logical; if \code{FALSE} (default) return nearest points between all pairs, if \code{TRUE}, return nearest points between subsequent pairs.
#' @param ... ignored
#' @param x,y object of class \code{sfg}, \code{sfc} or \code{sf}
#' @param pairwise logical; if \code{FALSE} (default) return nearest points between all pairs,
#' if \code{TRUE}, return nearest points between subsequent pairs.
#' @param ... passed on to methods. Currently, only `pairwise` is implemented.
#' @seealso \link{st_nearest_feature} for finding the nearest feature
#' @return an \link{sfc} object with all two-point \code{LINESTRING} geometries of point pairs from the first to the second geometry, of length x * y, with y cycling fastest. See examples for ideas how to convert these to \code{POINT} geometries.
#' @return an \link{sfc} object with all two-point \code{LINESTRING} geometries of point pairs from the first to the second geometry, of length x * y, with y cycling fastest.
#' See examples for ideas how to convert these to \code{POINT} geometries.
#' @details in case \code{x} lies inside \code{y}, when using S2, the end points
#' are on polygon boundaries, when using GEOS the end point are identical to \code{x}.
#' @examples
Expand Down Expand Up @@ -34,6 +35,7 @@
#' plot(pts[seq(2, 200, 2)], add = TRUE, col = 'green')
#'
#' @export
#'
st_nearest_points = function(x, y, ...) UseMethod("st_nearest_points")

#' @export
Expand All @@ -55,13 +57,13 @@ st_nearest_points.sfc = function(x, y, ..., pairwise = FALSE) {
}

#' @export
#' @name st_nearest_points
#' @rdname st_nearest_points
st_nearest_points.sfg = function(x, y, ...) {
st_nearest_points(st_geometry(x), st_geometry(y), ...)
}

#' @export
#' @name st_nearest_points
#' @rdname st_nearest_points
st_nearest_points.sf = function(x, y, ...) {
st_nearest_points(st_geometry(x), st_geometry(y), ...)
}
Expand Down
Loading
Loading