Add petition map to medical cannabis petition page

Add anonymous SVG icon for anonymous signers

Add Norway fylker GeoJSON for map boundaries

Add CSS styles for petition map

Add JavaScript for interactive petition map

Add .htaccess to block direct access to data files

Add petition-map plugin to process and display map data

Add documentation for the petition-map plugin

Add mock petition data generator tool
This commit is contained in:
Ruben 2026-02-25 23:11:35 +01:00
parent 1ee0e0f0a0
commit a7829982d0
10 changed files with 1134 additions and 1 deletions

View file

@ -0,0 +1,106 @@
# 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):
```ini
plugins = "petition-form, petition-map"
```
Embed in a content file (e.g. `85-kart.php`):
```php
<?= $petition_map ?? '' ?>
```
## 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
```json
{
"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)
- **Initial animation**: all labels shuffled (Fisher-Yates) and staggered over 4.5s
- **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):
```sh
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.