- Maps: MapLibre GL + self-hosted Protomaps tiles + ESRI satellite toggle - FAB 'Créer' chooser (incl. Soumission wizard) via useCreateSignal - Quote wizard: new default tiers (80/500/1500 @ 39.95/49.95/49.95), competitor price-match, mini-map pin w/ green check, carry qualified address - api/address.reverse() + reverseGeocodeOSM tries RQA-backed hub, Nominatim fallback Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
66 lines
4.4 KiB
JavaScript
66 lines
4.4 KiB
JavaScript
// Fond de carte OSM AUTO-HÉBERGÉ : MapLibre GL + tuiles Protomaps (pmtiles) servies same-origin (/ops/tiles).
|
|
// Zéro Mapbox → aucun jeton, aucun logo, aucun appel à api.mapbox.com. La seule attribution restante est
|
|
// « © OpenStreetMap » (exigée par la licence ODbL des données). Routage = notre OSRM ; tuiles = notre pmtiles.
|
|
import { ref } from 'vue'
|
|
import maplibregl from 'maplibre-gl'
|
|
import 'maplibre-gl/dist/maplibre-gl.css'
|
|
import { Protocol } from 'pmtiles'
|
|
import { layers, namedTheme } from 'protomaps-themes-base'
|
|
|
|
// Chemin des tuiles = base de l'app + tiles/. Dev : public/tiles/. Prod : /opt/ops-app/tiles/ servi sous /ops/tiles (Traefik strip /ops).
|
|
export const TILES_URL = `${import.meta.env.BASE_URL || '/'}tiles/southern-quebec.pmtiles`
|
|
// Glyphes (étiquettes) + sprites (icônes) : assets ouverts Protomaps — pas de Mapbox. {fontstack}/{range} résolus par MapLibre.
|
|
const GLYPHS = 'https://protomaps.github.io/basemaps-assets/fonts/{fontstack}/{range}.pbf'
|
|
const SPRITE = (t) => `https://protomaps.github.io/basemaps-assets/sprites/v4/${t}`
|
|
|
|
let _installed = false
|
|
// Alias maplibregl → window.mapboxgl : l'API (Map/Marker/Popup/LngLatBounds/NavigationControl) est compatible,
|
|
// donc tout le code existant qui référence window.mapboxgl continue de marcher sans réécriture. Enregistre aussi pmtiles://.
|
|
export function installMaplibre () {
|
|
if (!_installed) {
|
|
const protocol = new Protocol()
|
|
maplibregl.addProtocol('pmtiles', protocol.tile)
|
|
window.mapboxgl = maplibregl // compat : marqueurs / popups / bounds existants
|
|
_installed = true
|
|
}
|
|
return maplibregl
|
|
}
|
|
|
|
// Crée une carte MapLibre de planification avec les réglages COMMUNS (fond OSM + bouton zoom + Street View au clic droit).
|
|
// Réutilisé par les 4 cartes de PlanificationPage (détail job · sélecteur domicile/dépôt · panneau à assigner · éditeur jour)
|
|
// → une seule init au lieu de 4 blocs quasi identiques. Les marqueurs/couches spécifiques restent gérés par chaque appelant.
|
|
export function createPlanMap (el, opts = {}) {
|
|
installMaplibre()
|
|
const map = new maplibregl.Map({ container: el, style: basemapStyle(opts.theme || 'light'), center: opts.center || [-73.6756, 45.1599], zoom: opts.zoom != null ? opts.zoom : 9 })
|
|
map.addControl(new maplibregl.NavigationControl({ showCompass: false }), 'top-right')
|
|
if (typeof opts.streetView === 'function') map.on('contextmenu', (e) => opts.streetView(e.lngLat.lng, e.lngLat.lat)) // clic droit → Street View
|
|
return map
|
|
}
|
|
|
|
// Style MapLibre (thème Protomaps) : 'dark' pour le board Dispatch (exception mode sombre), 'light' pour les Tournées.
|
|
export function basemapStyle (theme = 'light') {
|
|
const th = theme === 'dark' ? 'dark' : 'light'
|
|
return {
|
|
version: 8,
|
|
glyphs: GLYPHS,
|
|
sprite: SPRITE(th),
|
|
sources: { protomaps: { type: 'vector', url: 'pmtiles://' + TILES_URL, attribution: '© OpenStreetMap' } },
|
|
layers: layers('protomaps', namedTheme(th), { lang: 'fr' }), // namedTheme() = objet flavor (v4) ; la string seule laisse les couleurs undefined
|
|
}
|
|
}
|
|
|
|
// ── Couche SATELLITE (ESRI World Imagery — libre, sans jeton). Réutilisable par toutes les cartes createPlanMap. ──
|
|
const SAT_TILES = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'
|
|
// Préférence satellite PARTAGÉE app-wide (même clé que PlanificationPage : 'plan_sat') → cohérente entre toutes les cartes.
|
|
export const satellitePref = ref((() => { try { return localStorage.getItem('plan_sat') === '1' } catch (e) { return false } })())
|
|
export function setSatellitePref (on) { satellitePref.value = !!on; try { localStorage.setItem('plan_sat', on ? '1' : '0') } catch (e) {} }
|
|
// Ajoute la couche raster satellite (sous les marqueurs, ajoutés APRÈS) + applique la visibilité. Idempotent.
|
|
export function addSatelliteLayer (map, visible) {
|
|
try {
|
|
if (!map.getSource('sat')) map.addSource('sat', { type: 'raster', tiles: [SAT_TILES], tileSize: 256, attribution: '© Esri, Maxar, Earthstar Geographics' })
|
|
if (!map.getLayer('sat')) map.addLayer({ id: 'sat', type: 'raster', source: 'sat', layout: { visibility: visible ? 'visible' : 'none' } })
|
|
else map.setLayoutProperty('sat', 'visibility', visible ? 'visible' : 'none')
|
|
} catch (e) { /* style pas prêt */ }
|
|
}
|
|
export function setSatelliteVisible (map, on) { try { if (map && map.getLayer && map.getLayer('sat')) map.setLayoutProperty('sat', 'visibility', on ? 'visible' : 'none') } catch (e) {} }
|