Intégrer des points JSON externes
Objectif
Pattern associé: application -> carte.
Cette intégration est faisable sans créer de composant dans l’API Geoviewer.
Le principe est de charger le viewer côté application, récupérer viewerService, ajouter une couche runtime GRAPHICS, puis transformer un tableau JSON métier en features cartographiques cliquables.
Objets API utilisés
Flux de données
- l’application charge ou reçoit un tableau JSON externe
- elle récupère
viewerServicevia le Web Component - elle crée une couche
GRAPHICSavecmapManager.addGraphicMapService(...) - elle transforme chaque entrée JSON en
ApiFeaturePoint - elle choisit un marker selon
travelModes - elle écoute le clic sur la couche pour zoomer et ouvrir une popup
Démo visuelle
La page ci-dessous est une vraie instanciation du viewer tutoriel JSON_POINT avec:
- un jeu de données JSON servi par cette webapp doc
- trois markers SVG servis par cette webapp doc
- une symbolisation pilotée par
travelModes - une couche
GRAPHICSajoutée à chaud - un clic marker => zoom et infoWindow
Fichiers hébergés pour cette démo:
doc/public/tutorials/json-point-demo/index.htmldoc/public/tutorials/json-point-demo/data/eco-vision-counters.jsondoc/public/tutorials/json-point-demo/markers/marker-pedestrian.svgdoc/public/tutorials/json-point-demo/markers/marker-bike.svgdoc/public/tutorials/json-point-demo/markers/marker-mixed.svg
Snippet minimal
L’exemple ci-dessous part d’un tableau JSON:
[ { "id": "100056099", "coords": [50.459927476934546, 4.871012439320255], "name": "L'Enjambée Jambes", "domain": { "id": "4320", "name": "Ville de Namur" }, "travelModes": ["pedestrian", "bike"] }]Important: dans cet exemple, coords est fourni sous la forme [latitude, longitude].
Pour créer un point Geoviewer, il faut passer x = longitude et y = latitude.
Règle de symbolisation utilisée dans la démo:
["pedestrian"]=> marker piéton["bike"]=> marker vélo["pedestrian", "bike"]=> marker mixte
<!doctype html><html lang="fr"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Compteurs externes</title>
<style> html, body, geoviewer-esri-element { width: 100%; height: 100%; margin: 0; padding: 0; } </style>
<script src="https://geoservices.wallonie.be/geoviewer-api/api/<TOKEN>/<CODE_VIEWER>"></script> </head>
<body> <geoviewer-esri-element id="viewer"></geoviewer-esri-element>
<script type="module"> const MARKERS = { pedestrian: '/markers/marker-pedestrian.svg', bike: '/markers/marker-bike.svg', mixed: '/markers/marker-mixed.svg', };
function resolveTravelModeKey(travelModes) { const normalized = [...new Set((travelModes ?? []).map((mode) => String(mode).toLowerCase()))].sort();
if (normalized.includes('pedestrian') && normalized.includes('bike')) { return 'mixed'; }
if (normalized.includes('bike')) { return 'bike'; }
return 'pedestrian'; }
function resolveMarkerSymbol(travelModes) { return { type: 'picture-point', source: MARKERS[resolveTravelModeKey(travelModes)], width: 30, height: 30, }; }
const viewerElement = document.getElementById('viewer');
viewerElement.onViewerServiceReady((viewerService) => { viewerService.onReady(async () => { const response = await fetch('https://mon-api.exemple/eco-vision/counters'); const counters = await response.json();
const mapManager = viewerService.mapManager; const featureFactory = mapManager.tools.featureFactory; const zoom = mapManager.tools.zoom; const events = mapManager.tools.events;
const countersLayer = mapManager.addGraphicMapService({ id: 'eco-vision-counters', type: 'GRAPHICS', label: 'Compteurs', visible: true, removable: true, identifiable: true, opacity: 1, toc: { visible: true, open: false, }, objectIdProperty: 'id', editing: false, isDraw: false, symbols: { point: { type: 'simple-point', color: '#0f766e', size: 10, }, polyline: { type: 'simple-polyline', color: '#0f766e', width: 2, style: 'SOLID', }, polygon: { type: 'simple-polygon', color: [15, 118, 110, 0.2], border: { type: 'simple-polyline', color: '#0f766e', width: 1, style: 'SOLID', }, }, }, });
const features = counters.map((counter) => featureFactory.createPoint({ x: counter.coords[1], y: counter.coords[0], wkid: 4326, symbol: resolveMarkerSymbol(counter.travelModes), attributes: { id: counter.id, name: counter.name, domainId: counter.domain?.id ?? null, domainName: counter.domain?.name ?? null, travelModes: (counter.travelModes ?? []).join(', '), markerType: resolveTravelModeKey(counter.travelModes), raw: counter, }, }), );
countersLayer.addFeatures(features);
events.listenGraphicMapServiceClick(countersLayer, async (clickedFeatures) => { const [feature] = clickedFeatures; if (!feature) { return; }
await zoom.zoomToFeature(feature);
await mapManager.openPopup( feature.attributes.name, ` <div> <strong>ID:</strong> ${feature.attributes.id}<br /> <strong>Marker:</strong> ${feature.attributes.markerType}<br /> <strong>Modes:</strong> ${feature.attributes.travelModes} </div> `, { location: feature, popupPosition: 'top-right', }, ); }); }); }); </script> </body></html>Mettre à jour les points
Si les données changent, il n’est pas nécessaire de recréer le viewer.
Il suffit de réutiliser la couche GRAPHICS:
function replaceCounters(countersLayer, counters, featureFactory) { countersLayer.removeAll();
const features = counters.map((counter) => featureFactory.createPoint({ x: counter.coords[1], y: counter.coords[0], wkid: 4326, symbol: resolveMarkerSymbol(counter.travelModes), attributes: { id: counter.id, name: counter.name, markerType: resolveTravelModeKey(counter.travelModes), }, }), );
countersLayer.addFeatures(features);}Pièges fréquents
- Vérifiez l’ordre des coordonnées. Les API renvoient parfois
[latitude, longitude], alors que Geoviewer attendx = longitude,y = latitude. - Gardez la couche
GRAPHICSet remplacez seulement ses features quand les données changent. Inutile de recréer le viewer. - Le marker peut être porté par
feature.symbol, ce qui est pratique pour une symbolisation pilotée par attribut. - Si vous avez besoin d’une UI métier embarquée dans le viewer, ce n’est plus une simple intégration externe: il faudra envisager un widget dédié.