import { GeoJSON } from "ol/format"; import { Vector } from "ol/source"; import { useEffect, useRef } from "preact/hooks"; import { FIT_ON_FEATURES_MAX_ZOOM_POINT } from "../constants"; import useMapContext from "./useMapContext"; import type { Feature, Map } from "ol"; import type { GeoJSONFeature } from "ol/format/GeoJSON"; const geojson = new GeoJSON(); export type FitOnFeatures = ( features: (Feature | GeoJSONFeature)[], willOverlayOpen?: boolean, map?: Map, ) => void; const useFitOnFeatures = () => { const { isOverlayOpen, map: contextMap } = useMapContext(); const isOverlayOpenRef = useRef(isOverlayOpen); const fitOnFeatures = useRef(); useEffect(() => { isOverlayOpenRef.current = isOverlayOpen; }, [isOverlayOpen]); useEffect(() => { fitOnFeatures.current = ( features: (Feature | GeoJSONFeature)[], willOverlayOpen = false, map?: Map, ) => { if ((!map && !contextMap) || !features?.length) { return; } let feats = features as Feature[]; // Convert to ol features if GeoJSON const geoJSONFeature = features?.[0] as GeoJSONFeature; if (geoJSONFeature?.geometry && geoJSONFeature?.type === "Feature") { // Single feature case feats = geojson.readFeatures( { features, type: "FeatureCollection", }, { featureProjection: "EPSG:3857" }, ); } const mapToUse = map || contextMap; const extent = new Vector({ features: feats }).getExtent(); mapToUse.getView().fit(extent, { duration: 500, maxZoom: extent[0] === extent[2] || extent[1] === extent[3] ? FIT_ON_FEATURES_MAX_ZOOM_POINT : undefined, padding: [ 50, 50, 50, willOverlayOpen || isOverlayOpenRef.current ? 350 : 50, ], }); return () => { mapToUse?.getView().cancelAnimations(); }; }; }, [contextMap]); return fitOnFeatures; }; export default useFitOnFeatures;