Skip to content

Commit

Permalink
Merge branch 'wkt2' into SetFromUserInput
Browse files Browse the repository at this point in the history
  • Loading branch information
edzer committed Jan 5, 2020
2 parents b1a37ec + 85b8188 commit d16c352
Show file tree
Hide file tree
Showing 27 changed files with 611 additions and 355 deletions.
2 changes: 2 additions & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,5 @@ Collate:
'deprecated.R'
'z_range.R'
'm_range.R'
'shift_longitude.R'
'make_grid.R'
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ S3method(st_segmentize,sfc)
S3method(st_segmentize,sfg)
S3method(st_set_precision,sf)
S3method(st_set_precision,sfc)
S3method(st_shift_longitude,sf)
S3method(st_shift_longitude,sfc)
S3method(st_simplify,sf)
S3method(st_simplify,sfc)
S3method(st_simplify,sfg)
Expand Down Expand Up @@ -419,6 +421,7 @@ export(st_set_geometry)
export(st_set_precision)
export(st_sf)
export(st_sfc)
export(st_shift_longitude)
export(st_simplify)
export(st_snap)
export(st_sym_difference)
Expand Down
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

* reorganize `crs` objects to reflect our post-proj4string world (#1146; #1225)

* `st_sample` directly interfaces `spatstat` sampling methods, e.g. `type = "Thomas"` calls `spatstat::rThomas` after converting input arguments (window) and converts returned `ppp` object to `sf`'s `POINT` geometries; #1204 with help from Ege Rubak and Jakub Nowosad

* `sf_project` gains an option `keep = TRUE` to return `Inf` values for points not projectable; #1228

* support `vctrs` methods for geometry list columns; this makes `unnest` work again (#1172); #1196 by Lionel Henry

* `st_as_sf.pq_geometry` converts binary geom columns from RPostgres::dbGetQuery; #1195
Expand All @@ -16,6 +20,8 @@

* a new UBSAN error in `wkb_read` was resolved; #1154, #1152

* new method `st_shift_longitude` to re-center data for a Pacific view. #1218

* output of `st_as_text()` with `MULTIPOINT` now has nested parentheses around points. E.g., `MULTIPOINT ((0 0), (1 1))` instead of `MULTIPOINT (0 0, 1 1)`; #1219, #1221

# version 0.8-0
Expand Down
4 changes: 2 additions & 2 deletions R/RcppExports.R
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,8 @@ CPL_have_datum_files <- function(foo) {
.Call('_sf_CPL_have_datum_files', PACKAGE = 'sf', foo)
}

CPL_proj_direct <- function(from_to, pts, warn = TRUE) {
.Call('_sf_CPL_proj_direct', PACKAGE = 'sf', from_to, pts, warn)
CPL_proj_direct <- function(from_to, pts, keep, warn = TRUE) {
.Call('_sf_CPL_proj_direct', PACKAGE = 'sf', from_to, pts, keep, warn)
}

CPL_proj_info <- function(type) {
Expand Down
6 changes: 4 additions & 2 deletions R/crs.R
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ Ops.crs <- function(e1, e2) {
#' of a simple feature geometry list-column. This attribute is of class \code{crs},
#' and is a list consisting of \code{input} (user input, e.g. "EPSG:4326" or "WGS84"
#' or a proj4string), and \code{wkt}, an automatically generated wkt representation of the crs.
#' The operators \code{==} and \code{!=} are overloaded for \code{crs} objects to establish semantical identity.
#'
#' Comparison of two objects of class \code{crs} uses the GDAL function
#' \code{OGRSpatialReference::IsSame}.
#' @return Object of class \code{crs}, which is a list with elements \code{input} (length-1 character)
#' and \code{wkt} (length-1 character.
#' and \code{wkt} (length-1 character).
#' Elements may be \code{NA} valued; if all elements are \code{NA} the CRS is missing valued, and coordinates are
#' assumed to relate to an arbitrary Cartesian coordinate system.
st_crs = function(x, ...) UseMethod("st_crs")
Expand Down
95 changes: 0 additions & 95 deletions R/geom.R
Original file line number Diff line number Diff line change
Expand Up @@ -1185,101 +1185,6 @@ st_line_sample = function(x, n, density, type = "regular", sample = NULL) {
st_sfc(CPL_gdal_linestring_sample(x, distList), crs = st_crs(x))
}

#' Create a regular tesselation over the bounding box of an sf or sfc object
#'
#' Create a square or hexagonal grid over the bounding box of an sf or sfc object
#' @param x object of class \link{sf} or \link{sfc}
#' @param cellsize target cellsize
#' @param offset numeric of lengt 2; lower left corner coordinates (x, y) of the grid
#' @param n integer of length 1 or 2, number of grid cells in x and y direction (columns, rows)
#' @param crs object of class \code{crs}; coordinate reference system of the target of the target grid in case argument \code{x} is missing, if \code{x} is not missing, its crs is inherited.
#' @param what character; one of: \code{"polygons"}, \code{"corners"}, or \code{"centers"}
#' @param square logical; if \code{FALSE}, create hexagonal grid
#' @return Object of class \code{sfc} (simple feature geometry list column) with, depending on \code{what} and \code{square},
#' square or hexagonal polygons, corner points of these polygons, or center points of these polygons.
#' @examples
#' plot(st_make_grid(what = "centers"), axes = TRUE)
#' plot(st_make_grid(what = "corners"), add = TRUE, col = 'green', pch=3)
#' sfc = st_sfc(st_polygon(list(rbind(c(0,0), c(1,0), c(1,1), c(0,0)))))
#' plot(st_make_grid(sfc, cellsize = .1, square = FALSE))
#' plot(sfc, add = TRUE)
#' # non-default offset:
#' plot(st_make_grid(sfc, cellsize = .1, square = FALSE, offset = c(0, .05 / (sqrt(3)/2))))
#' plot(sfc, add = TRUE)
#' @export
st_make_grid = function(x,
cellsize = c(diff(st_bbox(x)[c(1,3)]), diff(st_bbox(x)[c(2,4)]))/n,
offset = st_bbox(x)[c("xmin", "ymin")], n = c(10, 10),
crs = if (missing(x)) NA_crs_ else st_crs(x),
what = "polygons", square = TRUE) {

if (missing(x) && missing(cellsize) && missing(offset)
&& missing(n) && missing(crs)) # create global 10 x 10 degree grid
return(st_make_grid(cellsize = c(10,10), offset = c(-180,-90), n = c(36,18),
crs = st_crs(4326), what = what))

if (! square)
return(hex_grid(x, dx = cellsize[1], pt = offset, points = what != "polygons", clip = TRUE))

bb = if (!missing(n) && !missing(offset) && !missing(cellsize)) {
cellsize = rep(cellsize, length.out = 2)
n = rep(n, length.out = 2)
bb_wrap(c(offset, offset + n * cellsize))
} else
st_bbox(x)

cellsize_missing = if (! missing(cellsize)) {
cellsize = rep(cellsize, length.out = 2)
FALSE
} else
TRUE

if (missing(n)) {
nx = ceiling((bb[3] - offset[1])/cellsize[1])
ny = ceiling((bb[4] - offset[2])/cellsize[2])
} else {
n = rep(n, length.out = 2)
nx = n[1]
ny = n[2]
}

# corner points:
if (cellsize_missing) {
xc = seq(offset[1], bb[3], length.out = nx + 1)
yc = seq(offset[2], bb[4], length.out = ny + 1)
} else {
xc = offset[1] + (0:nx) * cellsize[1]
yc = offset[2] + (0:ny) * cellsize[2]
}

if (what == "polygons") {
ret = vector("list", nx * ny)
square = function(x1, y1, x2, y2)
st_polygon(list(matrix(c(x1, x2, x2, x1, x1, y1, y1, y2, y2, y1), 5)))
for (i in 1:nx)
for (j in 1:ny)
ret[[(j - 1) * nx + i]] = square(xc[i], yc[j], xc[i+1], yc[j+1])
} else if (what == "centers") {
ret = vector("list", nx * ny)
cent = function(x1, y1, x2, y2)
st_point(c( (x1+x2)/2, (y1+y2)/2 ))
for (i in 1:nx)
for (j in 1:ny)
ret[[(j - 1) * nx + i]] = cent(xc[i], yc[j], xc[i+1], yc[j+1])
} else if (what == "corners") {
ret = vector("list", (nx + 1) * (ny + 1))
for (i in 1:(nx + 1))
for (j in 1:(ny + 1))
ret[[(j - 1) * (nx + 1) + i]] = st_point(c(xc[i], yc[j]))
} else
stop("unknown value of `what'")

if (missing(x))
st_sfc(ret, crs = crs)
else
st_sfc(ret, crs = st_crs(x))
}

#' Internal functions
#' @name internal
#' @param msg error message
Expand Down
166 changes: 166 additions & 0 deletions R/make_grid.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#' Create a regular tesselation over the bounding box of an sf or sfc object
#'
#' Create a square or hexagonal grid over the bounding box of an sf or sfc object
#' @param x object of class \link{sf} or \link{sfc}
#' @param cellsize target cellsize
#' @param offset numeric of lengt 2; lower left corner coordinates (x, y) of the grid
#' @param n integer of length 1 or 2, number of grid cells in x and y direction (columns, rows)
#' @param crs object of class \code{crs}; coordinate reference system of the target of the target grid in case argument \code{x} is missing, if \code{x} is not missing, its crs is inherited.
#' @param what character; one of: \code{"polygons"}, \code{"corners"}, or \code{"centers"}
#' @param square logical; if \code{FALSE}, create hexagonal grid
#' @param flat_topped logical; if \code{TRUE} generate flat topped hexagons, else generate pointy topped
#' @return Object of class \code{sfc} (simple feature geometry list column) with, depending on \code{what} and \code{square},
#' square or hexagonal polygons, corner points of these polygons, or center points of these polygons.
#' @examples
#' plot(st_make_grid(what = "centers"), axes = TRUE)
#' plot(st_make_grid(what = "corners"), add = TRUE, col = 'green', pch=3)
#' sfc = st_sfc(st_polygon(list(rbind(c(0,0), c(1,0), c(1,1), c(0,0)))))
#' plot(st_make_grid(sfc, cellsize = .1, square = FALSE))
#' plot(sfc, add = TRUE)
#' # non-default offset:
#' plot(st_make_grid(sfc, cellsize = .1, square = FALSE, offset = c(0, .05 / (sqrt(3)/2))))
#' plot(sfc, add = TRUE)
#' @export
st_make_grid = function(x,
cellsize = c(diff(st_bbox(x)[c(1,3)]), diff(st_bbox(x)[c(2,4)]))/n,
offset = st_bbox(x)[c("xmin", "ymin")], n = c(10, 10),
crs = if (missing(x)) NA_crs_ else st_crs(x),
what = "polygons", square = TRUE, flat_topped = FALSE) {

if (missing(x) && missing(cellsize) && missing(offset)
&& missing(n) && missing(crs)) # create global 10 x 10 degree grid
return(st_make_grid(cellsize = c(10,10), offset = c(-180,-90), n = c(36,18),
crs = st_crs(4326), what = what))

if (! square) { # hexagons:
hex = make_hex_grid(x, dx = cellsize[1]/sqrt(3), pt = offset, what = what,
flat_topped = flat_topped)
if (what == "corners")
hex = st_cast(hex, "POINT")[x]
return(hex)
}

bb = if (!missing(n) && !missing(offset) && !missing(cellsize)) {
cellsize = rep(cellsize, length.out = 2)
n = rep(n, length.out = 2)
bb_wrap(c(offset, offset + n * cellsize))
} else
st_bbox(x)

cellsize_missing = if (! missing(cellsize)) {
cellsize = rep(cellsize, length.out = 2)
FALSE
} else
TRUE

if (missing(n)) {
nx = ceiling((bb[3] - offset[1])/cellsize[1])
ny = ceiling((bb[4] - offset[2])/cellsize[2])
} else {
n = rep(n, length.out = 2)
nx = n[1]
ny = n[2]
}

# corner points:
if (cellsize_missing) {
xc = seq(offset[1], bb[3], length.out = nx + 1)
yc = seq(offset[2], bb[4], length.out = ny + 1)
} else {
xc = offset[1] + (0:nx) * cellsize[1]
yc = offset[2] + (0:ny) * cellsize[2]
}

if (what == "polygons") {
ret = vector("list", nx * ny)
square = function(x1, y1, x2, y2)
st_polygon(list(matrix(c(x1, x2, x2, x1, x1, y1, y1, y2, y2, y1), 5)))
for (i in 1:nx)
for (j in 1:ny)
ret[[(j - 1) * nx + i]] = square(xc[i], yc[j], xc[i+1], yc[j+1])
} else if (what == "centers") {
ret = vector("list", nx * ny)
cent = function(x1, y1, x2, y2)
st_point(c( (x1+x2)/2, (y1+y2)/2 ))
for (i in 1:nx)
for (j in 1:ny)
ret[[(j - 1) * nx + i]] = cent(xc[i], yc[j], xc[i+1], yc[j+1])
} else if (what == "corners") {
ret = vector("list", (nx + 1) * (ny + 1))
for (i in 1:(nx + 1))
for (j in 1:(ny + 1))
ret[[(j - 1) * (nx + 1) + i]] = st_point(c(xc[i], yc[j]))
} else
stop("unknown value of `what'")

if (missing(x))
st_sfc(ret, crs = crs)
else
st_sfc(ret, crs = st_crs(x))
}


### hex grid tesselation that
## - covers a bounding box st_bbox(obj)
## - contains pt
## - has x spacing dx: the shortest distance between x coordinates with identical y coordinate
## - selects geometries intersecting with obj
make_hex_grid = function(obj, pt, dx, what, flat_topped = TRUE) {

dy = sqrt(3) * dx / 2
bb = st_bbox(obj)
if (!flat_topped) { # pointy topped -- swap x and y:
ylim = bb[c("xmin", "xmax")]
xlim = bb[c("ymin", "ymax")]
pt = pt[2:1]
} else {
xlim = bb[c("xmin", "xmax")]
ylim = bb[c("ymin", "ymax")]
}
offset = c(x = (pt[1] - xlim[1]) %% dx, y = (pt[2] - ylim[1]) %% (2 * dy))
x0 = seq(xlim[1] - dx, xlim[2] + 2 * dx, dx) + offset[1]
y0 = seq(ylim[1] - 2 * dy, ylim[2] + 2 * dy, dy) + offset[2]

y <- rep(y0, each = length(x0))
x <- rep(c(x0, x0 + dx / 2), length.out = length(y))
xy = cbind(x, y) # the coordinates

# compute the indexes, using double coordinates:
odd <- seq(1, by = 2, length.out = length(x0))
even <- seq(2, by = 2, length.out = length(x0))
xi <- rep(c(odd, even), length.out = length(y))
yi <- rep(seq_along(y0), each = length(x0))
# hexagon centers are columns with x index 3, 6, 9, ... :
centers = cbind(xi,yi)[xi %in% seq(3, max(xi) - 2, by = 3) & yi > 1 & yi < max(yi),]

# relative offset in double coordinates, https://www.redblobgames.com/grids/hexagons/
nx = length(x0)
xy_pattern = rbind(c(-2,0), c(-1,-1), c(1,-1), c(2,0), c(1,1), c(-1,1), c(-2,0))
i_from_x = function(x) ((x[,1] - 1) %/% 2 + 1) + (x[,2] - 1) * nx

mk_point = if (flat_topped)
function(center) st_point(xy[i_from_x(matrix(center, ncol = 2)),])
else
function(center) st_point(xy[i_from_x(matrix(center, ncol = 2)),2:1])

mk_pol = if (flat_topped)
function(center) {
m = matrix(center, ncol=2, nrow = 7, byrow=TRUE) + xy_pattern
st_polygon(list(xy[i_from_x(m),]))
}
else
function(center) {
m = matrix(center, ncol=2, nrow = 7, byrow=TRUE) + xy_pattern
st_polygon(list(xy[i_from_x(m),2:1]))
}

ret = if (what == "points")
st_sfc(lapply(seq_len(nrow(centers)), function(i) mk_point(centers[i,])), crs = st_crs(bb))
else # points:
st_sfc(lapply(seq_len(nrow(centers)), function(i) mk_pol(centers[i,])), crs = st_crs(bb))

if (what == "points")
ret[obj]
else
ret[lengths(st_relate(ret, obj, "2********")) > 0] # part or total overlap
}
2 changes: 1 addition & 1 deletion R/read.R
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ process_cpl_read_ogr = function(x, quiet = FALSE, ..., check_ring_dir = FALSE,
x <- as.data.frame(set_utf8(x[-c(lc.other, which.geom)]), stringsAsFactors = stringsAsFactors)
if (as_tibble) {
# "sf" class is added later by `st_as_sf` (and sets all the attributes)
x <- tibble::new_tibble(x, attributes(x), nrow = nrow(x))
x <- tibble::new_tibble(x, nrow = nrow(x))
}
}

Expand Down
Loading

0 comments on commit d16c352

Please sign in to comment.