This repository has been archived on 2026-03-09. You can view files and clone it, but cannot push or open issues or pull requests.
as-app/src/scenes/AlertCurMap/useFeatures.js

168 lines
4.4 KiB
JavaScript

import { useState } from "react";
import { getDistance } from "geolib";
import Supercluster from "supercluster";
import useShallowMemo from "~/hooks/useShallowMemo";
import useShallowEffect from "~/hooks/useShallowEffect";
import { deepEqual } from "fast-equals";
export default function useFeatures({
clusterFeature,
alertingList,
userCoords,
routeCoords,
route,
alertCoords,
}) {
// Check if we have valid coordinates
const hasUserCoords =
userCoords && userCoords.longitude !== null && userCoords.latitude !== null;
const list = useShallowMemo(() => {
const computedList = alertingList.map((row) => {
const { oneAlert } = row;
const { coordinates: alertCoords } = oneAlert.location;
const [longitude, latitude] = alertCoords;
let distance;
if (longitude && latitude && hasUserCoords) {
distance = getDistance(
{ longitude, latitude },
{
longitude: userCoords.longitude,
latitude: userCoords.latitude,
},
);
}
return { ...row, alert: { ...oneAlert, distance } };
});
return computedList;
}, [alertingList, userCoords, hasUserCoords]);
const featureCollection = useShallowMemo(() => {
const features = list.map((row) => {
const { alert } = row;
const { level, state } = alert;
const [longitude, latitude] = alert.location.coordinates;
const coordinates = [longitude, latitude];
const id = `alert:${alert.id}`;
const icon = state === "open" ? level : `${level}Disabled`;
return {
type: "Feature",
id,
properties: {
id,
level,
icon,
alert,
coordinates,
},
geometry: {
type: "Point",
coordinates,
},
};
});
// Add initial location marker if locations are different
list.forEach((row) => {
const { alert } = row;
if (
alert.initialLocation &&
alert.location &&
!deepEqual(alert.initialLocation, alert.location)
) {
const [longitude, latitude] = alert.initialLocation.coordinates;
const coordinates = [longitude, latitude];
const id = `alert:${alert.id}:initial`;
features.push({
type: "Feature",
id,
properties: {
id,
icon: "origin",
level: alert.level,
alert,
coordinates,
isInitialLocation: true,
},
geometry: {
type: "Point",
coordinates,
},
});
}
});
return {
type: "FeatureCollection",
features,
};
}, [list]);
const superCluster = useShallowMemo(() => {
const cluster = new Supercluster({ radius: 40, maxZoom: 16 });
cluster.load(featureCollection.features);
return cluster;
}, [featureCollection.features]);
// console.log({ superCluster: JSON.stringify(superCluster) });
const [shape, setShape] = useState({
type: "FeatureCollection",
features: clusterFeature,
});
useShallowEffect(() => {
// Early return if no user coordinates
if (!hasUserCoords) {
setShape({ type: "FeatureCollection", features: clusterFeature });
return;
}
const userCoordinates = [userCoords.longitude, userCoords.latitude];
const features = [...clusterFeature];
// Only add route line if we have valid route data
const isRouteEnding = route?.distance !== 0 || routeCoords?.length === 0;
const hasValidAlertCoords =
Array.isArray(alertCoords) && alertCoords.length === 2;
if (isRouteEnding) {
const lineCoordinates = [userCoordinates];
// Add route coordinates if available
if (Array.isArray(routeCoords) && routeCoords.length > 0) {
lineCoordinates.push(...routeCoords);
}
// Add alert coordinates if valid
if (hasValidAlertCoords) {
lineCoordinates.push(alertCoords);
}
const lineString = {
type: "Feature",
properties: {},
geometry: {
type: "LineString",
coordinates: lineCoordinates,
},
};
features.push(lineString);
}
// console.log("features", JSON.stringify(features));
setShape({ type: "FeatureCollection", features });
}, [
setShape,
clusterFeature,
userCoords,
hasUserCoords,
routeCoords,
alertCoords,
route?.distance,
]);
return { superCluster, shape };
}