···11Package: osmdata
22Title: Import 'OpenStreetMap' Data as Simple Features or Spatial Objects
33-Version: 0.2.5.067
33+Version: 0.2.5.068
44Authors@R: c(
55 person("Joan", "Maspons", , "joanmaspons@gmail.com", role = c("aut", "cre"),
66 comment = c(ORCID = "0000-0003-2286-8727")),
+12-4
NEWS.md
···220.2.5.00x (dev version)
33===================
4455+## Breaking changes
66+77+- Remove `magrittr` from imports. User code relaying on reexported pipe `%>%`
88+ from `osmdata` must explicitly load it with `library(magrittr)`.
99+ Code examples, tests and vignettes now use the pipe from base (`|>`) available since R 4.1 (#361)
1010+- `getbb(..., format_out = "polygon")` return polygons following [https://www.ogc.org/standards/sfa/].
1111+ Polygons are defined by a list of matrices of coordinates. The first ring defines the exterior boundary, and the following rings define holes if present.
1212+ Also fix `getbb(..., format_out = "sf_polygon")` returning each (multi)polygon as a row in an `sf` object.
1313+ Before, every ring was an independent polygon, even for holes or multipolygons,
1414+ and for `format_out = "sf_polygon"`, the features were split in a list with polygons in one item and multipolygons in another (#378).
1515+516## Major changes
617718- Implemented `c.osmdata_sc` method to join `osmdata_sc` objects (#333)
88-- Remove `magrittr` from imports. User code relaying on reexported pipe `%>%` from `osmdata` must explicitly load it
99- with `library(magrittr)`. Code examples, tests and vignettes now use the pipe from base (`|>`) available since R 4.1
1010- (#361)
1119- Depends on R >= 4.1 to use the base pipe (`|>`) in examples and vignettes (#371)
1220- Deprecate `nodes_only` argument in `opq()`. Superseded by argument `osm_types` (#370)
1321- Deprecate `osmdata_sp` (#372)
···2937- Restructure class definitions of `osmdata_sf()` and `osmdata_sc()` objects (#373, #374)
3038- Revert added `osmdata` class to `osmdata_data_frame()` and `osmdata_sc()` +
3139 Fix docs to better represent classes accepted by `trim_osmdata()`, `osm_poly2line()` and extract function (#380)
3232-- Use terra functions instead of raster (obsolete) in osm_elevation() (#383)
4040+- Use `terra` functions instead of `raster` (obsolete) in `osm_elevation()` (#383)
3341344235430.2.5
+163-87
R/getbb.R
···141141#' @param key The API key to use for services that require it
142142#' @param silent Should the API be printed to screen? TRUE by default
143143#'
144144-#' @return Defaults to a matrix in the form:
145145-#' \code{
144144+#' @return For `format_out = "matrix"`, the default, return the bounding box:
145145+#' ```
146146#' min max
147147#' x ... ...
148148#' y ... ...
149149-#' }
149149+#' ```
150150#'
151151-#' If `format_out = "polygon"`, one or more two-columns matrices of polygonal
152152-#' longitude-latitude points. Where multiple `place_name` occurrences are found
153153-#' within `nominatim`, each item of the list of coordinates may itself contain
154154-#' multiple coordinate matrices where multiple exact matches exist. If one
155155-#' exact match exists with potentially multiple polygonal boundaries (for
156156-#' example, "london uk" is an exact match, but can mean either greater London or
157157-#' the City of London), only the first is returned. See examples below for
158158-#' illustration.
151151+#' If `format_out = "polygon"`, a list of polygons and multipolygons with one
152152+#' item for each `nominatim` result. The items are named with the OSM type and
153153+#' id. Each polygon is formed by one or more two-columns matrices of polygonal
154154+#' longitude-latitude points. The first matrix represents the outer boundary and
155155+#' the next ones represent holes. See examples below for illustration.
159156#'
160160-#' For `format_out = "osm_type_id"`, a character string representing an OSM object in overpass query
161161-#' language. For example: \code{"relation(id:11747082)"} represents the area of
162162-#' the Catalan Countries. If one exact match exists with potentially multiple
163163-#' polygonal boundaries, only the first relation or way is returned. A set of
164164-#' objects can also be represented for multiple results (e.g.
157157+#' If `format_out = "sf_polygon"`, a `sf` object. Each row correspond to a
158158+#' `place_name` within `nominatim` result.
159159+#'
160160+#' For `format_out = "osm_type_id"`, a character string representing an OSM
161161+#' object in overpass query language. For example:
162162+#' `"relation(id:11747082)"` represents the area of the Catalan Countries.
163163+#' If one exact match exists with potentially multiple polygonal boundaries,
164164+#' only the first relation or way is returned. A set of objects can also be
165165+#' represented for multiple results (e.g.
165166#' `relation(id:11747082,307833); way(id:22422490)`). See examples below for
166167#' illustration. The OSM objects that can be used as
167168#' [areas in overpass queries](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Map_way/relation_to_area_(map_to_area))
···187188#' getbb ("Hereford", format_out = "data.frame", limit = 3)
188189#'
189190#' # Examples of polygonal boundaries
190190-#' bb <- getbb ("london uk", format_out = "polygon") # single match
191191-#' dim (bb [[1]] [[1]]) # matrix of longitude/latitude pairs
191191+#' bb <- getbb ("Milano, Italy", format_out = "polygon")
192192+#' # A polygon and a multipolygon:
193193+#' str (bb) # matrices of longitude/latitude pairs
194194+#'
192195#' bb_sf <- getbb ("kathmandu", format_out = "sf_polygon")
196196+#' bb_sf
193197#' # sf:::plot.sf(bb_sf) # can be plotted if sf is installed
194198#' getbb ("london", format_out = "sf_polygon")
195195-#' getbb ("accra", format_out = "sf_polygon") # rectangular bb
196199#'
197197-#' area <- getbb ("València", format_out = "osm_type_id")
200200+#' getbb ("València", format_out = "osm_type_id")
198201#' # select multiple areas with format_out = "osm_type_id"
199202#' areas <- getbb ("València", format_out = "data.frame")
200203#' bbox_to_string (areas [areas$osm_type != "node", ])
···212215getbb <- function (place_name,
213216 display_name_contains = NULL,
214217 viewbox = NULL,
215215- format_out = "matrix",
218218+ format_out = c (
219219+ "matrix", "data.frame", "string",
220220+ "polygon", "sf_polygon", "osm_type_id"
221221+ ),
216222 base_url = "https://nominatim.openstreetmap.org",
217223 featuretype = "settlement",
218224 limit = 10,
219225 key = NULL,
220226 silent = TRUE) {
221227228228+ format_out <- match.arg (format_out)
222229 is_polygon <- grepl ("polygon", format_out)
223230224231 obj <- get_nominatim_query (
···261268 ret <- bb_mat
262269 } else if (format_out == "string") {
263270 ret <- bbox_to_string (bbox = bb_mat)
271271+264272 } else if (is_polygon) {
265273266274 gt_p <- get_geotext_poly (obj)
267275 gt_mp <- get_geotext_multipoly (obj)
268276269269- gt <- c (gt_p, gt_mp)
270270- # multipolys below are not strict SF MULTIPOLYGONs, rather just cases
271271- # where nominatim returns lists of multiple items
272272- if (length (gt) == 0) {
273273- message ("No polygonal boundary for ", place_name)
274274- ret <- bb_mat
275275- } else if (length (gt) == 1) {
276276- ret <- gt [[1]]
277277- } else {
278278- ret <- gt
279279- }
280280- } else {
281281- stop (paste0 (
282282- "format_out not recognised; please specify one of ",
283283- "[data.frame, matrix, string, polygon]"
284284- ))
285285- }
277277+ if (format_out == "polygon") {
278278+279279+ gt <- c (gt_p, gt_mp)
280280+ # multipolys below are not strict SF MULTIPOLYGONs, rather just
281281+ # cases where nominatim returns lists of multiple items
282282+ if (length (gt) == 0) {
283283+ message (
284284+ "No polygonal boundary for ", place_name,
285285+ ". Returning the bounding box of the first result"
286286+ )
287287+ ret <- bb_mat
288288+ } else {
289289+ poly_id <- names (gt)
290290+ obj_id <- paste0 (obj$osm_type, "/", obj$osm_id)
291291+ obj_id <- intersect (obj_id, poly_id)
292292+ # sort geometries following Nominatim order
293293+ ord_poly <- match (obj_id, poly_id)
294294+ ret <- gt [ord_poly]
295295+ }
296296+ } else if (format_out == "sf_polygon") {
286297287287- if (format_out == "sf_polygon") {
288288- ret_poly <- bb_as_sf_poly (gt_p, gt_mp, place_name)
289289- obj_index <- as.integer (c (names (gt_p), names (gt_mp)))
290290- ret_data <- obj [obj_index, which (!names (obj) %in% c ("boundingbox", "geotext"))]
291291- ret <- cbind (ret_data, ret_poly)
292292- # Then restore sf attributes:
293293- nms <- names (ret)
294294- attributes (ret) <- attributes (ret_poly)
295295- names (ret) <- nms
298298+ if (length (gt_p) + length (gt_mp) == 0) {
299299+ message (
300300+ "No polygonal boundary for ", place_name,
301301+ ". Returning the bounding boxes."
302302+ )
303303+ ret_poly <- lapply (obj$boundingbox, function (x) {
304304+ x <- as.numeric (x)
305305+ bb_mat <- matrix (
306306+ c (x [3:4], x [1:2]),
307307+ nrow = 2, byrow = TRUE
308308+ )
309309+ mat2sf_poly (list (bb_mat))
310310+ })
311311+ ret_poly <- do.call (rbind, ret_poly)
312312+ poly_id <- paste0 (obj$osm_type, "/", obj$osm_id)
313313+ } else {
314314+ ret_poly <- bb_as_sf_poly (gt_p, gt_mp)
315315+ poly_id <- c (names (gt_p), names (gt_mp))
316316+ }
317317+318318+ obj_id <- paste0 (obj$osm_type, "/", obj$osm_id)
319319+ cols <- setdiff (names (obj), c ("boundingbox", "geotext"))
320320+ ret <- obj [obj_id %in% poly_id, cols]
321321+ obj_id <- intersect (obj_id, poly_id)
322322+323323+ utf8cols <- c ("licence", "name", "display_name")
324324+ ret [, utf8cols] <- setenc_utf8 (ret [, utf8cols])
325325+326326+ # sort geometries following Nominatim order
327327+ ord_poly <- match (obj_id, poly_id)
328328+ geometry <- ret_poly$geometry [ord_poly]
329329+ # sub-setting without 'sf' loaded removes attributes:
330330+ attributes (geometry) <- attributes (ret_poly$geometry)
331331+332332+ ret <- make_sf (ret, geometry)
333333+ }
296334 }
297335298336 return (ret)
···368406#' Get all polygons from a 'geojson' object
369407#'
370408#' @param obj A 'geojson' object
371371-#' @return List of polygon matrices
409409+#' @return List of polygons. Each polygon is a list of matrices, the first
410410+#' defining the outer ring and the following ones, if present, define holes.
372411#' @noRd
373412get_geotext_poly <- function (obj) {
374413···411450 lens <- vapply (gt_p, length, integer (1))
412451 index_final <- rep (index_final, times = lens)
413452 gt_p <- do.call (c, gt_p)
414414- names (gt_p) <- as.character (index_final)
453453+454454+ # Aggregate rings by polygon
455455+ gt_p <- split (
456456+ gt_p,
457457+ paste0 (obj$osm_type [index_final], "/", obj$osm_id [index_final])
458458+ )
459459+ # Set names to the rings of the polygons
460460+ gt_p <- lapply (gt_p, function (x) {
461461+ if (length (x) > 1) {
462462+ inner <- paste0 ("inner_", seq_len (length (x) - 1))
463463+ } else {
464464+ inner <- character ()
465465+ }
466466+ names (x) <- c ("outer", inner)
467467+ x
468468+ })
415469 }
416470417471 return (gt_p)
···422476#' See Issue #195
423477#'
424478#' @param obj A 'geojson' object
425425-#' @return List of multipolygon matrices
479479+#' @return List of multipolygons. Each multipolygon is a list of polygons, and
480480+#' each polygon is a list of matrices where the first
481481+#' defines the outer ring and the following ones, if present, define holes.
426482#' @noRd
427483get_geotext_multipoly <- function (obj) {
428484···450506451507 if (length (gt_mp) > 0) {
452508 gt_mp <- lapply (gt_mp, function (i) get1bdypoly (i))
453453- lens <- vapply (gt_mp, length, integer (1))
454454- index_final <- rep (index_final, times = lens)
455455- names (gt_mp) <- as.character (index_final)
509509+510510+ # Aggregate polygons by multipolygon
511511+ gt_mp <- split (
512512+ gt_mp,
513513+ paste0 (obj$osm_type [index_final], "/", obj$osm_id [index_final])
514514+ )
515515+ # Set names to the polygons and rings of the multypolygons
516516+ gt_mp <- lapply (gt_mp, function (x) {
517517+ names (x) <- paste0 ("pol_", seq_len (length (x)))
518518+ x <- lapply (x, function (y) {
519519+ if (length (y) > 1) {
520520+ inner <- paste0 ("inner_", seq_len (length (y) - 1))
521521+ } else {
522522+ inner <- character ()
523523+ }
524524+ names (y) <- c ("outer", inner)
525525+ y
526526+ })
527527+528528+ x
529529+ })
456530 }
457531458532 return (gt_mp)
···501575 return (ret)
502576}
503577504504-#' convert a matrix to an sf polygon
578578+#' Convert a list of matrices to an sf polygon
505579#'
506506-#' @param mat A matrix
507507-#' @param pname The name of the polygon
580580+#' @param pol A list of matrices defining a polygon. First is the outer limit,
581581+#' other are holes.
508582#'
509509-#' @return A list that can be converted into a simple features geometry
583583+#' @return A `sf` object representing the polygon without using \pkg{sf}.
510584#' @noRd
511511-mat2sf_poly <- function (mat, pname) {
512512- if (nrow (mat) == 2) {
513513- x <- c (mat [1, 1], mat [1, 2], mat [1, 2], mat [1, 1], mat [1, 1])
514514- y <- c (mat [2, 2], mat [2, 2], mat [2, 1], mat [2, 1], mat [2, 2])
515515- mat <- cbind (x, y)
585585+mat2sf_poly <- function (pol) {
586586+ if (length (pol) == 1L && nrow (pol [[1]]) == 2L) {
587587+ # no polygon but a bounding box
588588+ pol <- pol [[1]]
589589+ x <- c (pol [1, 1], pol [1, 2], pol [1, 2], pol [1, 1], pol [1, 1])
590590+ y <- c (pol [2, 2], pol [2, 2], pol [2, 1], pol [2, 1], pol [2, 2])
591591+ pol <- list (cbind (x, y))
516592 }
517517- mat_sf <- list (mat)
518518- class (mat_sf) <- c ("XY", "POLYGON", "sfg")
519519- mat_sf <- list (mat_sf)
520520- attr (mat_sf, "class") <- c ("sfc_POLYGON", "sfc")
521521- attr (mat_sf, "precision") <- 0
522522- bb <- as.vector (t (apply (mat, 2, range)))
593593+ class (pol) <- c ("XY", "POLYGON", "sfg")
594594+ pol_sf <- list (pol)
595595+ attr (pol_sf, "class") <- c ("sfc_POLYGON", "sfc")
596596+ attr (pol_sf, "precision") <- 0
597597+ bb <- as.vector (t (apply (do.call (rbind, pol), 2, range)))
523598 names (bb) <- c ("xmin", "ymin", "xmax", "ymax")
524599 class (bb) <- "bbox"
525525- attr (mat_sf, "bbox") <- bb
600600+ attr (pol_sf, "bbox") <- bb
526601 crs <- list (
527602 input = "EPSG:4326",
528603 wkt = wkt4326
529604 )
530605 class (crs) <- "crs"
531531- attr (mat_sf, "crs") <- crs
532532- attr (mat_sf, "n_empty") <- 0L
533533- mat_sf <- make_sf (mat_sf)
534534- names (mat_sf) <- "geometry"
535535- attr (mat_sf, "sf_column") <- "geometry"
536536- return (mat_sf)
606606+ attr (pol_sf, "crs") <- crs
607607+ attr (pol_sf, "n_empty") <- 0L
608608+ pol_sf <- make_sf (pol_sf)
609609+ names (pol_sf) <- "geometry"
610610+ attr (pol_sf, "sf_column") <- "geometry"
611611+ return (pol_sf)
537612}
538613539614#' convert a list of matrices to an sf mulipolygon
540615#'
541616#' @param x A list of matrices
542542-#' @param mpname The name of the multipolygon
543617#'
544618#' @return A list that can be converted into a simple features geometry
545619#' @noRd
546546-mat2sf_multipoly <- function (x, mpname) {
620620+mat2sf_multipoly <- function (x) {
547621 # get bbox from matrices
548548- bb <- as.vector (t (apply (do.call (rbind, x [[1]]), 2, range)))
622622+ bb <- as.vector (t (apply (
623623+ do.call (rbind, unlist (x, recursive = FALSE)), 2, range
624624+ )))
549625 names (bb) <- c ("xmin", "ymin", "xmax", "ymax")
550626 class (bb) <- "bbox"
551627···567643 return (xsf)
568644}
569645570570-bb_as_sf_poly <- function (gt_p, gt_mp, place_name) {
646646+bb_as_sf_poly <- function (gt_p, gt_mp) {
571647572648 if (!is.null (gt_p)) {
573649 gt_p <- lapply (gt_p, function (i) {
574574- mat2sf_poly (i, pname = place_name)
650650+ mat2sf_poly (i)
575651 })
576652 }
577653 if (!is.null (gt_mp)) {
578654 gt_mp <- lapply (gt_mp, function (i) {
579579- mat2sf_multipoly (list (i), mpname = place_name)
655655+ mat2sf_multipoly (i)
580656 })
581657 }
582658583583- if (is.null (gt_p) & is.null (gt_mp)) {
659659+ if (length (gt_p) == 0 && length (gt_mp) == 0) {
584660 stop ("Query returned no polygons")
585585- } else if (is.null (gt_mp)) {
661661+ } else if (length (gt_mp) == 0) {
586662 ret <- do.call (rbind, gt_p)
587587- } else if (is.null (gt_p)) {
663663+ } else if (length (gt_p) == 0) {
588664 ret <- do.call (rbind, gt_mp)
589665 } else {
590590- ret <- list (
591591- "polygon" = do.call (rbind, gt_p),
592592- "multipolygon" = do.call (rbind, gt_mp)
666666+ ret <- do.call (
667667+ rbind,
668668+ c (polygon = gt_p, multipolygon = gt_mp)
593669 )
594670 }
595671
···88 place_name,
99 display_name_contains = NULL,
1010 viewbox = NULL,
1111- format_out = "matrix",
1111+ format_out = c("matrix", "data.frame", "string", "polygon", "sf_polygon",
1212+ "osm_type_id"),
1213 base_url = "https://nominatim.openstreetmap.org",
1314 featuretype = "settlement",
1415 limit = 10,
···4142\item{silent}{Should the API be printed to screen? TRUE by default}
4243}
4344\value{
4444-Defaults to a matrix in the form:
4545-\code{
4646- min max
4545+For \code{format_out = "matrix"}, the default, return the bounding box:
4646+4747+\if{html}{\out{<div class="sourceCode">}}\preformatted{ min max
4748x ... ...
4849y ... ...
4949-}
5050+}\if{html}{\out{</div>}}
50515151-If \code{format_out = "polygon"}, one or more two-columns matrices of polygonal
5252-longitude-latitude points. Where multiple \code{place_name} occurrences are found
5353-within \code{nominatim}, each item of the list of coordinates may itself contain
5454-multiple coordinate matrices where multiple exact matches exist. If one
5555-exact match exists with potentially multiple polygonal boundaries (for
5656-example, "london uk" is an exact match, but can mean either greater London or
5757-the City of London), only the first is returned. See examples below for
5858-illustration.
5252+If \code{format_out = "polygon"}, a list of polygons and multipolygons with one
5353+item for each \code{nominatim} result. The items are named with the OSM type and
5454+id. Each polygon is formed by one or more two-columns matrices of polygonal
5555+longitude-latitude points. The first matrix represents the outer boundary and
5656+the next ones represent holes. See examples below for illustration.
59576060-For \code{format_out = "osm_type_id"}, a character string representing an OSM object in overpass query
6161-language. For example: \code{"relation(id:11747082)"} represents the area of
6262-the Catalan Countries. If one exact match exists with potentially multiple
6363-polygonal boundaries, only the first relation or way is returned. A set of
6464-objects can also be represented for multiple results (e.g.
5858+If \code{format_out = "sf_polygon"}, a \code{sf} object. Each row correspond to a
5959+\code{place_name} within \code{nominatim} result.
6060+6161+For \code{format_out = "osm_type_id"}, a character string representing an OSM
6262+object in overpass query language. For example:
6363+\code{"relation(id:11747082)"} represents the area of the Catalan Countries.
6464+If one exact match exists with potentially multiple polygonal boundaries,
6565+only the first relation or way is returned. A set of objects can also be
6666+represented for multiple results (e.g.
6567\verb{relation(id:11747082,307833); way(id:22422490)}). See examples below for
6668illustration. The OSM objects that can be used as
6769\href{https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#Map_way/relation_to_area_(map_to_area)}{areas in overpass queries}
···9799getbb ("Hereford", format_out = "data.frame", limit = 3)
9810099101# Examples of polygonal boundaries
100100-bb <- getbb ("london uk", format_out = "polygon") # single match
101101-dim (bb [[1]] [[1]]) # matrix of longitude/latitude pairs
102102+bb <- getbb ("Milano, Italy", format_out = "polygon")
103103+# A polygon and a multipolygon:
104104+str (bb) # matrices of longitude/latitude pairs
105105+102106bb_sf <- getbb ("kathmandu", format_out = "sf_polygon")
107107+bb_sf
103108# sf:::plot.sf(bb_sf) # can be plotted if sf is installed
104109getbb ("london", format_out = "sf_polygon")
105105-getbb ("accra", format_out = "sf_polygon") # rectangular bb
106110107107-area <- getbb ("València", format_out = "osm_type_id")
111111+getbb ("València", format_out = "osm_type_id")
108112# select multiple areas with format_out = "osm_type_id"
109113areas <- getbb ("València", format_out = "data.frame")
110114bbox_to_string (areas [areas$osm_type != "node", ])