Skip to content

Commit 7c860cc

Browse files
committed
closes #1961
1 parent 53c7616 commit 7c860cc

File tree

4 files changed

+61
-28
lines changed

4 files changed

+61
-28
lines changed

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# version 1.0-13
22

3+
* `st_cast()` handles empty geometries; #1961
4+
35
* don't repeat longlat messages in `summarise.sf()`; #1519
46

57
* fix random sampling on the sphere; #2133

R/cast_sfc.R

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,18 @@ copy_sfc_attributes_from = function(x, ret) {
135135
bbox = attr(x, "bbox"), crs = attr(x, "crs"), n_empty = attr(x, "n_empty"))
136136
}
137137

138+
empty_sfg <- function(to) {
139+
switch(to,
140+
GEOMETRYCOLLECTION = st_geometrycollection(),
141+
MULTIPOLYGON = st_multipolygon(),
142+
POLYGON = st_polygon(),
143+
MULTILINESTRING = st_multilinestring(),
144+
LINESTRING = st_linestring(),
145+
MULTIPOINT = st_multipoint(),
146+
POINT = st_point()
147+
)
148+
}
149+
138150

139151
#' @name st_cast
140152
#' @param ids integer vector, denoting how geometries should be grouped (default: no grouping)
@@ -148,10 +160,20 @@ st_cast.sfc = function(x, to, ..., ids = seq_along(x), group_or_split = TRUE) {
148160
if (missing(to))
149161
return(st_cast_sfc_default(x))
150162

163+
e = rep(FALSE, length(x))
164+
if (!inherits(x, c("sfc_MULTICURVE", "sfc_COMPOUNDCURVE"))) {
165+
e = st_is_empty(x)
166+
if (all(e)) {
167+
x[e] = empty_sfg(to)
168+
return(x) # RETURNS
169+
}
170+
}
171+
if (any(e))
172+
x = x[!e]
151173
from_cls = substr(class(x)[1], 5, 100)
152174
from_col = which_sfc_col(from_cls)
153175
to_col = which_sfc_col(to)
154-
if (from_cls == to)
176+
ret = if (from_cls == to)
155177
x # returns x: do nothing
156178
else if (to == "GEOMETRY") # we can always do that:
157179
structure(x, class = c("sfc_GEOMETRY", "sfc"))
@@ -201,6 +223,14 @@ st_cast.sfc = function(x, to, ..., ids = seq_along(x), group_or_split = TRUE) {
201223
# EJP: FIXME:
202224
structure(reclass(ret, to, need_close(to)), ids = get_lengths(x))
203225
}
226+
if (any(e)) {
227+
crs = st_crs(x)
228+
x = vector("list", length = length(e))
229+
x[e] = list(empty_sfg(to))
230+
x[!e] = ret
231+
st_set_crs(do.call(st_sfc, x), crs)
232+
} else
233+
ret
204234
}
205235

206236
#' @name st_cast

tests/cast.R

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ st_cast(pol, "MULTILINESTRING")
1818
(pt = st_cast(mp, "POINT"))
1919
(i = attr(pt, "ids"))
2020
(xx = st_cast(pt, "MULTIPOINT", rep(seq_along(i), i)))
21-
(yy = st_cast(pt, "LINESTRING", rep(seq_along(i), i)))
21+
try(yy <- st_cast(pt, "LINESTRING", rep(seq_along(i), i)))
2222

23-
(zz = st_cast(yy, "MULTILINESTRING"))
23+
#(zz = st_cast(yy, "MULTILINESTRING"))
2424
#(zz = st_cast(yy, "POLYGON"))
2525

2626
st_cast(mls, "LINESTRING")
@@ -83,3 +83,6 @@ g <- st_as_sfc(wkt)
8383
g <- st_sf(demo = "test", geom = g, crs = 4326)
8484
m = st_cast(g, "MULTILINESTRING")
8585
identical(m$geom[[1]], st_cast(g$geom[[1]], "MULTILINESTRING"))
86+
87+
st_cast(st_sfc(st_geometrycollection(), st_multipolygon()), 'MULTIPOLYGON') #1961
88+
st_cast(st_sfc(st_geometrycollection(), st_multipolygon(), st_point(0:1)), 'POINT') #1961

tests/cast.Rout.save

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

2-
R version 4.0.4 (2021-02-15) -- "Lost Library Book"
3-
Copyright (C) 2021 The R Foundation for Statistical Computing
2+
R version 4.2.3 (2023-03-15) -- "Shortstop Beagle"
3+
Copyright (C) 2023 The R Foundation for Statistical Computing
44
Platform: x86_64-pc-linux-gnu (64-bit)
55

66
R is free software and comes with ABSOLUTELY NO WARRANTY.
@@ -84,29 +84,9 @@ MULTIPOINT ((1 3))
8484
MULTIPOINT ((10 13))
8585
MULTIPOINT ((11 14))
8686
MULTIPOINT ((12 15))
87-
> (yy = st_cast(pt, "LINESTRING", rep(seq_along(i), i)))
88-
Geometry set for 5 features
89-
Geometry type: LINESTRING
90-
Dimension: XY
91-
Bounding box: xmin: 0 ymin: 2 xmax: 12 ymax: 15
92-
CRS: NA
93-
LINESTRING (0 2)
94-
LINESTRING (1 3)
95-
LINESTRING (10 13)
96-
LINESTRING (11 14)
97-
LINESTRING (12 15)
87+
> try(yy <- st_cast(pt, "LINESTRING", rep(seq_along(i), i)))
9888
>
99-
> (zz = st_cast(yy, "MULTILINESTRING"))
100-
Geometry set for 5 features
101-
Geometry type: MULTILINESTRING
102-
Dimension: XY
103-
Bounding box: xmin: 0 ymin: 2 xmax: 12 ymax: 15
104-
CRS: NA
105-
MULTILINESTRING ((0 2))
106-
MULTILINESTRING ((1 3))
107-
MULTILINESTRING ((10 13))
108-
MULTILINESTRING ((11 14))
109-
MULTILINESTRING ((12 15))
89+
> #(zz = st_cast(yy, "MULTILINESTRING"))
11090
> #(zz = st_cast(yy, "POLYGON"))
11191
>
11292
> st_cast(mls, "LINESTRING")
@@ -235,6 +215,24 @@ POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (5 5, 5 6, 6 6, 6 5, 5 5))
235215
> identical(m$geom[[1]], st_cast(g$geom[[1]], "MULTILINESTRING"))
236216
[1] TRUE
237217
>
218+
> st_cast(st_sfc(st_geometrycollection(), st_multipolygon()), 'MULTIPOLYGON') #1961
219+
Geometry set for 2 features (with 2 geometries empty)
220+
Geometry type: MULTIPOLYGON
221+
Dimension: XY
222+
Bounding box: xmin: NA ymin: NA xmax: NA ymax: NA
223+
CRS: NA
224+
MULTIPOLYGON EMPTY
225+
MULTIPOLYGON EMPTY
226+
> st_cast(st_sfc(st_geometrycollection(), st_multipolygon(), st_point(0:1)), 'POINT') #1961
227+
Geometry set for 3 features (with 2 geometries empty)
228+
Geometry type: POINT
229+
Dimension: XY
230+
Bounding box: xmin: 0 ymin: 1 xmax: 0 ymax: 1
231+
CRS: NA
232+
POINT EMPTY
233+
POINT EMPTY
234+
POINT (0 1)
235+
>
238236
> proc.time()
239237
user system elapsed
240-
0.937 0.044 0.974
238+
1.501 0.751 1.420

0 commit comments

Comments
 (0)