Skip to content

Commit 49bb49e

Browse files
committed
shiny working, fill_alpha working for 3d polys
1 parent ea7b8a3 commit 49bb49e

5 files changed

Lines changed: 89 additions & 51 deletions

File tree

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Authors@R: c(
77
)
88
Description: The 'tmap' package provides two plotting modes for static and interactive thematic maps. This package extends 'tmap' with two additional modes based on 'Mapbox GL JS' and 'MapLibre GL JS'. These modes feature interactive vector tiles, globe views, and other modern web-mapping capabilities, while maintaining a consistent 'tmap' interface across all plotting modes.
99
License: GPL-3
10-
Version: 0.2-1
10+
Version: 0.3
1111
Encoding: UTF-8
1212
Depends:
1313
R (>= 4.1),

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
version 0.3
2+
- shiny integration
3+
- fill_alpha working for tm_polygons_3d
4+
15
version 0.2.1
26
- fixed minor bug
37

R/mapgl_init.R

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#' @param return.asp return.asp
22
#' @param vp vp
3+
#' @param dg base map widget (internal; \code{NULL} in proxy mode, where the
4+
#' proxy object \code{m} is used as the drawing canvas instead)
35
#' @param m the proxy object from `maplibre_proxy()`/`mapboxgl_proxy()`, supplied in proxy mode
46
#' @export
57
#' @keywords internal
@@ -9,42 +11,56 @@ tmapMapboxInit = function(o, return.asp = FALSE, vp, prx, dg = NULL, m = NULL, .
911
mapgl_init(o = o, return.asp = return.asp, vp = vp, prx = prx,
1012
dg = dg, m = m, e = .TMAP_MAPBOX, ...)
1113
}
12-
1314
#' @export
1415
#' @keywords internal
1516
#' @rdname tmapMapbox
1617
tmapMaplibreInit = function(o, return.asp = FALSE, vp, prx, dg = NULL, m = NULL, ...) {
1718
mapgl_init(o = o, return.asp = return.asp, vp = vp, prx = prx,
1819
dg = dg, m = m, e = .TMAP_MAPLIBRE, ...)
1920
}
20-
21-
2221
mapgl_init = function(o, return.asp, vp, prx, dg = NULL, m = NULL, e, ...) {
2322
if (return.asp) return(1)
24-
2523
per_page = rep(o$ncols * o$nrows, o$npages)
2624
k = o$ncols * o$nrows * o$npages
2725
if (o$n < k) {
2826
per_page[o$npages] = per_page[o$npages] - (k - o$n)
2927
}
30-
3128
proxy = isTRUE(.TMAP$proxy)
29+
if (proxy) {
30+
mode = if (identical(e, .TMAP_MAPBOX)) "mapbox" else "maplibre"
31+
32+
# Legends + controls are rebuilt wholesale on every render, so clear the
33+
# previous ones first (otherwise they stack). clear-all = no id argument.
34+
m = m |>
35+
mapgl::clear_legend() |>
36+
mapgl::clear_controls()
37+
38+
# clear_controls() also removes the navigation / globe controls. On a full
39+
# render those are created in mapgl_shape() (navigation then globe, leaving
40+
# the globe icon on top), but mapgl_shape() early-returns in proxy mode, so
41+
# re-add them here or they vanish after the first update.
42+
#
43+
# In the proxy re-add path the controls stack in the OPPOSITE visual order
44+
# to the initial widget render, so to keep the globe on top (as at start)
45+
# we add globe first, then navigation.
46+
if (mode == "maplibre") m = mapgl::add_globe_control(m)
47+
m = mapgl::add_navigation_control(m, visualize_pitch = TRUE)
48+
}
3249

33-
# proxy mode: draw onto the live map via `m`; otherwise use base widget `dg`
34-
base = if (proxy) m else dg
35-
50+
# layer removal is targeted: only the zindexes named by tm_remove_layer()
3651
if (proxy && length(prx)) {
3752
pane_name = getFromNamespace("pane_name", "tmap")
3853
zres = vapply(prx, function(p) p$zindex, FUN.VALUE = numeric(1))
39-
rm_ids = mapgl_layer_ids(e, pane_name(zres)) # 401 -> "tmap401", matches the keys
54+
rm_ids = mapgl_layer_ids(e, pane_name(zres))
4055
if (length(rm_ids)) {
4156
mapgl::clear_layer(m, rm_ids)
4257
e$layer_zindex[pane_name(zres)] = NULL
4358
}
4459
}
4560

61+
# proxy mode: draw onto the live map via `m`; otherwise use base widget `dg`
62+
base = if (proxy) m else dg
4663
ms = lapply(per_page, function(p) lapply(seq_len(p), function(i) base))
47-
4864
e$ms = ms
4965
e$nrow = o$nrows
5066
e$ncol = o$ncols
@@ -57,22 +73,16 @@ mapgl_init = function(o, return.asp, vp, prx, dg = NULL, m = NULL, e, ...) {
5773
}
5874
NULL
5975
}
60-
61-
62-
6376
#' @param q q
6477
#' @export
6578
#' @keywords internal
6679
#' @rdname tmapMapbox
6780
tmapMapboxAux = function(o, q) {
6881
NULL
6982
}
70-
71-
7283
#' @export
7384
#' @keywords internal
7485
#' @rdname tmapMapbox
7586
tmapMaplibreAux = function(o, q) {
7687
NULL
7788
}
78-

R/mapgl_layers.R

Lines changed: 55 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,18 @@ lty2dash <- function(lty) {
182182
}
183183
}
184184

185+
# ============================================================
186+
# Source id helper
187+
# ============================================================
188+
189+
# Source ids must be unique per render. clear_layer() (used by tm_remove_layer
190+
# in proxy mode) removes *layers* but not their *sources*, so reusing a source
191+
# id on a redraw collides with the still-present stale source: MapLibre keeps
192+
# the old data and the re-added layers recolour from it. .TMAP$stamp is bumped
193+
# by print.tmap() on every render, giving a fresh source id each time. Layer
194+
# ids stay stable (glid-based) so the tm_remove_layer registry keeps matching.
195+
mapgl_srcid <- function(base) sprintf("%s_%.0f", base, .TMAP$stamp * 1e6)
196+
185197
# ============================================================
186198
# mapgl_polygons
187199
# ============================================================
@@ -191,9 +203,6 @@ mapgl_polygons <- function(a, shpTM, dt, pdt, popup.format, hdt, idt, gp,
191203
id, pane, group, glid, o, ..., mode) {
192204

193205
m <- get_mapgl(facet_row, facet_col, facet_page, mode)
194-
message("== mapgl_polygons: class(m) = ", paste(class(m), collapse=", "),
195-
" | pane = ", pane)
196-
197206
rc_text <- frc(facet_row, facet_col)
198207
shp_is_pointer <- inherits(shpTM$shp, "character")
199208

@@ -204,7 +213,7 @@ mapgl_polygons <- function(a, shpTM, dt, pdt, popup.format, hdt, idt, gp,
204213
if (pmtiles_unsupported(shpTM, mode)) return(NULL)
205214

206215
smeta <- shpTM$smeta
207-
srcname <- paste0("layer", pane, "_", .TMAP$stamp)
216+
srcname <- mapgl_srcid(paste0("layer", pane))
208217
layername1 <- paste0(glid, "polygons_fill")
209218
layername2 <- paste0(glid, "polygons_border")
210219
url <- smeta$url
@@ -222,12 +231,12 @@ mapgl_polygons <- function(a, shpTM, dt, pdt, popup.format, hdt, idt, gp,
222231
# so there is no row-level join available at render time.
223232

224233
m |>
225-
mapgl::add_pmtiles_source(id = glid, url = url) |>
226-
mapgl::add_fill_layer(layername1, source = glid,
234+
mapgl::add_pmtiles_source(id = srcname, url = url) |>
235+
mapgl::add_fill_layer(layername1, source = srcname,
227236
source_layer = smeta$layer,
228237
fill_color = aes_f,
229238
fill_opacity = aes_fo) |>
230-
mapgl::add_line_layer(layername2, source = glid,
239+
mapgl::add_line_layer(layername2, source = srcname,
231240
source_layer = smeta$layer,
232241
line_color = aes_c,
233242
line_opacity = aes_co,
@@ -290,7 +299,7 @@ mapgl_polygons <- function(a, shpTM, dt, pdt, popup.format, hdt, idt, gp,
290299
ahp <- attach_hover_popup(shp2, dt, hdt, pdt, idt, popup.format)
291300
shp2 <- ahp$shp2
292301

293-
srcname <- paste0("layer", pane, "_", .TMAP$stamp)
302+
srcname <- mapgl_srcid(paste0("layer", pane))
294303
layername1 <- paste0(glid, "polygons_fill")
295304
layername2 <- paste0(glid, "polygons_border")
296305

@@ -329,7 +338,7 @@ mapgl_polygons_3d <- function(a, shpTM, dt, pdt, popup.format, hdt, idt, gp,
329338
if (pmtiles_unsupported(shpTM, mode)) return(NULL)
330339

331340
smeta <- shpTM$smeta
332-
srcname <- paste0("layer", pane)
341+
srcname <- mapgl_srcid(paste0("layer", pane))
333342
layername1 <- paste0(glid, "polygons_fill")
334343
layername2 <- paste0(glid, "polygons_border")
335344
url <- smeta$url
@@ -416,13 +425,13 @@ mapgl_polygons_3d <- function(a, shpTM, dt, pdt, popup.format, hdt, idt, gp,
416425
)
417426

418427
m |>
419-
mapgl::add_pmtiles_source(id = glid, url = url) |>
420-
mapgl::add_line_layer(layername2, source = glid,
428+
mapgl::add_pmtiles_source(id = srcname, url = url) |>
429+
mapgl::add_line_layer(layername2, source = srcname,
421430
source_layer = smeta$layer,
422431
line_color = aes_c,
423432
line_opacity = aes_co,
424433
line_width = aes_lwd) |>
425-
mapgl::add_fill_extrusion_layer(layername1, source = glid,
434+
mapgl::add_fill_extrusion_layer(layername1, source = srcname,
426435
source_layer = smeta$layer,
427436
fill_extrusion_color = aes_f,
428437
fill_extrusion_base = 0,
@@ -463,12 +472,23 @@ mapgl_polygons_3d <- function(a, shpTM, dt, pdt, popup.format, hdt, idt, gp,
463472
geometry = shp
464473
)
465474

475+
# NOTE on transparency: fill-extrusion layers do NOT support per-feature alpha.
476+
# The GL spec ignores the alpha channel of fill-extrusion-color (and a long-
477+
# standing engine bug renders alpha=0 as solid black), and fill-extrusion-opacity
478+
# is per-LAYER only, not data-driven. So we honour transparency as a single
479+
# layer-wide opacity, taken as the mean of the per-feature fill_alpha values.
480+
fill_ext_opacity <- {
481+
fa <- suppressWarnings(as.numeric(shp2$fill_alpha))
482+
fa <- fa[is.finite(fa)]
483+
if (!length(fa)) 1 else min(max(mean(fa), 0), 1)
484+
}
485+
466486
ahp <- attach_hover_popup(shp2, dt, hdt, pdt, idt, popup.format)
467487
shp2 <- ahp$shp2
468488

469-
srcname <- paste0("layer", pane)
470-
layername1 <- paste0(srcname, "polygons_fill")
471-
layername2 <- paste0(srcname, "polygons_border")
489+
srcname <- mapgl_srcid(paste0("layer", pane))
490+
layername1 <- paste0(glid, "polygons_fill")
491+
layername2 <- paste0(glid, "polygons_border")
472492

473493
if (is.character(a$height.max)) {
474494
is_perc_max <- grepl("%$", a$height.max)
@@ -513,11 +533,12 @@ mapgl_polygons_3d <- function(a, shpTM, dt, pdt, popup.format, hdt, idt, gp,
513533
line_opacity = mapgl::get_column("col_alpha"),
514534
line_width = mapgl::get_column("lwd")) |>
515535
mapgl::add_fill_extrusion_layer(layername1, source = srcname,
516-
fill_extrusion_color = mapgl::get_column("fill"),
517-
fill_extrusion_base = 0,
518-
fill_extrusion_height = mapgl::get_column("height"),
519-
tooltip = ahp$hdt_arg,
520-
popup = ahp$pdt_arg) |>
536+
fill_extrusion_color = mapgl::get_column("fill"),
537+
fill_extrusion_opacity = fill_ext_opacity,
538+
fill_extrusion_base = 0,
539+
fill_extrusion_height = mapgl::get_column("height"),
540+
tooltip = ahp$hdt_arg,
541+
popup = ahp$pdt_arg) |>
521542
assign_mapgl(facet_row, facet_col, facet_page, mode = mode)
522543

523544
mapgl_submit_group(group, c(layername1, layername2), mode, pane)
@@ -539,16 +560,16 @@ mapgl_lines <- function(a, shpTM, dt, pdt, popup.format, hdt, idt, gp,
539560
if (shp_is_pointer) {
540561
if (pmtiles_unsupported(shpTM, mode)) return(NULL)
541562
smeta <- shpTM$smeta
542-
srcname <- paste0("layer", pane)
563+
srcname <- mapgl_srcid(paste0("layer", pane))
543564
layername1 <- paste0(glid, "lines")
544565
url <- smeta$url
545566
get_pmt_aes <- make_get_pmt_aes(dt)
546567
aes_c <- get_pmt_aes("col")
547568
aes_co <- get_pmt_aes("col_alpha")
548569
aes_lwd <- get_pmt_aes("lwd")
549570
m |>
550-
mapgl::add_pmtiles_source(id = glid, url = url) |>
551-
mapgl::add_line_layer(layername1, source = glid,
571+
mapgl::add_pmtiles_source(id = srcname, url = url) |>
572+
mapgl::add_line_layer(layername1, source = srcname,
552573
source_layer = smeta$layer,
553574
line_color = aes_c,
554575
line_opacity = aes_co,
@@ -594,7 +615,7 @@ mapgl_lines <- function(a, shpTM, dt, pdt, popup.format, hdt, idt, gp,
594615

595616
ahp <- attach_hover_popup(shp2, dt, hdt, pdt, idt, popup.format)
596617
shp2 <- ahp$shp2
597-
srcname <- paste0("layer", pane, "_", .TMAP$stamp)
618+
srcname <- mapgl_srcid(paste0("layer", pane))
598619
layername1 <- paste0(glid, "lines") # was paste0(srcname, "lines") — fixed to match legend
599620

600621
m |>
@@ -629,7 +650,7 @@ mapgl_symbols <- function(a, shpTM, dt, pdt, popup.format, hdt, idt, gp,
629650
if (pmtiles_unsupported(shpTM, mode)) return(NULL)
630651

631652
smeta <- shpTM$smeta
632-
srcname <- paste0("layer", pane, "_", .TMAP$stamp)
653+
srcname <- mapgl_srcid(paste0("layer", pane))
633654
layername1 <- paste0(glid, "symbols_fill")
634655
url <- smeta$url
635656

@@ -646,8 +667,8 @@ mapgl_symbols <- function(a, shpTM, dt, pdt, popup.format, hdt, idt, gp,
646667
if (is.numeric(aes_size)) aes_size <- aes_size * 10
647668

648669
m |>
649-
mapgl::add_pmtiles_source(id = glid, url = url) |>
650-
mapgl::add_circle_layer(layername1, source = glid,
670+
mapgl::add_pmtiles_source(id = srcname, url = url) |>
671+
mapgl::add_circle_layer(layername1, source = srcname,
651672
source_layer = smeta$layer,
652673
circle_color = aes_f,
653674
circle_opacity = aes_fo,
@@ -714,7 +735,7 @@ mapgl_symbols <- function(a, shpTM, dt, pdt, popup.format, hdt, idt, gp,
714735

715736
ahp <- attach_hover_popup(shp2, dt, hdt, pdt, idt, popup.format)
716737
shp2 <- ahp$shp2
717-
srcname <- paste0("layer", pane, "_", .TMAP$stamp)
738+
srcname <- mapgl_srcid(paste0("layer", pane))
718739
layername1 <- paste0(glid, "symbols_fill") # was paste0(srcname, ...) — fixed
719740

720741
m |>
@@ -751,15 +772,15 @@ mapgl_raster <- function(a, shpTM, dt, gp, pdt, popup.format, hdt, idt,
751772
if (pmtiles_unsupported(shpTM, mode)) return(NULL)
752773

753774
smeta <- shpTM$smeta
754-
srcname <- paste0("layer", pane, "_", .TMAP$stamp)
755-
layername1 <- paste0(srcname, "raster")
775+
srcname <- mapgl_srcid(paste0("layer", pane))
776+
layername1 <- paste0(glid, "raster")
756777
url <- smeta$url
757778

758779
m <- get_mapgl(facet_row, facet_col, facet_page, mode = mode)
759780

760781
m |>
761-
mapgl::add_pmtiles_source(id = glid, url = url, source_type = "raster") |>
762-
mapgl::add_raster_layer(layername1, source = glid) |>
782+
mapgl::add_pmtiles_source(id = srcname, url = url, source_type = "raster") |>
783+
mapgl::add_raster_layer(layername1, source = srcname) |>
763784
assign_mapgl(facet_row, facet_col, facet_page, mode = mode)
764785

765786
return(NULL)
@@ -795,8 +816,8 @@ mapgl_raster <- function(a, shpTM, dt, gp, pdt, popup.format, hdt, idt,
795816
if (ext$ymax > 89.9) ext$ymax <- 89
796817
rst2 <- terra::crop(rst, ext)
797818

798-
srcname <- paste0("layer", pane, "_", .TMAP$stamp)
799-
layername1 <- paste0(srcname, "raster")
819+
srcname <- mapgl_srcid(paste0("layer", pane))
820+
layername1 <- paste0(glid, "raster")
800821

801822
m <- get_mapgl(facet_row, facet_col, facet_page, mode = mode)
802823

man/tmapMapbox.Rd

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)