Leaflet: Polygon plotting in static mapping packages not plotting in leaflet

Created on 29 May 2019  路  14Comments  路  Source: rstudio/leaflet

I ran the following reproducible lines of code and got this:

library(sf)
library(leaflet)
library(osmdata)

roundhay_park_all = opq("leeds") %>% 
  add_osm_feature(opq = ., key = "name", value = "Roundhay Park") %>% 
  osmdata_sf()

roundhay_park_multipoly = roundhay_park_all$osm_multipolygons
roundhay_park_poly = roundhay_park_multipoly %>% 
  st_cast("POLYGON")

leaflet() %>% 
  addTiles() %>% 
  addPolygons(data = roundhay_park_poly)

image

I expected something like this (the shape of Roundhay Park):

plot(roundhay_park_poly$geometry)

image

Most helpful comment

Talking with @jcheng5, we'll make a fix in leaflet. Thank you for the quick explorations and motivations, everyone!

All 14 comments

I noticed related issue https://github.com/rstudio/leaflet/issues/595 but removing the polygon names does not seem to resolve the issue, as illustrated in this extended example (see also https://github.com/mtennekes/tmap/issues/310 for what I think is a downstream impact of this):

library(sf)
library(leaflet)
library(osmdata)

roundhay_park_all = opq("leeds") %>% 
  add_osm_feature(opq = ., key = "name", value = "Roundhay Park") %>% 
  osmdata_sf()

roundhay_park_multipoly = roundhay_park_all$osm_multipolygons
roundhay_park_poly = roundhay_park_multipoly %>% 
  st_cast("POLYGON")

leaflet() %>% 
  addTiles() %>% 
  addPolygons(data = roundhay_park_poly)
names(roundhay_park_poly$geometry) = NULL
leaflet() %>% 
  addTiles() %>% 
  addPolygons(data = roundhay_park_poly)

# works with sf
plot(roundhay_park_poly$geometry)

I wonder if it's related to this: https://github.com/rstudio/leaflet/issues/593

I believe this is strongly related to #595 (and mtennekes/tmap#310 is probably related).

This is caused by named geometry. If we look at the structure of the geometry object, it has a name (40154170-607395081-607395084).

鉂澂 roundhay_park_multipoly$geometry %>% str()
sfc_MULTIPOLYGON of length 1; first list element: List of 1
 $ :List of 1
  ..$ 40154170-607395081-607395084: num [1:119, 1:2] -1.5 -1.5 -1.5 -1.5 -1.49 ...
  .. ..- attr(*, "dimnames")=List of 2
  .. .. ..$ : chr [1:119] "2841764957" "3469911" "3469939" "10456198" ...
  .. .. ..$ : chr [1:2] "lat" "lon"
 - attr(*, "class")= chr [1:3] "XY" "MULTIPOLYGON" "sfg"

If we remove it, everything works as expected.

I'd like to push back on sf on this issue. From my minimal understanding, the geometry objects should all be unnamed.

library(sf)
#> Linking to GEOS 3.6.1, GDAL 2.1.3, PROJ 4.9.3
library(leaflet)
library(osmdata)
#> Registered S3 method overwritten by 'rvest':
#>   method            from
#>   read_xml.response xml2
#> Data (c) OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright

roundhay_park_all = opq("leeds") %>% 
  add_osm_feature(opq = ., key = "name", value = "Roundhay Park") %>% 
  osmdata_sf()

roundhay_park_multipoly = roundhay_park_all$osm_multipolygons

rpp <- roundhay_park_multipoly
names(rpp$geometry[[1]][[1]]) <- NULL
leaflet() %>%
  addTiles() %>%
  addPolygons(data = rpp)

Created on 2019-05-29 by the reprex package (v0.2.1)

Thanks for the quickfire response @schloerke :world_map: :rocket: Closing this issue but the can of worms remains open. For clarity, what do you mean by

I'd like to push back on sf on this issue

If there are changes in sf that you think would make this easier to resolve I imagine @edzer would be interested and I'd be up for helping out there. The only thing that comes to mind would be a function like st_remove_geometry_names() or perhaps making names(obj$geometry) strip names recursively, to put it crudely.

@jcheng5 What's the main idea for geometry items from sf? They should have no names whatsoever, correct? Or should we be recursively removing them?

Reopening until it is decided where the fix should take place

What is the culprit here? The dimnames attribute of the sfc_column elements or the fact that the list itself has names? If the former, I think this is already solved in the latest CRAN version of sf. See https://github.com/r-spatial/sf/issues/909.
Regarding the fact that the list itself is named, my guess is that this is originating somewhere in package osmdata. But only @mpadge would know for sure

I'm using

 sf          * 0.7-4      2019-04-25 [1] CRAN (R 3.6.0)                     
 osmdata     * 0.1.0      2019-04-25 [1] CRAN (R 3.6.0)                     

Yeah, as @tim-salabim well knows, this all boils down to osmdata "pretending" to return "standard" sf objects, yet actually naming both (1) all list items; and (2) all rows within the numeric matrices defining coordinates. It's a trick to enable 'sf' to be used to represent complex hierarchical inter-relationships between objects, and thus far only has this documented negative side effect. osmdata is to the best of my knowledge the only package that (mis-)treats sf like this; all and any other sources for sf data will conform. Regardless, removing the names is very trivial, as is checking for their presence, so perhaps not too onerous to implement on the leaflet side?

As a comparison: deck.gl via mapdeck deals seamlessly with this, so perhaps good motivation for leaflet to indeed handle this case?

@tim-salabim Correct! I remember that issue as well. This seems different. The named list (with name 40154170-607395081-607395084) is being returned in osmdata_sf().

library(sf)
#> Linking to GEOS 3.6.1, GDAL 2.1.3, PROJ 4.9.3
library(leaflet)
library(osmdata)
#> Registered S3 method overwritten by 'rvest':
#>   method            from
#>   read_xml.response xml2
#> Data (c) OpenStreetMap contributors, ODbL 1.0. http://www.openstreetmap.org/copyright

opq("leeds") %>% 
  add_osm_feature(opq = ., key = "name", value = "Roundhay Park") %>% 
  osmdata_sf() %>%
  str()
#> List of 8
#>  $ bbox             : chr "53.6989675,-1.8004214,53.9458715,-1.2903516"
#>  $ overpass_call    : chr "[out:xml][timeout:25];\n(\n node  [\"name\"=\"Roundhay Park\"] (53.6989675,-1.8004214,53.9458715,-1.2903516);\n"| __truncated__
#>  $ meta             :List of 3
#>   ..$ timestamp       : chr "[ Wed 4 May 2019 20:53:02 ]"
#>   ..$ OSM_version     : chr "0.6"
#>   ..$ overpass_version: chr "Overpass API 0.7.55.7 8b86ff77"
#>  $ osm_points       :Classes 'sf' and 'data.frame':  121 obs. of  25 variables:
#>   ..$ osm_id                : chr [1:121] "3469911" "3469939" "3470030" "3470343" ...
#>   ..$ name                  : chr [1:121] NA NA NA NA ...
#>   ..$ barrier               : chr [1:121] NA NA NA NA ...
#>   ..$ bench                 : chr [1:121] NA NA NA NA ...
#>   ..$ description           : chr [1:121] NA NA NA NA ...
#>   ..$ highway               : chr [1:121] NA NA NA NA ...
#>   ..$ information           : chr [1:121] NA NA NA NA ...
#>   ..$ kerb                  : chr [1:121] NA NA NA NA ...
#>   ..$ layby                 : chr [1:121] NA NA NA NA ...
#>   ..$ local_ref             : chr [1:121] NA NA NA NA ...
#>   ..$ naptan.AtcoCode       : chr [1:121] NA NA NA NA ...
#>   ..$ naptan.Bearing        : chr [1:121] NA NA NA NA ...
#>   ..$ naptan.CommonName     : chr [1:121] NA NA NA NA ...
#>   ..$ naptan.Crossing       : chr [1:121] NA NA NA NA ...
#>   ..$ naptan.Indicator      : chr [1:121] NA NA NA NA ...
#>   ..$ naptan.Landmark       : chr [1:121] NA NA NA NA ...
#>   ..$ naptan.Notes          : chr [1:121] NA NA NA NA ...
#>   ..$ naptan.PlusbusZoneRef : chr [1:121] NA NA NA NA ...
#>   ..$ naptan.ShortCommonName: chr [1:121] NA NA NA NA ...
#>   ..$ naptan.Street         : chr [1:121] NA NA NA NA ...
#>   ..$ naptan.verified       : chr [1:121] NA NA NA NA ...
#>   ..$ shelter               : chr [1:121] NA NA NA NA ...
#>   ..$ source                : chr [1:121] NA NA NA NA ...
#>   ..$ tourism               : chr [1:121] NA NA NA NA ...
#>   ..$ geometry              :sfc_POINT of length 121; first list element:  'XY' num [1:2] -1.5 53.8
#>   ..- attr(*, "sf_column")= chr "geometry"
#>   ..- attr(*, "agr")= Factor w/ 3 levels "constant","aggregate",..: NA NA NA NA NA NA NA NA NA NA ...
#>   .. ..- attr(*, "names")= chr [1:24] "osm_id" "name" "barrier" "bench" ...
#>  $ osm_lines        :Classes 'sf' and 'data.frame':  3 obs. of  2 variables:
#>   ..$ wikidata: chr [1:3] "Q3305358" NA NA
#>   ..$ geometry:sfc_LINESTRING of length 3; first list element:  'XY' num [1:64, 1:2] -1.5 -1.5 -1.5 -1.5 -1.49 ...
#>   .. ..- attr(*, "dimnames")=List of 2
#>   .. .. ..$ : chr [1:64] "2841764957" "3469911" "3469939" "10456198" ...
#>   .. .. ..$ : chr [1:2] "lon" "lat"
#>   ..- attr(*, "sf_column")= chr "geometry"
#>   ..- attr(*, "agr")= Factor w/ 3 levels "constant","aggregate",..: NA
#>   .. ..- attr(*, "names")= chr "wikidata"
#>  $ osm_polygons     : NULL
#>  $ osm_multilines   : NULL
#>  $ osm_multipolygons:Classes 'sf' and 'data.frame':  1 obs. of  12 variables:
#>   ..$ osm_id             : chr "8444263"
#>   ..$ name               : chr "Roundhay Park"
#>   ..$ fee                : chr "no"
#>   ..$ leisure            : chr "park"
#>   ..$ naptan.StopAreaCode: chr ""
#>   ..$ naptan.StopAreaType: chr ""
#>   ..$ naptan.verified    : chr ""
#>   ..$ site               : chr ""
#>   ..$ source             : chr "OS_OpenData_StreetView"
#>   ..$ type               : chr "multipolygon"
#>   ..$ website            : chr "https://www.roundhaypark.org.uk/"
#>   ..$ geometry           :sfc_MULTIPOLYGON of length 1; first list element: List of 1
#>   .. ..$ :List of 1
#>   .. .. ..$ 40154170-607395081-607395084: num [1:119, 1:2] -1.5 -1.5 -1.5 -1.5 -1.49 ...
#>   .. .. .. ..- attr(*, "dimnames")=List of 2
#>   .. .. .. .. ..$ : chr [1:119] "2841764957" "3469911" "3469939" "10456198" ...
#>   .. .. .. .. ..$ : chr [1:2] "lat" "lon"
#>   .. ..- attr(*, "class")= chr [1:3] "XY" "MULTIPOLYGON" "sfg"
#>   ..- attr(*, "sf_column")= chr "geometry"
#>   ..- attr(*, "agr")= Factor w/ 3 levels "constant","aggregate",..: NA NA NA NA NA NA NA NA NA NA ...
#>   .. ..- attr(*, "names")= chr [1:11] "osm_id" "name" "fee" "leisure" ...
#>  - attr(*, "class")= chr [1:3] "list" "osmdata" "osmdata_sf"

Created on 2019-05-29 by the reprex package (v0.2.1)

Talking with @jcheng5, we'll make a fix in leaflet. Thank you for the quick explorations and motivations, everyone!

Thanks for the quickfire response @schloerke and all, great example of collaboration across projects to make open source software better for all. As you know, many packages and people depend on leaflet() downstream so great to hear you're on it! Any way I can help (e.g. testing a fix on a dev branch) let me know!

Update on this: if there are multiple polygons with names, stripping the names with the following command before plotting works:

for(i in seq(nrow(parks))) {
  names(parks$geometry[i][[1]]) = NULL 
}

A temporary solution could be to add a check for named polygons before plotting and create a warning if there are any...

This came up recently again in mapview where I've learnt that @mpadge already has a function to unname things - unname_osmdata_sf() - so that users can do this before passing data to leaflet etc... See https://github.com/r-spatial/mapview/issues/367#issuecomment-812504450

Was this page helpful?
0 / 5 - 0 ratings