Cartographie interactive multi-géométrie : bloc Gutenberg et page d'archive POI synchronisée

Développement en sous-traitance de l'ensemble du système cartographique du site WordPress de l'EPAGE HuCA : un bloc Gutenberg réutilisable (POI en trois géométries, popups, légende dynamique, RGAA) et une page d'archive des POI en split-screen avec liste et carte synchronisées au filtrage.

Contexte

Refonte du site de l'EPAGE HuCA, établissement public de gestion des cours d'eau et des risques d'inondation sur le bassin de l'Huveaune. Projet mené en sous-traitance pour Canopée, qui a réalisé le thème WordPress sur mesure (huca-theme). Mon périmètre : l'ensemble du système de cartographie interactive — bloc Gutenberg réutilisable et page d'archive des points d'intérêt.

Le bloc Gutenberg « Cartes interactives »

Bloc ACF enregistré nativement via block.json (apiVersion 2, namespace acf/blockmaps), sans passer par @wordpress/scripts. Le rendu est délégué à un template PHP classique (renderTemplate: blockmaps.php) plutôt qu'à un render_callback en JS — approche ACF Blocks standard, plus simple à maintenir pour un client non-développeur. Catégorie d'éditeur dédiée (huca-theme-category), dépendances déclarées wp-element et wp-blocks, support couleur limité au fond uniquement (texte et lien désactivés pour éviter les incohérences graphiques).

Récupération des POI : taxonomies croisées

Le bloc interroge le CPT poi via WP_Query avec un tax_query en relation AND sur deux taxonomies : poi_category (famille de POI) et area (zone géographique). Les catégories et zones sélectionnées dans l'éditeur Gutenberg peuvent arriver sous deux formes selon le contexte ACF — objets complets ou simples IDs — d'où une vérification is_object($tableau[0]) avant d'extraire les term_id via wp_list_pluck. Si aucune sélection n'est faite, le bloc récupère automatiquement tous les termes existants (get_terms avec hide_empty=0).

Couleurs et pictogrammes par catégorie

Chaque catégorie de POI porte ses propres champs ACF au niveau du terme de taxonomie : poi_category_color (avec repli sur #fac02c si non renseigné) et poi_category_icon. Cas particulier : la catégorie « bassin-versant » est récupérée explicitement par son slug pour extraire son icône, réutilisée comme icône générique dans le rendu des popups quel que soit le POI concerné.

Trois types de géométrie, une seule structure de données

Le champ ACF poi_type détermine comment les coordonnées sont lues : marker combine poi_latitude/poi_longitude en une chaîne "lat,lng", linestring et polygon lisent directement les champs poi_linestring / poi_polygon, saisis en base sous forme de chaînes brutes lat,lng;lat,lng;.... Un POI sans poi_type renseigné est ignoré et signalé (console.log côté navigateur pour repérage rapide en back-office).

Affichage des projets sur la même carte

Le champ blockmaps_quelles_actions_afficher propose trois modes : Aucunes, Toutes (tous les posts project publiés, filtrés par zone si des zones sont sélectionnées dans le bloc), Certaines (sélection manuelle via blockmaps_actions). Les projets sont injectés dans le même tableau de données que les POI, avec une classe CSS dédiée cat-project et une couleur fixe (#1D71B8) qui les distingue visuellement sur la carte et dans la légende.

Transmission des données PHP → JavaScript

Pas d'appel REST : les données sont sérialisées directement en JSON dans une balise <script> inline, stockées dans window.blockmapsInstances indexé par l'ID unique du bloc ($block['id']). Ce choix permet à plusieurs instances du bloc de coexister sur une même page sans collision, chacune initialisant sa propre carte Mapbox au chargement du DOM.

Parsing des coordonnées et génération du GeoJSON

La fonction parseRawCoordinates() découpe la chaîne brute par ; puis par ,, valide chaque paire avec parseFloat et isNaN, puis inverse l'ordre ([longitude, latitude], format GeoJSON). Pour les polygones : suppression des points internes dupliqués (en conservant premier et dernier point) et fermeture automatique de l'anneau si le dernier point ne correspond pas au premier.

Rendu Mapbox : marqueurs DOM et couche de détection invisible

Les marqueurs visibles sont des éléments div HTML positionnés via mapboxgl.Marker, colorés par element.color. Problème technique : l'API queryRenderedFeatures de Mapbox GL ne détecte pas les marqueurs DOM. Solution : une couche symbol invisible (icon-opacity: 0, icône custom-goutte) est superposée aux mêmes coordonnées, uniquement pour capter les clics et retrouver le POI correspondant.

Ordre d'empilement et gestion des popups

Avant rendu, les données sont triées (usort) dans l'ordre polygone → ligne → marqueur, pour que les points restent toujours cliquables au-dessus des zones et des lignes. Deux popups distinctes coexistent avec un état géré manuellement (_clickPopup, _hoverPopup, _clickedFeatureId) : le survol affiche uniquement le titre, le clic ouvre une popup complète (catégorie avec pictogramme, titre, bassin versant, description, image, lien « en savoir plus »). Le survol est désactivé sur l'élément actuellement cliqué pour éviter les conflits d'affichage. La détection de clic priorise la couche de marqueurs, puis retombe sur une recherche de géométrie ligne ou polygone si rien n'est trouvé.

Légende dynamique

Construite en JavaScript à partir des données réellement affichées, avec dédoublonnage par clé composite (type + couleur + classe) via un Set. Rendu différencié par type : carré coloré pour les marqueurs, trait supérieur coloré pour les lignes, rectangle arrondi avec bordure pour les polygones. Entrée fixe « Projets et Réalisations » ajoutée en tête si des éléments de classe cat-project sont présents.

La page d'archive des POI : un système distinct

page-poi.php n'est qu'une redirection (wp_redirect) vers l'archive native du CPT, gérée par archive-poi.php. Cette page utilise une implémentation entièrement différente du bloc Gutenberg — plus ancienne, en jQuery. Mise en page split-screen : liste filtrable à gauche (shortcode Search & Filter), carte Mapbox à droite. Chaque POI de la boucle WordPress pousse un objet dans un tableau JS listMarkers (coordonnées, couleur, description). Les popups ne sont pas construites en JS : elles sont pré-générées côté PHP dans des éléments cachés (article#popup-{id}) puis récupérées via jQuery (.html()) au moment de l'ouverture.

Synchronisation liste/carte au filtrage

À chaque requête AJAX du plugin Search & Filter, l'événement sf:ajaxstart vide le tableau listMarkers, puis sf:ajaxfinish reconstruit les marqueurs et met à jour le compteur de résultats. Le centrage de la carte s'adapte au nombre de résultats : recentrage sur la position par défaut si aucun marqueur, zoom rapproché sur l'unique résultat si un seul, fitBounds sur l'ensemble sinon.

Environnement technique

Le thème repose sur jQuery et Bootstrap 5 pour l'ensemble de ses composants (functions.php : enqueue de jquery, popper, bootstrap.js), ce qui explique le choix de jQuery pour archive-poi.php — cohérent avec le reste du thème — face à du JavaScript natif pour le bloc Gutenberg, développé indépendamment.

Note

Le bloc Gutenberg est livré et fonctionnel. À ce jour, le client a fait le choix de ne pas l'utiliser sur le site en production.