Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ Suggests:
leaflet,
withr
Config/testthat/edition: 3
Config/roxygen2/version: 8.0.0
Config/roxygen2/version: 8.1.0
16 changes: 15 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
# mapgl (development version)

* Update Mapbox GL JS to v3.31.0-rc.1, which brings a much smaller memory footprint for large GeoJSON sources.
* Donut count labels use the same overlap and collision-placement rules as their icons, preventing collision-hidden numbers inside visible donuts for native and precomputed clusters. Ordinary circle-cluster labels are unchanged.

* Interactive categorical legends preserve hexagon and custom SVG patch shapes when toggling categories or resetting filters.

* Clusters can be drawn as donut charts showing each cluster's category mix with `cluster_options(donut_column = )`, for live GeoJSON clustering and pre-clustered vector tiles.

* `add_layer()` gains a `metadata` argument.

* Abbreviated cluster count labels now switch to millions and billions (e.g. "1.7M" or "1.5B" instead of "1735k" or "1500M").

* Clustered layers now render in `compare()` views, and cluster circle paint options (`circle_opacity`, strokes, blur) now reach maps updated through a Shiny proxy.

* Update Mapbox GL JS to v3.31.0, which brings a much smaller memory footprint for large GeoJSON sources.

* Raster PMTiles sources on Mapbox maps now use Mapbox GL JS's native PMTiles support. The custom `pmtile-source` plugin, which stopped loading on Mapbox GL JS v3.29+, has been removed.

* Popups and tooltips support conditional logic evaluated per feature at render time, with new builders `if_else_expr()`, `case_expr()`, `coalesce_expr()`, `has_column()`, `is_blank()`, and `html_escape_expr()` that compose with `concat()`, `get_column()`, and `number_format()`. Useful for remote sources like PMTiles where popup columns can't be precomputed. See `?conditional_expressions`.

Expand Down
1,170 changes: 914 additions & 256 deletions R/layers.R

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions R/sources.R
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ add_video_source <- function(map, id, urls, coordinates) {
#' @param map A map object created by the `mapboxgl` or `maplibre` function.
#' @param id A unique ID for the source.
#' @param url A URL pointing to the PMTiles archive.
#' @param source_type The source type for MapLibre maps. Either "vector" (default) or "raster".
#' @param source_type The source type. Either "vector" (default) or "raster".
#' @param maxzoom Only used when source_type is "raster". The maximum zoom level for the PMTiles source. Defaults to 22.
#' @param tilesize Only used when source_type is "raster". The size of the tiles in the PMTiles source. Defaults to 256.
#' @param promote_id An optional property name to use as the feature ID. This is required for hover effects on vector sources.
Expand Down Expand Up @@ -796,16 +796,17 @@ add_pmtiles_source <- function(
inherits(map, "mapboxgl_compare_proxy")

if (is_mapbox) {
# Mapbox GL JS v3.21.0+ has native PMTiles support for vector tiles
# Mapbox GL JS detects .pmtiles URLs natively via its TileProvider API
# (vector since v3.21.0, raster since v3.23.0)
if (source_type == "raster") {
# Raster PMTiles still require the custom source type
source <- list(
id = id,
type = "pmtile-source",
url = url
type = "raster",
url = url,
tileSize = tilesize,
maxzoom = maxzoom
)
} else {
# Vector PMTiles use native TileProvider API (auto-detects .pmtiles URLs)
source <- list(
id = id,
type = "vector",
Expand Down
35 changes: 4 additions & 31 deletions inst/htmlwidgets/lib/legend-interactivity/legend-interactivity.js
Original file line number Diff line number Diff line change
Expand Up @@ -454,29 +454,13 @@ function initCategoricalLegend(map, mapId, legendElement, filterColumn, config)
interactiveState.filterColumn = filterColumn;
});

// Store original colors for each item
var originalColors = {};

// Add click handlers to each item
items.forEach(function (item, idx) {
var displayValue = item.getAttribute("data-value") || String(displayValues[idx]);
item.setAttribute("data-value", displayValue);
item.setAttribute("data-index", idx);
item.setAttribute("data-enabled", "true");

// Store original color - try multiple selectors for different patch types
var colorSpan = item.querySelector(".legend-color") ||
item.querySelector(".legend-shape-svg") ||
item.querySelector(".legend-shape-custom");
if (colorSpan) {
originalColors[idx] = colorSpan.style.backgroundColor ||
colorSpan.getAttribute("fill") ||
config.colors[idx];
} else {
// Fallback to config colors
originalColors[idx] = config.colors[idx];
}

item.addEventListener("click", function (e) {
e.preventDefault();
e.stopPropagation();
Expand All @@ -486,18 +470,13 @@ function initCategoricalLegend(map, mapId, legendElement, filterColumn, config)

item.setAttribute("data-enabled", String(newState));

// Disabled opacity/grayscale is handled by the shared CSS. Leave
// patch paints untouched: an SVG background colors its rectangular
// viewport, not its polygon/path, and corrupts hex/custom patches.
if (newState) {
enabledIndices.add(idx);
// Restore original color
if (colorSpan) {
colorSpan.style.backgroundColor = originalColors[idx];
}
} else {
enabledIndices.delete(idx);
// Grey out
if (colorSpan) {
colorSpan.style.backgroundColor = "#cccccc";
}
}

// Apply filter to all associated layers
Expand Down Expand Up @@ -550,14 +529,8 @@ function initCategoricalLegend(map, mapId, legendElement, filterColumn, config)

// Add reset button
addResetButton(legendElement, function () {
items.forEach(function (item, i) {
items.forEach(function (item) {
item.setAttribute("data-enabled", "true");
var colorSpan = item.querySelector(".legend-color") ||
item.querySelector(".legend-shape-svg") ||
item.querySelector(".legend-shape-custom");
if (colorSpan && originalColors[i] !== undefined) {
colorSpan.style.backgroundColor = originalColors[i];
}
});
enabledIndices.clear();
for (var i = 0; i < numCategories; i++) {
Expand Down
Loading
Loading