--- title: "Interactive Leaflet Maps and Shiny Apps with vantorr" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Interactive Leaflet Maps and Shiny Apps with vantorr} %\VignetteEngine{knitr::knitr} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ## Overview The `vantorr` package makes it straightforward to stream high-resolution Vantor / Maxar satellite imagery into interactive `leaflet` web maps and `shiny` applications, while connecting map interactions directly to R spatial workflows (`sf`, `terra`, and `stars`). This vignette covers: 1. **Authentication for Browser Tile Streaming**: Setting up a Vantor API key (`VANTOR_API_KEY`). 2. **Interactive `leaflet` Maps**: Adding Vantor Vivid mosaics (`WMTS` / `WMS`), seamline overlays (`WMS` and interactive `WFS` polygons), and archive strips. 3. **Building a Custom Shiny App**: Writing a custom Shiny app with a `leaflet` map that inspects imagery vintage and downloads GeoTIFFs for the current viewport. 4. **Using Drop-in `vantorr` Shiny Modules**: Embedding `vantor_auth_ui()` and `vantor_explorer_ui()` into any Shiny dashboard in just a few lines of code. --- ## 1. Authentication for Browser Tile Streaming Vantor Hub endpoints support two authentication mechanisms with distinct scopes: - **OGC & Non-Administrative APIs** (`get_maxar_wms_basemap()`, `get_maxar_wms_tiles()`, `vantor_get_seamlines()`): Work with **either** an **API Key** (`maxar-api-key`) or an **OAuth2 Bearer Token** (`Authorization: Bearer `). - **Account Services / Admin APIs** (`vantor_get_usage()`): Require an **OAuth2 Bearer Token** (`vantor_auth(username = "...", password = "...")`), as Vantor's Admin APIs do not accept API keys. - **Browser-Based `leaflet` Tile Layers** (`add_vantor_basemap()`, `add_vantor_seamlines(mode = "wms")`, `add_vantor_imagery()`): Require an **API Key** (`maxar_api_key` URL query parameter), because web browsers rendering `` tile requests cannot attach custom HTTP `Authorization: Bearer` headers. To use `leaflet` tile layers seamlessly, configure your API key in `.Renviron` (you can also set `VANTOR_USERNAME` and `VANTOR_PASSWORD` alongside it if you use `vantor_get_usage()`): ```r # In your ~/.Renviron file: VANTOR_API_KEY="your_vantor_api_key" ``` Or register it at the start of your R session or Shiny app: ```{r} library(vantorr) # Set a placeholder API key for demonstration vantor_auth(api_key = "example_api_key", verbose = FALSE) ``` --- ## 2. Interactive `leaflet` Maps ### Generating OGC WMTS Tile URL Templates If you want the raw OGC Web Map Tile Service (`WMTS`) URL template (`EPSG:3857`) for use in `leaflet`, `mapgl`, or `tmap`, use `vantor_wmts_url()`: ```{r} wmts_template <- vantor_wmts_url(product = "VIVID_STANDARD_30") wmts_template ``` ### Adding Vantor Vivid Basemaps and Seamlines `add_vantor_basemap()` (or its camelCase alias `addVantorBasemap()`) adds Vantor Vivid Mosaic imagery (`"VIVID_STANDARD_30"` at 30 cm or `"VIVID_ADVANCED_15"` at 15 cm) to a `leaflet` or `leafletProxy` widget. By default it uses pre-cached `WMTS` tiles (`service = "WMTS"`) for fast panning and zooming, and can optionally overlay seamlines (`show_seamlines = TRUE`): ```{r, eval = requireNamespace("leaflet", quietly = TRUE)} library(leaflet) map_widget <- leaflet() |> setView(lng = -104.995, lat = 39.740, zoom = 15) |> add_vantor_basemap( product = "VIVID_STANDARD_30", service = "WMTS", show_seamlines = TRUE, group = "Vantor Vivid 30cm", verbose = FALSE ) |> addLayersControl( baseGroups = "Vantor Vivid 30cm", overlayGroups = "Vantor Seamlines" ) ``` ### Inspecting Seamline Acquisition Dates via WFS (`mode = "wfs"`) For survey fieldwork and machine learning labeling, knowing the exact **acquisition date** (`acq_time`), **ground sample distance** (`gsd`), and **sensor** (`sensor`) of each mosaic strip is critical. `add_vantor_seamlines()` supports two modes: - `mode = "wms"` (default): Renders a lightweight transparent raster cutline overlay across all zoom levels. - `mode = "wfs"`: Queries the Vantor OGC WFS endpoint via `vantor_get_seamlines()` for a given `bbox` and adds interactive `sf` polygons with clickable HTML metadata popups: ```{r, eval = FALSE} aoi <- sf::st_bbox( c(xmin = -105.01, ymin = 39.73, xmax = -104.98, ymax = 39.75), crs = sf::st_crs(4326) ) leaflet() |> fitBounds(-105.01, 39.73, -104.98, 39.75) |> add_vantor_basemap(product = "VIVID_STANDARD_30") |> add_vantor_seamlines( product = "VIVID_STANDARD_30", mode = "wfs", bbox = aoi ) ``` ### Streaming Specific Archive Strips (`add_vantor_imagery()`) To stream an individual archive strip by its catalog `feature_id` or a custom CQL filter (e.g., filtering by `acquisitionDate` and `cloudCover`), use `add_vantor_imagery()`: ```{r, eval = requireNamespace("leaflet", quietly = TRUE)} archive_map <- leaflet() |> setView(lng = -104.995, lat = 39.740, zoom = 15) |> add_vantor_imagery( feature_id = "10300100EBEDF600", group = "Archive Strip", verbose = FALSE ) ``` ```{r, include = FALSE} vantor_auth_clear(verbose = FALSE) ``` --- ## 3. Building a Custom Shiny Application with a Leaflet Map Below is a complete, minimal Shiny application built from scratch using `leaflet` and `vantorr`. It allows the user to: 1. Switch between **Vivid Standard (30 cm)** and **Vivid Advanced (15 cm)** basemaps. 2. Toggle the **WMS Seamlines** cutline overlay. 3. Click **Fetch WFS Seamlines** to load interactive `sf` seamline polygons for the current map viewport (`input$map_bounds`) and display their metadata table. 4. Click **Download Viewport GeoTIFF** to export the visible map extent via `get_maxar_wms_basemap()`. ```{r, eval = FALSE} library(shiny) library(leaflet) library(sf) library(vantorr) # Ensure VANTOR_API_KEY is set in .Renviron or call vantor_auth(api_key = "...") ui <- fluidPage( titlePanel("Custom Vantor Leaflet + Shiny App"), sidebarLayout( sidebarPanel( width = 4, selectInput( "product", "Vivid Product:", choices = c( "Vivid Standard 30 cm" = "VIVID_STANDARD_30", "Vivid Advanced 15 cm" = "VIVID_ADVANCED_15" ) ), checkboxInput("seamlines", "Show WMS Seamlines", value = FALSE), actionButton( "load_wfs", "Fetch WFS Seamlines in View", class = "btn-info btn-block" ), hr(), numericInput("res", "Export Resolution (m):", value = 0.5, min = 0.15, max = 5, step = 0.15), downloadButton("download_tif", "Download Viewport GeoTIFF", class = "btn-success btn-block"), hr(), h5("Seamline Vintage Summary"), tableOutput("seamline_table") ), mainPanel( width = 8, leafletOutput("map", height = "600px") ) ) ) server <- function(input, output, session) { seamlines_rv <- reactiveVal(NULL) # Helper reactive: convert Leaflet map bounds to an sf::bbox (EPSG:4326) viewport_bbox <- reactive({ req(input$map_bounds) b <- input$map_bounds sf::st_bbox( c(xmin = b$west, ymin = b$south, xmax = b$east, ymax = b$north), crs = sf::st_crs(4326) ) }) # 1. Initialize the Leaflet map output$map <- renderLeaflet({ leaflet() |> addTiles(group = "OpenStreetMap") |> setView(lng = -104.995, lat = 39.740, zoom = 15) |> add_vantor_basemap( product = "VIVID_STANDARD_30", group = "Vantor Vivid", verbose = FALSE ) |> addLayersControl( baseGroups = c("Vantor Vivid", "OpenStreetMap"), overlayGroups = c("Vantor Seamlines", "WFS Polygons"), options = layersControlOptions(collapsed = FALSE) ) }) # 2. Update basemap and WMS seamlines via leafletProxy() observe({ leafletProxy("map", session = session) |> clearGroup("Vantor Vivid") |> clearGroup("Vantor Seamlines") |> add_vantor_basemap( product = input$product, group = "Vantor Vivid", show_seamlines = input$seamlines, verbose = FALSE ) }) # 3. Query WFS vector seamlines for the current viewport on button click observeEvent(input$load_wfs, { bb <- viewport_bbox() withProgress(message = "Fetching WFS seamlines...", value = 0.5, { sl <- vantor_get_seamlines(bbox = bb, product = input$product) seamlines_rv(sl) }) if (!is.null(seamlines_rv()) && nrow(seamlines_rv()) > 0) { leafletProxy("map", session = session) |> clearGroup("WFS Polygons") |> addPolygons( data = seamlines_rv(), group = "WFS Polygons", color = "#00E5FF", weight = 2, fillOpacity = 0.15 ) } }) # 4. Render summary table of seamline acquisition dates and sensors output$seamline_table <- renderTable({ sl <- seamlines_rv() req(sl) df <- sf::st_drop_geometry(sl) cols <- intersect(c("acq_time", "sensor", "gsd", "catid"), names(df)) head(df[, cols, drop = FALSE], 10) }) # 5. Download GeoTIFF for the current map viewport output$download_tif <- downloadHandler( filename = function() { paste0("vantor_viewport_", Sys.Date(), ".tif") }, content = function(file) { withProgress(message = "Downloading GeoTIFF...", value = 0.4, { get_maxar_wms_basemap( bbox = viewport_bbox(), output_file = file, resolution = input$res, product = input$product, return_type = "file" ) }) } ) } shinyApp(ui, server) ``` --- ## 4. Using Drop-In `vantorr` Shiny Modules To avoid writing boilerplate UI and server code in every application, `vantorr` provides two pre-built, namespace-safe Shiny modules: - **`vantor_auth_ui(id)` / `vantor_auth_server(id)`**: Manages API Key or OAuth2 login, displays connection status badges, queries account credit usage via `vantor_get_usage()`, and automatically refreshes OAuth2 tokens in the background via `shiny::invalidateLater()`. - **`vantor_explorer_ui(id)` / `vantor_explorer_server(id)`**: Provides the complete interactive `leaflet` map, product/protocol switcher, WFS seamline date inspector with HTML popups, live area ($\text{km}^2$) calculator, and multi-tile GeoTIFF downloader (`get_maxar_wms_tiles()` with progress bars). ```{r, eval = FALSE} library(shiny) library(vantorr) ui <- fluidPage( titlePanel("Fieldwork AOI Selector"), vantor_auth_ui("vantor_login"), vantor_explorer_ui("vantor_map", height = "550px"), verbatimTextOutput("selected_bbox") ) server <- function(input, output, session) { # 1. Authentication module (auto-refreshes OAuth2 tokens) auth_state <- vantor_auth_server("vantor_login") # 2. Explorer module (returns reactive bbox, seamlines, product, and resolution) explorer <- vantor_explorer_server( "vantor_map", auth_reactive = auth_state ) # 3. Use the reactive bounding box in your own downstream Shiny logic output$selected_bbox <- renderPrint({ explorer$bbox() }) } shinyApp(ui, server) ``` Or launch the built-in standalone demonstration app directly from an interactive R session with a single function call: ```{r, eval = FALSE} if (interactive()) { run_vantor_demo_app() } ```