aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2024-07-01 16:10:10 -0700
committerGitHub <noreply@github.com>2024-07-01 16:10:10 -0700
commite15f1097e46ca128fc4ece1ce3c6f6e24a69b33d (patch)
tree0c00e40ab964e0e4642305a64134f74f889da611 /src
parent66b964c8c187d7002db829be213837dc614b98ac (diff)
parent1c779e8cecc605ec69821b0157776eee9ddf7a73 (diff)
downloadtrackermap-web-e15f1097e46ca128fc4ece1ce3c6f6e24a69b33d.tar.gz
trackermap-web-e15f1097e46ca128fc4ece1ce3c6f6e24a69b33d.tar.bz2
trackermap-web-e15f1097e46ca128fc4ece1ce3c6f6e24a69b33d.zip
Merge pull request #1250 from jcardus/speed-based-color
keep old behaviour in RoutePath when we just have coordinates (and no…
Diffstat (limited to 'src')
-rw-r--r--src/common/util/colors.js23
-rw-r--r--src/map/MapRouteCoordinates.js90
-rw-r--r--src/map/MapRoutePath.js54
-rw-r--r--src/map/MapRoutePoints.js15
-rw-r--r--src/map/core/preloadImages.js2
-rw-r--r--src/reports/CombinedReportPage.jsx5
-rw-r--r--src/resources/images/arrow.svg4
7 files changed, 152 insertions, 41 deletions
diff --git a/src/common/util/colors.js b/src/common/util/colors.js
new file mode 100644
index 00000000..1cb35c04
--- /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);
+};
diff --git a/src/map/MapRouteCoordinates.js b/src/map/MapRouteCoordinates.js
new file mode 100644
index 00000000..9a43ce4a
--- /dev/null
+++ b/src/map/MapRouteCoordinates.js
@@ -0,0 +1,90 @@
+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,
+ },
+ });
+ 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, reportColor]);
+
+ return null;
+};
+
+export default MapRouteCoordinates;
diff --git a/src/map/MapRoutePath.js b/src/map/MapRoutePath.js
index 20269140..716a4bd1 100644
--- a/src/map/MapRoutePath.js
+++ b/src/map/MapRoutePath.js
@@ -2,8 +2,9 @@ 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 = ({ positions }) => {
const id = useId();
const theme = useTheme();
@@ -19,7 +20,7 @@ const MapRoutePath = ({ name, positions, coordinates }) => {
}
}
}
- return theme.palette.geometry.main;
+ return null;
});
useEffect(() => {
@@ -46,21 +47,6 @@ const MapRoutePath = ({ name, positions, coordinates }) => {
'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`)) {
@@ -76,21 +62,31 @@ const MapRoutePath = ({ name, positions, coordinates }) => {
}, []);
useEffect(() => {
- if (!coordinates) {
- coordinates = positions.map((item) => [item.longitude, item.latitude]);
+ const maxSpeed = positions.map((item) => item.speed).reduce((a, b) => Math.max(a, b), -Infinity);
+ const features = [];
+ for (let i = 0; i < positions.length - 1; i += 1) {
+ features.push({
+ type: 'Feature',
+ geometry: {
+ type: 'LineString',
+ coordinates: [[positions[i].longitude, positions[i].latitude], [positions[i + 1].longitude, positions[i + 1].latitude]],
+ },
+ properties: {
+ color: reportColor || getSpeedColor(
+ theme.palette.success.main,
+ theme.palette.warning.main,
+ theme.palette.error.main,
+ positions[i + 1].speed,
+ maxSpeed,
+ ),
+ },
+ });
}
map.getSource(id)?.setData({
- type: 'Feature',
- geometry: {
- type: 'LineString',
- coordinates,
- },
- properties: {
- name,
- color: reportColor,
- },
+ type: 'FeatureCollection',
+ features,
});
- }, [theme, positions, coordinates, reportColor]);
+ }, [theme, positions, reportColor]);
return null;
};
diff --git a/src/map/MapRoutePoints.js b/src/map/MapRoutePoints.js
index e329da81..929a0d50 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 a0056d4c..19c3c35e 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/reports/CombinedReportPage.jsx b/src/reports/CombinedReportPage.jsx
index f591a7a1..46dbf66b 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();
@@ -62,10 +62,11 @@ const CombinedReportPage = () => {
<MapView>
<MapGeofence />
{items.map((item) => (
- <MapRoutePath
+ <MapRouteCoordinates
key={item.deviceId}
name={devices[item.deviceId].name}
coordinates={item.route}
+ deviceId={item.deviceId}
/>
))}
<MapMarkers markers={createMarkers()} />
diff --git a/src/resources/images/arrow.svg b/src/resources/images/arrow.svg
deleted file mode 100644
index d0f30a2e..00000000
--- a/src/resources/images/arrow.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="20px" height="20px" fill="#000000" version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
- <path d="m12 2-7.5 18.29 0.71 0.71 6.79-3 6.79 3 0.71-0.71z" fill="#3bb2d0"/>
-</svg>