Skip to content

Commit 9750fa2

Browse files
committed
route sf::st_bbox to mapbox's fitBounds method
1 parent 078736c commit 9750fa2

File tree

4 files changed

+57
-16
lines changed

4 files changed

+57
-16
lines changed

R/plotly_build.R

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ plotly_build.plotly <- function(p, registerFrames = TRUE) {
117117

118118
# set special defaults for sf
119119
if (inherits(dat, "sf")) {
120+
# TODO:
121+
# (1) check/change the crs? https://github.com/rstudio/leaflet/blob/d489e2cd/R/normalize-sf.R#L94-L113
122+
# (2) One trace/layer can sometime map to multiple traces (e.g., an sf object with points and lines)
123+
# (3) st_cast() if a geometry collection?
124+
x$`_bbox` <- sf::st_bbox(dat)
120125
dat <- fortify_sf(dat)
121126
x$x <- ~x
122127
x$y <- ~y
@@ -333,6 +338,10 @@ plotly_build.plotly <- function(p, registerFrames = TRUE) {
333338
tr
334339
})
335340

341+
# Compute sensible bounding boxes for each mapbox subplot
342+
# TODO: could/should we do this for plot_ly()/plot_geo() as well?
343+
p <- mapbox_fit_bounds(p)
344+
336345
# polar charts don't like null width/height keys
337346
if (is.null(p$x$layout[["height"]])) p$x$layout[["height"]] <- NULL
338347
if (is.null(p$x$layout[["width"]])) p$x$layout[["width"]] <- NULL

R/utils.R

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,31 @@ mapbox_token <- function() {
209209
token
210210
}
211211

212+
mapbox_fit_bounds <- function(p) {
213+
mapboxIDs <- grep("^mapbox", sapply(p$x$data, "[[", "subplot"), value = TRUE)
214+
for (id in mapboxIDs) {
215+
bboxes <- lapply(p$x$data, function(tr) if (identical(id, tr$subplot)) tr[["_bbox"]])
216+
# intentionally an array of numbers in [west, south, east, north] order
217+
# https://www.mapbox.com/mapbox-gl-js/api/#lnglatboundslike
218+
p$x$layout[[id]]$`_fitBounds` <- list(
219+
bounds = c(
220+
min(unlist(lapply(bboxes, "[[", "xmin")), na.rm = TRUE),
221+
min(unlist(lapply(bboxes, "[[", "ymin")), na.rm = TRUE),
222+
max(unlist(lapply(bboxes, "[[", "xmax")), na.rm = TRUE),
223+
max(unlist(lapply(bboxes, "[[", "ymax")), na.rm = TRUE)
224+
),
225+
options = list(
226+
padding = 10,
227+
linear = FALSE,
228+
# NOTE TO SELF: can do something like this to customize easing
229+
# easing = htmlwidgets::JS("function(x) { return 1; }"),
230+
offset = c(0, 0)
231+
)
232+
)
233+
}
234+
p
235+
}
236+
212237
# rename attrs (unevaluated arguments) from geo locations (lat/lon) to cartesian
213238
geo2cartesian <- function(p) {
214239
p$x$attrs <- lapply(p$x$attrs, function(tr) {

demo/sf-data-scattermapbox.R

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,15 @@ library(sf)
22
library(plotly)
33

44
nc <- st_read(system.file("shape/nc.shp", package = "sf"), quiet = TRUE)
5-
bb <- st_bbox(nc)
65

7-
# TODO: make this more obvious and automatic
8-
plot_mapbox(
9-
data = group2NA(nc),
10-
x = ~x,
11-
y = ~y,
6+
p1 <- plot_mapbox(
7+
nc,
128
mode = "lines",
139
fillcolor = "green",
1410
fill = "toself",
1511
line = list(size = 0.01, color = "black")
16-
) %>%
17-
layout(
18-
mapbox = list(
19-
center = list(
20-
lat = mean(bb[c("ymin", "ymax")]),
21-
lon = mean(bb[c("xmin", "xmax")])
22-
),
23-
zoom = 6
24-
)
25-
)
12+
)
13+
14+
subplot(p1, p1)
15+
16+
# TODO: dropdown button for changing the baselayer

inst/htmlwidgets/plotly.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,22 @@ HTMLWidgets.widget({
192192
Plotly[msg.method].apply(null, args);
193193
});
194194
}
195+
196+
// plotly's mapbox API doesn't currently support setting bounding boxes
197+
// https://www.mapbox.com/mapbox-gl-js/example/fitbounds/
198+
// so we do this manually...
199+
var mapboxIDs = graphDiv._fullLayout._subplots.mapbox;
200+
for (var i = 0; i < mapboxIDs.length; i++) {
201+
var id = mapboxIDs[i];
202+
var mapOpts = x.layout[id] || {};
203+
var args = mapOpts._fitBounds || {}
204+
if (!args) {
205+
continue;
206+
}
207+
var mapObj = graphDiv._fullLayout[id]._subplot.map;
208+
mapObj.fitBounds(args.bounds, args.options);
209+
}
210+
195211
});
196212

197213
// Attach attributes (e.g., "key", "z") to plotly event data

0 commit comments

Comments
 (0)