webmaps and
dashboards
bnhr.xyz
--- ## Contents 1. The Application Problem 2. The Application Layer 3. Track A — Geospatial Applications 4. Track B — Data Dashboards 5. Demo --- ## Retrieval practice *Without looking at your notes:* What would you use to get a JSON list of all layers in GeoNode? What does the URL look like? What does the response include? Note: No slides. Prompt on the board. 2 min individual write, cold-call 3–4 participants. Expected: http://localhost/api/v2/resources/?resource_type=layer — returns title, abstract, WMS link, WFS link, download URL. Correct in 2–3 minutes. This activates the GeoNode catalog API knowledge from Day 3 — it is the data source both demo tracks will consume this morning. ---
the application
problem
--- ## The data is there — but who can actually use it? - PostGIS stores it, GeoServer shares it, GeoNode catalogs it but a fisheries officer, regional manager, data analyst, or the general public may need some guidance and training before they can use these tools. - What if we need something that anyone open in a browser and undrestand intuitively? - The application layer bridges the open stack to the people who actually need the data. ---
the application
layer
--- ## The open stack now has four layers — the application sits on top | Layer | Tool | Day | |---|---|---| | **User-facing application** | **← This is what Day 4 builds** | **Day 4** | | Catalog | GeoNode | Day 3 | | Services | GeoServer (WMS / WFS) | Day 2 | | Storage | PostGIS | Day 1 | The layers below are already running. The application layer is what stakeholders, decision-makers, and external users actually touch. Note: The bottom three layers are done. PostGIS stores it, GeoServer shares it, GeoNode catalogs it. Today's question: how do you build something a decision-maker can actually use? The WMS endpoints are live. The GeoNode API returns JSON. Now — what goes on top? --- ## The AGOL web map has an open equivalent for every component
ArcGIS Online web map
Loads hosted feature layers from AGOL service infrastructure Renders with Esri basemaps and symbology Pan, zoom, click features, view attributes
Open stack equivalent
WMS / WFS endpoints from GeoServer replace hosted feature layers MapLibre GL JS or Leaflet handle rendering; OSM replaces Esri basemaps Click / identify behavior implemented in JavaScript or Python
Note: The migration is: replace AGOL service URLs with GeoServer WMS/WFS URLs, replace the AGOL JavaScript SDK with MapLibre or Leaflet, and host the result on your own server. The architecture is identical — the URLs are different. --- ## Three tiers of complexity — match the tool to the use case | Tier | Effort | Skill required | Best for | |---|---|---|---| | **No-code** | Minutes | None | Sharing a GeoNode map, embedding a WMS in an existing portal | | **Low-code** | Hours | Python or SQL | Analyst dashboards, internal reporting, quick prototypes | | **Full-code** | Days–weeks | JavaScript / React | Public-facing web maps, interactive cartographic products, production apps | Note: The GeoNode built-in map from Day 3 is the no-code option — it already exists and requires no development. The low-code and full-code tiers are what Day 4 builds. Not every use case needs a full-code application. ---
track a
geospatial
applications
--- ## Track A has three tools at three levels of complexity
| Tool | Level | Best for | |---|---|---| | **GeoNode built-in functions** | No-code | Simple, shareable maps that anyone with a browser can view | | **Leaflet** or **MapLibre GL JS** | Low-code / beginner to intermediate JS | Basic to intermediate web maps with custom interactions | | **Complete web application** | Programmer/Developer | Multi-page apps, dashboards mixing maps with charts and tables, complex user interactions |
- All three tracks can consume from the same data endpoints (files, databases, GeoServer, GeoNode, etc.). The difference is the rendering layer and the application wrapper. --- ## Leaflet loads a WMS layer in 6 lines of JavaScript ```javascript const map = L.map('map').setView([12.5, 122.5], 6); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap' }).addTo(map); L.tileLayer.wms('http://localhost:8080/geoserver/psa/wms', { layers: 'psa:palay_distribution', format: 'image/png', transparent: true }).addTo(map); ``` The WMS URL is the same GeoServer endpoint from Day 2. Leaflet calls it as a tile source. Note: Demo Part 1 builds this live from scratch. The key message: the only thing that changed from an AGOL map is the URL. The map behavior — pan, zoom, overlay — is identical. --- ## MapLibre GL JS supports vector files natively - Open-source fork of Mapbox GL JS - Renders vector tiles (PMTiles / MVT) natively - Supports WMS as a raster source via the tile URL pattern from Day 2 - Smooth pan and zoom without tile seams ``` ?service=WMS&version=1.1.1&request=GetMap &layers=psa:palay_distribution &format=image/png&transparent=true &width=256&height=256&srs=EPSG:3857 &bbox={bbox-epsg-3857} ``` The `{bbox-epsg-3857}` placeholder is filled by MapLibre per tile request. Note: Demo Part 2a builds this. The WMS GetMap anatomy is identical to Day 2 — the only new piece is the {bbox-epsg-3857} substitution pattern that MapLibre uses to turn a WMS endpoint into a tile source. --- ## A complete frontend application with React - Component-based frontend framework - MapLibre, react-leaflet, or deck.gl as the map component - Multi-page routing, authentication, production deployment Note: Demo Part 3a shows a pre-built React + MapLibre component. Not built live — shown from facilitator's machine. Participants who choose Track A will build toward this in the afternoon. ---
The Application Layer
track b —
data dashboards
--- ## Track B has three tools across Python and JavaScript
| Tool | Language | Best for | |---|---|---| | **Streamlit** | Python | Analysts who write Python; rapid internal dashboards; data exploration | | **Observable Framework** | JavaScript | Publication-quality interactive reports; reactive notebook feel | | **Evidence.dev** | SQL + Markdown | BI-style dashboards without a BI tool license; PostGIS via DuckDB |
Track B is not "no maps" — Streamlit supports Folium, Observable supports Leaflet and D3. It means the dashboard is not *primarily* a map. Note: Self-selection should be based on what participants want the product to feel like, not whether it has a map. If a Track B participant wants a map panel in their Streamlit app, that is fully achievable in the afternoon. --- ## Streamlit turns a Python script into an interactive dashboard ```python import streamlit as st import requests, pandas as pd st.title("PSA AFCD Agricultural Data Dashboard") @st.cache_data def get_layers(): r = requests.get( "http://localhost/api/v2/resources/?resource_type=layer&page_size=20" ) return r.json()["resources"] layers = get_layers() df = pd.DataFrame(layers)[["title", "abstract", "date", "typename"]] st.subheader("Available Layers") st.dataframe(df) ``` Run with `streamlit run app.py`. The GeoNode catalog drives the dashboard automatically. Note: Demo Part 2b builds this live. The key message: every time a new layer is uploaded to GeoNode, this dashboard reflects it — no manual maintenance. The data source is the same GeoNode API endpoint from Day 3. --- ## Observable Framework is a reactive notebook for data visualization ```javascript const layers = await fetch( "/api/v2/resources/?resource_type=layer" ).then(r => r.json()); display(Plot.plot({ marks: [ Plot.barY(layers.resources, {x: "title", y: d => d.popular_count}) ] })); ``` - Markdown + JavaScript; each page is a reactive notebook - Live-reload development server (`npm run dev`) - Excellent for exploratory analysis with Observable Plot charts Note: Demo Part 3b shows a pre-built Observable Framework page. Best for participants comfortable with JavaScript who want publication-quality interactive reports. --- ## Evidence.dev builds BI dashboards from SQL queries and Markdown ```sql -- palay_by_province.sql SELECT province, SUM(total_yield) AS total_yield FROM palay_distribution GROUP BY province ORDER BY total_yield DESC ``` ```markdown ## Production by Province
``` Write SQL, write Markdown — the framework builds a static site. Data from PostGIS via DuckDB. Note: Demo Part 3b also shows Evidence.dev. For the Day 4 demo, use a DuckDB-based data source — export PostGIS tables to GeoParquet, which DuckDB reads natively. Full PostGIS connector setup is a stretch task for Track B participants who finish early. --- ## Five questions point toward the right track
| Question | Points toward Track A | Points toward Track B | |---|---|---| | Is geography the primary interface? | Yes | No — charts, tables, KPIs | | Will external / public users use it? | Likely | Maybe not | | What skills does the team have? | JavaScript comfort | Python or SQL comfort | | Does it need to update automatically? | Via live WMS | Via API polling or rebuild | | How complex is the UI? | Map-centric | Multi-panel dashboard |
There is no wrong choice. Many production applications combine both. Note: If a participant is unsure, ask: "Do you want the map to be the whole product, or one panel among several?" This distinction usually resolves the choice. Participants who want both tracks will self-select Track A in practice — the mapping is the harder skill to add later. --- ## Traffic light check-in
1. I understand the three tiers (no-code / low-code / full-code) and which tier fits my use case. 2. I can explain what sits at each layer of the open stack — storage, services, catalog, application. 3. I understand why you would build a custom app on top of GeoNode/GeoServer. 4. I can describe at least one tool in Track A and one tool in Track B, and what each is best for. 5. I can describe what a stakeholder or decision-maker would experience with webmap or dashboard I'm building 6. I know which afternoon track I am leaning toward. **Green** = confident | **Yellow** = mostly | **Red** = still unclear
Note: Hands or sticky notes. If more than 1/3 of the room is red or yellow on statement 3 (architecture connection), trace the four-container diagram before opening the demo. Statement 4 checks pipeline comprehension — re-run the upload slide if needed. Statements 5–6 are the governance angle; if red, revisit the permissions slide before Demo Part 2. ---
Demo
part 1 —
geonode map +
minimal web map
--- ## Part 1a — the no-code option already exists Open the GeoNode map from Day 3: `PSA AFCD Agricultural Overview 2024` - Show the **share URL** — anyone with a browser can view this map - Show the **embed code** GeoNode generates automatically - No code required *If all you need is a shareable map that anyone with a browser can view, this already exists. The question for today is: what do you need that GeoNode's map can't give you?* Note: I Do phase. 5 minutes. This sets up the motivation: the no-code option is real and useful. Participants who only need to share a static map may not need Track A or Track B at all. But most AFCD use cases — custom queries, integrated charts, role-based access — need more. --- ## Part 1b — a working WMS web map in 20 lines of HTML ```html
PSA AFCD Agricultural Map
``` Note: I Do phase. 30 minutes. Build live from a blank index.html. Open in browser. Show the live WMS layer on OSM tiles. Narrate: "This is the ArcGIS Online web map, migrated. The only difference is the WMS URL — it now points to our GeoServer." Add seaweed_farms as a second layer to show it's just another .addTo(map) call. ---
Demo
part 2 —
maplibre +
streamlit
--- ## Part 2a — MapLibre GL JS as a Track A preview ```javascript const map = new maplibregl.Map({ container: 'map', style: 'https://demotiles.maplibre.org/style.json', center: [122.5, 12.5], zoom: 5 }); map.on('load', () => { map.addSource('palay', { type: 'raster', tiles: [ 'http://localhost:8080/geoserver/psa/wms?service=WMS&version=1.1.1' + '&request=GetMap&layers=psa:palay_distribution&format=image/png' + '&transparent=true&width=256&height=256&srs=EPSG:3857&bbox={bbox-epsg-3857}' ], tileSize: 256 }); map.addLayer({ id: 'palay-layer', type: 'raster', source: 'palay' }); }); ``` Note: We Do phase. 20 minutes. Participants follow along. Show the smooth pan/zoom. Narrate the WMS tile URL pattern — it's the same GetMap anatomy from Day 2, now used as a raster tile source. Then ask the think-pair-share question. --- ## Think-pair-share *What would you change in this MapLibre WMS URL to add the `seaweed_farms` layer?* 2 minutes to discuss with a neighbour — then share. Note: Expected answer: change `layers=psa:palay_distribution` to `layers=psa:seaweed_farms`. The rest of the URL stays the same. Cold-call 2–3 pairs. This confirms understanding of the GetMap URL anatomy from Day 2 before moving to the Streamlit demo. --- ## Part 2b — Streamlit as a Track B preview ```python import streamlit as st, requests, pandas as pd st.title("PSA AFCD Agricultural Data Dashboard") @st.cache_data def get_layers(): r = requests.get( "http://localhost/api/v2/resources/?resource_type=layer&page_size=20" ) return r.json()["resources"] layers = get_layers() df = pd.DataFrame(layers)[["title", "abstract", "date", "typename"]] st.subheader("Available Layers") st.dataframe(df) selected = st.selectbox("Select a layer to inspect", df["title"]) ``` Run: `streamlit run app.py` Note: We Do phase. 15 minutes. Run with streamlit run app.py. Show the live dashboard in the browser. Narrate: "This reads from the GeoNode catalog API — the same endpoint from Day 3. Every time a new layer is uploaded to GeoNode, this dashboard reflects it automatically." The st.map() placeholder is expanded in the afternoon. ---
Demo
part 3 —
track previews
--- ## Part 3a — React + MapLibre (Track A capstone preview) ```jsx import Map, { Source, Layer } from 'react-map-gl/maplibre'; const wmsUrl = 'http://localhost:8080/geoserver/psa/wms'; export default function PSAMap() { return (
); } ``` Note: I Do phase. 15 minutes. Shown from pre-built facilitator files — not built live. Narrate: "This is the same WMS source, now inside a React component. Combine this map panel with a chart panel from Recharts — that's the full AGOL dashboard replacement." Then show the completed example: React page with MapLibre map on left and bar chart of total_yield by province on right. --- ## Part 3b — Observable Framework and Evidence.dev (Track B capstone preview)
Observable Framework
Reactive notebook in Markdown + JavaScript Fetches GeoNode API directly as a data source Live-reload dev server — notebook feel with production output
Evidence.dev
SQL queries + Markdown pages Chart components with no JavaScript required PostGIS via DuckDB — static site output
Note: I Do phase. 20 minutes. Show both pre-built examples from facilitator's machine. Observable: show the dev server, live-reload, and a completed page with a choropleth and summary statistics. Evidence.dev: show a pre-built report with multiple pages, KPI row, and province production table. Both are capstone deliverables for Track B. --- ## Track selection + exit ticket **Choose your afternoon track:** - **Track A** — I want to build a web map or spatial application *(MapLibre / Leaflet / React)* - **Track B** — I want to build an analytical dashboard *(Streamlit / Observable / Evidence.dev)* *Not sure?* Ask yourself: do you want the map to be the whole product, or one panel among several? --- ## Exit ticket
*Let's check how we're doing* 1. How clear was this session for you? 2. How confident are you to try this on your own? 3. Which topic did you find most difficult or confusing? 4. In one word — what's your reaction to what we just covered? 5. What is your biggest question right now? 6. What webmap or dashboard do you plan to build?
---
questions?