Files
meshtastic-web/apps/web/src/components/PageComponents/Map/Layers/HeatmapLayer.tsx
T
AustinandGitHub d2cb51d489 Execute oxfmt (#1166)
Ran
pnpm oxfmt .

This should get the web repo aligned so that we can better enforce oxfmt going forward.
2026-06-16 20:30:08 -04:00

107 lines
2.6 KiB
TypeScript

import { hasPos, toLngLat } from "@core/utils/geo";
import type { Protobuf } from "@meshtastic/sdk";
import type { Feature, FeatureCollection } from "geojson";
import type { HeatmapLayerSpecification } from "maplibre-gl";
import { useMemo } from "react";
import { Layer, Source } from "react-map-gl/maplibre";
export type HeatmapMode = "density" | "snr";
export interface HeatmapLayerProps {
id: string;
filteredNodes: Protobuf.Mesh.NodeInfo[];
mode: HeatmapMode;
isVisible: boolean;
}
export const HeatmapLayer = ({
id,
filteredNodes,
mode,
isVisible,
}: HeatmapLayerProps) => {
if (!isVisible) return null;
const data: FeatureCollection = useMemo(() => {
const features: Feature[] = filteredNodes
.filter((node) => hasPos(node.position))
.filter((node) => mode !== "snr" || node.snr !== undefined)
.map((node) => ({
type: "Feature",
geometry: {
type: "Point",
coordinates: toLngLat(node.position),
},
properties: {
snr: node.snr,
name: node.user?.longName,
shortName: node.user?.shortName,
num: node.num,
},
}));
return {
type: "FeatureCollection",
features,
};
}, [filteredNodes, mode]);
const paintProps: HeatmapLayerSpecification["paint"] = useMemo(
() => ({
"heatmap-weight":
mode === "density"
? 1
: ["interpolate", ["linear"], ["get", "snr"], -20, 0, 10, 1],
"heatmap-intensity": ["interpolate", ["linear"], ["zoom"], 0, 1, 15, 3],
// Color ramp for heatmap. Domain is 0 (low) to 1 (high).
// Begin color ramp at 0-stop with a 0-transparancy color
// to create a blur-like effect.
"heatmap-color": [
"interpolate",
["linear"],
["heatmap-density"],
0,
"rgba(33,102,172,0)",
0.2,
"rgb(103,169,207)",
0.4,
"rgb(209,229,240)",
0.6,
"rgb(253,219,199)",
0.8,
"rgb(239,138,98)",
1,
"rgb(178,24,43)",
],
"heatmap-radius": [
"interpolate",
["linear"],
["zoom"],
0,
2,
9,
20,
15,
30,
],
// Opacity 0.7 to be visible but not blocking
"heatmap-opacity": 0.7,
}),
[mode],
);
return (
<Source type="geojson" data={data}>
<Layer id={id} type="heatmap" paint={paintProps} />
<Layer
id={`${id}-interaction`}
type="circle"
paint={{
"circle-radius": 15,
"circle-opacity": 0,
"circle-stroke-opacity": 0,
}}
/>
</Source>
);
};