aboutsummaryrefslogtreecommitdiff
path: root/modern/src/map/PositionsMap.js
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2020-10-24 16:42:29 -0700
committerAnton Tananaev <anton.tananaev@gmail.com>2020-10-24 16:42:29 -0700
commite9f0913d2b1b66764931b6c1235877a44e72890b (patch)
tree404241616fcd340dd5b8b16e699ac8c1e1be2bf1 /modern/src/map/PositionsMap.js
parent7806cb2d725ab1aa4f66db86bc376a027dda6df5 (diff)
downloadetbsa-traccar-web-e9f0913d2b1b66764931b6c1235877a44e72890b.tar.gz
etbsa-traccar-web-e9f0913d2b1b66764931b6c1235877a44e72890b.tar.bz2
etbsa-traccar-web-e9f0913d2b1b66764931b6c1235877a44e72890b.zip
Refactor map implementation
Diffstat (limited to 'modern/src/map/PositionsMap.js')
-rw-r--r--modern/src/map/PositionsMap.js112
1 files changed, 112 insertions, 0 deletions
diff --git a/modern/src/map/PositionsMap.js b/modern/src/map/PositionsMap.js
new file mode 100644
index 0000000..6a7a68b
--- /dev/null
+++ b/modern/src/map/PositionsMap.js
@@ -0,0 +1,112 @@
+import React, { useEffect } from 'react';
+import ReactDOM from 'react-dom';
+import mapboxgl from 'mapbox-gl';
+import { Provider, useSelector } from 'react-redux';
+
+import { map } from './Map';
+import store from '../store';
+import { useHistory } from 'react-router-dom';
+import StatusView from './StatusView';
+
+const PositionsMap = () => {
+ const id = 'positions';
+
+ const history = useHistory();
+
+ const createFeature = (state, position) => {
+ const device = state.devices.items[position.deviceId] || null;
+ return {
+ deviceId: position.deviceId,
+ name: device ? device.name : '',
+ category: device && device.category || 'default',
+ }
+ };
+
+ const positions = useSelector(state => ({
+ type: 'FeatureCollection',
+ features: Object.values(state.positions.items).map(position => ({
+ type: 'Feature',
+ geometry: {
+ type: 'Point',
+ coordinates: [position.longitude, position.latitude]
+ },
+ properties: createFeature(state, position),
+ })),
+ }));
+
+ const onMouseEnter = () => map.getCanvas().style.cursor = 'pointer';
+ const onMouseLeave = () => map.getCanvas().style.cursor = '';
+
+ const onClick = event => {
+ const feature = event.features[0];
+ let coordinates = feature.geometry.coordinates.slice();
+ while (Math.abs(event.lngLat.lng - coordinates[0]) > 180) {
+ coordinates[0] += event.lngLat.lng > coordinates[0] ? 360 : -360;
+ }
+
+ const placeholder = document.createElement('div');
+ ReactDOM.render(
+ <Provider store={store}>
+ <StatusView deviceId={feature.properties.deviceId} onShowDetails={positionId => history.push(`/position/${positionId}`)} />
+ </Provider>,
+ placeholder
+ );
+
+ new mapboxgl.Popup({
+ offset: 25,
+ anchor: 'top'
+ })
+ .setDOMContent(placeholder)
+ .setLngLat(coordinates)
+ .addTo(map);
+ };
+
+ useEffect(() => {
+ map.addSource(id, {
+ 'type': 'geojson',
+ 'data': positions,
+ });
+ map.addLayer({
+ 'id': id,
+ 'type': 'symbol',
+ 'source': id,
+ 'layout': {
+ 'icon-image': '{category}',
+ 'icon-allow-overlap': true,
+ 'text-field': '{name}',
+ 'text-allow-overlap': true,
+ 'text-anchor': 'bottom',
+ 'text-offset': [0, -2],
+ 'text-font': ['Roboto Regular'],
+ 'text-size': 12,
+ },
+ 'paint': {
+ 'text-halo-color': 'white',
+ 'text-halo-width': 1,
+ },
+ });
+
+ map.on('mouseenter', id, onMouseEnter);
+ map.on('mouseleave', id, onMouseLeave);
+ map.on('click', id, onClick);
+
+ return () => {
+ Array.from(map.getContainer().getElementsByClassName('mapboxgl-popup')).forEach(el => el.remove());
+
+ map.off('mouseenter', id, onMouseEnter);
+ map.off('mouseleave', id, onMouseLeave);
+ map.off('click', id, onClick);
+
+ map.removeLayer(id);
+ map.removeSource(id);
+ };
+ }, []);
+
+ useEffect(() => {
+ map.getSource(id).setData(positions);
+ }, [positions]);
+
+ return null;
+}
+
+export default PositionsMap;