Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 59 additions & 31 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ class App extends React.Component {
activeMenuIndex: null,
initialMapBounds: null,
selectedRowIndexPerDataset: [-1, -1, -1, -1, -1],
useResponseLocationPerDataset: [false, false, false, false, false],
currentLogData: {
...this.props.logData,
taskLogs: new TaskLogs(this.props.logData.tripLogs),
Expand Down Expand Up @@ -223,6 +224,16 @@ class App extends React.Component {
this.setState({ featuredObject: featuredObject });
}

setUseResponseLocation = (useResponseLocation) => {
if (this.state.activeDatasetIndex !== null) {
this.setState((prevState) => {
const newValues = [...prevState.useResponseLocationPerDataset];
newValues[prevState.activeDatasetIndex] = useResponseLocation;
return { useResponseLocationPerDataset: newValues };
});
}
};

setFocusOnRowFunction = (func) => {
this.focusOnRowFunction = func;
};
Expand Down Expand Up @@ -261,31 +272,42 @@ class App extends React.Component {
});
}

getFilteredLogs = () => {
const { currentLogData, timeRange, filters, activeDatasetIndex } = this.state;
if (!currentLogData || !currentLogData.tripLogs) return [];

const cacheKey = `${activeDatasetIndex}-${timeRange.minTime}-${timeRange.maxTime}-${JSON.stringify(filters)}`;

if (this._logsCache && this._logsCache.key === cacheKey) {
return this._logsCache.logs;
}

const logs = currentLogData.tripLogs
.getLogs_(new Date(timeRange.minTime), new Date(timeRange.maxTime), filters)
.value();

this._logsCache = { key: cacheKey, logs };
return logs;
};

selectFirstRow = () => {
return new Promise((resolve) => {
this.setState((prevState) => {
const minDate = new Date(prevState.timeRange.minTime);
const maxDate = new Date(prevState.timeRange.maxTime);
const logs = this.state.currentLogData.tripLogs.getLogs_(minDate, maxDate, prevState.filters).value();
if (logs.length > 0) {
const firstRow = logs[0];
setTimeout(() => this.focusOnSelectedRow(), 0);
const logs = this.getFilteredLogs();
if (logs.length > 0) {
const firstRow = logs[0];
this.setState({ featuredObject: firstRow }, () => {
this.focusOnSelectedRow();
resolve(firstRow);
return { featuredObject: firstRow };
} else {
console.log("selectFirstRow: No logs found in the current time range");
resolve(null);
return null;
}
});
});
} else {
console.log("selectFirstRow: No logs found in the current time range");
resolve(null);
}
});
};

selectLastRow = () => {
const minDate = new Date(this.state.timeRange.minTime);
const maxDate = new Date(this.state.timeRange.maxTime);
const logsWrapper = this.state.currentLogData.tripLogs.getLogs_(minDate, maxDate, this.state.filters);
const logs = logsWrapper.value();
const logs = this.getFilteredLogs();
if (logs.length > 0) {
const lastRow = logs[logs.length - 1];
this.setFeaturedObject(lastRow);
Expand All @@ -296,10 +318,8 @@ class App extends React.Component {
};

handleRowChange = async (direction) => {
const { featuredObject, filters } = this.state;
const minDate = new Date(this.state.timeRange.minTime);
const maxDate = new Date(this.state.timeRange.maxTime);
const logs = this.state.currentLogData.tripLogs.getLogs_(minDate, maxDate, filters).value();
const { featuredObject } = this.state;
const logs = this.getFilteredLogs();
let newFeaturedObject = featuredObject;
const currentIndex = logs.findIndex((log) => log.timestamp === featuredObject.timestamp);

Expand Down Expand Up @@ -345,7 +365,14 @@ class App extends React.Component {

handleSpeedChange = (event) => {
const newSpeed = parseInt(event.target.value);
this.setState({ playSpeed: newSpeed });
this.setState({ playSpeed: newSpeed }, () => {
if (this.state.isPlaying) {
clearInterval(this.timerID);
this.timerID = setInterval(() => {
this.handleNextEvent();
}, newSpeed);
}
});
};

handleKeyPress = (event) => {
Expand Down Expand Up @@ -909,9 +936,7 @@ class App extends React.Component {

setTimeout(() => {
if (savedRowIndex >= 0) {
const minDate = new Date(this.state.timeRange.minTime);
const maxDate = new Date(this.state.timeRange.maxTime);
const logs = tripLogs.getLogs_(minDate, maxDate).value();
const logs = this.getFilteredLogs();

if (savedRowIndex < logs.length) {
log(`Restoring row at index ${savedRowIndex}`);
Expand Down Expand Up @@ -1014,6 +1039,12 @@ class App extends React.Component {
focusSelectedRow={this.focusOnSelectedRow}
initialMapBounds={this.state.initialMapBounds}
filters={filters}
useResponseLocation={
this.state.activeDatasetIndex !== null
? this.state.useResponseLocationPerDataset[this.state.activeDatasetIndex]
: false
}
setUseResponseLocation={this.setUseResponseLocation}
/>
</div>
<TimeSlider
Expand Down Expand Up @@ -1053,11 +1084,8 @@ class App extends React.Component {
</div>
<div>
<button onClick={this.handlePlayStop}>{this.state.isPlaying ? "Stop" : "Play"}</button>
<select
value={this.state.playSpeed}
onChange={this.handleSpeedChange}
disabled={this.state.isPlaying}
>
<select value={this.state.playSpeed} onChange={this.handleSpeedChange}>
<option value="100">0.1s</option>
<option value="250">0.25s</option>
<option value="500">0.5s</option>
<option value="1000">1s</option>
Expand Down
4 changes: 3 additions & 1 deletion src/LogTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,9 @@ function LogTable(props) {
const [selectedRowIndex, setSelectedRowIndex] = useState(-1);
const minTime = props.timeRange.minTime;
const maxTime = props.timeRange.maxTime;
const data = props.logData.tripLogs.getLogs_(new Date(minTime), new Date(maxTime), props.filters).value();
const data = React.useMemo(() => {
return props.logData.tripLogs.getLogs_(new Date(minTime), new Date(maxTime), props.filters).value();
}, [props.logData.tripLogs, minTime, maxTime, props.filters]);
const columnShortWidth = 50;
const columnRegularWidth = 120;
const columnLargeWidth = 150;
Expand Down
102 changes: 85 additions & 17 deletions src/Map.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ function MapComponent({
setCenterOnLocation,
setRenderMarkerOnMap,
filters,
useResponseLocation,
setUseResponseLocation,
}) {
const { tripLogs, taskLogs, jwt, projectId, mapId } = logData;

Expand Down Expand Up @@ -112,6 +114,7 @@ function MapComponent({
mapRef.current = map;

const tripObjects = new TripObjects({ map, setFeaturedObject, setTimeRange });
mapDivRef.current.tripObjects = tripObjects;

const addTripPolys = () => {
const trips = tripLogs.getTrips();
Expand Down Expand Up @@ -163,15 +166,52 @@ function MapComponent({
};
map.controls[window.google.maps.ControlPosition.TOP_LEFT].push(polylineButton);

const bottomControlsWrapper = document.createElement("div");
bottomControlsWrapper.className = "map-controls-bottom-left";

const followButton = document.createElement("div");
followButton.className = "follow-vehicle-button";
followButton.innerHTML = `<div class="follow-vehicle-background"></div><svg xmlns="http://www.w3.org/2000/svg" viewBox="-10 -10 20 20" width="24" height="24" class="follow-vehicle-chevron"><path d="M -10,10 L 0,-10 L 10,10 L 0,5 z" fill="#4285F4" stroke="#4285F4" stroke-width="1"/></svg>`;
followButton.onclick = () => {
log("Follow vehicle button clicked.");
recenterOnVehicleWrapper();
map.setZoom(17);
};
map.controls[window.google.maps.ControlPosition.LEFT_BOTTOM].push(followButton);

const toggleContainer = document.createElement("div");
toggleContainer.className = "map-toggle-container";

const updateToggleStyles = (reqActive) => {
reqBtn.className = reqActive ? "map-toggle-button active" : "map-toggle-button";
resBtn.className = reqActive ? "map-toggle-button" : "map-toggle-button active";
};

const reqBtn = document.createElement("button");
reqBtn.textContent = "Request";
reqBtn.className = "map-toggle-button";
reqBtn.onclick = () => {
setUseResponseLocation(false);
updateToggleStyles(true);
};

const resBtn = document.createElement("button");
resBtn.textContent = "Response";
resBtn.className = "map-toggle-button";
resBtn.onclick = () => {
setUseResponseLocation(true);
updateToggleStyles(false);
};

const separator = document.createElement("div");
separator.className = "map-toggle-separator";

updateToggleStyles(!useResponseLocation);

toggleContainer.appendChild(reqBtn);
toggleContainer.appendChild(resBtn);

bottomControlsWrapper.appendChild(followButton);
bottomControlsWrapper.appendChild(toggleContainer);
map.controls[window.google.maps.ControlPosition.LEFT_BOTTOM].push(bottomControlsWrapper);

const centerListener = map.addListener(
"center_changed",
Expand Down Expand Up @@ -245,16 +285,17 @@ function MapComponent({
if (!map) return;
log("recenterOnVehicleWrapper called for follow mode.");

let position = null;
if (selectedRow?.lastlocation?.rawlocation) {
position = selectedRow.lastlocation.rawlocation;
} else if (lastValidPositionRef.current) {
position = lastValidPositionRef.current;
if (!isFollowingVehicle) {
const locationObj = useResponseLocation ? selectedRow?.lastlocationResponse : selectedRow?.lastlocation;
const position = locationObj?.location || locationObj?.rawlocation || lastValidPositionRef.current;
if (position) {
map.setCenter({ lat: position.latitude, lng: position.longitude });
map.setZoom(17);
}
}

if (position) map.setCenter({ lat: position.latitude, lng: position.longitude });
setIsFollowingVehicle((prev) => !prev);
}, [selectedRow]);
}, [selectedRow, useResponseLocation, isFollowingVehicle]);

useEffect(() => {
const followButton = document.querySelector(".follow-vehicle-button");
Expand Down Expand Up @@ -338,16 +379,13 @@ function MapComponent({
return;
}

const location =
_.get(selectedRow.lastlocation, "location") ||
_.get(selectedRow.lastlocation, "rawlocation") ||
_.get(selectedRow.lastlocationResponse, "location");
const locationObj = useResponseLocation ? selectedRow.lastlocationResponse : selectedRow.lastlocation;
const location = _.get(locationObj, "location") || _.get(locationObj, "rawlocation");

if (location?.latitude && location?.longitude) {
const pos = { lat: location.latitude, lng: location.longitude };
lastValidPositionRef.current = pos;
const heading =
_.get(selectedRow.lastlocation, "heading") || _.get(selectedRow.lastlocationResponse, "heading") || 0;
const heading = _.get(locationObj, "heading") || 0;

if (vehicleMarkersRef.current.background) {
vehicleMarkersRef.current.background.setPosition(pos);
Expand Down Expand Up @@ -396,7 +434,7 @@ function MapComponent({
});
}

const rawLocation = _.get(selectedRow.lastlocation, "rawlocation");
const rawLocation = _.get(locationObj, "rawlocation");
if (rawLocation?.latitude && rawLocation?.longitude) {
const rawPos = { lat: rawLocation.latitude, lng: rawLocation.longitude };
if (vehicleMarkersRef.current.rawLocation) {
Expand Down Expand Up @@ -429,7 +467,37 @@ function MapComponent({
} else {
Object.values(vehicleMarkersRef.current).forEach((marker) => marker && marker.setMap(null));
}
}, [selectedRow, isFollowingVehicle]);
}, [selectedRow, isFollowingVehicle, useResponseLocation]);

// Update trip objects when toggle changes
useEffect(() => {
if (mapDivRef.current && mapDivRef.current.tripObjects) {
log(`Updating TripObjects useResponseLocation to ${useResponseLocation}`);
const tripObjects = mapDivRef.current.tripObjects;
tripObjects.setUseResponseLocation(useResponseLocation);

// Redraw trips
const trips = tripLogs.getTrips();
_.forEach(trips, (trip) => {
tripObjects.addTripVisuals(trip, minDate, maxDate);
});
}
}, [useResponseLocation, tripLogs, minDate, maxDate]);

// Update toggle button UI
useEffect(() => {
const container = document.querySelector(".map-toggle-container");
if (container) {
const [reqBtn, , resBtn] = container.children;
if (reqBtn && resBtn) {
reqBtn.className = `map-toggle-button${!useResponseLocation ? " active" : ""}`;
resBtn.className = `map-toggle-button${useResponseLocation ? " active" : ""}`;
}
}
if (isFollowingVehicle && selectedRow) {
// Re-center if we are following and the toggle changed
}
}, [useResponseLocation, isFollowingVehicle, selectedRow, recenterOnVehicleWrapper]);

const toggleHandlers = useMemo(() => {
const map = mapRef.current;
Expand Down
17 changes: 14 additions & 3 deletions src/Trip.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Trip {
this.tripName = tripName;
this.updateRequests = 1;
this.pathCoords = [];
this.pathCoordsResponse = [];
this.tripDuration = 0;
this.creationTime = "Unknown";
this.firstUpdate = firstUpdate;
Expand Down Expand Up @@ -37,11 +38,12 @@ class Trip {
};
}

getPathCoords(minDate, maxDate) {
getPathCoords(minDate, maxDate, useResponse = false) {
const coords = useResponse ? this.pathCoordsResponse : this.pathCoords;
if (!(minDate && maxDate)) {
return this.pathCoords;
return coords;
}
return _(this.pathCoords)
return _(coords)
.filter((le) => {
return le.date >= minDate && le.date <= maxDate;
})
Expand All @@ -59,6 +61,15 @@ class Trip {
});
}

appendResponseCoords(lastLocation, timestamp) {
this.pathCoordsResponse.push({
lat: lastLocation.location.latitude,
lng: lastLocation.location.longitude,
trip_id: this.tripName,
date: new Date(timestamp),
});
}

setPlannedPath(plannedPath) {
this.plannedPath = plannedPath.map((coords) => {
return { lat: coords.latitude, lng: coords.longitude };
Expand Down
Loading
Loading