dawarich/app/javascript/maps/polylines.js
2025-01-10 23:03:07 +01:00

251 lines
7.8 KiB
JavaScript

import { formatDate } from "../maps/helpers";
import { formatDistance } from "../maps/helpers";
import { getUrlParameter } from "../maps/helpers";
import { minutesToDaysHoursMinutes } from "../maps/helpers";
import { haversineDistance } from "../maps/helpers";
function getSpeedColor(speedKmh) {
console.log('Speed to color:', speedKmh + ' km/h');
if (speedKmh > 100) {
console.log('Red - Very fast');
return '#FF0000';
}
if (speedKmh > 70) {
console.log('Orange - Fast');
return '#FFA500';
}
if (speedKmh > 40) {
console.log('Yellow - Moderate');
return '#FFFF00';
}
if (speedKmh > 20) {
console.log('Light green - Normal');
return '#90EE90';
}
console.log('Green - Slow');
return '#008000';
}
function calculateSpeed(point1, point2) {
const distanceKm = haversineDistance(point1[0], point1[1], point2[0], point2[1]); // in kilometers
const timeDiffSeconds = point2[4] - point1[4];
// Convert to km/h: (kilometers / seconds) * (3600 seconds / hour)
const speed = (distanceKm / timeDiffSeconds) * 3600;
console.log('Speed calculation:', {
distance: distanceKm + ' km',
timeDiff: timeDiffSeconds + ' seconds',
speed: speed + ' km/h',
point1: point1,
point2: point2
});
return speed;
}
export function addHighlightOnHover(polylineGroup, map, polylineCoordinates, userSettings, distanceUnit) {
const highlightStyle = { opacity: 1, weight: 5 };
const normalStyle = { opacity: userSettings.routeOpacity, weight: 3 };
const startPoint = polylineCoordinates[0];
const endPoint = polylineCoordinates[polylineCoordinates.length - 1];
const firstTimestamp = formatDate(startPoint[4], userSettings.timezone);
const lastTimestamp = formatDate(endPoint[4], userSettings.timezone);
const minutes = Math.round((endPoint[4] - startPoint[4]) / 60);
const timeOnRoute = minutesToDaysHoursMinutes(minutes);
const totalDistance = polylineCoordinates.reduce((acc, curr, index, arr) => {
if (index === 0) return acc;
const dist = haversineDistance(arr[index - 1][0], arr[index - 1][1], curr[0], curr[1]);
return acc + dist;
}, 0);
const startIcon = L.divIcon({ html: "🚥", className: "emoji-icon" });
const finishIcon = L.divIcon({ html: "🏁", className: "emoji-icon" });
const startMarker = L.marker([startPoint[0], startPoint[1]], { icon: startIcon });
const endMarker = L.marker([endPoint[0], endPoint[1]], { icon: finishIcon });
let hoverPopup = null;
polylineGroup.on("mouseover", function (e) {
// Find the closest segment and its speed
let closestSegment = null;
let minDistance = Infinity;
let currentSpeed = 0;
polylineGroup.eachLayer((layer) => {
if (layer instanceof L.Polyline) {
const layerLatLngs = layer.getLatLngs();
const distance = L.LineUtil.pointToSegmentDistance(
e.latlng,
layerLatLngs[0],
layerLatLngs[1]
);
if (distance < minDistance) {
minDistance = distance;
closestSegment = layer;
// Get the coordinates of the segment
const startPoint = layerLatLngs[0];
const endPoint = layerLatLngs[1];
console.log('Closest segment found:', {
startPoint,
endPoint,
distance
});
// Find matching points in polylineCoordinates
const startIdx = polylineCoordinates.findIndex(p => {
const latMatch = Math.abs(p[0] - startPoint.lat) < 0.0000001;
const lngMatch = Math.abs(p[1] - startPoint.lng) < 0.0000001;
return latMatch && lngMatch;
});
console.log('Start point index:', startIdx);
console.log('Original point:', startIdx !== -1 ? polylineCoordinates[startIdx] : 'not found');
if (startIdx !== -1 && startIdx < polylineCoordinates.length - 1) {
currentSpeed = calculateSpeed(
polylineCoordinates[startIdx],
polylineCoordinates[startIdx + 1]
);
console.log('Speed calculated:', currentSpeed);
}
}
}
});
// Highlight all segments in the group
polylineGroup.eachLayer((layer) => {
if (layer instanceof L.Polyline) {
layer.setStyle({
...highlightStyle,
color: layer.options.originalColor
});
}
});
startMarker.addTo(map);
endMarker.addTo(map);
const popupContent = `
<strong>Start:</strong> ${firstTimestamp}<br>
<strong>End:</strong> ${lastTimestamp}<br>
<strong>Duration:</strong> ${timeOnRoute}<br>
<strong>Total Distance:</strong> ${formatDistance(totalDistance, distanceUnit)}<br>
<strong>Current Speed:</strong> ${Math.round(currentSpeed)} km/h
`;
if (hoverPopup) {
map.closePopup(hoverPopup);
}
hoverPopup = L.popup()
.setLatLng(e.latlng)
.setContent(popupContent)
.openOn(map);
});
polylineGroup.on("mouseout", function () {
// Restore original styles for all segments
polylineGroup.eachLayer((layer) => {
if (layer instanceof L.Polyline) {
layer.setStyle({
...normalStyle,
color: layer.options.originalColor
});
}
});
if (hoverPopup) {
map.closePopup(hoverPopup);
}
map.removeLayer(startMarker);
map.removeLayer(endMarker);
});
polylineGroup.on("click", function () {
map.fitBounds(polylineGroup.getBounds());
});
}
export function createPolylinesLayer(markers, map, timezone, routeOpacity, userSettings, distanceUnit) {
const splitPolylines = [];
let currentPolyline = [];
const distanceThresholdMeters = parseInt(userSettings.meters_between_routes) || 500;
const timeThresholdMinutes = parseInt(userSettings.minutes_between_routes) || 60;
// Split into separate polylines based on distance/time thresholds
for (let i = 0, len = markers.length; i < len; i++) {
if (currentPolyline.length === 0) {
currentPolyline.push(markers[i]);
} else {
const lastPoint = currentPolyline[currentPolyline.length - 1];
const currentPoint = markers[i];
const distance = haversineDistance(lastPoint[0], lastPoint[1], currentPoint[0], currentPoint[1]);
const timeDifference = (currentPoint[4] - lastPoint[4]) / 60;
if (distance > distanceThresholdMeters || timeDifference > timeThresholdMinutes) {
splitPolylines.push([...currentPolyline]);
currentPolyline = [currentPoint];
} else {
currentPolyline.push(currentPoint);
}
}
}
if (currentPolyline.length > 0) {
splitPolylines.push(currentPolyline);
}
return L.layerGroup(
splitPolylines.map((polylineCoordinates) => {
const segmentGroup = L.featureGroup();
// Create segments with different colors based on speed
for (let i = 0; i < polylineCoordinates.length - 1; i++) {
const speed = calculateSpeed(polylineCoordinates[i], polylineCoordinates[i + 1]);
const color = getSpeedColor(speed);
const segment = L.polyline(
[
[polylineCoordinates[i][0], polylineCoordinates[i][1]],
[polylineCoordinates[i + 1][0], polylineCoordinates[i + 1][1]]
],
{
color: color,
originalColor: color,
opacity: routeOpacity,
weight: 3
}
);
segmentGroup.addLayer(segment);
}
// Add hover effect to the entire group of segments
addHighlightOnHover(segmentGroup, map, polylineCoordinates, userSettings, distanceUnit);
return segmentGroup;
})
).addTo(map);
}
export function updatePolylinesOpacity(polylinesLayer, opacity) {
polylinesLayer.eachLayer((groupLayer) => {
if (groupLayer instanceof L.LayerGroup) {
groupLayer.eachLayer((segment) => {
if (segment instanceof L.Polyline) {
segment.setStyle({ opacity: opacity });
}
});
}
});
}