From 02a7e05d3c7c436e806c9ac946c51469ffe596fd Mon Sep 17 00:00:00 2001 From: jcardus Date: Fri, 28 Jun 2024 18:23:11 +0100 Subject: [PATCH 1/7] keep old behaviour in RoutePath when we just have coordinates (and no positions) include name property in both cases. --- src/map/MapRoutePath.js | 52 +++++++++++++++++++++++++++------- src/map/MapRoutePoints.js | 15 +++++++--- src/map/core/preloadImages.js | 2 -- src/resources/images/arrow.svg | 4 --- 4 files changed, 52 insertions(+), 21 deletions(-) delete mode 100644 src/resources/images/arrow.svg diff --git a/src/map/MapRoutePath.js b/src/map/MapRoutePath.js index 202691401b..39a9ad5f27 100644 --- a/src/map/MapRoutePath.js +++ b/src/map/MapRoutePath.js @@ -2,6 +2,7 @@ import { useTheme } from '@mui/styles'; import { useId, useEffect } from 'react'; import { useSelector } from 'react-redux'; import { map } from './core/MapView'; +import { getSpeedColor } from '../common/util/colors'; const MapRoutePath = ({ name, positions, coordinates }) => { const id = useId(); @@ -78,18 +79,47 @@ const MapRoutePath = ({ name, positions, coordinates }) => { useEffect(() => { if (!coordinates) { coordinates = positions.map((item) => [item.longitude, item.latitude]); + const speeds = positions.map((item) => item.speed); + const maxSpeed = speeds.reduce((a, b) => Math.max(a, b), -Infinity); + const features = []; + for (let i = 0; i < coordinates.length - 1; i += 1) { + features.push({ + type: 'Feature', + geometry: { + type: 'LineString', + coordinates: [coordinates[i], coordinates[i + 1]], + }, + properties: { + color: getSpeedColor( + theme.palette.success.main, + theme.palette.warning.main, + theme.palette.error.main, + speeds[i + 1], + maxSpeed, + ), + }, + }); + } + map.getSource(id)?.setData({ + type: 'FeatureCollection', + features, + properties: { + name, + }, + }); + } else { + map.getSource(id)?.setData({ + type: 'Feature', + geometry: { + type: 'LineString', + coordinates, + }, + properties: { + name, + color: reportColor, + }, + }); } - map.getSource(id)?.setData({ - type: 'Feature', - geometry: { - type: 'LineString', - coordinates, - }, - properties: { - name, - color: reportColor, - }, - }); }, [theme, positions, coordinates, reportColor]); return null; diff --git a/src/map/MapRoutePoints.js b/src/map/MapRoutePoints.js index e329da8118..929a0d5069 100644 --- a/src/map/MapRoutePoints.js +++ b/src/map/MapRoutePoints.js @@ -1,8 +1,11 @@ import { useId, useCallback, useEffect } from 'react'; +import { useTheme } from '@mui/styles'; import { map } from './core/MapView'; +import { getSpeedColor } from '../common/util/colors'; const MapRoutePoints = ({ positions, onClick }) => { const id = useId(); + const theme = useTheme(); const onMouseEnter = () => map.getCanvas().style.cursor = 'pointer'; const onMouseLeave = () => map.getCanvas().style.cursor = ''; @@ -27,11 +30,13 @@ const MapRoutePoints = ({ positions, onClick }) => { id, type: 'symbol', source: id, + paint: { + 'text-color': ['get', 'color'], + }, layout: { - 'icon-image': 'arrow', - 'icon-allow-overlap': true, - 'icon-rotate': ['get', 'rotation'], - 'icon-rotation-alignment': 'map', + 'text-field': '▲', + 'text-allow-overlap': true, + 'text-rotate': ['get', 'rotation'], }, }); @@ -54,6 +59,7 @@ const MapRoutePoints = ({ positions, onClick }) => { }, [onMarkerClick]); useEffect(() => { + const maxSpeed = positions.map((p) => p.speed).reduce((a, b) => Math.max(a, b), -Infinity); map.getSource(id)?.setData({ type: 'FeatureCollection', features: positions.map((position, index) => ({ @@ -66,6 +72,7 @@ const MapRoutePoints = ({ positions, onClick }) => { index, id: position.id, rotation: position.course, + color: getSpeedColor(theme.palette.success.main, theme.palette.warning.main, theme.palette.error.main, position.speed, maxSpeed), }, })), }); diff --git a/src/map/core/preloadImages.js b/src/map/core/preloadImages.js index a0056d4c3b..19c3c35ea6 100644 --- a/src/map/core/preloadImages.js +++ b/src/map/core/preloadImages.js @@ -2,7 +2,6 @@ import { grey } from '@mui/material/colors'; import createPalette from '@mui/material/styles/createPalette'; import { loadImage, prepareIcon } from './mapUtil'; -import arrowSvg from '../../resources/images/arrow.svg'; import directionSvg from '../../resources/images/direction.svg'; import backgroundSvg from '../../resources/images/background.svg'; import animalSvg from '../../resources/images/icon/animal.svg'; @@ -65,7 +64,6 @@ export default async () => { const background = await loadImage(backgroundSvg); mapImages.background = await prepareIcon(background); mapImages.direction = await prepareIcon(await loadImage(directionSvg)); - mapImages.arrow = await prepareIcon(await loadImage(arrowSvg)); await Promise.all(Object.keys(mapIcons).map(async (category) => { const results = []; ['info', 'success', 'error', 'neutral'].forEach((color) => { diff --git a/src/resources/images/arrow.svg b/src/resources/images/arrow.svg deleted file mode 100644 index d0f30a2efe..0000000000 --- a/src/resources/images/arrow.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - From 870ef74ef258b197137f545f22664a426c474afd Mon Sep 17 00:00:00 2001 From: jcardus Date: Sun, 30 Jun 2024 17:57:12 +0100 Subject: [PATCH 2/7] split MapRoutePath --- src/map/MapRouteCoordinates.js | 92 ++++++++++++++++++++++++++++++ src/map/MapRoutePath.js | 75 ++++++++---------------- src/reports/CombinedReportPage.jsx | 5 +- 3 files changed, 118 insertions(+), 54 deletions(-) create mode 100644 src/map/MapRouteCoordinates.js diff --git a/src/map/MapRouteCoordinates.js b/src/map/MapRouteCoordinates.js new file mode 100644 index 0000000000..1e3f934f5c --- /dev/null +++ b/src/map/MapRouteCoordinates.js @@ -0,0 +1,92 @@ +import { useTheme } from '@mui/styles'; +import { useId, useEffect } from 'react'; +import { useSelector } from 'react-redux'; +import { map } from './core/MapView'; + +const MapRouteCoordinates = ({ name, coordinates, deviceId }) => { + const id = useId(); + + const theme = useTheme(); + + const reportColor = useSelector((state) => { + const attributes = state.devices.items[deviceId]?.attributes; + if (attributes) { + const color = attributes['web.reportColor']; + if (color) { + return color; + } + } + return theme.palette.geometry.main; + }); + + useEffect(() => { + map.addSource(id, { + type: 'geojson', + data: { + type: 'Feature', + geometry: { + type: 'LineString', + coordinates: [], + }, + }, + }); + map.addLayer({ + source: id, + id: `${id}-line`, + type: 'line', + layout: { + 'line-join': 'round', + 'line-cap': 'round', + }, + paint: { + 'line-color': ['get', 'color'], + 'line-width': 2, + }, + }); + if (name) { + map.addLayer({ + source: id, + id: `${id}-title`, + type: 'symbol', + layout: { + 'text-field': '{name}', + 'text-size': 12, + }, + paint: { + 'text-halo-color': 'white', + 'text-halo-width': 1, + }, + }); + } + + return () => { + if (map.getLayer(`${id}-title`)) { + map.removeLayer(`${id}-title`); + } + if (map.getLayer(`${id}-line`)) { + map.removeLayer(`${id}-line`); + } + if (map.getSource(id)) { + map.removeSource(id); + } + }; + }, []); + + useEffect(() => { + map.getSource(id)?.setData({ + type: 'Feature', + geometry: { + type: 'LineString', + coordinates, + }, + properties: { + name, + color: reportColor, + }, + }); + }, [theme, coordinates]); + + return null; +}; + +export default MapRouteCoordinates; diff --git a/src/map/MapRoutePath.js b/src/map/MapRoutePath.js index 39a9ad5f27..cf55d290b0 100644 --- a/src/map/MapRoutePath.js +++ b/src/map/MapRoutePath.js @@ -1,28 +1,13 @@ import { useTheme } from '@mui/styles'; import { useId, useEffect } from 'react'; -import { useSelector } from 'react-redux'; import { map } from './core/MapView'; import { getSpeedColor } from '../common/util/colors'; -const MapRoutePath = ({ name, positions, coordinates }) => { +const MapRoutePath = ({ name, positions }) => { const id = useId(); const theme = useTheme(); - const reportColor = useSelector((state) => { - const position = positions?.find(() => true); - if (position) { - const attributes = state.devices.items[position.deviceId]?.attributes; - if (attributes) { - const color = attributes['web.reportColor']; - if (color) { - return color; - } - } - } - return theme.palette.geometry.main; - }); - useEffect(() => { map.addSource(id, { type: 'geojson', @@ -77,50 +62,36 @@ const MapRoutePath = ({ name, positions, coordinates }) => { }, []); useEffect(() => { - if (!coordinates) { - coordinates = positions.map((item) => [item.longitude, item.latitude]); - const speeds = positions.map((item) => item.speed); - const maxSpeed = speeds.reduce((a, b) => Math.max(a, b), -Infinity); - const features = []; - for (let i = 0; i < coordinates.length - 1; i += 1) { - features.push({ - type: 'Feature', - geometry: { - type: 'LineString', - coordinates: [coordinates[i], coordinates[i + 1]], - }, - properties: { - color: getSpeedColor( - theme.palette.success.main, - theme.palette.warning.main, - theme.palette.error.main, - speeds[i + 1], - maxSpeed, - ), - }, - }); - } - map.getSource(id)?.setData({ - type: 'FeatureCollection', - features, - properties: { - name, - }, - }); - } else { - map.getSource(id)?.setData({ + const coordinates = positions.map((item) => [item.longitude, item.latitude]); + const speeds = positions.map((item) => item.speed); + const maxSpeed = speeds.reduce((a, b) => Math.max(a, b), -Infinity); + const features = []; + for (let i = 0; i < coordinates.length - 1; i += 1) { + features.push({ type: 'Feature', geometry: { type: 'LineString', - coordinates, + coordinates: [coordinates[i], coordinates[i + 1]], }, properties: { - name, - color: reportColor, + color: getSpeedColor( + theme.palette.success.main, + theme.palette.warning.main, + theme.palette.error.main, + speeds[i + 1], + maxSpeed, + ), }, }); } - }, [theme, positions, coordinates, reportColor]); + map.getSource(id)?.setData({ + type: 'FeatureCollection', + features, + properties: { + name, + }, + }); + }, [theme, positions]); return null; }; diff --git a/src/reports/CombinedReportPage.jsx b/src/reports/CombinedReportPage.jsx index 32ad5df0f8..f90aad25cf 100644 --- a/src/reports/CombinedReportPage.jsx +++ b/src/reports/CombinedReportPage.jsx @@ -9,7 +9,6 @@ import PageLayout from '../common/components/PageLayout'; import ReportsMenu from './components/ReportsMenu'; import { useCatch } from '../reactHelper'; import MapView from '../map/core/MapView'; -import MapRoutePath from '../map/MapRoutePath'; import useReportStyles from './common/useReportStyles'; import TableShimmer from '../common/components/TableShimmer'; import MapCamera from '../map/MapCamera'; @@ -17,6 +16,7 @@ import MapGeofence from '../map/MapGeofence'; import { formatTime } from '../common/util/formatter'; import { prefixString } from '../common/util/stringUtils'; import MapMarkers from '../map/MapMarkers'; +import MapRouteCoordinates from '../map/MapRouteCoordinates'; const CombinedReportPage = () => { const classes = useReportStyles(); @@ -60,10 +60,11 @@ const CombinedReportPage = () => { {items.map((item) => ( - ))} From 28d8a5b202c6a04364144533409755c92385982a Mon Sep 17 00:00:00 2001 From: jcardus Date: Sun, 30 Jun 2024 18:02:51 +0100 Subject: [PATCH 3/7] simplify --- src/map/MapRoutePath.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/map/MapRoutePath.js b/src/map/MapRoutePath.js index cf55d290b0..c6790db162 100644 --- a/src/map/MapRoutePath.js +++ b/src/map/MapRoutePath.js @@ -62,23 +62,21 @@ const MapRoutePath = ({ name, positions }) => { }, []); useEffect(() => { - const coordinates = positions.map((item) => [item.longitude, item.latitude]); - const speeds = positions.map((item) => item.speed); - const maxSpeed = speeds.reduce((a, b) => Math.max(a, b), -Infinity); + const maxSpeed = positions.map((item) => item.speed).reduce((a, b) => Math.max(a, b), -Infinity); const features = []; - for (let i = 0; i < coordinates.length - 1; i += 1) { + for (let i = 0; i < positions.length - 1; i += 1) { features.push({ type: 'Feature', geometry: { type: 'LineString', - coordinates: [coordinates[i], coordinates[i + 1]], + coordinates: [[positions[i].longitude, positions[i].latitude], [positions[i + 1].longitude, positions[i + 1].latitude]], }, properties: { color: getSpeedColor( theme.palette.success.main, theme.palette.warning.main, theme.palette.error.main, - speeds[i + 1], + positions[i + 1].speed, maxSpeed, ), }, From 27f6cc527d7ca7d52c3a8fd3f70de6105c944e24 Mon Sep 17 00:00:00 2001 From: jcardus Date: Sun, 30 Jun 2024 18:07:25 +0100 Subject: [PATCH 4/7] add colors.js --- src/common/util/colors.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/common/util/colors.js diff --git a/src/common/util/colors.js b/src/common/util/colors.js new file mode 100644 index 0000000000..1cb35c04fe --- /dev/null +++ b/src/common/util/colors.js @@ -0,0 +1,23 @@ +import { decomposeColor } from '@mui/material'; + +export const interpolateColor = (color1, color2, factor) => { + if (factor > 1) factor = 1; + if (factor < 0) factor = 0; + + const c1 = decomposeColor(color1).values; + const c2 = decomposeColor(color2).values; + + const r = Math.round(c1[0] + factor * (c2[0] - c1[0])); + const g = Math.round(c1[1] + factor * (c2[1] - c1[1])); + const b = Math.round(c1[2] + factor * (c2[2] - c1[2])); + + return `rgb(${r}, ${g}, ${b})`; +}; + +export const getSpeedColor = (color1, color2, color3, speed, max) => { + const factor = speed / max; + if (factor <= 0.5) { + return interpolateColor(color1, color2, factor * 2); + } + return interpolateColor(color2, color3, (factor - 0.5) * 2); +}; From 244028cf64a975668a25dd39850b7bafe4a50b81 Mon Sep 17 00:00:00 2001 From: jcardus Date: Sun, 30 Jun 2024 18:13:17 +0100 Subject: [PATCH 5/7] missing reportColor --- src/map/MapRouteCoordinates.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/MapRouteCoordinates.js b/src/map/MapRouteCoordinates.js index 1e3f934f5c..9ad0c9b97a 100644 --- a/src/map/MapRouteCoordinates.js +++ b/src/map/MapRouteCoordinates.js @@ -84,7 +84,7 @@ const MapRouteCoordinates = ({ name, coordinates, deviceId }) => { color: reportColor, }, }); - }, [theme, coordinates]); + }, [theme, coordinates, reportColor]); return null; }; From abe03b64f8bbc59eebdfefad6a1c7ee624760535 Mon Sep 17 00:00:00 2001 From: jcardus Date: Mon, 1 Jul 2024 00:24:51 +0100 Subject: [PATCH 6/7] missing reportColor --- src/map/MapRouteCoordinates.js | 28 +++++++++++++--------------- src/map/MapRoutePath.js | 20 +------------------- 2 files changed, 14 insertions(+), 34 deletions(-) diff --git a/src/map/MapRouteCoordinates.js b/src/map/MapRouteCoordinates.js index 9ad0c9b97a..9a43ce4a84 100644 --- a/src/map/MapRouteCoordinates.js +++ b/src/map/MapRouteCoordinates.js @@ -43,21 +43,19 @@ const MapRouteCoordinates = ({ name, coordinates, deviceId }) => { 'line-width': 2, }, }); - if (name) { - map.addLayer({ - source: id, - id: `${id}-title`, - type: 'symbol', - layout: { - 'text-field': '{name}', - 'text-size': 12, - }, - paint: { - 'text-halo-color': 'white', - 'text-halo-width': 1, - }, - }); - } + map.addLayer({ + source: id, + id: `${id}-title`, + type: 'symbol', + layout: { + 'text-field': '{name}', + 'text-size': 12, + }, + paint: { + 'text-halo-color': 'white', + 'text-halo-width': 1, + }, + }); return () => { if (map.getLayer(`${id}-title`)) { diff --git a/src/map/MapRoutePath.js b/src/map/MapRoutePath.js index c6790db162..d7ac2f94da 100644 --- a/src/map/MapRoutePath.js +++ b/src/map/MapRoutePath.js @@ -3,7 +3,7 @@ import { useId, useEffect } from 'react'; import { map } from './core/MapView'; import { getSpeedColor } from '../common/util/colors'; -const MapRoutePath = ({ name, positions }) => { +const MapRoutePath = ({ positions }) => { const id = useId(); const theme = useTheme(); @@ -32,21 +32,6 @@ const MapRoutePath = ({ name, positions }) => { 'line-width': 2, }, }); - if (name) { - map.addLayer({ - source: id, - id: `${id}-title`, - type: 'symbol', - layout: { - 'text-field': '{name}', - 'text-size': 12, - }, - paint: { - 'text-halo-color': 'white', - 'text-halo-width': 1, - }, - }); - } return () => { if (map.getLayer(`${id}-title`)) { @@ -85,9 +70,6 @@ const MapRoutePath = ({ name, positions }) => { map.getSource(id)?.setData({ type: 'FeatureCollection', features, - properties: { - name, - }, }); }, [theme, positions]); From 1c779e8cecc605ec69821b0157776eee9ddf7a73 Mon Sep 17 00:00:00 2001 From: jcardus Date: Mon, 1 Jul 2024 18:41:36 +0100 Subject: [PATCH 7/7] don't override reportColor --- src/map/MapRoutePath.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/map/MapRoutePath.js b/src/map/MapRoutePath.js index d7ac2f94da..716a4bd122 100644 --- a/src/map/MapRoutePath.js +++ b/src/map/MapRoutePath.js @@ -1,5 +1,6 @@ import { useTheme } from '@mui/styles'; import { useId, useEffect } from 'react'; +import { useSelector } from 'react-redux'; import { map } from './core/MapView'; import { getSpeedColor } from '../common/util/colors'; @@ -8,6 +9,20 @@ const MapRoutePath = ({ positions }) => { const theme = useTheme(); + const reportColor = useSelector((state) => { + const position = positions?.find(() => true); + if (position) { + const attributes = state.devices.items[position.deviceId]?.attributes; + if (attributes) { + const color = attributes['web.reportColor']; + if (color) { + return color; + } + } + } + return null; + }); + useEffect(() => { map.addSource(id, { type: 'geojson', @@ -57,7 +72,7 @@ const MapRoutePath = ({ positions }) => { coordinates: [[positions[i].longitude, positions[i].latitude], [positions[i + 1].longitude, positions[i + 1].latitude]], }, properties: { - color: getSpeedColor( + color: reportColor || getSpeedColor( theme.palette.success.main, theme.palette.warning.main, theme.palette.error.main, @@ -71,7 +86,7 @@ const MapRoutePath = ({ positions }) => { type: 'FeatureCollection', features, }); - }, [theme, positions]); + }, [theme, positions, reportColor]); return null; };