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

move POINT geometries out of a list column, into a matrix #2059

Open
wants to merge 38 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ec68f40
first shot at implementing sfc_POINT with matrix
edzer Dec 13, 2022
d9c5de0
pass check with points in matrix attribute
edzer Dec 13, 2022
7210201
speed up st_combine
edzer Dec 13, 2022
b16648e
fix as(x, "Spatial") for points in matrix attr
edzer Dec 14, 2022
1bfb7ea
update st_as_sfc.SpatialPoints; see also #2060
edzer Dec 14, 2022
ea0e62a
Merge branch 'main' into pointx
edzer Dec 19, 2022
d0dab0f
Merge branch 'main' into pointx
edzer Dec 23, 2022
fc83931
merge main
edzer Dec 23, 2022
5275389
Merge branch 'main' into pointx
edzer Jan 6, 2023
54a5835
Merge branch 'main' into pointx
edzer Feb 5, 2023
3401d0d
Merge branch 'main' into pointx
edzer Feb 8, 2023
f30aa65
Merge branch 'main' into pointx
edzer Mar 13, 2023
d81f8ca
remove min/max warnings on empty point sets
edzer Mar 13, 2023
f96e0fa
Merge branch 'main' into pointx
edzer Mar 27, 2023
dcda1fd
Merge branch 'main' into pointx
edzer Apr 11, 2023
4713008
Merge branch 'main' into pointx
edzer Apr 27, 2023
250cc44
Merge branch 'main' into pointx
edzer Jun 14, 2023
c0e56b4
Merge branch 'main' into pointx
edzer Jun 14, 2023
86059e2
Merge branch 'main' into pointx
edzer Jun 27, 2023
e3def06
Merge branch 'pointx' of github.com:r-spatial/sf into pointx
edzer Jun 27, 2023
3f03184
Merge branch 'main' into pointx
edzer Jun 29, 2023
3ec76b8
modifications handling c() and st_distance
edzer Jun 29, 2023
b8dc2b3
address comment from @bart1
edzer Jun 30, 2023
203187d
filter empty point; #2059
edzer Jun 30, 2023
1bcce69
Merge branch 'main' into pointx
edzer Aug 21, 2023
4ecabf0
keep crs in dplyr::filter
edzer Aug 30, 2023
d38b3dc
Merge branch 'main' into pointx
edzer Aug 30, 2023
230cde6
Merge branch 'main' into pointx
edzer Jan 23, 2024
4ce501e
update for this branch
edzer Jan 23, 2024
5126cd6
Fix value replacement for sfc_POINT
Feb 16, 2024
c818629
Merge pull request #2345 from bart1/pointx
edzer Feb 22, 2024
4722081
Merge branch 'main' into pointx
edzer Feb 23, 2024
f687956
ensure vctrs operations work
Feb 28, 2024
b90139f
Merge pull request #2350 from bart1/pointx
edzer Feb 28, 2024
d2800ea
Merge branch 'main' into pointx
edzer Mar 17, 2024
4a40825
Merge branch 'pointx' of github.com:r-spatial/sf into pointx
edzer Mar 17, 2024
7fd0ab1
Merge branch 'main' into pointx
edzer Oct 10, 2024
82fe210
align with main
edzer Oct 10, 2024
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
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ S3method("$<-",sf)
S3method("[",sf)
S3method("[",sfc)
S3method("[<-",sfc)
S3method("[[",sfc)
S3method("[[<-",sf)
S3method("st_agr<-",sf)
S3method("st_crs<-",bbox)
Expand Down
5 changes: 5 additions & 0 deletions R/arith.R
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ Ops.sfc <- function(e1, e2) {
if (length(e1) == 0) # empty set
return(e1)

if (inherits(e1, "sfc")) # realize
e1 = e1[]
if (inherits(e2, "sfc"))
e2 = e2[]

if (is.numeric(e2) && !is.matrix(e2) && length(e2) <= 2 && .Generic %in% c("+", "-")) {
if (.Generic == "-")
e2 <- -e2
Expand Down
8 changes: 7 additions & 1 deletion R/bbox.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ bb_wrap = function(bb) {
structure(as.double(bb), names = c("xmin", "ymin", "xmax", "ymax"), class = "bbox")
}

bbox.pointmatrix = function(obj, ...) {
bb_wrap(as.vector(t(apply(obj[,1:2,drop=FALSE], 2, range))))
}

bbox.Set = function(obj, ...) {
sel = !sfc_is_empty(obj)
if (! any(sel))
Expand Down Expand Up @@ -134,7 +138,9 @@ print.bbox = function(x, ...) {
}

compute_bbox = function(obj) {
switch(class(obj)[1],
if (!is.null(pts <- attr(obj, "points")))
bbox.pointmatrix(pts)
else switch(class(obj)[1],
sfc_POINT = bb_wrap(bbox.Set(obj)),
sfc_MULTIPOINT = bb_wrap(bbox.MtrxSet(obj)),
sfc_LINESTRING = bb_wrap(bbox.MtrxSet(obj)),
Expand Down
6 changes: 5 additions & 1 deletion R/bind.R
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ rbind.sf = function(..., deparse.level = 1) {
if (!all(equal_crs))
stop("arguments have different crs", call. = FALSE)
}
ret = st_sf(rbind.data.frame(...), crs = crs0, sf_column_name = sf_column)
for (i in seq_along(dots)) {
if (all(sapply(unclass(st_geometry(dots[[i]])), is.null)))
st_geometry(dots[[i]]) = st_geometry(dots[[i]])[] # realize
}
ret = st_sf(do.call(rbind.data.frame, dots), crs = crs0, sf_column_name = sf_column)
st_geometry(ret) = st_sfc(st_geometry(ret)) # might need to reclass to GEOMETRY
bb = do.call(rbind, lapply(dots, st_bbox))
bb = bb_wrap(c(min(bb[,1L], na.rm = TRUE), min(bb[,2L], na.rm = TRUE),
Expand Down
12 changes: 9 additions & 3 deletions R/geom-transformers.R
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ st_minimum_rotated_rectangle.sf = function(x, dTolerance, ...) {
#' n = 100
#' pts = st_as_sf(data.frame(matrix(runif(n), , 2), id = 1:(n/2)), coords = c("X1", "X2"))
#' # compute Voronoi polygons:
#' pols = st_collection_extract(st_voronoi(do.call(c, st_geometry(pts))))
#' pols = st_collection_extract(st_voronoi(st_combine(pts)))
#' # match them to points:
#' pts$pols = pols[unlist(st_intersects(pts, pols))]
#' plot(pts["id"], pch = 16) # ID is color
Expand Down Expand Up @@ -651,8 +651,14 @@ st_segmentize.sf = function(x, dfMaxLength, ...) {
#' @examples
#' nc = st_read(system.file("shape/nc.shp", package="sf"))
#' st_combine(nc)
st_combine = function(x)
st_sfc(do.call(c, st_geometry(x)), crs = st_crs(x)) # flatten/merge
st_combine = function(x) {
x = st_geometry(x)
if (inherits(x, "sfc_POINT") && !is.null(pts <- attr(x, "points"))) {
mp = structure(list(st_multipoint(pts, attr(x, "point_dim"))), bbox = st_bbox(x))
st_sfc(mp, crs = st_crs(x))
} else
st_sfc(do.call(c, x), crs = st_crs(x)) # flatten/merge
}

# x: object of class sf
# y: object of class sf or sfc
Expand Down
8 changes: 7 additions & 1 deletion R/m_range.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ mb_wrap = function(mb) {
structure(mb, names = c("mmin", "mmax"), class = "m_range")
}

m_range.pointmatrix = function(obj, ...) {
mb_wrap(range(obj[,ncol(obj)]))
}

m_range.Set = function(obj, ...) {
sel = vapply(obj, function(x) { length(x) && !all(is.na(x)) }, TRUE)
if (! any(sel))
Expand Down Expand Up @@ -130,7 +134,9 @@ print.m_range = function(x, ...) {
}

compute_m_range = function(obj) {
switch(class(obj)[1],
if (!is.null(pts <- attr(obj, "points")))
m_range.pointmatrix(pts)
else switch(class(obj)[1],
sfc_POINT = mb_wrap(m_range.Set(obj)),
sfc_MULTIPOINT = mb_wrap(m_range.MtrxSet(obj)),
sfc_LINESTRING = mb_wrap(m_range.MtrxSet(obj)),
Expand Down
3 changes: 2 additions & 1 deletion R/plot.R
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,8 @@ plot.sfc_POINT = function(x, y, ..., pch = 1, cex = 1, col = 1, bg = 0, lwd = 1,
col = rep(col, length.out = npts)
bg = rep(bg, length.out = npts)
cex = rep(cex, length.out = npts)
mat = t(matrix(unlist(x, use.names = FALSE), ncol = length(x))) #933
#mat = t(matrix(unlist(x, use.names = FALSE), ncol = length(x))) #933
mat = st_coordinates(x)
if (!is.null(mat)) {
ne = !is.na(rowMeans(mat)) ## faster than apply; #933
points(mat[ne,, drop = FALSE], pch = pch[ne], col = col[ne], bg = bg[ne],
Expand Down
17 changes: 13 additions & 4 deletions R/sf.R
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,22 @@ st_as_sf.data.frame = function(x, ..., agr = NA_agr_, coords, wkt,
if (na.fail && any(is.na(cc)))
stop("missing values in coordinates not allowed")
# classdim = getClassDim(rep(0, length(coords)), length(coords), dim, "POINT")
x$geometry = structure( points_rcpp(as.matrix(cc), dim),
# x$geometry = structure( points_rcpp(attr(x, "points"), dim),
if (length(coords) == 2)
dim = "XY"
stopifnot(length(coords) == nchar(dim), dim %in% c("XY", "XYZ", "XYZM", "XYM"))
points = as.matrix(cc)
dimnames(points) = NULL
x$geometry = structure(vector("list", length = nrow(cc)),
points = points,
points_dim = dim,
n_empty = 0L, precision = 0, crs = NA_crs_,
bbox = structure(
c(xmin = min(cc[[1]], na.rm = TRUE),
ymin = min(cc[[2]], na.rm = TRUE),
xmax = max(cc[[1]], na.rm = TRUE),
ymax = max(cc[[2]], na.rm = TRUE)), class = "bbox"),
class = c("sfc_POINT", "sfc" ), names = NULL)
class = c("sfc_POINT", "sfc"), names = NULL)

if (is.character(coords))
coords = match(coords, names(x))
Expand Down Expand Up @@ -406,9 +414,10 @@ print.sf = function(x, ..., n = getOption("sf_max_print", default = 10)) {
app = paste0(app, "\n", "Active geometry column: ", attr(x, "sf_column"))
print(st_geometry(x), n = 0, what = "Simple feature collection with", append = app)
if (n > 0) {
if (inherits(x, "tbl_df"))
if (inherits(x, "tbl_df")) {
st_geometry(x) = x[[attr(x, "sf_column")]][] # note the extra []: this reloads points
NextMethod()
else {
} else {
y <- x
if (nrow(y) > n) {
cat(paste("First", n, "features:\n"))
Expand Down
57 changes: 49 additions & 8 deletions R/sfc.R
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,24 @@ st_sfc = function(..., crs = NA_crs_, precision = 0.0, check_ring_dir = FALSE, d
lst = lst[[1]]
stopifnot(is.numeric(crs) || is.character(crs) || inherits(crs, "crs"))

points_in_attr <- !is.null(attr(lst, "points"))

# check for NULLs:
a = attributes(lst)
is_null = sfc_is_null(lst)
is_null = if (points_in_attr)
rep(FALSE, length(lst))
else
sfc_is_null(lst)
lst = unclass(lst)
lst = lst[! is_null]
if (!points_in_attr)
lst = lst[! is_null]

attributes(lst) = a

dims_and_types = sfc_unique_sfg_dims_and_types(lst)
dims_and_types = if (points_in_attr)
list(class_dim = attr(lst, "points_dim"), class_type = "POINT")
else
sfc_unique_sfg_dims_and_types(lst)

cls = if (length(lst) == 0) # empty set: no geometries read
c("sfc_GEOMETRY", "sfc")
Expand Down Expand Up @@ -140,8 +150,12 @@ st_sfc = function(..., crs = NA_crs_, precision = 0.0, check_ring_dir = FALSE, d
"[.sfc" = function(x, i, j, ..., op = st_intersects) {
if (!missing(i) && (inherits(i, "sf") || inherits(i, "sfc") || inherits(i, "sfg")))
i = lengths(op(x, i, ...)) != 0
st_sfc(NextMethod(), crs = st_crs(x), precision = st_precision(x),
dim = if(length(x)) class(x[[1]])[1] else "XY")
if (inherits(x, "sfc_POINT") && !is.null(attr(x, "points")))
st_sfc(restore_points(x, i), crs = st_crs(x), precision = st_precision(x),
dim = if(length(x)) class(x[[1]])[1] else "XY")
else
st_sfc(NextMethod() , crs = st_crs(x), precision = st_precision(x),
dim = if(length(x)) class(x[[1]])[1] else "XY")
}


Expand Down Expand Up @@ -487,16 +501,22 @@ st_coordinates.sfc = function(x, ...) {
return(matrix(nrow = 0, ncol = 2))

ret = switch(class(x)[1],
sfc_POINT = matrix(unlist(x, use.names = FALSE), nrow = length(x), byrow = TRUE,
dimnames = NULL),
sfc_POINT = if (is.null(attr(x, "points"))) {
matrix(unlist(x, use.names = FALSE), nrow = length(x), byrow = TRUE, dimnames = NULL)
} else {
attr(x, "points")
},
sfc_MULTIPOINT = ,
sfc_LINESTRING = coord_2(x),
sfc_MULTILINESTRING = ,
sfc_POLYGON = coord_3(x),
sfc_MULTIPOLYGON = coord_4(x),
stop(paste("not implemented for objects of class", class(x)[1]))
)
Dims = class(x[[1]])[1]
Dims = if (!is.null(attr(x, "points_dim")))
attr(x, "points_dim")
else
class(x[[1]])[1]
ncd = nchar(Dims)
colnames(ret)[1:ncd] = vapply(seq_len(ncd), function(i) substr(Dims, i, i), "")
ret
Expand Down Expand Up @@ -579,3 +599,24 @@ st_as_sfc.bbox = function(x, ...) {
box = st_polygon(list(matrix(x[c(1, 2, 3, 2, 3, 4, 1, 4, 1, 2)], ncol = 2, byrow = TRUE)))
st_sfc(box, crs = st_crs(x))
}

#' @export
`[[.sfc` = function(x, i, j, ..., exaxt = TRUE) {
if (inherits(x, "sfc_POINT") && !is.null(attr(x, "points")))
restore_point(x, i)
else
NextMethod()
}

restore_point = function(x, i = TRUE) {
restore_points(x, i)[[1]]
}

restore_points = function(x, i = TRUE) {
a = attributes(x)
points = a$points[i, , drop=FALSE]
structure(points_rcpp(points, a$points_dim),
n_empty = 0L, precision = a$precision, crs = a$crs,
bbox = bbox.pointmatrix(points), class = a$class,
points = NULL, points_dim = NULL)
}
2 changes: 2 additions & 0 deletions R/sfg.R
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ c.sfg = function(..., recursive = FALSE, flatten = TRUE) {
Paste0 = function(lst) lapply(lst, unclass)
Paste1 = function(lst) do.call(c, lapply(lst, unclass))
lst = list(...)
if (length(lst) && is.null(lst[[1]]))
stop("to combine POINTs into MULTIPOINT, use st_combine(), or realize them first using x[]")
if (flatten) {
cls = vapply(lst, function(x) class(x)[2], "")
ucls = unique(cls)
Expand Down
4 changes: 3 additions & 1 deletion R/tidyverse.R
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,8 @@ nest.sf = function (.data, ...) {
if (!requireNamespace("tidyr", quietly = TRUE))
stop("tidyr required: install first?")

if (inherits(g <- st_geometry(.data), "sfc_POINT") && !is.null(attr(g, "points")))
st_geometry(.data) = g[] # realize
class(.data) <- setdiff(class(.data), "sf")
ret = tidyr::nest(.data, ...)
lst = which(sapply(ret, inherits, "list"))[1]
Expand Down Expand Up @@ -582,7 +584,7 @@ type_sum.sfc <- function(x, ...) {
#' Summarize simple feature item for tibble
#' @name tibble
obj_sum.sfc <- function(x) {
vapply(x, function(sfg) format(sfg, width = 15L), "")
vapply(x[], function(sfg) format(sfg, width = 15L), "")
}

#' @name tibble
Expand Down
8 changes: 7 additions & 1 deletion R/z_range.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ zb_wrap = function(zb) {
structure(zb, names = c("zmin", "zmax"), class = "z_range")
}

z_range.pointmatrix = function(obj, ...) {
zb_wrap(range(obj[,3]))
}

z_range.Set = function(obj, ...) {
sel = vapply(obj, function(x) { length(x) && !all(is.na(x)) }, TRUE)
if (! any(sel))
Expand Down Expand Up @@ -129,7 +133,9 @@ print.z_range = function(x, ...) {
}

compute_z_range = function(obj) {
switch(class(obj)[1],
if (!is.null(pts <- attr(obj, "points")))
z_range.pointmatrix(pts)
else switch(class(obj)[1],
sfc_POINT = zb_wrap(z_range.Set(obj)),
sfc_MULTIPOINT = zb_wrap(z_range.MtrxSet(obj)),
sfc_LINESTRING = zb_wrap(z_range.MtrxSet(obj)),
Expand Down
2 changes: 1 addition & 1 deletion man/geos_unary.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading