add Flowmaps, layer tuner, time control#205
Conversation
- Updated flowmap-gl vendor manifest with new SHA256 and byte size. - Revised documentation for add_flowmap to clarify parameters and added new options for flow_dark_mode and flow_blend. - Implemented flow_blend parameter to control CSS blending modes, with recommendations for usage based on map styles. - Added tests to validate flow_blend behavior, including auto-resolution based on map style and error handling for invalid inputs. - Enhanced is_dark_style utility to classify basemaps correctly and added tests for various styles.
- Introduced lil-gui library for enhanced UI controls in the map visualization. - Updated mapboxgl.js and maplibregl.js to initialize the layer tuner if enabled. - Added new add_layer_tuner function to allow real-time customization of map layers. - Updated documentation for add_flowmap and added new documentation for add_layer_tuner. - Included lil-gui as a dependency in both mapboxgl.yaml and maplibregl.yaml.
- Implemented time control functionality in mapboxgl and maplibregl widgets. - Added support for flowmap filter and settings updates, including new parameters for temporal filtering. - Enhanced documentation for new functions: add_time_control, set_flowmap_filter, and set_flowmap_settings. - Updated tests to validate new features and ensure proper functionality of flowmap settings. - Included dependencies for d3 and time-control in YAML configuration files.
- Added support for multiple selected time ranges in time control, allowing users to shift-drag to select additional ranges. - Updated time control UI with improved collapse functionality and accessibility features. - Introduced tooltip configuration options for flowmap, enabling customizable tooltips for locations and flows. - Enhanced flowmap serialization to include tooltip settings and validate new parameters. - Updated documentation to reflect new parameters and tooltip options. - Added tests for time control and flowmap tooltip functionalities to ensure proper serialization and behavior.
|
Time filter 001.mp4maplibre(
style = carto_style("dark-matter"),
center = c(-73.58, 45.50),
zoom = 11,
projection = "mercator"
) |>
add_flowmap(
id = "bixi-rides",
locations = bixi_locations,
flows = bixi_flows,
flow_time_column = "time",
flow_color_scheme = "Teal",
flow_dark_mode = TRUE
) |>
add_time_control(
data = bixi_flows,
time_column = "time",
time_interval = "hour",
title = "BIXI Montréal Rides"
) |
|
Compare view + flows + layer tuner 002.mp4library(mapgl)
mapbox_token <- Sys.getenv("MAPBOX_API_TOKEN")
if (!requireNamespace("flowmapper", quietly = TRUE)) {
stop("The flowmapper package is required for flowmap ordering artifacts.")
}
ordering_dir <- "private/flowmap-manual/ordering"
unlink(ordering_dir, recursive = TRUE, force = TRUE)
dir.create(ordering_dir, recursive = TRUE, showWarnings = FALSE)
utils::data("cantons", package = "flowmapper")
utils::data("CH_migration_data", package = "flowmapper")
if (is.na(sf::st_crs(cantons))) {
sf::st_crs(cantons) <- 3857
}
canton_points <- suppressWarnings(sf::st_point_on_surface(cantons)) |>
sf::st_transform(4326)
coords <- sf::st_coordinates(canton_points)
locations <- data.frame(
id = canton_points$NAME_1,
name = canton_points$NAME_1,
lat = coords[, "Y"],
lon = coords[, "X"]
)
flows <- rbind(
data.frame(
origin = CH_migration_data$id_a,
dest = CH_migration_data$id_b,
count = CH_migration_data$flow_ab
),
data.frame(
origin = CH_migration_data$id_b,
dest = CH_migration_data$id_a,
count = CH_migration_data$flow_ba
)
)
flows <- flows[flows$count > 0, , drop = FALSE]
set.seed(20260529)
review_point_rows <- sample(seq_len(nrow(locations)), 8)
review_points <- locations[review_point_rows, c("id", "lat", "lon")]
review_points$name <- paste("Review point", seq_len(nrow(review_points)))
review_points$lat <- review_points$lat +
stats::runif(nrow(review_points), -0.035, 0.035)
review_points$lon <- review_points$lon +
stats::runif(nrow(review_points), -0.05, 0.05)
review_points <- sf::st_as_sf(
review_points,
coords = c("lon", "lat"),
crs = 4326
)
# MapLibre: flowmap is added last, so it renders above the review points.
# mapboxgl(
# style = mapbox_style("dark"),
# center = c(8.25, 46.85),
# zoom = 7,
# projection = "mercator",
# access_token = mapbox_token
# ) |>
m1 <- maplibre(
# style = carto_style("positron"),
style = carto_style("dark-matter"),
center = c(8.25, 46.85),
zoom = 7,
projection = "mercator"
) |>
add_layer_tuner() |>
add_fill_layer(
id = "cantons-fill",
source = cantons,
fill_color = "#7d48d4",
fill_opacity = 0.3,
fill_outline_color = "#d7e8ec"
) |>
add_flowmap(
id = "manual-flowmap",
locations = locations,
flows = flows
# flow_color_scheme = "Teal",
# flow_dark_mode = "auto",
# flow_blend = "auto"
# flow_opacity = 0.9
) |>
add_circle_layer(
id = "review-points",
source = review_points,
circle_color = "#ffcf5a",
circle_radius = 6,
circle_stroke_color = "#2b2f33",
circle_stroke_width = 1.5
) |>
add_layers_control(
position = "top-right",
layers = list(
"Canton polygons" = "cantons-fill",
"Review points" = "review-points",
"Migration flowmap" = "manual-flowmap"
)
)
m2 <- maplibre(
style = carto_style("positron"),
# style = carto_style("dark-matter"),
center = c(8.25, 46.85),
zoom = 7,
projection = "mercator"
) |>
add_layer_tuner() |>
add_fill_layer(
id = "cantons-fill",
source = cantons,
fill_color = "#7d48d4",
fill_opacity = 0.3,
fill_outline_color = "#d7e8ec"
) |>
add_flowmap(
id = "manual-flowmap",
locations = locations,
flows = flows
# flow_color_scheme = "Teal",
# flow_dark_mode = "auto",
# flow_blend = "auto"
# flow_opacity = 0.9
) |>
add_circle_layer(
id = "review-points",
source = review_points,
circle_color = "#ffcf5a",
circle_radius = 6,
circle_stroke_color = "#2b2f33",
circle_stroke_width = 1.5,
tooltip = "id"
) |>
add_layers_control(
position = "top-right",
layers = list(
"Canton polygons" = "cantons-fill",
"Review points" = "review-points",
"Migration flowmap" = "manual-flowmap"
)
)
compare(m1, m2)
|
|
Layer tuner with flows + copy code of adjusted map when done 003.mp4maplibre(
style = carto_style("dark-matter"),
center = c(-73.58, 45.50),
zoom = 11,
projection = "mercator"
) |>
add_flowmap(
id = "bixi-rides",
locations = bixi_locations,
flows = bixi_flows,
flow_time_column = "time",
flow_color_scheme = "Teal",
flow_dark_mode = TRUE
) |>
add_layer_tuner(position = "top-right", width = 320) |
|
Awesome just in time for workshop! |
|
The new functionality worked great for me and students at Mobile Tartu 2026, some results can be seen in the course website and slides made by the students who made great use of it: https://tdscience.github.io/tartu26/ |
|
@e-kotov - I'm closing this in favor of #207 as we've talked about. I tried to honor your contributions as best as possible; flowmaps land pretty much as-is (just a couple bug fixes in there), the time control is incorporated into an Your examples will largely work as-written with the changes. The Bixi dataset is also now included in the package and will go into a new vignette that showcases your contributions. The layer tuner is deferred for a separate PR / future development. |
|
@walkerke awesome! |
Co-authored-by: Egor Kotov <kotov.egor@gmail.com>
This PR adds
Videos with demo of what it can do will follow.
Will partially close #37 (e.g. no arcs from deck.gl, lost more work to improve flows experience, but the baseline is solid)
FYI @Robinlovelace @ilyabo @JohMast