Ledenkaart XM Club
Waar wonen de leden van de XM Club? De club heeft leden in een tiental landen, en het bestuur wilde dat zichtbaar maken op de website: per land een kaart met vlekken waar de leden wonen, met de grote plaatsen voor de oriëntatie, en per jaar terug te kijken. Eén eis stond voorop: er mochten nergens aantallen of adressen zichtbaar worden. Het resultaat is een interactieve ledenkaart, gevoed door de ledenadministratie van de afgelopen jaren.
Doelstellingen
- Per jaar en per land laten zien waar leden wonen, als heatmap met semi-transparante vlekken.
- Per land alleen het percentage van het totaal tonen, nooit aantallen.
- De historie vanaf 2022 opnemen uit ledenlijsten, uit te breiden met oudere jaren.
- Vanaf nu elk jaar automatisch een nieuwe stand vastleggen vanuit de ledenadministratie.
- Geen namen, adressen of lidnummers opslaan in de database van de kaart.
Ledenkaart
Waar wonen onze leden?Toon jaar
Kaart wordt geladen…
Architectuur
De oplossing heeft drie lagen.
Bronlaag. De historie komt uit Excel-ledenlijsten per jaar, teruggebracht tot postcode, woonplaats en land. Een Python-script op de Mac mini leest ze in, zoekt elke woonplaats één keer op via OpenStreetMap en schrijft het resultaat direct naar MySQL. Vanaf nu legt WordPress elk jaar op 31 december zelf de stand vast uit de actieve lidmaatschappen in WooCommerce Memberships.
Opslaglaag. MySQL op de Mac mini, met een eigen database xm_ledenkaart. De tabel lk_plaatsen bevat per woonplaats de locatie, lk_momentopnamen per jaar per plaats het aantal leden, en lk_landen de landnamen en kaartuitsneden.
Presentatielaag. Een Code Snippet in WordPress biedt een eigen REST-endpoint dat alleen percentages en relatieve vlekken teruggeeft, met een cache van 12 uur. Een tweede snippet levert de jaarknoppen, een landenlijst met percentages en per land een kaart met Leaflet en Leaflet.heat op een rustige ondergrond van OpenStreetMap.
Privacy by design
Omdat het om woonadressen gaat, is de privacy vanaf het begin in het ontwerp meegenomen.
- De database bevat alleen per jaar per woonplaats een aantal, geen gegevens over personen.
- Elke plaats staat 1 tot 3 km verschoven op de kaart. De verschuiving is afgeleid van een hash van de plaatsnaam, zodat een punt elk jaar op dezelfde plek staat en de vlekken niet verspringen.
- De kleur van een vlek is relatief binnen het land: 100% is de plek met de meeste leden in dat land. Zo verraadt de kaart geen aantallen.
- Het endpoint geeft alleen percentages en relatieve gewichten terug, nooit de aantallen uit de database.
- WordPress gebruikt een eigen MySQL-gebruiker met alleen rechten op de drie tabellen van de kaart.
- Bij het opzoeken gaat alleen een plaatsnaam, postcode en landcode naar OpenStreetMap.
Software en techniek
- Python 3 met openpyxl en mysql-connector-python voor het importscript.
- OpenStreetMap Nominatim voor het opzoeken van woonplaatsen, met een lokale cache en maximaal één verzoek per seconde.
- MySQL op de Mac mini voor opslag.
- WordPress met Code Snippets voor het REST-endpoint, de jaarlijkse momentopname via WP-Cron en een beheerpagina.
- Leaflet 1.9.4 en Leaflet.heat voor de kaart en de vlekken, met de ondergrond op basis van OpenStreetMap.
Uitdagingen en oplossingen
Ledenlijsten die per jaar verschillen. Over de jaren veranderden de kolommen en de opbouw van de lidnummers. De oplossing was een vaste opzet van vier kolommen per jaar. Het script trekt landnamen gelijk naar landcodes en gebruikt het lidnummer alleen om dubbele regels binnen één jaar te verwijderen.
Plaatsen die niet gevonden worden. Van ruim 300 woonplaatsen vond OpenStreetMap er twee niet, door een afwijkende schrijfwijze of omdat het een dorp binnen een grotere gemeente is. Die krijgen hun locatie via een correctiebestand of een formulier op de beheerpagina in WordPress.
Verbinding tussen website en Mac mini. MySQL weigerde de website eerst, omdat de gebruiker alleen voor een ander adres bestond. De beheerpagina toont nu de exacte foutmelding van MySQL. Daarin stond de hostnaam waarmee de webserver binnenkomt, en daarmee was de gebruiker snel goed aangemaakt.
Resultaten
De ledenkaart toont voor elk jaar vanaf 2022 de verdeling van de leden over de landen, in procenten, en per land een kaart met vlekken van licht naar donker, met een legenda van 0% tot 100%. Op de beheerpagina in WordPress zie je in één oogopslag welke jaren en plaatsen in de database staan. Vanaf eind 2026 komt er elk jaar automatisch een nieuw jaar bij. De kaart gaat binnenkort live op de website van de XM Club.
Wat ik ervan geleerd heb
- Sla aggregaten op in plaats van personen: wat je niet bewaart, kan ook niet uitlekken.
- Een vaste, uit de plaatsnaam afgeleide verschuiving houdt een kaart anoniem én stabiel over de jaren.
- Een duidelijke foutmelding op de beheerpagina scheelt veel zoekwerk bij verbindingsproblemen.
- Houd configuratie gelijk over je scripts heen; dat maakt beheer en hergebruik eenvoudiger.
Vervolgstappen: de oudere jaren 2016 tot en met 2021 toevoegen, livegang op xmclub.nl, en een vergelijkmodus om twee jaren naast elkaar te zetten, met de groei of afname per land in procenten.