@trackunit/react-map 0.2.203 → 0.2.205
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs.js +60 -25
- package/index.esm.js +60 -25
- package/package.json +11 -11
- package/src/layers/useHeatmap/useHeatmap.d.ts +1 -1
- package/src/layers/useHeatmap/utils/normalizeHeatmapData.d.ts +19 -0
- package/src/layers/useHeatmap/utils/warnHeatmap.d.ts +4 -0
- package/migrations/entry.js.map +0 -1
package/index.cjs.js
CHANGED
|
@@ -5887,6 +5887,57 @@ const useFitToContent = (api, handles, options) => {
|
|
|
5887
5887
|
return react.useMemo(() => ({ hasFitted, isWaiting, fitNow }), [hasFitted, isWaiting, fitNow]);
|
|
5888
5888
|
};
|
|
5889
5889
|
|
|
5890
|
+
/**
|
|
5891
|
+
* Single dev-warning channel for `useHeatmap` (invalid data).
|
|
5892
|
+
*/
|
|
5893
|
+
const warnHeatmap = (message, ...details) => {
|
|
5894
|
+
// eslint-disable-next-line no-console -- Intentional: the one heatmap warning channel; invalid input is dropped and must stay visible
|
|
5895
|
+
console.warn(`[useHeatmap] ${message}`, ...details);
|
|
5896
|
+
};
|
|
5897
|
+
|
|
5898
|
+
/**
|
|
5899
|
+
* Converts consumer items into weighted GeoJSON `Point` features.
|
|
5900
|
+
*
|
|
5901
|
+
* Null positions and zero weights are dropped silently. Invalid positions and
|
|
5902
|
+
* negative or non-finite weights are dropped with one grouped warning per pass.
|
|
5903
|
+
*/
|
|
5904
|
+
const normalizeHeatmapData = ({ data, getPosition, getWeight, }) => {
|
|
5905
|
+
const features = [];
|
|
5906
|
+
const positions = [];
|
|
5907
|
+
let invalidPositionCount = 0;
|
|
5908
|
+
let invalidWeightCount = 0;
|
|
5909
|
+
for (const item of data) {
|
|
5910
|
+
const position = getPosition(item);
|
|
5911
|
+
if (position === null)
|
|
5912
|
+
continue;
|
|
5913
|
+
const parsedPosition = geoJsonUtils.geoJsonPositionSchema.safeParse(position);
|
|
5914
|
+
if (!parsedPosition.success) {
|
|
5915
|
+
invalidPositionCount++;
|
|
5916
|
+
continue;
|
|
5917
|
+
}
|
|
5918
|
+
const weight = getWeight?.(item) ?? 1;
|
|
5919
|
+
if (weight === 0)
|
|
5920
|
+
continue;
|
|
5921
|
+
if (!Number.isFinite(weight) || weight < 0) {
|
|
5922
|
+
invalidWeightCount++;
|
|
5923
|
+
continue;
|
|
5924
|
+
}
|
|
5925
|
+
positions.push(parsedPosition.data);
|
|
5926
|
+
features.push({
|
|
5927
|
+
type: "Feature",
|
|
5928
|
+
geometry: { type: "Point", coordinates: parsedPosition.data },
|
|
5929
|
+
properties: { weight },
|
|
5930
|
+
});
|
|
5931
|
+
}
|
|
5932
|
+
if (invalidPositionCount > 0 || invalidWeightCount > 0) {
|
|
5933
|
+
warnHeatmap(`Filtered ${invalidPositionCount} item(s) with invalid position and ${invalidWeightCount} item(s) with invalid weight. Expected [longitude, latitude] with longitude between -180 and 180, latitude between -90 and 90, and a finite weight >= 0.`);
|
|
5934
|
+
}
|
|
5935
|
+
return {
|
|
5936
|
+
features: { type: "FeatureCollection", features },
|
|
5937
|
+
positions,
|
|
5938
|
+
};
|
|
5939
|
+
};
|
|
5940
|
+
|
|
5890
5941
|
const EMPTY_CONTROLS$2 = {};
|
|
5891
5942
|
/**
|
|
5892
5943
|
* Weighted point heatmap layer handle (provider-agnostic).
|
|
@@ -5898,31 +5949,15 @@ const EMPTY_CONTROLS$2 = {};
|
|
|
5898
5949
|
const useHeatmap = (options) => {
|
|
5899
5950
|
const { id, name, data, getPosition, getWeight, style, controls, loading, fitParticipation = "all" } = options;
|
|
5900
5951
|
const layerReady = useLayerReady(loading);
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
continue;
|
|
5911
|
-
const weight = getWeight?.(item) ?? 1;
|
|
5912
|
-
if (!Number.isFinite(weight) || weight <= 0)
|
|
5913
|
-
continue;
|
|
5914
|
-
positions.push(parsedPosition.data);
|
|
5915
|
-
features.push({
|
|
5916
|
-
type: "Feature",
|
|
5917
|
-
geometry: { type: "Point", coordinates: parsedPosition.data },
|
|
5918
|
-
properties: { weight },
|
|
5919
|
-
});
|
|
5920
|
-
}
|
|
5921
|
-
return {
|
|
5922
|
-
features: { type: "FeatureCollection", features },
|
|
5923
|
-
positions,
|
|
5924
|
-
};
|
|
5925
|
-
}, [data, getPosition, getWeight]);
|
|
5952
|
+
// Keyed on `data` (adjust-state-during-render + isEqual) so inline accessors
|
|
5953
|
+
// don't re-normalize the whole dataset on every render. Accessor changes
|
|
5954
|
+
// apply on the next `data` change.
|
|
5955
|
+
const [prevData, setPrevData] = react.useState(data);
|
|
5956
|
+
const [normalized, setNormalized] = react.useState(() => normalizeHeatmapData({ data, getPosition, getWeight }));
|
|
5957
|
+
if (data !== prevData && !esToolkit.isEqual(data, prevData)) {
|
|
5958
|
+
setPrevData(data);
|
|
5959
|
+
setNormalized(normalizeHeatmapData({ data, getPosition, getWeight }));
|
|
5960
|
+
}
|
|
5926
5961
|
const resolvedStyle = react.useMemo(() => ({
|
|
5927
5962
|
radiusPixels: style?.radiusPixels ?? 30,
|
|
5928
5963
|
intensity: style?.intensity ?? 1,
|
package/index.esm.js
CHANGED
|
@@ -5886,6 +5886,57 @@ const useFitToContent = (api, handles, options) => {
|
|
|
5886
5886
|
return useMemo(() => ({ hasFitted, isWaiting, fitNow }), [hasFitted, isWaiting, fitNow]);
|
|
5887
5887
|
};
|
|
5888
5888
|
|
|
5889
|
+
/**
|
|
5890
|
+
* Single dev-warning channel for `useHeatmap` (invalid data).
|
|
5891
|
+
*/
|
|
5892
|
+
const warnHeatmap = (message, ...details) => {
|
|
5893
|
+
// eslint-disable-next-line no-console -- Intentional: the one heatmap warning channel; invalid input is dropped and must stay visible
|
|
5894
|
+
console.warn(`[useHeatmap] ${message}`, ...details);
|
|
5895
|
+
};
|
|
5896
|
+
|
|
5897
|
+
/**
|
|
5898
|
+
* Converts consumer items into weighted GeoJSON `Point` features.
|
|
5899
|
+
*
|
|
5900
|
+
* Null positions and zero weights are dropped silently. Invalid positions and
|
|
5901
|
+
* negative or non-finite weights are dropped with one grouped warning per pass.
|
|
5902
|
+
*/
|
|
5903
|
+
const normalizeHeatmapData = ({ data, getPosition, getWeight, }) => {
|
|
5904
|
+
const features = [];
|
|
5905
|
+
const positions = [];
|
|
5906
|
+
let invalidPositionCount = 0;
|
|
5907
|
+
let invalidWeightCount = 0;
|
|
5908
|
+
for (const item of data) {
|
|
5909
|
+
const position = getPosition(item);
|
|
5910
|
+
if (position === null)
|
|
5911
|
+
continue;
|
|
5912
|
+
const parsedPosition = geoJsonPositionSchema.safeParse(position);
|
|
5913
|
+
if (!parsedPosition.success) {
|
|
5914
|
+
invalidPositionCount++;
|
|
5915
|
+
continue;
|
|
5916
|
+
}
|
|
5917
|
+
const weight = getWeight?.(item) ?? 1;
|
|
5918
|
+
if (weight === 0)
|
|
5919
|
+
continue;
|
|
5920
|
+
if (!Number.isFinite(weight) || weight < 0) {
|
|
5921
|
+
invalidWeightCount++;
|
|
5922
|
+
continue;
|
|
5923
|
+
}
|
|
5924
|
+
positions.push(parsedPosition.data);
|
|
5925
|
+
features.push({
|
|
5926
|
+
type: "Feature",
|
|
5927
|
+
geometry: { type: "Point", coordinates: parsedPosition.data },
|
|
5928
|
+
properties: { weight },
|
|
5929
|
+
});
|
|
5930
|
+
}
|
|
5931
|
+
if (invalidPositionCount > 0 || invalidWeightCount > 0) {
|
|
5932
|
+
warnHeatmap(`Filtered ${invalidPositionCount} item(s) with invalid position and ${invalidWeightCount} item(s) with invalid weight. Expected [longitude, latitude] with longitude between -180 and 180, latitude between -90 and 90, and a finite weight >= 0.`);
|
|
5933
|
+
}
|
|
5934
|
+
return {
|
|
5935
|
+
features: { type: "FeatureCollection", features },
|
|
5936
|
+
positions,
|
|
5937
|
+
};
|
|
5938
|
+
};
|
|
5939
|
+
|
|
5889
5940
|
const EMPTY_CONTROLS$2 = {};
|
|
5890
5941
|
/**
|
|
5891
5942
|
* Weighted point heatmap layer handle (provider-agnostic).
|
|
@@ -5897,31 +5948,15 @@ const EMPTY_CONTROLS$2 = {};
|
|
|
5897
5948
|
const useHeatmap = (options) => {
|
|
5898
5949
|
const { id, name, data, getPosition, getWeight, style, controls, loading, fitParticipation = "all" } = options;
|
|
5899
5950
|
const layerReady = useLayerReady(loading);
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
continue;
|
|
5910
|
-
const weight = getWeight?.(item) ?? 1;
|
|
5911
|
-
if (!Number.isFinite(weight) || weight <= 0)
|
|
5912
|
-
continue;
|
|
5913
|
-
positions.push(parsedPosition.data);
|
|
5914
|
-
features.push({
|
|
5915
|
-
type: "Feature",
|
|
5916
|
-
geometry: { type: "Point", coordinates: parsedPosition.data },
|
|
5917
|
-
properties: { weight },
|
|
5918
|
-
});
|
|
5919
|
-
}
|
|
5920
|
-
return {
|
|
5921
|
-
features: { type: "FeatureCollection", features },
|
|
5922
|
-
positions,
|
|
5923
|
-
};
|
|
5924
|
-
}, [data, getPosition, getWeight]);
|
|
5951
|
+
// Keyed on `data` (adjust-state-during-render + isEqual) so inline accessors
|
|
5952
|
+
// don't re-normalize the whole dataset on every render. Accessor changes
|
|
5953
|
+
// apply on the next `data` change.
|
|
5954
|
+
const [prevData, setPrevData] = useState(data);
|
|
5955
|
+
const [normalized, setNormalized] = useState(() => normalizeHeatmapData({ data, getPosition, getWeight }));
|
|
5956
|
+
if (data !== prevData && !isEqual(data, prevData)) {
|
|
5957
|
+
setPrevData(data);
|
|
5958
|
+
setNormalized(normalizeHeatmapData({ data, getPosition, getWeight }));
|
|
5959
|
+
}
|
|
5925
5960
|
const resolvedStyle = useMemo(() => ({
|
|
5926
5961
|
radiusPixels: style?.radiusPixels ?? 30,
|
|
5927
5962
|
intensity: style?.intensity ?? 1,
|
package/package.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-map",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.205",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=24.x"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@trackunit/react-components": "3.8.
|
|
11
|
-
"@trackunit/css-class-variance-utilities": "2.3.
|
|
12
|
-
"@trackunit/react-form-components": "2.10.
|
|
13
|
-
"@trackunit/react-core-hooks": "1.25.
|
|
14
|
-
"@trackunit/geo-json-utils": "1.18.
|
|
15
|
-
"@trackunit/i18n-library-translation": "2.8.
|
|
10
|
+
"@trackunit/react-components": "3.8.1",
|
|
11
|
+
"@trackunit/css-class-variance-utilities": "2.3.4",
|
|
12
|
+
"@trackunit/react-form-components": "2.10.7",
|
|
13
|
+
"@trackunit/react-core-hooks": "1.25.4",
|
|
14
|
+
"@trackunit/geo-json-utils": "1.18.4",
|
|
15
|
+
"@trackunit/i18n-library-translation": "2.8.4",
|
|
16
16
|
"react-minimal-pie-chart": "^8.4.0",
|
|
17
|
-
"@trackunit/react-map-adapter-shared": "0.0.
|
|
18
|
-
"@trackunit/react-map-color-utils": "0.0.
|
|
19
|
-
"@trackunit/shared-utils": "1.19.
|
|
20
|
-
"@trackunit/ui-design-tokens": "1.18.
|
|
17
|
+
"@trackunit/react-map-adapter-shared": "0.0.155",
|
|
18
|
+
"@trackunit/react-map-color-utils": "0.0.130",
|
|
19
|
+
"@trackunit/shared-utils": "1.19.4",
|
|
20
|
+
"@trackunit/ui-design-tokens": "1.18.4",
|
|
21
21
|
"@floating-ui/react": "^0.26.25",
|
|
22
22
|
"es-toolkit": "^1.52.0",
|
|
23
23
|
"tailwind-merge": "^2.0.0",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { GeoJsonPosition } from "@trackunit/geo-json-utils";
|
|
2
2
|
import type { CategoryKey, ControlConfig } from "../../controls/types";
|
|
3
3
|
import type { FitParticipation, HeatmapLayerHandle } from "../types";
|
|
4
4
|
export type HeatmapStyle = Readonly<{
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type GeoJsonPosition } from "@trackunit/geo-json-utils";
|
|
2
|
+
import type { GeoJsonFeatureCollection } from "../../types";
|
|
3
|
+
export type NormalizedHeatmapData = Readonly<{
|
|
4
|
+
features: GeoJsonFeatureCollection;
|
|
5
|
+
positions: ReadonlyArray<GeoJsonPosition>;
|
|
6
|
+
}>;
|
|
7
|
+
type NormalizeHeatmapDataOptions<TItem> = Readonly<{
|
|
8
|
+
data: ReadonlyArray<TItem>;
|
|
9
|
+
getPosition: (item: TItem) => GeoJsonPosition | null;
|
|
10
|
+
getWeight: ((item: TItem) => number) | undefined;
|
|
11
|
+
}>;
|
|
12
|
+
/**
|
|
13
|
+
* Converts consumer items into weighted GeoJSON `Point` features.
|
|
14
|
+
*
|
|
15
|
+
* Null positions and zero weights are dropped silently. Invalid positions and
|
|
16
|
+
* negative or non-finite weights are dropped with one grouped warning per pass.
|
|
17
|
+
*/
|
|
18
|
+
export declare const normalizeHeatmapData: <TItem>({ data, getPosition, getWeight, }: NormalizeHeatmapDataOptions<TItem>) => NormalizedHeatmapData;
|
|
19
|
+
export {};
|
package/migrations/entry.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../../../../../libs/react/map/migrations/entry.ts"],"names":[],"mappings":"","sourcesContent":["export {};\n"]}
|