Interactive maps

Last updated on 2026-09-01 | Edit this page

Estimated time: 84 minutes

Overview

Questions

  • FIXME

Objectives

  • FIXME

R

library(terra)
files <- list.files("data/", pattern = ".tiff", full.names = TRUE)
sentinel <- rast(files)

Make sure you have a data folder in your project folder, and then run this code:

R

urls <- c("https://raw.githubusercontent.com/KUBDatalab/R-toolbox/main/episodes/data/2026-05-25-00_00_2026-05-25-23_59_Sentinel-2_L2A_B02_(Raw).tiff",
"https://raw.githubusercontent.com/KUBDatalab/R-toolbox/main/episodes/data/2026-05-25-00_00_2026-05-25-23_59_Sentinel-2_L2A_B03_(Raw).tiff",
"https://raw.githubusercontent.com/KUBDatalab/R-toolbox/main/episodes/data/2026-05-25-00_00_2026-05-25-23_59_Sentinel-2_L2A_B04_(Raw).tiff",
"https://raw.githubusercontent.com/KUBDatalab/R-toolbox/main/episodes/data/2026-05-25-00_00_2026-05-25-23_59_Sentinel-2_L2A_B08_(Raw).tiff",
"https://raw.githubusercontent.com/KUBDatalab/R-toolbox/main/episodes/data/cph_neighborhoods_and_frederiksberg.gpkg",
"https://raw.githubusercontent.com/KUBDatalab/R-toolbox/main/episodes/data/parks_cph_frederiksberg.gpkg")

download.file(
  urls,
  destfile = file.path("data", basename(urls)),
  mode = "wb"
)

R

names(sentinel) <- c("blue", "green", "red", "nir")
sentinel <- sentinel/65535

R

neighbourhoods <- vect("data/cph_neighborhoods_and_frederiksberg.gpkg")
parks <- vect("data/parks_cph_frederiksberg.gpkg")

oprette et interaktivt Leaflet-kort fra et sf-objekt, transformere spatiale data til et CRS, der kan vises i Leaflet, visualisere punkter og polygoner med relevante visuelle egenskaber, tilføje labels, popups, farveskalaer og signaturforklaringer, kombinere flere datalag og give brugeren kontrol over dem, gemme og publicere kortet som en selvstændig HTML-fil.

Interactive maps

source(“scripts/make-leaflet-map.R”)

Kan vi smide det ind her?

Kan vi skrive indholdet af en chunk?

R

library(leaflet)
library(htmlwidgets)

set.seed(123)

punkter <- data.frame(
  navn = paste("Punkt", 1:10),
  lon = runif(10, 12.45, 12.65),
  lat = runif(10, 55.62, 55.73)
)

kort <- leaflet(punkter) |>
  addProviderTiles(providers$CartoDB.Positron) |>
  addCircleMarkers(
    lng = ~lon,
    lat = ~lat,
    label = ~navn,
    radius = 6
  )

htmlwidgets::saveWidget(
  widget = kort,
  file = "files/leaflet-eksempel.html",
  selfcontained = TRUE,
  title = "Leaflet-eksempel"
)

cat('
<iframe
  src="files/leaflet-eksempel.html"
  title="Interaktivt Leaflet-kort"
  width="100%"
  height="550"
  style="border: 1px solid #cccccc;">
</iframe>
')

Introduction to interactive maps


Formål: Forklare hvad Leaflet tilføjer i forhold til plot() og ggplot2.

Vis et færdigt kort tidligt.

Overskrifter Static and interactive maps When is an interactive map useful? The Leaflet map model Maps, basemaps and data layers Our final map

Centrale pointer Et interaktivt kort er ikke automatisk bedre end et statisk kort. Leaflet er særlig nyttigt, når brugeren skal: zoome og panorere, undersøge enkelte objekter, tænde og slukke lag, se attributter via labels og popups. Et Leaflet-kort bygges trinvist ved at tilføje lag som tiles, markers, polygons og controls. Kort øvelse

Challenge

Challenge

Which information are suitiable to show with:

  • Geometry
  • colour or size
  • popup
  • label
  • signaturforklaring.

Geometri - det der faktisk har geografisk placering og udstrækning. Parker - polygoner. Stationer som punkter, veje som linier Farve eller størrelse - variable der skal kunne sammenlignes mellem objekter- befolkningstæthed er et bud. Antal besøgende i parker ved størrelse popups - ting der først er relevatne når brugeren klikker. Navn, areal, befolkning, datakilder og den slags Label - kort identifikation der kan aflæses ved hover. eg navn eller en enkelt central værdi. Signaturforklaring. Forklaring af de visuelle koder. Hvad betyder farver, størrelser, symboler etc.

Creating your first Leaflet map


Dette bør være det egentlige startpunkt for kodningen.

Overskrifter Creating an empty map Adding a basemap Adding spatial data Setting the initial view Understanding the pipe-based workflow Grundeksempel library(sf) library(leaflet)

neighborhoods <- read_sf( “data/bydele_and_frederiksberg.gpkg” )

leaflet(neighborhoods) |> addProviderTiles(providers$CartoDB.Positron) |> addPolygons() Underemner leaflet(data) addTiles() og addProviderTiles() setView() kontra fitBounds() Hvad der ligger i data-argumentet Leaflets lagfunktioner

Jeg ville bruge fitBounds() eller lade geometrien bestemme udsnittet frem for at starte med håndskrevne koordinater.

Øvelse

Skift mellem to eller tre baggrundskort, og bed kursisterne vælge det, der bedst understøtter dataene.

Coordinate reference systems in Leaflet


Selv om CRS allerede er gennemgået, bør dette have sin egen korte sektion, fordi Leaflet opfører sig anderledes end mange statiske kortværktøjer.

Overskrifter Leaflet maps use longitude and latitude Inspecting the CRS Transforming data for Leaflet Projected data versus displayed data Kernekode st_crs(neighborhoods)

neighborhoods <- neighborhoods |> st_transform(4326) Budskab

Data kan analyseres i eksempelvis:

EPSG:25832

men bør som hovedregel transformeres til:

EPSG:4326

inden de sendes til Leaflet.

Jeg ville understrege forskellen mellem:

analyse-CRS, eksempelvis UTM til afstande og buffere, visnings-CRS, som Leaflet bruger på webkortet.

Det er et godt sted til en challenge, hvor kursisterne forsøger at vise UTM-data og derefter diagnosticerer problemet.

Mapping point data


Punkter er den letteste geometri at introducere interaktivitet med.

Overskrifter Markers and circle markers Encoding values with size Labels and popups Clustering many points Choosing between markers and circles Funktioner addMarkers() addCircleMarkers() addCircles()

Jeg ville primært undervise i addCircleMarkers(). De er lettere at formatere og er normalt mere velegnede til datavisualisering end de klassiske nålemarkører.

Eksempel leaflet(parks) |> addProviderTiles(providers$CartoDB.Positron) |> addCircleMarkers( radius = 5, label = ~navn, popup = ~paste0( “”, navn, “
”, “Type:”, type ) )

Leaflet skelner mellem label, der typisk vises ved hover, og popup, der åbnes ved klik. Markører kan desuden grupperes i clusters ved store punktmængder.

Øvelse

Lad kursisterne:

tilføje parkernes navne som labels, indsætte flere attributter i en popup, ændre radius ud fra en numerisk variabel. 5. Styling spatial data

Dette er efter min vurdering modulets vigtigste episode. Her går kortet fra “geometri oven på et baggrundskort” til egentlig datavisualisering.

Overskrifter Mapping variables to visual properties Creating colour functions Continuous and categorical palettes Missing values Adding a legend Choosing sensible classifications Funktioner colorNumeric() colorBin() colorQuantile() colorFactor() addLegend() Eksempel med befolkning pal <- colorBin( palette = “YlOrRd”, domain = neighborhoods$population, bins = 5 )

leaflet(neighborhoods) |> addProviderTiles(providers$CartoDB.Positron) |> addPolygons( fillColor = ~pal(population), fillOpacity = 0.7, weight = 1, color = “white”, popup = ~paste0( “”, navn, “
”, “Population:”, population ) ) |> addLegend( pal = pal, values = ~population, title = “Population” )

Leaflet har særskilte palettefunktioner og addLegend() til at holde kortets farver og signaturforklaring konsistente.

Vigtig faglig diskussion

Her ville jeg have et afsnit om:

hvorfor man ikke bør bruge tilfældige farver til numeriske værdier, forskellen på kontinuerte værdier og kategorier, hvorfor polygonareal kan dominere kortets visuelle indtryk, forskellen på total befolkning og befolkningstæthed.

Befolkningstæthed er et oplagt sted at genbruge deres viden om projicerede CRS:

neighborhoods |> st_transform(25832) |> mutate( area_km2 = as.numeric(st_area(geometry)) / 1e6, density = population / area_km2 ) |> st_transform(4326) ## Polygon maps and choropleths

Denne episode kan enten være selvstændig eller lægges sammen med styling.

Overskrifter Adding polygons Choropleth maps Borders, fills and transparency Highlighting features Writing informative popups Avoiding misleading choropleths Relevant funktionalitet addPolygons( highlightOptions = highlightOptions( weight = 3, bringToFront = TRUE ) ) Øvelse

Lav to kort:

samlet befolkning pr. bydel, befolkning pr. km².

Lad derefter kursisterne forklare, hvorfor de to kort fortæller forskellige historier.

Det vil være en fagligt stærkere øvelse end blot at bede dem ændre farver.

Combining several spatial layers


Her samles punkter, linjer og polygoner.

Overskrifter Layer groups Overlaying points and polygons Controlling layer order Adding a layer control Base groups and overlay groups Showing and hiding layers Eksempel leaflet() |> addProviderTiles( providers\(CartoDB.Positron, group = "Light" ) |> addProviderTiles( providers\)OpenStreetMap, group = “OpenStreetMap” ) |> addPolygons( data = neighborhoods, group = “Neighborhoods” ) |> addCircleMarkers( data = parks, group = “Parks” ) |> addLayersControl( baseGroups = c( “Light”, “OpenStreetMap” ), overlayGroups = c( “Neighborhoods”, “Parks” ) )

Leaflet bruger group sammen med addLayersControl() til at vise og skjule lag.

God challenge

Tilføj:

bydele, parker, en eller flere ruter eller linjer, to forskellige baggrundskort.

Lad derefter kursisterne beslutte, hvilke lag der skal være synlige ved start.

Adding raster data


Fordi I allerede arbejder med Sentinel og NDVI, ville jeg have dette med — men som valgfri eller avanceret episode.

Overskrifter Vector and raster layers in Leaflet Preparing a raster for display Colouring raster values Adding an NDVI layer Transparency and layer controls Browser and file-size limitations Relevant eksempel library(terra) library(leaflet)

ndvi <- rast(“data/ndvi.tif”)

pal_ndvi <- colorNumeric( palette = “YlGn”, domain = values(ndvi), na.color = “transparent” )

leaflet() |> addProviderTiles(providers$CartoDB.Positron) |> addRasterImage( ndvi, colors = pal_ndvi, opacity = 0.7, group = “NDVI” ) |> addLegend( pal = pal_ndvi, values = values(ndvi), title = “NDVI” ) Her bør I tale om rasterens opløsning, filstørrelse, reprojektion, nedskalering, hvorfor et stort Sentinel-raster ikke nødvendigvis bør sendes direkte til browseren.

Leaflets officielle materiale behandler rastervisning og projektion som særskilte spatial-data-emner, så det passer naturligt som en mere avanceret del.

Designing useful popups and labels


Dette kan være en mindre sektion, men det er værd at behandle som design og ikke kun syntaks.

Overskrifter What belongs in a popup? Labels versus popups Formatting text with HTML Formatting numbers Handling missing values Avoiding information overload Eksempel popup_text <- paste0( “”, neighborhoods\(navn, "</strong><br>", "Population: ", format( neighborhoods\)population, big.mark = “,” ), “
”, “Area:”, round(neighborhoods$area_km2, 1), ” km²” )

Undervis gerne i at konstruere popupteksten som en separat kolonne:

neighborhoods <- neighborhoods |> mutate( popup = paste0( “”, navn, “
”, “Population:”, population ) )

Det er renere end at lægge en meget lang paste0() direkte inde i addPolygons().

Saving and publishing the map


Denne del er særlig relevant i jeres Carpentries-system.

Overskrifter Leaflet maps are HTML widgets Saving a map as an HTML file Opening a map outside R Embedding maps in the lesson website Relative paths and project structure Reproducible map generation Centrale funktioner htmlwidgets::saveWidget()

og iframe-løsningen, vi netop har talt om.

Jeg ville vise en foreslået projektstruktur:

episodes/ leaflet-maps.Rmd files/ leaflet-final.html

data/ bydele_and_frederiksberg.gpkg parks.gpkg

scripts/ prepare-spatial-data.R

Adskil gerne:

databearbejdning, kortkonstruktion, publicering.

Kursisterne skal helst ikke tro, at alle GIS-operationer nødvendigvis hører hjemme i den samme lange Leaflet-pipeline.

Afsluttende projekt

Jeg ville afslutte med én større challenge:

Challenge: Build an interactive map of Copenhagen

Krav til kortet:

Vis København og Frederiksbergs områder som polygoner. Farvelæg dem efter befolkningstæthed. Tilføj parker som et separat lag. Tilføj labels med områdenavn. Tilføj popups med relevante værdier. Tilføj en signaturforklaring. Tilføj mindst to baggrundskort. Tilføj lagkontrol. Gem kortet som en HTML-fil. Udvidelser Tilføj en NDVI-raster. Brug marker clustering. Tilføj en målestok. Tilføj en titel med addControl(). Fremhæv polygoner ved hover. Lav en anden klassifikation og sammenlign resultatet.

Det er en god Carpentries-øvelse, fordi den tester konkrete færdigheder, som svarer direkte til læringsmålene. Carpentries’ designmateriale anbefaler netop at definere læringsmål og derefter konstruere øvelser, der demonstrerer, om deltagerne har nået dem.

Foreslåede samlede læringsmål

Efter modulet skal deltagerne kunne:

oprette et interaktivt Leaflet-kort fra et sf-objekt, transformere spatiale data til et CRS, der kan vises i Leaflet, visualisere punkter og polygoner med relevante visuelle egenskaber, tilføje labels, popups, farveskalaer og signaturforklaringer, kombinere flere datalag og give brugeren kontrol over dem, gemme og publicere kortet som en selvstændig HTML-fil. Hvad jeg ville holde ude af grundmodulet

Key Points
  • FIXME