// Carte principale var mymap; var poiLayer; var vectorLayer; var icons = {}; $(document).ready(function() { // --------------------------------------------------------- // Fonds de carte // --------------------------------------------------------- var topo_maptiler = L.tileLayer( 'https://api.maptiler.com/maps/topographique/{z}/{x}/{y}.png?key=Sm8M7mJ53GtYdl773rpi', { tms: false, tileSize: 512, zoomOffset: -1, minZoom: 1, attribution: 'Carte © MapTiler, Données © OpenStreetMap' } ); var ign = L.tileLayer( 'https://data.geopf.fr/private/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&STYLE=normal&TILEMATRIXSET=PM&FORMAT=image/jpeg&LAYER=GEOGRAPHICALGRIDSYSTEMS.MAPS&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&apikey=ign_scan_ws', { attribution: 'Carte © IGN-F/Géoportail' } ); var baseLayers = { "OpenStreetMap": topo_maptiler, "IGN France": ign }; // --------------------------------------------------------- // Carte // --------------------------------------------------------- mymap = L.map('mapid', { zoomControl: false, layers: [topo_maptiler] }).setView([47, 3], 6); // Pane dédiée pour les vector tiles, au-dessus du fond if (!mymap.getPane('vectorPane')) { mymap.createPane('vectorPane'); mymap.getPane('vectorPane').style.zIndex = 650; mymap.getPane('vectorPane').style.pointerEvents = 'auto'; } $("#map-credits").html(topo_maptiler.getAttribution()); poiLayer = L.layerGroup().addTo(mymap); // --------------------------------------------------------- // Styles des POIs pour les vector tiles // --------------------------------------------------------- // Fonction qui génère le SVG du point pour les calques en pbf function svgDataUriFor(color, size, shape, strokeOpacity = 0.4) { const w = size, h = size, strokeWidth = 1, r = Math.max(1, Math.round(size * 0.15)); const inset = 2 + strokeWidth / 2, innerW = w - inset * 2, innerH = h - inset * 2; let shapeSvg; if (shape === 'triangle') { const p1 = `${w/2},${inset}`, p2 = `${w-inset},${h-inset}`, p3 = `${inset},${h-inset}`, points = `${p1} ${p2} ${p3}`; shapeSvg = ` `; } else { shapeSvg = ` `; } const svg = `${shapeSvg}`; return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`; } function getPoiStyle(type) { if (window.poi_style_map && window.poi_style_map[type]) { return window.poi_style_map[type]; } // Final hardcoded fallback (keeps previous defaults) return { color: "#ef2929", size: 14, shape: "square" }; } function buildPoiVectorIcon(properties, zoom) { var type = (properties && (properties.type || properties.poi_type || properties.poiType)) || "default"; var style = getPoiStyle(type); var scale = 1; if (zoom === 8) { scale = 1.1; } else if (zoom >= 9) { scale = 1.2; } var size = Math.max(1, Math.round(style.size * scale)); var iconUrl = svgDataUriFor(style.color, size, style.shape); return L.icon({ iconUrl: iconUrl, iconSize: [size, size], iconAnchor: [Math.floor(size / 2), size], popupAnchor: [0, -size] }); } // Couche vectorielle utilisée pour les faibles zooms vectorLayer = L.vectorGrid.protobuf( root + "poi/tile/{z}/{x}/{y}.pbf", { maxZoom: 9, rendererFactory: L.svg.tile, pane: 'vectorPane', vectorTileLayerStyles: { pois: function(properties, zoom) { var icon = buildPoiVectorIcon(properties, zoom); return { icon: icon, }; } } } ); // --------------------------------------------------------- // Contrôles // --------------------------------------------------------- L.control.scale({ position: "bottomleft", imperial: false }).addTo(mymap); var credits = L.easyButton( 'fa-info', function() { $("footer").hide(); $("#footer-credits").show(); $("#footer-legend").hide(); }, 'Credits' ); var legend = L.easyButton( 'fa-question', function() { $("footer").hide(); $("#footer-credits").hide(); $("#footer-legend").show(); }, 'Legend' ); L.easyBar( [credits, legend], { position: "bottomleft" } ).addTo(mymap); L.control.fullscreen({ position: "bottomleft" }).addTo(mymap); L.control.zoom({ zoomOutText: '', zoomInText: '', position: "bottomleft" }).addTo(mymap); L.control.layers( baseLayers, null, { position: "bottomright" } ).addTo(mymap); mymap.removeControl(mymap.attributionControl); // --------------------------------------------------------- // Footer // --------------------------------------------------------- $(".close-link").click(function() { $("footer").show(); $("#footer-credits").hide(); $("#footer-legend").hide(); }); mymap.on('baselayerchange', function(e) { $("#map-credits").html( e.layer.getAttribution() ); if (mymap.hasLayer(vectorLayer)) { if (vectorLayer.bringToFront) { vectorLayer.bringToFront(); } else { mymap.removeLayer(vectorLayer); mymap.addLayer(vectorLayer); } } }); // --------------------------------------------------------- // Chargement des POI visibles // --------------------------------------------------------- function loadVisiblePois() { if (mymap.getZoom() < 10) return; var b = mymap.getBounds(); var currentZoom = mymap.getZoom(); // helper local : construit un L.Icon en fonction du type et du zoom function getMarkerIconFor(type, zoom) { var size = (zoom > 11) ? 32 : 24; return L.icon({ iconUrl: window.poi_icons[type], iconSize: [size, size], iconAnchor: [Math.floor(size / 2), size], popupAnchor: [0, -size] }); } $.getJSON( root + "poi/api_bbox", { north: b.getNorth(), south: b.getSouth(), east: b.getEast(), west: b.getWest() }, function(pois) { poiLayer.clearLayers(); pois.forEach(function(poi) { var markerIcon = getMarkerIconFor(poi.type, currentZoom); var marker = L.marker( [poi.lat, poi.lon], { icon: markerIcon } ); marker.bindTooltip( poi.name, { direction: "top", offset: [0, -10], opacity: 0.9, className: "poi-tooltip" } ); marker.on("click", function() { window.location = root + "poi/" + poi.permalink; }); poiLayer.addLayer(marker); }); } ); } // --------------------------------------------------------- // Bascule entre le calque vectoriel et geojson // --------------------------------------------------------- function switchLayers() { var z = mymap.getZoom(); if (z < 10) { poiLayer.clearLayers(); if (!mymap.hasLayer(vectorLayer)) { mymap.addLayer(vectorLayer); } } else { if (mymap.hasLayer(vectorLayer)) { mymap.removeLayer(vectorLayer); } loadVisiblePois(); } } // --------------------------------------------------------- // Rechargement automatique // --------------------------------------------------------- mymap.on("zoomend", switchLayers); mymap.on("moveend", function() { if (mymap.getZoom() >= 10) { loadVisiblePois(); } }); switchLayers(); });