Created
June 19, 2025 12:14
-
-
Save youssef22222/86a99b6082b38c76b712d6be021ff139 to your computer and use it in GitHub Desktop.
Speed Violation Map - 6379 XBDMazda (FMC150) - 1750335295
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Speed Violation Path - 6379 XBDMazda (FMC150)</title> | |
<meta charset="utf-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" /> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> | |
<style> | |
html, body { | |
height: 100%; | |
margin: 0; | |
padding: 0; | |
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif; | |
} | |
#map { | |
height: 100%; | |
position: relative; | |
} | |
/* Enhanced marker styles */ | |
.number-icon { | |
background-color: #FF1744; | |
border: 2px solid white; | |
border-radius: 50%; | |
color: white; | |
font-weight: bold; | |
text-align: center; | |
font-size: 12px; | |
line-height: 20px; | |
width: 24px; | |
height: 24px; | |
margin-left: -12px; | |
margin-top: -12px; | |
box-shadow: 0 2px 5px rgba(0,0,0,0.3); | |
} | |
.violation-icon { | |
background-color: #FF1744; | |
border: 3px solid white; | |
width: 32px; | |
height: 32px; | |
line-height: 26px; | |
font-size: 16px; | |
font-weight: bold; | |
margin-left: -16px; | |
margin-top: -16px; | |
position: relative; | |
} | |
/* Pulsing animation for last violation point */ | |
.pulse-ring { | |
border: 3px solid #FF1744; | |
border-radius: 50%; | |
height: 50px; | |
width: 50px; | |
position: absolute; | |
left: 50%; | |
top: 50%; | |
transform: translate(-50%, -50%); | |
animation: pulsate 2s ease-out; | |
animation-iteration-count: infinite; | |
opacity: 0; | |
} | |
@keyframes pulsate { | |
0% { | |
transform: translate(-50%, -50%) scale(0.1, 0.1); | |
opacity: 0; | |
} | |
50% { | |
opacity: 1; | |
} | |
100% { | |
transform: translate(-50%, -50%) scale(1.2, 1.2); | |
opacity: 0; | |
} | |
} | |
/* Enhanced popup styles */ | |
.popup-content { | |
min-width: 280px; | |
font-family: inherit; | |
} | |
.popup-content table { | |
width: 100%; | |
border-collapse: collapse; | |
} | |
.popup-content td { | |
padding: 4px 8px; | |
border-bottom: 1px solid #eee; | |
font-size: 13px; | |
} | |
.popup-content td:first-child { | |
font-weight: 600; | |
width: 45%; | |
color: #555; | |
} | |
.popup-content td:last-child { | |
color: #2c3e50; | |
} | |
.popup-title { | |
font-weight: 600; | |
color: #2c3e50; | |
margin-bottom: 8px; | |
padding-bottom: 8px; | |
border-bottom: 2px solid #e9ecef; | |
font-size: 16px; | |
} | |
.speed-violation { | |
color: #e74c3c; | |
font-weight: bold; | |
} | |
/* Enhanced legend styles */ | |
.legend { | |
position: absolute; | |
bottom: 20px; | |
right: 20px; | |
background: white; | |
padding: 15px 20px; | |
border-radius: 12px; | |
box-shadow: 0 4px 20px rgba(0,0,0,0.15); | |
font-size: 13px; | |
z-index: 1000; | |
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1); | |
transform: translateX(0); | |
min-width: 200px; | |
} | |
.legend.collapsed { | |
transform: translateX(calc(100% + 20px)); | |
} | |
.legend-toggle-btn { | |
position: absolute; | |
left: -35px; | |
top: 50%; | |
width: 35px; | |
height: 50px; | |
background-color: #3498db; | |
color: white; | |
border: none; | |
cursor: pointer; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
font-size: 16px; | |
box-shadow: 0 2px 8px rgba(0,0,0,0.15); | |
transition: all 0.3s ease; | |
z-index: 1001; | |
border-radius: 8px 0 0 8px; | |
transform: translateY(-50%); | |
} | |
.legend-toggle-btn:hover { | |
background-color: #2980b9; | |
box-shadow: 0 4px 12px rgba(0,0,0,0.2); | |
} | |
.legend-toggle-btn i { | |
transition: transform 0.3s ease; | |
} | |
.legend.collapsed .legend-toggle-btn i { | |
transform: rotate(180deg); | |
} | |
.legend-title { | |
font-weight: 600; | |
margin-bottom: 12px; | |
color: #2c3e50; | |
font-size: 14px; | |
} | |
.legend-item { | |
display: flex; | |
align-items: center; | |
gap: 12px; | |
margin-bottom: 8px; | |
font-size: 12px; | |
} | |
.legend-item:last-child { | |
margin-bottom: 0; | |
} | |
.legend-marker { | |
width: 18px; | |
height: 18px; | |
border-radius: 50%; | |
border: 2px solid white; | |
box-shadow: 0 1px 3px rgba(0,0,0,0.2); | |
flex-shrink: 0; | |
} | |
.legend-line { | |
width: 25px; | |
height: 4px; | |
border-radius: 2px; | |
flex-shrink: 0; | |
} | |
.legend-arrow { | |
width: 25px; | |
height: 4px; | |
background: #3498db; | |
position: relative; | |
flex-shrink: 0; | |
} | |
.legend-arrow::after { | |
content: ''; | |
position: absolute; | |
right: 0; | |
top: -3px; | |
width: 0; | |
height: 0; | |
border-left: 8px solid #3498db; | |
border-top: 5px solid transparent; | |
border-bottom: 5px solid transparent; | |
} | |
/* Info control styles */ | |
.info-control { | |
min-width: 200px; | |
position: absolute; | |
top: 20px; | |
left: 20px; | |
background: rgba(255,255,255,0.95); | |
padding: 15px; | |
border-radius: 10px; | |
box-shadow: 0 2px 10px rgba(0,0,0,0.1); | |
font-size: 14px; | |
z-index: 1000; | |
} | |
.info-control h4 { | |
margin: 0 0 8px 0; | |
color: #2c3e50; | |
font-size: 16px; | |
} | |
.info-control div { | |
margin: 3px 0; | |
} | |
/* Enhanced leaflet popup */ | |
.leaflet-popup-content { | |
margin: 13px 19px; | |
font-size: 14px; | |
line-height: 1.5; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="map"></div> | |
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script> | |
<script src="https://unpkg.com/leaflet-polylinedecorator@1.6.0/dist/leaflet.polylineDecorator.js"></script> | |
<script> | |
// Initialize the map | |
var map = L.map('map').setView([26.27184975, 50.18594229166666], 14); | |
// Add OpenStreetMap tile layer | |
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { | |
maxZoom: 19, | |
attribution: '© OpenStreetMap contributors' | |
}).addTo(map); | |
// Define the coordinates and the detailed path data | |
var latlngs = [ | |
[26.414945, 50.100758], | |
[26.291895, 50.173475], | |
[26.25849, 50.191772], | |
[26.243777, 50.196963], | |
[26.220972, 50.195948], | |
[26.216337, 50.19647], | |
[26.211635, 50.197023], | |
[26.341878, 50.179105], | |
[26.346603, 50.184645], | |
[26.36123, 50.178463], | |
[26.352682, 50.178615], | |
[26.29282, 50.171893], | |
[26.273518, 50.184727], | |
[26.241112, 50.195882], | |
[26.215413, 50.196618], | |
[26.181058, 50.182332], | |
[26.223673, 50.195855], | |
[26.26686, 50.187773], | |
[26.284925, 50.193497], | |
[26.28543, 50.192782], | |
[26.288358, 50.183153], | |
[26.218443, 50.206807], | |
[26.209582, 50.197942], | |
[26.282758, 50.200117] | |
]; | |
var pathData = [{"latitude": 26.414945, "longitude": 50.100758, "time": "06/18/25,18:19:50", "speed": 73, "speed_limit": 60, "street_name": null, "street_name_ar": null, "distance_to_segment": 1.29, "vehicle_direction": 317, "segment_bearing": 318.5, "nearest_segment": [[26.414706, 50.1010118], [26.4151274, 50.1005948]], "closest_point": [26.414952521479808, 50.10076785258696]}, {"latitude": 26.291895, "longitude": 50.173475, "time": "06/18/25,18:38:58", "speed": 88, "speed_limit": 80, "street_name": "Custodian of the Two Holly Mosques Road", "street_name_ar": "\u0637\u0631\u064a\u0642 \u062e\u0627\u062f\u0645 \u0627\u0644\u062d\u0631\u0645\u064a\u0646 \u0627\u0644\u0634\u0631\u064a\u0641\u064a\u0646", "distance_to_segment": 2.93, "vehicle_direction": 125, "segment_bearing": 126.1, "nearest_segment": [[26.2921171, 50.1730856], [26.2916041, 50.1738709]], "closest_point": [26.291875975026826, 50.17345471428528]}, {"latitude": 26.25849, "longitude": 50.191772, "time": "06/18/25,20:26:12", "speed": 80, "speed_limit": 60, "street_name": null, "street_name_ar": "\u0637\u0631\u064a\u0642 \u0627\u0644\u0645\u0644\u0643 \u062e\u0627\u0644\u062f \u0627\u0644\u0641\u0631\u0639\u064a", "distance_to_segment": 3.37, "vehicle_direction": 324, "segment_bearing": 324.7, "nearest_segment": [[26.2584696, 50.1918295], [26.2587105, 50.1916395]], "closest_point": [26.258508492507847, 50.191798825122014]}, {"latitude": 26.243777, "longitude": 50.196963, "time": "06/18/25,20:40:51", "speed": 117, "speed_limit": 110, "street_name": "King Khaled Road", "street_name_ar": "\u0637\u0631\u064a\u0642 \u0627\u0644\u0645\u0644\u0643 \u062e\u0627\u0644\u062f", "distance_to_segment": 12.52, "vehicle_direction": 194, "segment_bearing": 192.0, "nearest_segment": [[26.2446286, 50.1970457], [26.2435093, 50.1967798]], "closest_point": [26.24375884903155, 50.196839080562434]}, {"latitude": 26.220972, "longitude": 50.195948, "time": "06/18/25,20:42:17", "speed": 118, "speed_limit": 110, "street_name": "King Khaled Road", "street_name_ar": "\u0637\u0631\u064a\u0642 \u0627\u0644\u0645\u0644\u0643 \u062e\u0627\u0644\u062f", "distance_to_segment": 5.52, "vehicle_direction": 174, "segment_bearing": 174.0, "nearest_segment": [[26.2239462, 50.1955447], [26.2190474, 50.1961212]], "closest_point": [26.220953033987122, 50.195896903569924]}, {"latitude": 26.216337, "longitude": 50.19647, "time": "06/18/25,20:42:33", "speed": 119, "speed_limit": 110, "street_name": "King Khaled Road", "street_name_ar": "\u0637\u0631\u064a\u0642 \u0627\u0644\u0645\u0644\u0643 \u062e\u0627\u0644\u062f", "distance_to_segment": 5.29, "vehicle_direction": 174, "segment_bearing": 173.7, "nearest_segment": [[26.2175131, 50.1962734], [26.2161644, 50.1964401]], "closest_point": [26.216318660361527, 50.196421032128576]}, {"latitude": 26.211635, "longitude": 50.197023, "time": "06/18/25,20:42:50", "speed": 117, "speed_limit": 100, "street_name": "King Khaled Road", "street_name_ar": "\u0637\u0631\u064a\u0642 \u0627\u0644\u0645\u0644\u0643 \u062e\u0627\u0644\u062f", "distance_to_segment": 4.61, "vehicle_direction": 173, "segment_bearing": 174.5, "nearest_segment": [[26.2124636, 50.1968903], [26.2089591, 50.1972637]], "closest_point": [26.211619398550393, 50.19698023347207]}, {"latitude": 26.341878, "longitude": 50.179105, "time": "06/19/25,03:44:45", "speed": 85, "speed_limit": 60, "street_name": null, "street_name_ar": "King Saud Road", "distance_to_segment": 4.66, "vehicle_direction": 358, "segment_bearing": 358.3, "nearest_segment": [[26.3414148, 50.1791649], [26.342014, 50.1791449]], "closest_point": [26.341892183090938, 50.179148965557275]}, {"latitude": 26.346603, "longitude": 50.184645, "time": "06/19/25,03:46:08", "speed": 45, "speed_limit": 40, "street_name": null, "street_name_ar": null, "distance_to_segment": 4.22, "vehicle_direction": 65, "segment_bearing": 66.1, "nearest_segment": [[26.346444, 50.1841434], [26.3466943, 50.1847745]], "closest_point": [26.34664051785803, 50.18463889477558]}, {"latitude": 26.36123, "longitude": 50.178463, "time": "06/19/25,06:06:24", "speed": 94, "speed_limit": 40, "street_name": null, "street_name_ar": null, "distance_to_segment": 5.55, "vehicle_direction": 359, "segment_bearing": 358.7, "nearest_segment": [[26.3611317, 50.1785184], [26.361875, 50.1784997]], "closest_point": [26.361246686187595, 50.17851550663199]}, {"latitude": 26.352682, "longitude": 50.178615, "time": "06/19/25,06:07:33", "speed": 96, "speed_limit": 60, "street_name": "King Saud Road", "street_name_ar": "\u0637\u0631\u064a\u0642 \u0627\u0644\u0645\u0644\u0643 \u0633\u0639\u0648\u062f", "distance_to_segment": 4.05, "vehicle_direction": 178, "segment_bearing": 178.4, "nearest_segment": [[26.3561771, 50.1784666], [26.3526239, 50.1785782]], "closest_point": [26.352669706331714, 50.17857676011639]}, {"latitude": 26.29282, "longitude": 50.171893, "time": "06/19/25,06:13:15", "speed": 90, "speed_limit": 80, "street_name": "Custodian of the Two Holly Mosques Road", "street_name_ar": "\u0637\u0631\u064a\u0642 \u062e\u0627\u062f\u0645 \u0627\u0644\u062d\u0631\u0645\u064a\u0646 \u0627\u0644\u0634\u0631\u064a\u0641\u064a\u0646", "distance_to_segment": 6.91, "vehicle_direction": 129, "segment_bearing": 126.1, "nearest_segment": [[26.2944474, 50.1695184], [26.2921171, 50.1730856]], "closest_point": [26.292864902141282, 50.1719408785184]}, {"latitude": 26.273518, "longitude": 50.184727, "time": "06/19/25,06:14:43", "speed": 113, "speed_limit": 110, "street_name": "King Khaled Road", "street_name_ar": "\u0637\u0631\u064a\u0642 \u0627\u0644\u0645\u0644\u0643 \u062e\u0627\u0644\u062f", "distance_to_segment": 0.95, "vehicle_direction": 159, "segment_bearing": 159.2, "nearest_segment": [[26.2751079, 50.1840428], [26.2634782, 50.1889746]], "closest_point": [26.27351365292669, 50.184718807568544]}, {"latitude": 26.241112, "longitude": 50.195882, "time": "06/19/25,06:16:52", "speed": 118, "speed_limit": 110, "street_name": "King Khaled Road", "street_name_ar": "\u0637\u0631\u064a\u0642 \u0627\u0644\u0645\u0644\u0643 \u062e\u0627\u0644\u062f", "distance_to_segment": 4.06, "vehicle_direction": 186, "segment_bearing": 199.3, "nearest_segment": [[26.242216, 50.1962734], [26.2407109, 50.195685]], "closest_point": [26.241110677825688, 50.19584128327084]}, {"latitude": 26.215413, "longitude": 50.196618, "time": "06/19/25,06:18:27", "speed": 112, "speed_limit": 100, "street_name": "King Khaled Road", "street_name_ar": "\u0637\u0631\u064a\u0642 \u0627\u0644\u0645\u0644\u0643 \u062e\u0627\u0644\u062f", "distance_to_segment": 8.88, "vehicle_direction": 173, "segment_bearing": 173.4, "nearest_segment": [[26.2157716, 50.1964861], [26.2135156, 50.1967749]], "closest_point": [26.215382070588568, 50.196535960531186]}, {"latitude": 26.181058, "longitude": 50.182332, "time": "06/19/25,06:39:05", "speed": 82, "speed_limit": 40, "street_name": null, "street_name_ar": null, "distance_to_segment": 2.43, "vehicle_direction": 210, "segment_bearing": 196.3, "nearest_segment": [[26.1810931, 50.1823198], [26.1810353, 50.182301]], "closest_point": [26.181056014052757, 50.182307737434755]}, {"latitude": 26.223673, "longitude": 50.195855, "time": "06/19/25,06:42:45", "speed": 113, "speed_limit": 110, "street_name": "King Khaled Road", "street_name_ar": "\u0637\u0631\u064a\u0642 \u0627\u0644\u0645\u0644\u0643 \u062e\u0627\u0644\u062f", "distance_to_segment": 5.89, "vehicle_direction": 353, "segment_bearing": 354.3, "nearest_segment": [[26.221408, 50.1961629], [26.2244909, 50.1958212]], "closest_point": [26.223693060473813, 50.195909618097026]}, {"latitude": 26.26686, "longitude": 50.187773, "time": "06/19/25,06:45:38", "speed": 114, "speed_limit": 110, "street_name": "King Khaled Road", "street_name_ar": "\u0637\u0631\u064a\u0642 \u0627\u0644\u0645\u0644\u0643 \u062e\u0627\u0644\u062f", "distance_to_segment": 0.4, "vehicle_direction": 340, "segment_bearing": 338.8, "nearest_segment": [[26.2635741, 50.1892005], [26.2679627, 50.1872996]], "closest_point": [26.266861830637357, 50.18777642185928]}, {"latitude": 26.284925, "longitude": 50.193497, "time": "06/19/25,06:48:43", "speed": 97, "speed_limit": 80, "street_name": "Custodian of the Two Holy Mosques Road", "street_name_ar": "\u0637\u0631\u0642 \u062e\u0627\u062f\u0645 \u0627\u0644\u062d\u0631\u0645\u064a\u0646 \u0627\u0644\u0634\u0631\u064a\u0641\u064a\u0646", "distance_to_segment": 4.41, "vehicle_direction": 110, "segment_bearing": 108.1, "nearest_segment": [[26.2860426, 50.1898255], [26.2848734, 50.1938096]], "closest_point": [26.284957845296088, 50.193521850960586]}, {"latitude": 26.28543, "longitude": 50.192782, "time": "06/19/25,06:59:22", "speed": 61, "speed_limit": 60, "street_name": null, "street_name_ar": null, "distance_to_segment": 4.82, "vehicle_direction": 287, "segment_bearing": 287.9, "nearest_segment": [[26.2854571, 50.1928395], [26.2855764, 50.1924283]], "closest_point": [26.28546592944857, 50.19280906691799]}, {"latitude": 26.288358, "longitude": 50.183153, "time": "06/19/25,07:00:18", "speed": 67, "speed_limit": 60, "street_name": null, "street_name_ar": null, "distance_to_segment": 2.51, "vehicle_direction": 288, "segment_bearing": 291.2, "nearest_segment": [[26.2880199, 50.1841939], [26.2886242, 50.182454]], "closest_point": [26.288376290082425, 50.183167783522826]}, {"latitude": 26.218443, "longitude": 50.206807, "time": "06/19/25,07:07:24", "speed": 75, "speed_limit": 50, "street_name": null, "street_name_ar": null, "distance_to_segment": 0.78, "vehicle_direction": 84, "segment_bearing": 91.6, "nearest_segment": [[26.218454, 50.2066242], [26.2184355, 50.2073813]], "closest_point": [26.218449460354652, 50.20680998194038]}, {"latitude": 26.209582, "longitude": 50.197942, "time": "06/19/25,11:46:23", "speed": 61, "speed_limit": 40, "street_name": null, "street_name_ar": null, "distance_to_segment": 2.28, "vehicle_direction": 12, "segment_bearing": 359.1, "nearest_segment": [[26.2091175, 50.1979283], [26.2097614, 50.1979172]], "closest_point": [26.20957525492087, 50.19792040826063]}, {"latitude": 26.282758, "longitude": 50.200117, "time": "06/19/25,12:14:55", "speed": 74, "speed_limit": 60, "street_name": null, "street_name_ar": null, "distance_to_segment": 8.52, "vehicle_direction": 110, "segment_bearing": 108.4, "nearest_segment": [[26.2829269, 50.1998118], [26.2825731, 50.2009957]], "closest_point": [26.282821284844616, 50.20016521429913]}]; | |
// Add device name to each point's data | |
pathData.forEach(function(data) { | |
data.device_name = "6379 XBDMazda (FMC150)"; | |
}); | |
// Function to create numbered div icons for violations | |
function createNumberedIcon(number, isLast = false) { | |
if (isLast) { | |
return L.divIcon({ | |
className: 'number-icon violation-icon', | |
html: 'V<div class="pulse-ring"></div>', | |
iconSize: [32, 32] | |
}); | |
} else { | |
return L.divIcon({ | |
className: 'number-icon', | |
html: number.toString(), | |
iconSize: [24, 24] | |
}); | |
} | |
} | |
// Function to create popup content | |
function createPopupContent(pointNum, data, isLast) { | |
var lat = data.latitude; | |
var lon = data.longitude; | |
var speedClass = 'speed-violation'; | |
var html = '<div class="popup-content">'; | |
html += '<div class="popup-title">Violation Point ' + pointNum + (isLast ? ' (Latest)' : '') + '</div>'; | |
html += '<table>'; | |
html += '<tr><td>Latitude:</td><td>' + lat.toFixed(6) + '</td></tr>'; | |
html += '<tr><td>Longitude:</td><td>' + lon.toFixed(6) + '</td></tr>'; | |
if (data.time) { | |
html += '<tr><td>Time:</td><td>' + data.time + '</td></tr>'; | |
} | |
if (data.device_name) { | |
html += '<tr><td>Vehicle:</td><td>' + data.device_name + '</td></tr>'; | |
} | |
if (data.street_name_ar) { | |
html += '<tr><td>Street (AR):</td><td style="direction: rtl;">' + data.street_name_ar + '</td></tr>'; | |
} | |
if (data.street_name) { | |
html += '<tr><td>Street (EN):</td><td>' + data.street_name + '</td></tr>'; | |
} | |
if (data.distance_to_segment !== undefined) { | |
html += '<tr><td>Dist to street:</td><td>' + data.distance_to_segment.toFixed(2) + ' m</td></tr>'; | |
} | |
if (data.vehicle_direction !== undefined) { | |
html += '<tr><td>Vehicle Dir:</td><td>' + data.vehicle_direction.toFixed(1) + '°</td></tr>'; | |
} | |
if (data.segment_bearing !== undefined) { | |
html += '<tr><td>Street Bearing:</td><td>' + data.segment_bearing.toFixed(1) + '°</td></tr>'; | |
} | |
if (data.speed_limit) { | |
html += '<tr><td>Speed Limit:</td><td>' + data.speed_limit + ' km/h</td></tr>'; | |
} | |
if (data.speed) { | |
html += '<tr><td>Vehicle Speed:</td><td class="' + speedClass + '">' + data.speed + ' km/h</td></tr>'; | |
} | |
if (data.speed && data.speed_limit) { | |
var overSpeed = data.speed - data.speed_limit; | |
html += '<tr><td>Over Speed:</td><td class="speed-violation">+' + overSpeed + ' km/h</td></tr>'; | |
} | |
html += '</table>'; | |
html += '</div>'; | |
return html; | |
} | |
// Function to create direction arrow from point | |
function createDirectionArrow(lat, lon, direction) { | |
if (direction === undefined || direction === null) return null; | |
var arrowLength = 0.0002; // Length of arrow in degrees | |
var radians = direction * Math.PI / 180; | |
var endLat = lat + arrowLength * Math.cos(radians); | |
var endLon = lon + arrowLength * Math.sin(radians); | |
var arrow = L.polyline([[lat, lon], [endLat, endLon]], { | |
color: '#3498db', | |
weight: 3, | |
opacity: 0.8 | |
}); | |
var decorator = L.polylineDecorator(arrow, { | |
patterns: [{ | |
offset: '100%', | |
repeat: 0, | |
symbol: L.Symbol.arrowHead({ | |
pixelSize: 12, | |
polygon: false, | |
pathOptions: { | |
stroke: true, | |
color: '#3498db', | |
weight: 2, | |
opacity: 0.9 | |
} | |
}) | |
}] | |
}); | |
return { arrow: arrow, decorator: decorator }; | |
} | |
// Add markers for all points - all are violation points | |
var allBounds = []; | |
for (var i = 0; i < pathData.length; i++) { | |
var data = pathData[i]; | |
var isLast = (i === latlngs.length - 1); | |
var icon = createNumberedIcon(i + 1, isLast); | |
var popupContent = createPopupContent(i + 1, data, isLast); | |
// Add marker to map | |
var vehicleMarker = L.marker([data.latitude, data.longitude], {icon: icon}) | |
.addTo(map) | |
.bindPopup(popupContent); | |
allBounds.push([data.latitude, data.longitude]); | |
// Add direction arrow for each point | |
if (data.vehicle_direction !== undefined && data.vehicle_direction !== null) { | |
var directionArrowData = createDirectionArrow(data.latitude, data.longitude, data.vehicle_direction); | |
if (directionArrowData) { | |
directionArrowData.arrow.addTo(map); | |
directionArrowData.decorator.addTo(map); | |
} | |
} | |
// Draw the nearest street segment for each point if available | |
if (data.nearest_segment && data.nearest_segment.length === 2) { | |
var segmentPolyline = L.polyline(data.nearest_segment, { | |
color: '#9b59b6', | |
weight: 8, | |
opacity: 0.8 | |
}).addTo(map).bindPopup('Nearest Street Segment for Violation ' + (i + 1)); | |
// Add enhanced start and end circles for the segment | |
L.circleMarker(data.nearest_segment[0], { | |
radius: 6, | |
color: '#8e44ad', | |
fillColor: '#9b59b6', | |
fillOpacity: 1, | |
weight: 2 | |
}).addTo(map); | |
L.circleMarker(data.nearest_segment[1], { | |
radius: 6, | |
color: '#8e44ad', | |
fillColor: '#9b59b6', | |
fillOpacity: 1, | |
weight: 2 | |
}).addTo(map); | |
} | |
// Draw the closest point on the segment and a line to it | |
if (data.closest_point && data.closest_point.length === 2) { | |
// Enhanced closest point marker | |
L.circleMarker(data.closest_point, { | |
radius: 8, | |
color: '#e67e22', | |
fillColor: '#f39c12', | |
fillOpacity: 1, | |
weight: 3 | |
}).addTo(map).bindPopup('Closest Point on Street<br>Distance: ' + (data.distance_to_segment ? data.distance_to_segment.toFixed(2) + ' m' : 'N/A')); | |
// Enhanced dashed line from vehicle to closest point | |
var lineToClosest = [ | |
[data.latitude, data.longitude], | |
data.closest_point | |
]; | |
L.polyline(lineToClosest, { | |
color: '#3498db', | |
weight: 3, | |
opacity: 0.7, | |
dashArray: '8, 8' | |
}).addTo(map); | |
} | |
} | |
// Fit the map to show all points | |
if (allBounds.length > 0) { | |
var bounds = L.latLngBounds(allBounds); | |
map.fitBounds(bounds.pad(0.1)); | |
} | |
// Add enhanced legend with toggle functionality | |
var legendHtml = '<button class="legend-toggle-btn" onclick="toggleLegend()"><i class="fas fa-chevron-right"></i></button>'; | |
legendHtml += '<div class="legend-title">Map Legend</div>'; | |
legendHtml += '<div class="legend-item"><div class="legend-marker" style="background: #FF1744;"></div><span>Violation Points</span></div>'; | |
legendHtml += '<div class="legend-item"><div class="legend-marker" style="background: #FF1744; width: 24px; height: 24px;"></div><span>Latest Violation</span></div>'; | |
legendHtml += '<div class="legend-item"><div class="legend-arrow"></div><span>Vehicle Direction</span></div>'; | |
legendHtml += '<div class="legend-item"><div class="legend-line" style="background: #9b59b6; height: 6px;"></div><span>Nearest Street Segment</span></div>'; | |
legendHtml += '<div class="legend-item"><div class="legend-marker" style="background: #f39c12;"></div><span>Closest Point</span></div>'; | |
legendHtml += '<div class="legend-item"><div class="legend-line" style="background: #3498db; border: 2px dashed #3498db; height: 2px;"></div><span>Distance Line</span></div>'; | |
var legendControl = L.control({position: 'bottomright'}); | |
legendControl.onAdd = function (map) { | |
var div = L.DomUtil.create('div', 'legend'); | |
div.id = 'mapLegend'; | |
div.innerHTML = legendHtml; | |
return div; | |
}; | |
legendControl.addTo(map); | |
// Toggle legend function | |
function toggleLegend() { | |
var legend = document.getElementById('mapLegend'); | |
legend.classList.toggle('collapsed'); | |
} | |
// Add info control | |
var infoControl = L.control({position: 'topleft'}); | |
infoControl.onAdd = function (map) { | |
var div = L.DomUtil.create('div', 'info-control'); | |
div.innerHTML = '<h4><i class="fas fa-car" style="margin-right: 8px; color: #3498db;"></i>Vehicle Path Analysis</h4>' + | |
'<div><strong>Vehicle:</strong> 6379 XBDMazda (FMC150)</div>' + | |
'<div><strong>Total Violations:</strong> ' + latlngs.length + '</div>'; | |
return div; | |
}; | |
infoControl.addTo(map); | |
// Add zoom control to top right | |
L.control.zoom({position: 'topright'}).addTo(map); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment