import L from 'leaflet'; import markerIcon2x from 'leaflet/dist/images/marker-icon-2x.png'; import markerIcon from 'leaflet/dist/images/marker-icon.png'; import markerShadow from 'leaflet/dist/images/marker-shadow.png'; delete L.Icon.Default.prototype._getIconUrl; L.Icon.Default.mergeOptions({ iconUrl: markerIcon, iconRetinaUrl: markerIcon2x, shadowUrl: markerShadow, }); function escHtml(str) { return String(str ?? '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } const CLASSIFICATION_COLOURS = { current: '#22c55e', recent: '#64748b', stale: '#f59e0b', outdated: '#ef4444', }; const UK_CENTRE = [54.0, -2.0]; const UK_ZOOM = 6; export function stationMap(results) { return { results, _map: null, _markers: [], init() { this._map = L.map(this.$el, { zoomControl: true }).setView(UK_CENTRE, UK_ZOOM); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors', maxZoom: 19, }).addTo(this._map); if (this.results && this.results.length > 0) { this._plotMarkers(); } this.$watch('results', () => this._plotMarkers()); }, _clearMarkers() { this._markers.forEach((m) => m.remove()); this._markers = []; }, destroy() { if (this._map) { this._map.remove(); this._map = null; this._markers = []; } }, _plotMarkers() { if (!this._map) { return; } this._clearMarkers(); if (!this.results || this.results.length === 0) { return; } const bounds = []; this.results.forEach((station) => { const colour = CLASSIFICATION_COLOURS[station.price_classification] ?? '#64748b'; const miles = (station.distance_km * 0.621371).toFixed(1); const supermarketTag = station.is_supermarket ? 'Supermarket' : ''; const popup = `
${escHtml(station.name)}${supermarketTag}
${Number(station.price).toFixed(1)}p
${escHtml(miles)} miles away
${escHtml(station.address)}, ${escHtml(station.postcode)}
`; const marker = L.circleMarker([station.lat, station.lng], { radius: 9, fillColor: colour, color: '#ffffff', weight: 2, opacity: 1, fillOpacity: 0.85, }).bindPopup(popup); marker.addTo(this._map); this._markers.push(marker); bounds.push([station.lat, station.lng]); }); if (bounds.length === 1) { this._map.setView(bounds[0], 14); } else { this._map.fitBounds(bounds, { padding: [40, 40], maxZoom: 14 }); } }, }; }