Skip to content

Commit

Permalink
Merge branch 'master' into wkt2
Browse files Browse the repository at this point in the history
  • Loading branch information
edzer committed Jan 5, 2020
2 parents d669dc2 + ab5ac2a commit 85b8188
Show file tree
Hide file tree
Showing 30 changed files with 653 additions and 228 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
8 changes: 8 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# version 0.8-1

* `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 @@ -14,6 +18,10 @@

* 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

* fix tests for PROJ 6.2.0 not accepting +units=
Expand Down
4 changes: 2 additions & 2 deletions R/RcppExports.R
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,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
3 changes: 2 additions & 1 deletion R/crs.R
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ Ops.crs <- function(e1, e2) {
#' @details The *crs functions create, get, set or replace the \code{crs} attribute of a simple feature geometry
#' list-column. This attribute is of class \code{crs}, and is a list consisting of \code{epsg} (integer EPSG
#' code) and \code{proj4string} (character).
#' 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{epsg} (length-1 integer),
#' \code{proj4string} (length-1 character) and \code{wkt2} (length-1 character; only for GDAL >= 3 and PROJ >= 6).
#' Elements may have \code{NA} valued; if all elements are \code{NA} the CRS is missing valued, and coordinates are
Expand Down
2 changes: 2 additions & 0 deletions R/gdal_utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ resampling_method = function(option = "near") {
#' @export
#' @examples
#'
#' if (sf_extSoftVersion()["GDAL"] > "2.1.0") {
#' # info utils can be used to list information about about a raster
#' # dataset. More info: https://gdal.org/programs/gdalinfo.html
#' in_file <- system.file("tif/geomatrix.tif", package = "sf")
Expand Down Expand Up @@ -66,6 +67,7 @@ resampling_method = function(option = "near") {
#' # The parameter s_srs had to be specified because, in this case, the in_file
#' # has no associated SRS.
#' st_read(in_file)
#' }

gdal_utils = function(util = "info", source, destination, options = character(0),
quiet = FALSE, processing = character(0), colorfilename = character(0)) {
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 85b8188

Please sign in to comment.