demarches-normaliennes/app/javascript/components/MapReader/useMapbox.js
2021-05-24 11:57:00 +02:00

104 lines
2.7 KiB
JavaScript

import { useCallback, useRef, useEffect, useMemo } from 'react';
import mapboxgl, { Popup } from 'mapbox-gl';
import {
filterFeatureCollection,
findFeature,
getBounds,
getCenter
} from '../shared/mapbox/utils';
const SOURCE_CADASTRE = 'cadastre';
export function useMapbox(featureCollection) {
const mapRef = useRef();
const selectedCadastresRef = useRef(() => new Set());
const isSupported = useMemo(() => mapboxgl.supported());
const fitBounds = useCallback((bbox) => {
mapRef.current.fitBounds(bbox, { padding: 100 });
}, []);
const onLoad = useCallback(
(map) => {
if (!mapRef.current) {
mapRef.current = map;
mapRef.current.fitBounds(featureCollection.bbox, { padding: 100 });
onStyleChange();
}
},
[featureCollection]
);
const onStyleChange = useCallback(() => {
if (mapRef.current) {
selectedCadastresRef.current = new Set(
filterFeatureCollection(
featureCollection,
SOURCE_CADASTRE
).features.map(({ properties }) => properties.cid)
);
if (selectedCadastresRef.current.size > 0) {
mapRef.current.setFilter('parcelle-highlighted', [
'in',
'id',
...selectedCadastresRef.current
]);
}
}
}, [featureCollection]);
const popup = useMemo(
() =>
new Popup({
closeButton: false,
closeOnClick: false
})
);
const onMouseEnter = useCallback(
(event) => {
const feature = event.features[0];
if (feature.properties && feature.properties.description) {
const coordinates = getCenter(feature.geometry, event.lngLat);
const description = feature.properties.description;
mapRef.current.getCanvas().style.cursor = 'pointer';
popup.setLngLat(coordinates).setHTML(description).addTo(mapRef.current);
} else {
popup.remove();
}
},
[popup]
);
const onMouseLeave = useCallback(() => {
mapRef.current.getCanvas().style.cursor = '';
popup.remove();
}, [popup]);
useExternalEvents(featureCollection, { fitBounds });
return { isSupported, onLoad, onStyleChange, onMouseEnter, onMouseLeave };
}
function useExternalEvents(featureCollection, { fitBounds }) {
const onFeatureFocus = useCallback(
({ detail }) => {
const { id } = detail;
const feature = findFeature(featureCollection, id);
if (feature) {
fitBounds(getBounds(feature.geometry));
}
},
[featureCollection, fitBounds]
);
useEvent('map:feature:focus', onFeatureFocus);
}
export function useEvent(eventName, callback) {
return useEffect(() => {
addEventListener(eventName, callback);
return () => removeEventListener(eventName, callback);
}, [eventName, callback]);
}