From 0b50dd10fea33f28116c613be391b79d916d56e6 Mon Sep 17 00:00:00 2001
From: regeter <2320305+regeter@users.noreply.github.com>
Date: Mon, 17 Feb 2025 19:17:22 -0800
Subject: [PATCH 1/4] fix: Rename some Toggles to make it clearer
---
src/App.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/App.js b/src/App.js
index 6256a39..5c83a1f 100644
--- a/src/App.js
+++ b/src/App.js
@@ -161,14 +161,14 @@ class App extends React.Component {
},
{
id: "showTraffic",
- name: "Traffic",
+ name: "Live Traffic",
docLink: "https://github.com/googlemaps/fleet-debugger/blob/main/README.md",
columns: [],
solutionTypes: ["ODRD", "LMFS"],
},
{
id: "showLiveJS",
- name: "Start Live Journey Sharing for newest trip",
+ name: "Live Journey Sharing",
docLink: "https://github.com/googlemaps/fleet-debugger/blob/main/README.md",
columns: [],
solutionTypes: ["ODRD", "LMFS"],
From 680542ed0eb884ed2e2af505b133e7a0584ff71a Mon Sep 17 00:00:00 2001
From: regeter <2320305+regeter@users.noreply.github.com>
Date: Mon, 17 Feb 2025 19:18:01 -0800
Subject: [PATCH 2/4] feat: Add TripObjects to handle polylines and markers of
trips
---
src/TripObjects.js | 217 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 217 insertions(+)
create mode 100644 src/TripObjects.js
diff --git a/src/TripObjects.js b/src/TripObjects.js
new file mode 100644
index 0000000..b12040a
--- /dev/null
+++ b/src/TripObjects.js
@@ -0,0 +1,217 @@
+// src/TripObjects.js
+
+import { log } from "./Utils";
+import { getColor } from "./Trip";
+
+export class TripObjects {
+ constructor({ map, setFeaturedObject, setTimeRange }) {
+ this.map = map;
+ this.markers = new Map();
+ this.paths = new Map();
+ this.arrows = new Map();
+ this.setFeaturedObject = setFeaturedObject;
+ this.setTimeRange = setTimeRange;
+ }
+
+ createSVGMarker(type, color) {
+ const isPickup = type.toLowerCase().includes("pickup");
+ const isActual = type.includes("actual");
+
+ const createChevronPath = (direction, shift = 0) => {
+ let points;
+ if (direction === "up") {
+ points = [
+ { x: 12, y: 20 + shift }, // Top point of chevron
+ { x: 8, y: 24 + shift }, // Bottom left
+ { x: 16, y: 24 + shift }, // Bottom right
+ { x: 12, y: 20 + shift },
+ ];
+ } else {
+ // "down"
+ points = [
+ { x: 12, y: 24 + shift }, // Bottom point of chevron
+ { x: 8, y: 20 + shift }, // Top left
+ { x: 16, y: 20 + shift }, // Top right
+ { x: 12, y: 24 + shift },
+ ];
+ }
+
+ return ``;
+ };
+
+ const svgBase = `
+ `;
+
+ return {
+ url: "data:image/svg+xml;charset=UTF-8," + encodeURIComponent(svgBase),
+ scaledSize: new google.maps.Size(24, 24),
+ anchor: new google.maps.Point(12, 24),
+ };
+ }
+
+ createMarkerWithEvents(point, type, color, pairedPoint, tripId) {
+ if (!point) return null;
+
+ const marker = new google.maps.Marker({
+ position: { lat: point.latitude, lng: point.longitude },
+ icon: this.createSVGMarker(type, color),
+ map: this.map,
+ });
+
+ if (pairedPoint) {
+ // Store the arrow in the class instance using a unique key
+ const arrowKey = `${tripId}_${type.includes("actual") ? type.replace("actual", "") : type}`;
+
+ google.maps.event.addListener(marker, "click", () => {
+ log(`${type} marker clicked`);
+
+ if (this.arrows.has(arrowKey)) {
+ this.arrows.get(arrowKey).setMap(null);
+ this.arrows.delete(arrowKey);
+ } else {
+ // Create arrow from requested to actual
+ const from = type.includes("actual") ? pairedPoint : point;
+ const to = type.includes("actual") ? point : pairedPoint;
+ const arrow = this.createConnectingArrow(from, to, color);
+ if (arrow) {
+ this.arrows.set(arrowKey, arrow);
+ }
+ }
+ });
+ }
+
+ return marker;
+ }
+
+ createConnectingArrow(from, to, color) {
+ if (!from || !to) return null;
+
+ return new google.maps.Polyline({
+ path: [
+ { lat: from.latitude, lng: from.longitude },
+ { lat: to.latitude, lng: to.longitude },
+ ],
+ geodesic: true,
+ strokeColor: color,
+ strokeOpacity: 1,
+ strokeWeight: 1,
+ icons: [
+ {
+ icon: {
+ path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,
+ scale: 1,
+ },
+ offset: "100%",
+ },
+ ],
+ map: this.map,
+ });
+ }
+
+ addTripVisuals(trip, minDate, maxDate) {
+ const tripId = trip.tripName;
+ log(`Processing trip visuals for ${tripId}`, {
+ pickupPoint: trip.getPickupPoint(),
+ actualPickupPoint: trip.getActualPickupPoint(),
+ dropoffPoint: trip.getDropoffPoint(),
+ actualDropoffPoint: trip.getActualDropoffPoint(),
+ });
+
+ this.clearTripObjects(tripId);
+
+ // Add path polyline
+ const tripCoords = trip.getPathCoords(minDate, maxDate);
+ if (tripCoords.length > 0) {
+ const path = new google.maps.Polyline({
+ path: tripCoords,
+ geodesic: true,
+ strokeColor: getColor(trip.tripIdx),
+ strokeOpacity: 0.5,
+ strokeWeight: 6,
+ map: this.map,
+ });
+
+ // Add path events
+ google.maps.event.addListener(path, "mouseover", () => {
+ path.setOptions({ strokeOpacity: 1, strokeWeight: 8 });
+ });
+
+ google.maps.event.addListener(path, "mouseout", () => {
+ path.setOptions({ strokeOpacity: 0.5, strokeWeight: 6 });
+ });
+
+ google.maps.event.addListener(path, "click", () => {
+ log("Trip polyline clicked");
+ const fd = trip.getFeaturedData();
+ this.setFeaturedObject(fd);
+ // TODO: https://github.com/googlemaps/fleet-debugger/issues/79
+ // this time range won't capture the createTrip logs
+ this.setTimeRange(fd.firstUpdate.getTime(), fd.lastUpdate.getTime());
+ });
+
+ this.paths.set(tripId, path);
+ }
+
+ const markers = [];
+
+ // Get points
+ const pickupPoint = trip.getPickupPoint();
+ const actualPickupPoint = trip.getActualPickupPoint();
+ const dropoffPoint = trip.getDropoffPoint();
+ const actualDropoffPoint = trip.getActualDropoffPoint();
+
+ // Create pickup markers
+ const pickupMarker = this.createMarkerWithEvents(pickupPoint, "pickup", "#3d633d", actualPickupPoint, tripId);
+ if (pickupMarker) markers.push(pickupMarker);
+
+ const actualPickupMarker = this.createMarkerWithEvents(
+ actualPickupPoint,
+ "actualPickup",
+ "#3d633d",
+ pickupPoint,
+ tripId
+ );
+ if (actualPickupMarker) markers.push(actualPickupMarker);
+
+ // Create dropoff markers
+ const dropoffMarker = this.createMarkerWithEvents(dropoffPoint, "dropoff", "#0000FF", actualDropoffPoint, tripId);
+ if (dropoffMarker) markers.push(dropoffMarker);
+
+ const actualDropoffMarker = this.createMarkerWithEvents(
+ actualDropoffPoint,
+ "actualDropoff",
+ "#0000FF",
+ dropoffPoint,
+ tripId
+ );
+ if (actualDropoffMarker) markers.push(actualDropoffMarker);
+
+ this.markers.set(tripId, markers);
+ }
+
+ clearTripObjects(tripId) {
+ if (this.paths.has(tripId)) {
+ this.paths.get(tripId).setMap(null);
+ this.paths.delete(tripId);
+ }
+
+ if (this.markers.has(tripId)) {
+ this.markers.get(tripId).forEach((marker) => marker.setMap(null));
+ this.markers.delete(tripId);
+ }
+
+ if (this.arrows.has(tripId)) {
+ this.arrows.get(tripId).forEach((arrow) => arrow.setMap(null));
+ this.arrows.delete(tripId);
+ }
+ }
+
+ clearAll() {
+ for (const tripId of this.paths.keys()) {
+ this.clearTripObjects(tripId);
+ }
+ }
+}
From 666c359f1e4efc763a3331f9e4994d83effd07e2 Mon Sep 17 00:00:00 2001
From: regeter <2320305+regeter@users.noreply.github.com>
Date: Mon, 17 Feb 2025 19:20:19 -0800
Subject: [PATCH 3/4] feat: Also add createTrip and updateTrip to trip related
logs.
---
src/TripLogs.js | 30 +++++++++++++++++++++++++-----
1 file changed, 25 insertions(+), 5 deletions(-)
diff --git a/src/TripLogs.js b/src/TripLogs.js
index 16e5d4a..73acda8 100644
--- a/src/TripLogs.js
+++ b/src/TripLogs.js
@@ -446,10 +446,24 @@ class TripLogs {
let tripIdx = 0;
let nonTripIdx = 0;
let lastTripStatus = "no status";
- // assumes logs are already sorted
- // also assumes out-of-order updates can't happen. Unclear
- // if this is a good assumption, but it might be worth it to call out
- // places where it happens (since that might actually be a client bug).
+
+ // First, create a map of trip IDs to their logs
+ const tripLogs = new Map();
+
+ // Collect all trip-related logs
+ _.forEach(this.rawLogs, (le) => {
+ if (le["@type"] === "createTrip" || le["@type"] === "updateTrip") {
+ const tripId = le.request?.tripid || _.get(le, "request.trip.name");
+ if (tripId) {
+ if (!tripLogs.has(tripId)) {
+ tripLogs.set(tripId, []);
+ }
+ tripLogs.get(tripId).push(le);
+ }
+ }
+ });
+
+ // Process vehicle updates and create trip segments
_.forEach(this.rawLogs, (le) => {
if (le["@type"] === "updateVehicle" || le["@type"] === "updateDeliveryVehicle") {
const newTripId = this.getSegmentID(le);
@@ -457,6 +471,13 @@ class TripLogs {
curTripId = newTripId;
const tripName = newTripId ? newTripId : "non-trip-segment-" + nonTripIdx;
curTripData = new Trip(tripIdx, tripName, new Date(le.timestamp));
+
+ // If this is an actual trip (not a non-trip-segment), add its logs
+ if (tripLogs.has(newTripId)) {
+ curTripData.logs = tripLogs.get(newTripId);
+ log(`Added ${curTripData.logs.length} logs to trip ${newTripId}`);
+ }
+
this.trips.push(curTripData);
this.trip_ids.push(curTripData.tripName);
@@ -479,7 +500,6 @@ class TripLogs {
}
}
const tripStatus = _.get(le, "response.tripstatus");
- // if the logs had a trip status, and it changed update
if (tripStatus && tripStatus !== lastTripStatus) {
this.tripStatusChanges.push({
newStatus: tripStatus,
From 4bff45288517fde84bcbb6e4baed2fdad117c1ad Mon Sep 17 00:00:00 2001
From: regeter <2320305+regeter@users.noreply.github.com>
Date: Mon, 17 Feb 2025 19:21:09 -0800
Subject: [PATCH 4/4] feat: Update Maps to use new TripObjects and draw pickup
and dropoff markers
---
src/Map.js | 93 ++++++------------------------------------
src/TrafficPolyline.js | 5 ---
src/Trip.js | 33 +++++++++++++++
3 files changed, 46 insertions(+), 85 deletions(-)
diff --git a/src/Map.js b/src/Map.js
index d153c23..0c1b79d 100644
--- a/src/Map.js
+++ b/src/Map.js
@@ -8,11 +8,11 @@ import Utils, { log } from "./Utils";
import PolylineCreation from "./PolylineCreation";
import { decode } from "s2polyline-ts";
import TrafficPolyline from "./TrafficPolyline";
+import { TripObjects } from "./TripObjects";
+import { getColor } from "./Trip";
let minDate;
let maxDate;
-let allPaths = [];
-let allMarkers = [];
let map;
let apikey;
let mapId;
@@ -24,7 +24,6 @@ let panorama;
let jwt;
let projectId;
let locationProvider;
-let solutionType;
let tripLogs;
let taskLogs;
let setFeaturedObject;
@@ -37,62 +36,24 @@ const render = (status) => {
};
function addTripPolys(map) {
- _.forEach(allPaths, (p) => p.setMap(null));
- allPaths = [];
- _.forEach(allMarkers, (m) => m.setMap(null));
- allMarkers = [];
-
+ const tripObjects = new TripObjects({
+ map,
+ setFeaturedObject,
+ setTimeRange,
+ });
const trips = tripLogs.getTrips();
const vehicleBounds = new window.google.maps.LatLngBounds();
- let lastVehicleCoords;
+
_.forEach(trips, (trip) => {
+ tripObjects.addTripVisuals(trip, minDate, maxDate);
+
+ // Update bounds
const tripCoords = trip.getPathCoords(minDate, maxDate);
if (tripCoords.length > 0) {
- lastVehicleCoords = _.last(tripCoords);
- const path = new window.google.maps.Polyline({
- path: tripCoords,
- geodesic: true,
- strokeColor: getColor(trip.tripIdx),
- strokeOpacity: 0.5,
- strokeWeight: 6,
- });
- google.maps.event.addListener(path, "mouseover", () => {
- path.setOptions({
- strokeOpacity: 1,
- strokeWeight: 8,
- });
- });
- google.maps.event.addListener(path, "mouseout", () => {
- path.setOptions({
- strokeOpacity: 0.5,
- strokeWeight: 6,
- });
- });
- google.maps.event.addListener(path, "click", () => {
- const fd = trip.getFeaturedData();
- setFeaturedObject(fd);
- // TODO: https://github.com/googlemaps/fleet-debugger/issues/79
- // this time range won't capture the createTrip logs
- setTimeRange(fd.firstUpdate.getTime(), fd.lastUpdate.getTime());
- });
- getPolyBounds(vehicleBounds, path);
- path.setMap(map);
- allPaths.push(path);
+ tripCoords.forEach((coord) => vehicleBounds.extend(coord));
}
});
- if (lastVehicleCoords) {
- const urlBase = "http://maps.google.com/mapfiles/kml/shapes/";
- const lastVehicleLocMark = new window.google.maps.Marker({
- position: lastVehicleCoords,
- map: map,
- icon: {
- url: urlBase + (solutionType === "LMFS" ? "truck.png" : "cabs.png"),
- scaledSize: new google.maps.Size(18, 18),
- },
- title: "Last Location",
- });
- allMarkers.push(lastVehicleLocMark);
- }
+
return vehicleBounds;
}
@@ -204,11 +165,6 @@ function MyMapComponent(props) {
_.get(props.selectedRow, "lastlocation.currentroutesegment");
if (routeSegment) {
- log("Processing new route segment polyline:", {
- rowTimestamp: props.selectedRow.timestamp,
- polyline: routeSegment,
- });
-
try {
const decodedPoints = decode(routeSegment);
@@ -218,12 +174,6 @@ function MyMapComponent(props) {
lng: point.lngDegrees(),
}));
- log("Creating new polyline with", {
- points: validWaypoints.length,
- firstPoint: validWaypoints[0],
- lastPoint: validWaypoints[validWaypoints.length - 1],
- });
-
const trafficRendering =
_.get(props.selectedRow, "request.vehicle.currentroutesegmenttraffic.trafficrendering") ||
_.get(props.selectedRow, "lastlocation.currentroutesegmenttraffic.trafficrendering");
@@ -389,22 +339,6 @@ function MyMapComponent(props) {
);
}
-function getPolyBounds(bounds, p) {
- p.getPath().forEach((e) => {
- bounds.extend(e);
- });
- return bounds;
-}
-
-/*
- * Deterministically assign a color per trip using tripIdx
- * Colors were chosen for visibility
- */
-function getColor(tripIdx) {
- const colors = ["#2d7dd2", "#97cc04", "#eeb902", "#f45d01", "#474647", "00aa00"];
- return colors[tripIdx % colors.length];
-}
-
function Map(props) {
tripLogs = props.logData.tripLogs;
taskLogs = props.logData.taskLogs;
@@ -415,7 +349,6 @@ function Map(props) {
mapId = urlParams.get("mapId") || props.logData.mapId;
jwt = props.logData.jwt;
projectId = props.logData.projectId;
- solutionType = props.logData.solutionType;
setFeaturedObject = props.setFeaturedObject;
setTimeRange = props.setTimeRange;
diff --git a/src/TrafficPolyline.js b/src/TrafficPolyline.js
index d06037b..75d57ae 100644
--- a/src/TrafficPolyline.js
+++ b/src/TrafficPolyline.js
@@ -57,11 +57,6 @@ export class TrafficPolyline {
},
...(trafficRendering.roadstretch || []),
];
-
- log("Added traveled route segment:", {
- lengthMeters: distanceInMeters,
- segments: trafficRendering.roadstretch.length,
- });
}
} catch (error) {
log("Error calculating traveled route segment:", error);
diff --git a/src/Trip.js b/src/Trip.js
index ba73ba4..8fe3d88 100644
--- a/src/Trip.js
+++ b/src/Trip.js
@@ -71,5 +71,38 @@ class Trip {
getPlannedPath() {
return this.plannedPath;
}
+
+ getPoint(type, path) {
+ return _.get(
+ _.find(this.logs, (log) => log["@type"] === type && _.get(log, path)),
+ path
+ );
+ }
+
+ getPickupPoint() {
+ return this.getPoint("createTrip", "request.trip.pickuppoint.point");
+ }
+
+ getDropoffPoint() {
+ return this.getPoint("createTrip", "request.trip.dropoffpoint.point");
+ }
+
+ getActualPickupPoint() {
+ return this.getPoint("updateTrip", "response.actualpickuppoint.point");
+ }
+
+ getActualDropoffPoint() {
+ return this.getPoint("updateTrip", "response.actualdropoffpoint.point");
+ }
}
+
+/*
+ * Deterministically assign a color per trip using tripIdx
+ * Colors were chosen for visibility
+ */
+export function getColor(tripIdx) {
+ const colors = ["#2d7dd2", "#97cc04", "#eeb902", "#f45d01", "#474647", "00aa00"];
+ return colors[tripIdx % colors.length];
+}
+
export { Trip as default };