From 1a78e532821ff69df855ea3bccd1bcfa4982b4cb Mon Sep 17 00:00:00 2001 From: Cidree Date: Fri, 26 Jun 2026 21:51:22 +0200 Subject: [PATCH] fix: pass simplify=FALSE to sf_geojson for geometry-only sf objects (#212) geojsonsf::sf_geojson() with simplify=TRUE (default) atomizes sf objects that have no non-geometry columns into a vector of individual geometry strings rather than a FeatureCollection. htmlwidgets cannot serialize a length-n json-class vector, causing a serialization error. Passing simplify=FALSE consistently returns a FeatureCollection regardless of whether the sf has property columns. Co-Authored-By: Claude Sonnet 4.6 --- DESCRIPTION | 2 +- NEWS.md | 4 ++++ R/layers.R | 2 +- R/shiny.R | 2 +- R/sources.R | 2 +- R/turf.R | 30 +++++++++++++++--------------- man/mapgl-package.Rd | 5 +++++ 7 files changed, 28 insertions(+), 19 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 863b4061..7e54036f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -16,7 +16,6 @@ License: MIT + file LICENSE Encoding: UTF-8 LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.3 Depends: R (>= 4.1.0) Imports: @@ -42,3 +41,4 @@ Suggests: usethis, leaflet Config/testthat/edition: 3 +Config/roxygen2/version: 8.0.0 diff --git a/NEWS.md b/NEWS.md index fb8302aa..9a20923c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# mapgl (development version) + +* Fixed a bug where passing a geometry-only `sf` object (no non-geometry columns) as a layer source caused a serialization error. `geojsonsf::sf_geojson()` simplifies property-less sf objects into a vector of individual geometry strings rather than a FeatureCollection, which `htmlwidgets` cannot serialize. All internal calls now use `simplify = FALSE` to consistently return a FeatureCollection (#212). + # mapgl 0.5.0 * Tooltips and popups now accept `{brace}` templates package-wide. Any layer's `tooltip`/`popup` (and `set_tooltip()`/`set_popup()`) can take a glue-style template such as `"{name}: {population}"`, in addition to the existing column name and `concat()`/`number_format()` expression forms; substituted values are HTML-escaped. diff --git a/R/layers.R b/R/layers.R index 29552868..52bbc8b9 100644 --- a/R/layers.R +++ b/R/layers.R @@ -96,7 +96,7 @@ add_layer <- function( if (sf::st_crs(source) != 4326) { source <- sf::st_transform(source, crs = 4326) } - geojson <- geojsonsf::sf_geojson(source) + geojson <- geojsonsf::sf_geojson(source, simplify = FALSE) source <- list( type = "geojson", data = geojson, diff --git a/R/shiny.R b/R/shiny.R index ba628395..106c0ae7 100644 --- a/R/shiny.R +++ b/R/shiny.R @@ -696,7 +696,7 @@ set_source <- function(map, layer_id = NULL, source, layer = NULL) { source <- geojsonsf::sf_geojson(sf::st_transform( source, crs = 4326 - )) + ), simplify = FALSE) } if ( diff --git a/R/sources.R b/R/sources.R index 8e7e5a56..3c2c6d3a 100644 --- a/R/sources.R +++ b/R/sources.R @@ -16,7 +16,7 @@ add_source <- function(map, id, data, ...) { if (sf::st_crs(data) != 4326) { data <- sf::st_transform(data, crs = 4326) } - geojson <- geojsonsf::sf_geojson(data) + geojson <- geojsonsf::sf_geojson(data, simplify = FALSE) } else if (is.character(data) && grepl("^http", data)) { geojson <- data } else { diff --git a/R/turf.R b/R/turf.R index b3247d77..d47f2ff0 100644 --- a/R/turf.R +++ b/R/turf.R @@ -66,7 +66,7 @@ turf_buffer <- function( if (!inherits(data, "sf")) { stop("data must be an sf object.") } - geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326)) + geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326), simplify = FALSE) } # Validate coordinates @@ -176,7 +176,7 @@ turf_union <- function( if (!inherits(data, "sf")) { stop("data must be an sf object.") } - geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326)) + geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326), simplify = FALSE) } # Handle proxy objects (Shiny) @@ -287,7 +287,7 @@ turf_intersect <- function( if (!inherits(data, "sf")) { stop("data must be an sf object.") } - geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326)) + geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326), simplify = FALSE) } # Convert sf data_2 to GeoJSON if provided @@ -299,7 +299,7 @@ turf_intersect <- function( geojson_data_2 <- geojsonsf::sf_geojson(sf::st_transform( data_2, crs = 4326 - )) + ), simplify = FALSE) } # Handle proxy objects (Shiny) @@ -414,7 +414,7 @@ turf_difference <- function( if (!inherits(data, "sf")) { stop("data must be an sf object.") } - geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326)) + geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326), simplify = FALSE) } # Convert sf data_2 to GeoJSON if provided @@ -426,7 +426,7 @@ turf_difference <- function( geojson_data_2 <- geojsonsf::sf_geojson(sf::st_transform( data_2, crs = 4326 - )) + ), simplify = FALSE) } # Handle proxy objects (Shiny) @@ -529,7 +529,7 @@ turf_convex_hull <- function( if (!inherits(data, "sf")) { stop("data must be an sf object.") } - geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326)) + geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326), simplify = FALSE) } # Validate and process coordinates @@ -649,7 +649,7 @@ turf_concave_hull <- function( if (!inherits(data, "sf")) { stop("data must be an sf object.") } - geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326)) + geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326), simplify = FALSE) } # Validate and process coordinates @@ -769,7 +769,7 @@ turf_voronoi <- function( if (!inherits(data, "sf")) { stop("data must be an sf object.") } - geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326)) + geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326), simplify = FALSE) } # Process bbox parameter @@ -931,7 +931,7 @@ turf_distance <- function( if (!inherits(data, "sf")) { stop("data must be an sf object.") } - geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326)) + geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326), simplify = FALSE) } # Validate coordinates @@ -1011,7 +1011,7 @@ turf_area <- function( if (!inherits(data, "sf")) { stop("data must be an sf object.") } - geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326)) + geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326), simplify = FALSE) } proxy_class <- if (inherits(proxy, "mapboxgl_proxy")) { @@ -1074,7 +1074,7 @@ turf_centroid <- function( if (!inherits(data, "sf")) { stop("data must be an sf object.") } - geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326)) + geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326), simplify = FALSE) } # Validate and process coordinates @@ -1196,7 +1196,7 @@ turf_center_of_mass <- function( if (!inherits(data, "sf")) { stop("data must be an sf object.") } - geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326)) + geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326), simplify = FALSE) } # Validate and process coordinates @@ -1332,7 +1332,7 @@ turf_filter <- function( if (!inherits(data, "sf")) { stop("data must be an sf object.") } - geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326)) + geojson_data <- geojsonsf::sf_geojson(sf::st_transform(data, crs = 4326), simplify = FALSE) } # Convert sf filter_data to GeoJSON if provided @@ -1344,7 +1344,7 @@ turf_filter <- function( geojson_filter_data <- geojsonsf::sf_geojson(sf::st_transform( filter_data, crs = 4326 - )) + ), simplify = FALSE) } # Handle proxy objects (Shiny) diff --git a/man/mapgl-package.Rd b/man/mapgl-package.Rd index 57ec637d..dce4f115 100644 --- a/man/mapgl-package.Rd +++ b/man/mapgl-package.Rd @@ -21,6 +21,11 @@ Useful links: \author{ \strong{Maintainer}: Kyle Walker \email{kyle@walker-data.com} +Authors: +\itemize{ + \item Kyle Walker \email{kyle@walker-data.com} +} + Other contributors: \itemize{ \item Egor Kotov \email{kotov.egor@gmail.com} [contributor]