aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Tananaev <anton@traccar.org>2022-10-16 18:30:55 -0700
committerAnton Tananaev <anton@traccar.org>2022-10-16 18:30:55 -0700
commit79841c3f94ff2e50f51c38907ba2923735611af5 (patch)
treebb4a35cedc210c7fa8ecb61dfd83066baa2e855a
parent6bfe52d4e0578ea83761ff174ae6dc205b97a4ac (diff)
downloadtrackermap-web-79841c3f94ff2e50f51c38907ba2923735611af5.tar.gz
trackermap-web-79841c3f94ff2e50f51c38907ba2923735611af5.tar.bz2
trackermap-web-79841c3f94ff2e50f51c38907ba2923735611af5.zip
Add route points
-rw-r--r--modern/src/map/MapPositions.js2
-rw-r--r--modern/src/map/MapRoutePoints.js77
-rw-r--r--modern/src/other/ReplayPage.js13
-rw-r--r--modern/src/reports/RouteReportPage.js19
4 files changed, 101 insertions, 10 deletions
diff --git a/modern/src/map/MapPositions.js b/modern/src/map/MapPositions.js
index 5e09bc9c..704c24bd 100644
--- a/modern/src/map/MapPositions.js
+++ b/modern/src/map/MapPositions.js
@@ -35,7 +35,7 @@ const MapPositions = ({ positions, onClick, showStatus, selectedPosition }) => {
const onMouseLeave = () => map.getCanvas().style.cursor = '';
const onMapClick = useCallback((event) => {
- if (!event.defaultPrevented) {
+ if (!event.defaultPrevented && onClick) {
onClick();
}
}, [onClick]);
diff --git a/modern/src/map/MapRoutePoints.js b/modern/src/map/MapRoutePoints.js
new file mode 100644
index 00000000..38a0e226
--- /dev/null
+++ b/modern/src/map/MapRoutePoints.js
@@ -0,0 +1,77 @@
+import { useId, useCallback, useEffect } from 'react';
+import { useTheme } from '@mui/styles';
+import { map } from './core/MapView';
+
+const MapPositions = ({ positions, onClick }) => {
+ const id = useId();
+
+ const theme = useTheme();
+
+ const onMouseEnter = () => map.getCanvas().style.cursor = 'pointer';
+ const onMouseLeave = () => map.getCanvas().style.cursor = '';
+
+ const onMarkerClick = useCallback((event) => {
+ event.preventDefault();
+ const feature = event.features[0];
+ if (onClick) {
+ onClick(feature.properties.id, feature.properties.index);
+ }
+ }, [onClick]);
+
+ useEffect(() => {
+ map.addSource(id, {
+ type: 'geojson',
+ data: {
+ type: 'FeatureCollection',
+ features: [],
+ },
+ });
+ map.addLayer({
+ id,
+ type: 'circle',
+ source: id,
+ paint: {
+ 'circle-radius': 5,
+ 'circle-color': theme.palette.colors.geometry,
+ },
+ });
+
+ map.on('mouseenter', id, onMouseEnter);
+ map.on('mouseleave', id, onMouseLeave);
+ map.on('click', id, onMarkerClick);
+
+ return () => {
+ map.off('mouseenter', id, onMouseEnter);
+ map.off('mouseleave', id, onMouseLeave);
+ map.off('click', id, onMarkerClick);
+
+ if (map.getLayer(id)) {
+ map.removeLayer(id);
+ }
+ if (map.getSource(id)) {
+ map.removeSource(id);
+ }
+ };
+ }, [onMarkerClick]);
+
+ useEffect(() => {
+ map.getSource(id).setData({
+ type: 'FeatureCollection',
+ features: positions.map((position, index) => ({
+ type: 'Feature',
+ geometry: {
+ type: 'Point',
+ coordinates: [position.longitude, position.latitude],
+ },
+ properties: {
+ index,
+ id: position.id,
+ },
+ })),
+ });
+ }, [positions]);
+
+ return null;
+};
+
+export default MapPositions;
diff --git a/modern/src/other/ReplayPage.js b/modern/src/other/ReplayPage.js
index 9d8503a8..ee9e15d9 100644
--- a/modern/src/other/ReplayPage.js
+++ b/modern/src/other/ReplayPage.js
@@ -16,6 +16,7 @@ import { useNavigate } from 'react-router-dom';
import { useSelector } from 'react-redux';
import MapView from '../map/core/MapView';
import MapRoutePath from '../map/MapRoutePath';
+import MapRoutePoints from '../map/MapRoutePoints';
import MapPositions from '../map/MapPositions';
import { formatTime } from '../common/util/formatter';
import ReportFilter from '../reports/components/ReportFilter';
@@ -132,7 +133,11 @@ const ReplayPage = () => {
}
}, [index, positions]);
- const onMapClick = useCallback((positionId) => {
+ const onPointClick = useCallback((_, index) => {
+ setIndex(index);
+ }, [setIndex]);
+
+ const onMarkerClick = useCallback((positionId) => {
setShowCard(!!positionId);
}, [setShowCard]);
@@ -166,11 +171,9 @@ const ReplayPage = () => {
<MapView>
<MapGeofence />
<MapRoutePath positions={positions} />
+ <MapRoutePoints positions={positions} onClick={onPointClick} />
{index < positions.length && (
- <MapPositions
- positions={[positions[index]]}
- onClick={onMapClick}
- />
+ <MapPositions positions={[positions[index]]} onClick={onMarkerClick} />
)}
</MapView>
<MapCamera positions={positions} />
diff --git a/modern/src/reports/RouteReportPage.js b/modern/src/reports/RouteReportPage.js
index a37ad7b1..a1212ecd 100644
--- a/modern/src/reports/RouteReportPage.js
+++ b/modern/src/reports/RouteReportPage.js
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { Fragment, useCallback, useState } from 'react';
import { useSelector } from 'react-redux';
import {
IconButton, Table, TableBody, TableCell, TableHead, TableRow,
@@ -16,6 +16,7 @@ import usePositionAttributes from '../common/attributes/usePositionAttributes';
import { useCatch } from '../reactHelper';
import MapView from '../map/core/MapView';
import MapRoutePath from '../map/MapRoutePath';
+import MapRoutePoints from '../map/MapRoutePoints';
import MapPositions from '../map/MapPositions';
import useReportStyles from './common/useReportStyles';
import TableShimmer from '../common/components/TableShimmer';
@@ -35,6 +36,10 @@ const RouteReportPage = () => {
const [loading, setLoading] = useState(false);
const [selectedItem, setSelectedItem] = useState(null);
+ const onMapPointClick = useCallback((positionId) => {
+ setSelectedItem(items.find((it) => it.id === positionId));
+ }, [items, setSelectedItem]);
+
const handleSubmit = useCatch(async ({ deviceIds, from, to, type }) => {
const query = new URLSearchParams({ from, to });
deviceIds.forEach((deviceId) => query.append('deviceId', deviceId));
@@ -69,9 +74,15 @@ const RouteReportPage = () => {
<div className={classes.containerMap}>
<MapView>
<MapGeofence />
- {[...new Set(items.map((it) => it.deviceId))].map((deviceId) => (
- <MapRoutePath key={deviceId} positions={items.filter((position) => position.deviceId === deviceId)} />
- ))}
+ {[...new Set(items.map((it) => it.deviceId))].map((deviceId) => {
+ const positions = items.filter((position) => position.deviceId === deviceId);
+ return (
+ <Fragment key={deviceId}>
+ <MapRoutePath positions={positions} />
+ <MapRoutePoints positions={positions} onClick={onMapPointClick} />
+ </Fragment>
+ );
+ })}
<MapPositions positions={[selectedItem]} />
</MapView>
<MapCamera positions={items} />