Spaces:
Sleeping
Sleeping
| /* app-render-map.js - Leaflet map rendering for the Bergamo FSR demo */ | |
| (function () { | |
| 'use strict'; | |
| var FSR = window.FSR = window.FSR || {}; | |
| var utils = FSR.utils; | |
| FSR.createMapRenderer = function (options) { | |
| return { renderMap: renderMap }; | |
| function renderMap(plan, routeGeometry) { | |
| if (!options.routeMap) return; | |
| options.routeMap.clearAll(); | |
| var locations = plan.locations || []; | |
| var visits = plan.service_visits || []; | |
| var routes = plan.technician_routes || []; | |
| var assigned = utils.assignedVisitSet(routes); | |
| var focusedRouteId = options.getFocusedRouteId ? options.getFocusedRouteId() : null; | |
| var fitPoints = []; | |
| var stopNumbers = []; | |
| var routeGeometryById = geometryByRouteId(routeGeometry); | |
| routes.forEach(function (route) { | |
| var start = locations[route.start_location_idx]; | |
| if (!start) return; | |
| fitPoints.push([utils.locationLat(start), utils.locationLng(start)]); | |
| options.routeMap.addVehicleMarker({ | |
| lat: utils.locationLat(start), | |
| lng: utils.locationLng(start), | |
| color: route.color || '#2563eb', | |
| }); | |
| }); | |
| visits.forEach(function (visit, idx) { | |
| var location = locations[visit.location_idx]; | |
| if (!location) return; | |
| fitPoints.push([utils.locationLat(location), utils.locationLng(location)]); | |
| options.routeMap.addVisitMarker({ | |
| lat: utils.locationLat(location), | |
| lng: utils.locationLng(location), | |
| color: assigned[idx] ? assigned[idx].color : '#64748b', | |
| icon: utils.iconForVisit(visit), | |
| assigned: !!assigned[idx], | |
| }); | |
| }); | |
| routes.forEach(function (route, routeIdx) { | |
| var routeId = routeKey(route, routeIdx); | |
| var style = routeStyle(route, routeId, focusedRouteId); | |
| drawRouteGeometry(routeGeometryById[routeId], route.color, style); | |
| (route.visits || []).forEach(function (visitIdx, sequenceIdx) { | |
| var visit = visits[visitIdx]; | |
| if (!visit) return; | |
| if (!focusedRouteId || focusedRouteId === routeId) { | |
| stopNumbers.push({ | |
| location: locations[visit.location_idx], | |
| number: sequenceIdx + 1, | |
| color: route.color, | |
| }); | |
| } | |
| }); | |
| }); | |
| placeStopNumbers(stopNumbers); | |
| fitMapToPoints(fitPoints); | |
| } | |
| function fitMapToPoints(points) { | |
| if (!points.length || !options.routeMap) return; | |
| if (options.routeMap.map && options.routeMap.map.invalidateSize) { | |
| options.routeMap.map.invalidateSize(); | |
| } | |
| if (window.L && options.routeMap.map && options.routeMap.map.fitBounds) { | |
| options.routeMap.map.fitBounds(window.L.latLngBounds(points), { | |
| maxZoom: 12, | |
| padding: [70, 70], | |
| }); | |
| return; | |
| } | |
| options.routeMap.fitBounds(); | |
| } | |
| function placeStopNumbers(stops) { | |
| var groups = {}; | |
| stops.forEach(function (stop) { | |
| if (!stop.location) return; | |
| var key = stopLocationKey(stop.location); | |
| if (!groups[key]) groups[key] = []; | |
| groups[key].push(stop); | |
| }); | |
| Object.keys(groups).forEach(function (key) { | |
| var group = groups[key]; | |
| group.forEach(function (stop, idx) { | |
| addStopNumber(stop.location, stop.number, stop.color, stopOffset(idx, group.length)); | |
| }); | |
| }); | |
| } | |
| function stopLocationKey(location) { | |
| return [ | |
| utils.locationLat(location).toFixed(6), | |
| utils.locationLng(location).toFixed(6), | |
| ].join(','); | |
| } | |
| function stopOffset(index, count) { | |
| if (count <= 1) return { x: 0, y: 0 }; | |
| var angle = (-Math.PI / 2) + ((Math.PI * 2 * index) / count); | |
| var radius = count === 2 ? 14 : 18; | |
| return { x: Math.cos(angle) * radius, y: Math.sin(angle) * radius }; | |
| } | |
| function addStopNumber(location, number, color, offset) { | |
| if (!location) return; | |
| var marker = options.routeMap.addStopNumber({ | |
| lat: utils.locationLat(location), | |
| lng: utils.locationLng(location), | |
| number: number, | |
| color: color || '#2563eb', | |
| }); | |
| applyStopOffset(marker, offset || { x: 0, y: 0 }); | |
| } | |
| function applyStopOffset(marker, offset) { | |
| if (!marker || (!offset.x && !offset.y)) return; | |
| var element = marker.getElement ? marker.getElement() : marker._icon; | |
| var stop = element && element.querySelector ? element.querySelector('.sf-marker-stop') : null; | |
| if (stop) stop.style.transform = 'translate(' + offset.x.toFixed(1) + 'px, ' + offset.y.toFixed(1) + 'px)'; | |
| } | |
| function geometryByRouteId(routeGeometry) { | |
| return ((routeGeometry && routeGeometry.routes) || []).reduce(function (index, route) { | |
| index[String(route.routeId)] = route; | |
| return index; | |
| }, {}); | |
| } | |
| function drawRouteGeometry(routeGeometry, color, style) { | |
| if (!routeGeometry || !routeGeometry.segments) return; | |
| routeGeometry.segments.forEach(function (segment) { | |
| if (segment.geometryStatus !== 'ROUTED' || !segment.reachable || !segment.encodedPolyline) return; | |
| options.routeMap.drawEncodedRoute({ | |
| encoded: segment.encodedPolyline, | |
| color: color || '#2563eb', | |
| opacity: style && style.opacity, | |
| weight: style && style.weight, | |
| }); | |
| }); | |
| } | |
| function routeKey(route, idx) { | |
| return String(route.id || route.technician_name || ('route-' + idx)); | |
| } | |
| function routeStyle(route, routeId, focusedRouteId) { | |
| if (!focusedRouteId) return { color: route.color || '#2563eb', opacity: 0.82, weight: 3 }; | |
| return focusedRouteId === routeId | |
| ? { color: route.color || '#2563eb', opacity: 1, weight: 5 } | |
| : { color: route.color || '#2563eb', opacity: 0.18, weight: 2 }; | |
| } | |
| }; | |
| })(); | |