diff options
author | Anton Tananaev <anton.tananaev@gmail.com> | 2020-10-24 16:42:29 -0700 |
---|---|---|
committer | Anton Tananaev <anton.tananaev@gmail.com> | 2020-10-24 16:42:29 -0700 |
commit | e9f0913d2b1b66764931b6c1235877a44e72890b (patch) | |
tree | 404241616fcd340dd5b8b16e699ac8c1e1be2bf1 /modern/src/map/PositionsMap.js | |
parent | 7806cb2d725ab1aa4f66db86bc376a027dda6df5 (diff) | |
download | trackermap-web-e9f0913d2b1b66764931b6c1235877a44e72890b.tar.gz trackermap-web-e9f0913d2b1b66764931b6c1235877a44e72890b.tar.bz2 trackermap-web-e9f0913d2b1b66764931b6c1235877a44e72890b.zip |
Refactor map implementation
Diffstat (limited to 'modern/src/map/PositionsMap.js')
-rw-r--r-- | modern/src/map/PositionsMap.js | 112 |
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 00000000..6a7a68bb --- /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; |