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
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
- FIXME