diff options
author | Anton Tananaev <anton@traccar.org> | 2022-10-16 18:30:55 -0700 |
---|---|---|
committer | Anton Tananaev <anton@traccar.org> | 2022-10-16 18:30:55 -0700 |
commit | 79841c3f94ff2e50f51c38907ba2923735611af5 (patch) | |
tree | bb4a35cedc210c7fa8ecb61dfd83066baa2e855a | |
parent | 6bfe52d4e0578ea83761ff174ae6dc205b97a4ac (diff) | |
download | trackermap-web-79841c3f94ff2e50f51c38907ba2923735611af5.tar.gz trackermap-web-79841c3f94ff2e50f51c38907ba2923735611af5.tar.bz2 trackermap-web-79841c3f94ff2e50f51c38907ba2923735611af5.zip |
Add route points
-rw-r--r-- | modern/src/map/MapPositions.js | 2 | ||||
-rw-r--r-- | modern/src/map/MapRoutePoints.js | 77 | ||||
-rw-r--r-- | modern/src/other/ReplayPage.js | 13 | ||||
-rw-r--r-- | modern/src/reports/RouteReportPage.js | 19 |
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} /> |