Skip to content

Commit 26f06ef

Browse files
feat: add flp marker and implement legend (#275)
* fix: Small increase in width to better fit seconds * feat(map): add flp marker and implement legend - Adds toggleable map legend - static legend content in `LegendContent.js`. 51b17aa
1 parent 8e43fb2 commit 26f06ef

File tree

3 files changed

+135
-15
lines changed

3 files changed

+135
-15
lines changed

src/LegendContent.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
export const LEGEND_HTML = `
2+
<div style="margin-top: 5px; margin-right: 0px; padding: 0; font-family: Roboto, Arial, sans-serif; color: #333; font-size: 16px; font-weight: 600; display: flex; flex-direction: column;">
3+
<div style="display: flex; flex-direction: column; gap: 4px;">
4+
<div style="display: grid; grid-template-columns: 24px 1fr; align-items: center; font-size: 12px; color: #333; text-shadow: 0 0 2px rgba(255, 255, 255, 0.8); background: transparent; padding: 2px 0; margin-bottom: 2px;">
5+
<span style="width: 6px; height: 6px; border-radius: 50%; display: inline-block; justify-self: center; background-color: #FF0000;"></span> Raw Location
6+
</div>
7+
<div style="display: grid; grid-template-columns: 24px 1fr; align-items: center; font-size: 12px; color: #333; text-shadow: 0 0 2px rgba(255, 255, 255, 0.8); background: transparent; padding: 2px 0; margin-bottom: 2px;">
8+
<span style="width: 6px; height: 6px; border-radius: 50%; display: inline-block; justify-self: center; background-color: #4285F4;"></span> FLP Location
9+
</div>
10+
<div style="display: grid; grid-template-columns: 24px 1fr; align-items: center; font-size: 12px; color: #333; text-shadow: 0 0 2px rgba(255, 255, 255, 0.8); background: transparent; padding: 2px 0; margin-bottom: 2px;">
11+
<span style="width: 9px; height: 9px; border-radius: 50%; display: inline-block; justify-self: center; background-color: #C71585;"></span> FLP = Raw Location
12+
</div>
13+
</div>
14+
15+
<div style="display: grid; grid-template-columns: 24px 1fr; align-items: center; font-size: 12px; color: #333; text-shadow: 0 0 2px rgba(255, 255, 255, 0.8); background: transparent; padding: 2px 0; margin-bottom: 2px;">
16+
<svg style="display: block; justify-self: center; margin-right: 0;" viewBox="0 0 24 24" width="24" height="24">
17+
<path d="M12 8 L5 16 L19 16 L12 8 Z" fill="#0dcaf0" stroke="black" stroke-width="1" stroke-linejoin="round"/>
18+
</svg>
19+
Pickup
20+
</div>
21+
22+
<div style="display: grid; grid-template-columns: 24px 1fr; align-items: center; font-size: 12px; color: #333; text-shadow: 0 0 2px rgba(255, 255, 255, 0.8); background: transparent; padding: 2px 0; margin-bottom: 2px;">
23+
<svg style="display: block; justify-self: center; margin-right: 0;" viewBox="0 0 24 24" width="24" height="24">
24+
<path d="M12 5 L5 13 L19 13 L12 5 Z" fill="#0dcaf0" stroke="black" stroke-width="1" stroke-linejoin="round"/>
25+
<path d="M12 10 L5 18 L19 18 L12 10 Z" fill="#0dcaf0" stroke="black" stroke-width="1" stroke-linejoin="round"/>
26+
</svg>
27+
Actual Pickup
28+
</div>
29+
30+
<div style="display: grid; grid-template-columns: 24px 1fr; align-items: center; font-size: 12px; color: #333; text-shadow: 0 0 2px rgba(255, 255, 255, 0.8); background: transparent; padding: 2px 0; margin-bottom: 2px;">
31+
<svg style="display: block; justify-self: center; margin-right: 0;" viewBox="0 0 24 24" width="24" height="24">
32+
<path d="M12 16 L5 8 L19 8 L12 16 Z" fill="#0dcaf0" stroke="black" stroke-width="1" stroke-linejoin="round"/>
33+
</svg>
34+
Dropoff
35+
</div>
36+
37+
<div style="display: grid; grid-template-columns: 24px 1fr; align-items: center; font-size: 12px; color: #333; text-shadow: 0 0 2px rgba(255, 255, 255, 0.8); background: transparent; padding: 2px 0; margin-bottom: 2px;">
38+
<svg style="display: block; justify-self: center; margin-right: 0;" viewBox="0 0 24 24" width="24" height="24">
39+
<path d="M12 18 L5 10 L19 10 L12 18 Z" fill="#0dcaf0" stroke="black" stroke-width="1" stroke-linejoin="round"/>
40+
<path d="M12 13 L5 5 L19 5 L12 13 Z" fill="#0dcaf0" stroke="black" stroke-width="1" stroke-linejoin="round"/>
41+
</svg>
42+
Actual Dropoff
43+
</div>
44+
</div>
45+
`;

src/LogTable.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ function LogTable(props) {
170170
}, [props.logData.tripLogs, minTime, maxTime, props.filters]);
171171
const columnShortWidth = 50;
172172
const columnRegularWidth = 120;
173-
const columnLargeWidth = 150;
173+
const columnLargeWidth = 152;
174174
const columns = React.useMemo(() => {
175175
const stdColumns = _.filter(
176176
[

src/Map.js

Lines changed: 89 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { decode } from "s2polyline-ts";
99
import TrafficPolyline from "./TrafficPolyline";
1010
import { TripObjects } from "./TripObjects";
1111
import { getToggleHandlers } from "./MapToggles.js";
12+
import { LEGEND_HTML } from "./LegendContent.js";
1213

1314
function MapComponent({
1415
logData,
@@ -109,6 +110,7 @@ function MapComponent({
109110
element: mapDivRef.current,
110111
locationProviders: [locationProviderRef.current],
111112
mapOptions: { mapId, mapTypeControl: true, streetViewControl: true, maxZoom: 22 },
113+
automaticViewportMode: "NONE",
112114
});
113115
const map = jsMapView.map;
114116
mapRef.current = map;
@@ -213,6 +215,36 @@ function MapComponent({
213215
bottomControlsWrapper.appendChild(toggleContainer);
214216
map.controls[window.google.maps.ControlPosition.LEFT_BOTTOM].push(bottomControlsWrapper);
215217

218+
// Legend Controls
219+
const legendToggleContainer = document.createElement("div");
220+
legendToggleContainer.className = "map-toggle-container";
221+
legendToggleContainer.style.marginTop = "10px";
222+
legendToggleContainer.style.marginRight = "10px";
223+
legendToggleContainer.style.marginBottom = "0px";
224+
225+
const legendBtn = document.createElement("button");
226+
legendBtn.textContent = "Legend";
227+
legendBtn.className = "map-toggle-button";
228+
legendToggleContainer.appendChild(legendBtn);
229+
230+
const legendContentDiv = document.createElement("div");
231+
legendContentDiv.style.display = "none";
232+
legendContentDiv.innerHTML = LEGEND_HTML;
233+
234+
legendBtn.onclick = () => {
235+
const isHidden = legendContentDiv.style.display === "none";
236+
if (isHidden) {
237+
legendContentDiv.style.display = "block";
238+
legendBtn.classList.add("active");
239+
} else {
240+
legendContentDiv.style.display = "none";
241+
legendBtn.classList.remove("active");
242+
}
243+
};
244+
245+
map.controls[window.google.maps.ControlPosition.TOP_RIGHT].push(legendToggleContainer);
246+
map.controls[window.google.maps.ControlPosition.TOP_RIGHT].push(legendContentDiv);
247+
216248
const centerListener = map.addListener(
217249
"center_changed",
218250
_.debounce(() => {
@@ -441,30 +473,73 @@ function MapComponent({
441473
}
442474

443475
const rawLocation = _.get(locationObj, "rawlocation");
444-
if (rawLocation?.latitude && rawLocation?.longitude) {
445-
const rawPos = { lat: rawLocation.latitude, lng: rawLocation.longitude };
446-
if (vehicleMarkersRef.current.rawLocation) {
447-
vehicleMarkersRef.current.rawLocation.setPosition(rawPos);
448-
if (!vehicleMarkersRef.current.rawLocation.getMap()) {
449-
vehicleMarkersRef.current.rawLocation.setMap(map);
476+
const flpLocation = _.get(locationObj, "flplocation");
477+
478+
const rawLat = rawLocation?.latitude;
479+
const rawLng = rawLocation?.longitude;
480+
const flpLat = flpLocation?.latitude;
481+
const flpLng = flpLocation?.longitude;
482+
483+
const hasRaw = rawLat !== undefined && rawLng !== undefined;
484+
const hasFlp = flpLat !== undefined && flpLng !== undefined;
485+
const isMatch = hasRaw && hasFlp && rawLat === flpLat && rawLng === flpLng;
486+
487+
const updateMarker = (markerRefName, position, color, zIndex, scale = 2) => {
488+
if (!position) {
489+
if (vehicleMarkersRef.current[markerRefName]) {
490+
vehicleMarkersRef.current[markerRefName].setMap(null);
491+
}
492+
return;
493+
}
494+
495+
const pos = { lat: position.latitude, lng: position.longitude };
496+
if (vehicleMarkersRef.current[markerRefName]) {
497+
vehicleMarkersRef.current[markerRefName].setPosition(pos);
498+
if (!vehicleMarkersRef.current[markerRefName].getMap()) {
499+
vehicleMarkersRef.current[markerRefName].setMap(map);
500+
}
501+
const icon = vehicleMarkersRef.current[markerRefName].getIcon();
502+
if (icon.fillColor !== color || icon.scale !== scale) {
503+
icon.fillColor = color;
504+
icon.strokeColor = color;
505+
icon.scale = scale;
506+
vehicleMarkersRef.current[markerRefName].setIcon(icon);
450507
}
451508
} else {
452-
vehicleMarkersRef.current.rawLocation = new window.google.maps.Marker({
453-
position: rawPos,
509+
vehicleMarkersRef.current[markerRefName] = new window.google.maps.Marker({
510+
position: pos,
454511
map,
455512
icon: {
456513
path: window.google.maps.SymbolPath.CIRCLE,
457-
fillColor: "#FF0000",
514+
fillColor: color,
458515
fillOpacity: 1,
459-
scale: 2,
460-
strokeColor: "#FF0000",
516+
scale: scale,
517+
strokeColor: color,
461518
strokeWeight: 1,
462519
},
463-
zIndex: 8,
520+
zIndex: zIndex,
464521
});
465522
}
466-
} else if (vehicleMarkersRef.current.rawLocation) {
467-
vehicleMarkersRef.current.rawLocation.setMap(null);
523+
};
524+
525+
if (isMatch) {
526+
updateMarker("matchLocation", rawLocation, "#C71585", 8, 3);
527+
updateMarker("rawLocation", null);
528+
updateMarker("flpLocation", null);
529+
} else {
530+
updateMarker("matchLocation", null);
531+
532+
if (hasRaw) {
533+
updateMarker("rawLocation", rawLocation, "#FF0000", 8, 2);
534+
} else {
535+
updateMarker("rawLocation", null);
536+
}
537+
538+
if (hasFlp) {
539+
updateMarker("flpLocation", flpLocation, "#4285F4", 8, 2);
540+
} else {
541+
updateMarker("flpLocation", null);
542+
}
468543
}
469544

470545
if (isFollowingVehicle) {

0 commit comments

Comments
 (0)