228 lines
6.1 KiB
JavaScript
228 lines
6.1 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";
|
|
import { useDefibsState } from "~/stores";
|
|
import { getDefibAvailability } from "~/utils/dae/getDefibAvailability";
|
|
|
|
export default function useFeatures({
|
|
clusterFeature,
|
|
alertingList,
|
|
userCoords,
|
|
routeCoords,
|
|
route,
|
|
alertCoords,
|
|
}) {
|
|
const { showDefibsOnAlertMap, corridorDefibs } = useDefibsState([
|
|
"showDefibsOnAlertMap",
|
|
"corridorDefibs",
|
|
]);
|
|
|
|
// 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,
|
|
},
|
|
});
|
|
}
|
|
});
|
|
|
|
// Add defibs (DAE) as separate, non-clustered features
|
|
if (showDefibsOnAlertMap && Array.isArray(corridorDefibs)) {
|
|
corridorDefibs.forEach((defib) => {
|
|
const lon = defib.longitude;
|
|
const lat = defib.latitude;
|
|
if (
|
|
lon === null ||
|
|
lat === null ||
|
|
lon === undefined ||
|
|
lat === undefined
|
|
) {
|
|
return;
|
|
}
|
|
const { status } = getDefibAvailability(
|
|
defib.horaires_std,
|
|
defib.disponible_24h,
|
|
);
|
|
const icon =
|
|
status === "open" ? "green" : status === "closed" ? "red" : "grey";
|
|
const id = `defib:${defib.id}`;
|
|
|
|
features.push({
|
|
type: "Feature",
|
|
id,
|
|
properties: {
|
|
id,
|
|
icon,
|
|
defib,
|
|
isDefib: true,
|
|
},
|
|
geometry: {
|
|
type: "Point",
|
|
coordinates: [lon, lat],
|
|
},
|
|
});
|
|
});
|
|
}
|
|
|
|
return {
|
|
type: "FeatureCollection",
|
|
features,
|
|
};
|
|
}, [list, showDefibsOnAlertMap, corridorDefibs]);
|
|
|
|
const superCluster = useShallowMemo(() => {
|
|
const cluster = new Supercluster({ radius: 40, maxZoom: 16 });
|
|
// Do not cluster defibs in v1
|
|
const clusterable = featureCollection.features.filter(
|
|
(f) => !f?.properties?.isDefib,
|
|
);
|
|
cluster.load(clusterable);
|
|
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];
|
|
|
|
// Ensure defibs are always present even if they are not part of the clustered set
|
|
if (showDefibsOnAlertMap && Array.isArray(featureCollection.features)) {
|
|
featureCollection.features.forEach((f) => {
|
|
if (f?.properties?.isDefib) {
|
|
features.push(f);
|
|
}
|
|
});
|
|
}
|
|
|
|
// 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,
|
|
featureCollection.features,
|
|
showDefibsOnAlertMap,
|
|
userCoords,
|
|
hasUserCoords,
|
|
routeCoords,
|
|
alertCoords,
|
|
route?.distance,
|
|
]);
|
|
|
|
return { superCluster, shape };
|
|
}
|