innhold/custom/plugins/page/petiton-map.docs.md
Ruben bd993ea6ca Increase petition map animation duration to 30 seconds
Add lazy initialization for petition map
Update documentation with new positioning guidance
Note full-width section rendering behavior
2026-02-26 21:40:56 +01:00

4.1 KiB

petition-map plugin

Page plugin that renders an interactive Leaflet map of petition signers across Norway's 15 fylker.

Enable

In a page's metadata.ini (must also have petition-form loaded first):

plugins = "petition-form, petition-map"

Embed in a content file (e.g. 90-form.php, between the form and the signatures list):

<?= $petition_map ?? '' ?>

The plugin renders a <section class="petition-map-section escape"> which breaks out of the content grid to fill the full page width.

Architecture

petition-form CSV
       │
       ▼
petition-map.php (PHP, page plugin)
  ├── Reads CSV, extracts: first name, anonymous flag, region, confirmed status
  ├── Writes private cache:  custom/data/petition-map-cache.json   (60s TTL)
  └── Writes public JSON:    custom/assets/petition-map-data.json  (served at /petition-map-data.json)
       │
       ▼
petition-map.js (client-side)
  ├── Fetches /petition-map-data.json + /norway-fylker.geojson
  ├── Renders Leaflet map with CartoDB Positron tiles (muted, no labels)
  ├── Draws 15 fylke outlines from GeoJSON
  ├── Places name labels (L.tooltip permanent) randomly within each fylke polygon
  └── Polls every 60s for new signers

Files

File Location Purpose
petition-map.php custom/plugins/page/ PHP plugin: cache logic, data builder, HTML widget renderer, TEMPLATE_VARS hook
petition-map.js custom/assets/ Client JS: Leaflet map, dot placement, point-in-polygon, polling
petition-map.css custom/assets/ Map and label styles
norway-fylker.geojson custom/assets/ Simplified fylke boundaries (15 current fylker, from OpenStreetMap Overpass, RDP simplified to ~69KB)
petition-map-data.json custom/assets/ Auto-generated public signer data (written by PHP, served as static asset)
petition-map-cache.json custom/data/ Private server-side cache (same data, controls rebuild frequency)
anon.svg custom/assets/ Mask icon for anonymous signers
generate-mock-petitions.php custom/tools/ Dev-only CLI to generate mock CSV data

Public JSON format

{
  "generated": 1772052913,
  "total": 500,
  "regions": {
    "oslo": {
      "count": 57,
      "signers": [
        {"n": "Kari", "a": false},
        {"n": null, "a": true}
      ]
    }
  }
}
  • n = first name (null if anonymous or empty)
  • a = anonymous boolean
  • No emails, surnames, tokens, or IP hashes ever leave the server

Privacy rules (from CSV display column)

  • full or semi: first name shown on map
  • anonymous: shown as mask icon, no name
  • Only confirmed status rows are included

Region keys

15 keys matching the petition-form select options and GeoJSON name property:

agder, akershus, buskerud, finnmark, innlandet, more_og_romsdal, nordland, oslo, rogaland, telemark, troms, trondelag, vestfold, vestland, ostfold

JS internals

  • Point placement: rejection sampling within fylke polygon bounding box, ray-casting point-in-polygon test, 4% edge inset
  • Label rendering: L.tooltip({ permanent: true }) bound to invisible zero-size L.marker in a custom dots pane (z-index 450)
  • Lazy init: map initialises only when #petition-map enters the viewport (IntersectionObserver, 10% threshold); falls back to immediate init on older browsers
  • Initial animation: all labels shuffled (Fisher-Yates) and staggered over 6s
  • Polling: fetches data JSON every 60s with cache-bust timestamp; new signers added immediately
  • Fylke interaction: hover highlights polygon, click shows popup with fylke name + count

Cache invalidation

Delete custom/data/petition-map-cache.json to force a rebuild on next page load. The mock generator does this automatically. The public JSON is rewritten whenever the cache rebuilds.

Dev tools

Generate mock data (run inside the container):

podman exec stopplidelsen.no php /var/www/custom/tools/generate-mock-petitions.php

Interactive: asks for count and regional variance. Writes CSV and invalidates cache files.