Skip to main content

kortagerd

Generate Iceland maps from cached LMI data — static (matplotlib) and interactive (Leaflet), with ISN93 derived cache.

Source facts

Repository
jokull/icelandic-data
Last source activity
September 10, 2026 at 15:02
Detected SKILL.md language
English
Stars
54
Forks
4

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
kortagerd
description
Generate Iceland maps from cached LMI data — static (matplotlib) and interactive (Leaflet), with ISN93 derived cache.
# Kortagerð (Mapmaking) — Iceland Maps **Requires:** Tier 1 (maps) — geopandas + matplotlib rendering; `uv sync --group maps`. Generate high-quality maps of Iceland using cached LMI geodata. Supports interactive HTML (Leaflet) and static PNG/SVG (geopandas + matplotlib). ## Quick Start ```bash # Ensure geodata is cached uv run python scripts/lmi.py download # Interactive map (full-screen, layer toggles, popups) uv run python scripts/kortagerð.py html -o reports/map.html # Static map (publication-quality PNG) uv run python scripts/kortagerð.py static -o reports/map.png # Zoomed to capital area with Reykjavík highlighted uv run python scripts/kortagerð.py static --bounds capital --highlight "Reykjavíkurborg" -o reports/rvk.png # Overlay custom points from CSV uv run python scripts/kortagerð.py static --points data/my_locations.csv -o reports/custom.png ``` ## Bounding Box Presets | Name | Area | Bounds [W, S, E, N] | |------|------|---------------------| | `iceland` | Full country (default) | -24.7, 63.2, -13.1, 66.6 | | `capital` / `reykjavik` | Greater Reykjavík | -22.1, 63.95, -21.3, 64.25 | | `southwest` | Reykjanes to Hekla | -22.5, 63.6, -19.5, 64.5 | | `north` | Skagafjörður to Húsavík | -20.0, 65.2, -15.0, 66.6 | | `east` | Eastfjords | -16.0, 64.2, -13.3, 65.8 | | `westfjords` | Westfjords | -24.7, 65.0, -21.0, 66.6 | | `south` | South coast | -21.0, 63.2, -17.5, 64.3 | | `akureyri` | Akureyri area | -18.4, 65.5, -17.7, 65.8 | ## Cached Layers (in `data/geodata/`) | File | What | When to use | |------|------|-------------| | `Landmask.geojson` | Iceland land polygon | Always — base fill for any map | | `CoastalLine.geojson` | Coastline | Border outline | | `AdministrativeUnit_level2.geojson` | 128 municipalities | Choropleth, boundaries, `--highlight` | | `RoadLines.geojson` | Road network | Infrastructure, transport maps | | `WatercourseLine.geojson` | Rivers (28k features) | Hydrology (skip for perf if not needed) | | `Lake_Reservoir.geojson` | Lakes | Hydrology, geography | | `LandIceArea.geojson` | Glaciers | Terrain, geography | | `BuiltupAreaPoints.geojson` | 98 settlements + pop | City labels, settlement maps | | `NatureParkArea.geojson` | National parks | Environmental, tourism maps | | `IslandArea.geojson` | Islands | Offshore geography | | `Airport_Airfield_points.geojson` | Airports | Transport maps | | `Port.geojson` | Harbors | Maritime, transport maps | ## Caching strategy (for raster-overlay maps) Vector layers are pre-cached at Tier 1 (`data/geodata/`). For maps that overlay rasters (e.g. Copernicus HRL Grassland), three more tiers exist under `data/cache/` to avoid re-downloading and re-projecting on every render. | Tier | Path | Built by | What | Speedup | |------|------|----------|------|--------:| | 1 | `data/geodata/*.geojson` | `scripts/lmi.py download` | LMI WFS vectors (~50 MB) | — | | 2 | `data/raw/lmi_hrl/*.tif` | `scripts/lmi_hrl.py fetch grassland` | Source HRL GeoTIFFs (~860 MB) | — | | 3 | `data/cache/rasters/*.tif` | `scripts/build_cache.py rasters` | LZW + ISN93-projected GeoTIFFs (~9 MB each — **98× smaller**) | skip 30 s reproject per render | | 4 | `data/cache/constants.json` | `scripts/build_cache.py constants` | Iceland total area + 4-CRS bbox + per-source SHA-256 + grassland area | skip 1.5 s polygon area + 826 MB scan per render | | 5 | `data/cache/arrays/*.npy` | written automatically on first map render | Decoded probability arrays (e.g. GRAVPI) | skip 5 s RGB-decode + reproject | Render scripts read these tiers via `scripts/utils/cache.py`: ```python from scripts.utils.cache import iceland_constants, cached_raster, CacheMissingError try: K = iceland_constants() iceland_km2 = K["iceland_total_area_km2"] except CacheMissingError as e: print(e.hint) # tells you the build_cache.py command to run ``` Tier 3+4 are explicit (`build_cache.py all`); Tier 5 is opportunistic (populated on first render). Sources are SHA-256 fingerprinted in `constants.json` so `scripts/build_cache.py status` flags stale entries when an upstream raster changes. ### Benchmark ```bash # Cold (full re-download): ~120 s for grassland # Warm-raw (rebuild Tier 3+4): ~10 s for grassland # Warm (steady state): ~3-6 s uv run python scripts/bench_maps.py run --mode warm uv run python scripts/bench_maps.py history ``` Results land in `data/cache/benchmarks.json` with timestamps and a delta vs. the last `warm` baseline. ### Tests ```bash # Cache-source consistency + map-output smoke tests uv run pytest tests/test_cache_consistency.py tests/test_maps_render.py -v ``` ### Troubleshooting - *"Missing data/cache/constants.json"* → run `scripts/build_cache.py all` - *"STALE — source changed"* in status → run `scripts/build_cache.py rasters` - Render is slow despite cache → check `scripts/build_cache.py status`; the tier-5 `.npy` file may be missing for `grassland_probability_heatmap.py` (auto-rebuilds on next run). ## Python Code Templates ### Load a layer with geopandas ```python import geopandas as gpd from pathlib import Path GEODATA = Path("data/geodata") land = gpd.read_file(GEODATA / "Landmask.geojson") municipalities = gpd.read_file(GEODATA / "AdministrativeUnit_level2.geojson") ``` ### Simple choropleth map ```python import geopandas as gpd import matplotlib.pyplot as plt from pathlib import Path GEODATA = Path("data/geodata") land = gpd.read_file(GEODATA / "Landmask.geojson") munic = gpd.read_file(GEODATA / "AdministrativeUnit_level2.geojson") # Merge your data onto municipalities # munic = munic.merge(your_data, left_on="namn", right_on="municipality") fig, ax = plt.subplots(figsize=(12, 8), facecolor="#f0f4f8") ax.set_facecolor("#c8d6e5") land.plot(ax=ax, color="#f5f0e6", edgecolor="#2d3436", linewidth=0.6) munic.plot(ax=ax, column="your_metric", cmap="YlOrRd", legend=True, edgecolor="#636e72", linewidth=0.3, alpha=0.8) ax.set_xlim(-24.7, -13.1) ax.set_ylim(63.2, 66.6) ax.set_aspect(1 / 0.42) # latitude correction at 65°N ax.set_title("Your Title") plt.tight_layout() fig.savefig("reports/choropleth.png", dpi=200, bbox_inches="tight") ``` ### Overlay custom points ```python import polars as pl import geopandas as gpd import matplotlib.pyplot as plt GEODATA = Path("data/geodata") land = gpd.read_file(GEODATA / "Landmask.geojson") # Your point data pts = pl.read_csv("data/processed/your_points.csv") fig, ax = plt.subplots(figsize=(12, 8)) land.plot(ax=ax, color="#f5f0e6", edgecolor="#2d3436", linewidth=0.6) ax.scatter(pts["lon"], pts["lat"], c="#e74c3c", s=20, zorder=10) ``` ### Interactive HTML with custom data overlay ```python import json from pathlib import Path GEODATA = Path("data/geodata") # Load base layers with open(GEODATA / "Landmask.geojson") as f: landmask = json.load(f) # Build Leaflet HTML with your data embedded as JSON # Follow the pattern in scripts/kortagerð.py cmd_html() ``` ## R Code Templates ### Load layers with sf ```r library(sf) library(ggplot2) geodata <- "data/geodata" land <- st_read(file.path(geodata, "Landmask.geojson")) munic <- st_read(file.path(geodata, "AdministrativeUnit_level2.geojson")) roads <- st_read(file.path(geodata, "RoadLines.geojson")) glaciers <- st_read(file.path(geodata, "LandIceArea.geojson")) lakes <- st_read(file.path(geodata, "Lake_Reservoir.geojson")) ``` ### Basic ggplot2 map ```r ggplot() + geom_sf(data = land, fill = "#f5f0e6", color = "#2d3436", linewidth = 0.3) + geom_sf(data = glaciers, fill = "#dfe6e9", color = "#b2bec3", linewidth = 0.2) + geom_sf(data = lakes, fill = "#74b9ff", color = "#0984e3", linewidth = 0.2) + geom_sf(data = roads, color = "#e17055", linewidth = 0.3) + coord_sf(xlim = c(-24.7, -13.1), ylim = c(63.2, 66.6)) + theme_minimal() + labs(title = "Iceland") ``` ### Choropleth with municipality data ```r # Merge your data onto municipalities munic_data <- munic %>% left_join(your_data, by = c("namn" = "municipality")) ggplot() + geom_sf(data = land, fill = "#f5f0e6", color = "#2d3436", linewidth = 0.3) + geom_sf(data = munic_data, aes(fill = your_metric), color = "#636e72", linewidth = 0.2) + scale_fill_viridis_c() + coord_sf(xlim = c(-24.7, -13.1), ylim = c(63.2, 66.6)) + theme_minimal() ``` ## Color Palette Standard colors used by `kortagerð.py`: | Element | Fill | Stroke | |---------|------|--------| | Ocean | `#c8d6e5` | — | | Land | `#f5f0e6` | `#2d3436` | | Glaciers | `#dfe6e9` | `#b2bec3` | | Lakes | `#74b9ff` | `#0984e3` | | Rivers | `#0984e3` | — | | Major roads | `#d63031` | — | | Minor roads | `#e17055` | — | | Nature parks | `#a8e6cf` | `#00b894` | | Municipalities | `#b2bec3` (dashed) | — | | Highlight | `#ffeaa7` | `#fdcb6e` | | Settlements | `#2d3436` | `#fff` | ## Tips - **Latitude correction**: At 65°N, set `ax.set_aspect(1/0.42)` in matplotlib or the map will appear stretched - **WatercourseLine** has 28k features — skip for faster rendering on zoomed maps - **Municipality names** are in the `namn` column of `AdministrativeUnit_level2.geojson` - **Settlement population** is in the `ppl` column of `BuiltupAreaPoints.geojson` - **Road classification**: filter by `rtt` field (1-3 = highways, 4-10 = regional, >10 = local) - For **extra layers** not pre-cached: `uv run python scripts/lmi.py fetch ERM:WetlandArea`
View on GitHub