@geolonia/geonicdb-sdk 0.11.0 → 0.12.0
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/README.md +51 -0
- package/geonicdb.d.ts +19 -0
- package/ngsi-v2/index.cjs +92 -0
- package/ngsi-v2/index.cjs.map +7 -0
- package/ngsi-v2/index.d.ts +58 -0
- package/ngsi-v2/index.mjs +73 -0
- package/ngsi-v2/index.mjs.map +7 -0
- package/package.json +36 -1
- package/react/index.cjs +266 -0
- package/react/index.cjs.map +7 -0
- package/react/index.d.ts +551 -0
- package/react/index.mjs +233 -0
- package/react/index.mjs.map +7 -0
package/react/index.cjs
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/sdk/react/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
GeonicDbMap: () => GeonicDbMap,
|
|
34
|
+
useLdEntities: () => useLdEntities,
|
|
35
|
+
useTemporalData: () => useTemporalData
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(index_exports);
|
|
38
|
+
|
|
39
|
+
// src/sdk/react/hooks.ts
|
|
40
|
+
var import_react = require("react");
|
|
41
|
+
function useLdEntities(client, options) {
|
|
42
|
+
const [entities, setEntities] = (0, import_react.useState)([]);
|
|
43
|
+
const [loading, setLoading] = (0, import_react.useState)(true);
|
|
44
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
45
|
+
const genRef = (0, import_react.useRef)(0);
|
|
46
|
+
const params = {};
|
|
47
|
+
if (options?.type) params.type = options.type;
|
|
48
|
+
if (options?.limit) params.limit = options.limit;
|
|
49
|
+
if (options?.attrs) params.attrs = options.attrs.join(",");
|
|
50
|
+
if (options?.q) params.q = options.q;
|
|
51
|
+
if (options?.scopeQ) params.scopeQ = options.scopeQ;
|
|
52
|
+
const paramsKey = JSON.stringify(params);
|
|
53
|
+
const fetchEntities = (0, import_react.useCallback)(async () => {
|
|
54
|
+
const gen = ++genRef.current;
|
|
55
|
+
setLoading(true);
|
|
56
|
+
setError(null);
|
|
57
|
+
try {
|
|
58
|
+
const data = await client.getEntities(params);
|
|
59
|
+
if (genRef.current === gen) setEntities(data);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
if (genRef.current === gen) setError(err instanceof Error ? err : new Error("Unknown error"));
|
|
62
|
+
} finally {
|
|
63
|
+
if (genRef.current === gen) setLoading(false);
|
|
64
|
+
}
|
|
65
|
+
}, [client, paramsKey]);
|
|
66
|
+
(0, import_react.useEffect)(() => {
|
|
67
|
+
void fetchEntities();
|
|
68
|
+
return () => {
|
|
69
|
+
genRef.current++;
|
|
70
|
+
};
|
|
71
|
+
}, [fetchEntities]);
|
|
72
|
+
return { entities, loading, error, refetch: fetchEntities };
|
|
73
|
+
}
|
|
74
|
+
function useTemporalData(client, options) {
|
|
75
|
+
const [data, setData] = (0, import_react.useState)([]);
|
|
76
|
+
const [loading, setLoading] = (0, import_react.useState)(false);
|
|
77
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
78
|
+
const genRef = (0, import_react.useRef)(0);
|
|
79
|
+
const enabled = options.enabled !== false;
|
|
80
|
+
const optionsKey = JSON.stringify(options);
|
|
81
|
+
const fetchData = (0, import_react.useCallback)(async () => {
|
|
82
|
+
const gen = ++genRef.current;
|
|
83
|
+
if (!enabled || !client) {
|
|
84
|
+
setData([]);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
setLoading(true);
|
|
88
|
+
setError(null);
|
|
89
|
+
try {
|
|
90
|
+
if (options.id) {
|
|
91
|
+
const entity = await client.getTemporalEntity(options.id);
|
|
92
|
+
if (genRef.current === gen) setData([entity]);
|
|
93
|
+
} else {
|
|
94
|
+
const entities = await client.getTemporalEntities(options);
|
|
95
|
+
if (genRef.current === gen) setData(entities);
|
|
96
|
+
}
|
|
97
|
+
} catch (err) {
|
|
98
|
+
if (genRef.current === gen) setError(err instanceof Error ? err : new Error("Unknown error"));
|
|
99
|
+
} finally {
|
|
100
|
+
if (genRef.current === gen) setLoading(false);
|
|
101
|
+
}
|
|
102
|
+
}, [client, optionsKey]);
|
|
103
|
+
(0, import_react.useEffect)(() => {
|
|
104
|
+
void fetchData();
|
|
105
|
+
return () => {
|
|
106
|
+
genRef.current++;
|
|
107
|
+
};
|
|
108
|
+
}, [fetchData]);
|
|
109
|
+
return { data, loading, error, refetch: fetchData };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/sdk/react/GeonicDbMap.tsx
|
|
113
|
+
var import_react2 = require("react");
|
|
114
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
115
|
+
var DEFAULT_STYLE = "geolonia/basic";
|
|
116
|
+
function escapeHtml(s) {
|
|
117
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
118
|
+
}
|
|
119
|
+
function getEntityLocation(entity) {
|
|
120
|
+
const location = entity.location;
|
|
121
|
+
if (!location || typeof location === "string") return null;
|
|
122
|
+
const ldLoc = location;
|
|
123
|
+
if (ldLoc.type === "GeoProperty" && ldLoc.value?.type === "Point" && Array.isArray(ldLoc.value.coordinates)) {
|
|
124
|
+
return ldLoc.value.coordinates;
|
|
125
|
+
}
|
|
126
|
+
const v2Loc = location;
|
|
127
|
+
if (v2Loc.value?.type === "Point" && Array.isArray(v2Loc.value.coordinates)) {
|
|
128
|
+
return v2Loc.value.coordinates;
|
|
129
|
+
}
|
|
130
|
+
const kv = location;
|
|
131
|
+
if (kv.type === "Point" && Array.isArray(kv.coordinates)) {
|
|
132
|
+
return kv.coordinates;
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
function getMarkerColor(entityType) {
|
|
137
|
+
const colors = {
|
|
138
|
+
Room: "#3b82f6",
|
|
139
|
+
ParkingSpot: "#22c55e",
|
|
140
|
+
AirQualitySensor: "#f59e0b",
|
|
141
|
+
TrafficFlow: "#ef4444",
|
|
142
|
+
EnvironmentSensor: "#8b5cf6",
|
|
143
|
+
EnvSensor: "#8b5cf6",
|
|
144
|
+
TemperatureSensor: "#f97316",
|
|
145
|
+
GeoFeature: "#0ea5e9"
|
|
146
|
+
};
|
|
147
|
+
return colors[entityType] ?? "#6b7280";
|
|
148
|
+
}
|
|
149
|
+
function GeonicDbMap({
|
|
150
|
+
center = [139.7671, 35.6812],
|
|
151
|
+
zoom = 13,
|
|
152
|
+
styleUrl,
|
|
153
|
+
entities = [],
|
|
154
|
+
onEntityClick,
|
|
155
|
+
onMapClick,
|
|
156
|
+
className = "",
|
|
157
|
+
style: customStyle,
|
|
158
|
+
showPopups = true,
|
|
159
|
+
fitToMarkers = false
|
|
160
|
+
}) {
|
|
161
|
+
const mapContainerRef = (0, import_react2.useRef)(null);
|
|
162
|
+
const mapRef = (0, import_react2.useRef)(null);
|
|
163
|
+
const markersRef = (0, import_react2.useRef)([]);
|
|
164
|
+
const mapLoadedRef = (0, import_react2.useRef)(false);
|
|
165
|
+
const pendingUpdateRef = (0, import_react2.useRef)(false);
|
|
166
|
+
(0, import_react2.useEffect)(() => {
|
|
167
|
+
if (!mapContainerRef.current) return;
|
|
168
|
+
mapLoadedRef.current = false;
|
|
169
|
+
void import("@geolonia/embed").then(({ Map: GeoMap }) => {
|
|
170
|
+
if (!mapContainerRef.current) return;
|
|
171
|
+
const map = new GeoMap({
|
|
172
|
+
container: mapContainerRef.current,
|
|
173
|
+
style: styleUrl ?? DEFAULT_STYLE,
|
|
174
|
+
center,
|
|
175
|
+
zoom
|
|
176
|
+
});
|
|
177
|
+
mapRef.current = map;
|
|
178
|
+
const onReady = () => {
|
|
179
|
+
mapLoadedRef.current = true;
|
|
180
|
+
if (pendingUpdateRef.current) {
|
|
181
|
+
pendingUpdateRef.current = false;
|
|
182
|
+
window.dispatchEvent(new CustomEvent("geonicdb-map-ready"));
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
map.on("load", onReady);
|
|
186
|
+
map.once("idle", onReady);
|
|
187
|
+
if (onMapClick) {
|
|
188
|
+
map.on("click", (e) => {
|
|
189
|
+
onMapClick([e.lngLat.lng, e.lngLat.lat]);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
return () => {
|
|
194
|
+
if (mapRef.current) {
|
|
195
|
+
mapRef.current.remove();
|
|
196
|
+
mapRef.current = null;
|
|
197
|
+
mapLoadedRef.current = false;
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
}, [styleUrl]);
|
|
201
|
+
const doUpdate = (0, import_react2.useCallback)(() => {
|
|
202
|
+
const map = mapRef.current;
|
|
203
|
+
if (!map) return;
|
|
204
|
+
markersRef.current.forEach((marker) => marker.remove());
|
|
205
|
+
markersRef.current = [];
|
|
206
|
+
void import("@geolonia/embed").then(({ Marker, geolonia }) => {
|
|
207
|
+
entities.forEach((entity) => {
|
|
208
|
+
const coords = getEntityLocation(entity);
|
|
209
|
+
if (!coords) return;
|
|
210
|
+
const color = getMarkerColor(entity.type);
|
|
211
|
+
const el = document.createElement("div");
|
|
212
|
+
el.style.cssText = `width:24px;height:24px;background-color:${color};border:2px solid white;border-radius:50%;cursor:pointer;box-shadow:0 2px 4px rgba(0,0,0,0.3);`;
|
|
213
|
+
const marker = new Marker({ element: el }).setLngLat(coords).addTo(map);
|
|
214
|
+
if (showPopups && geolonia?.Popup) {
|
|
215
|
+
const popup = new geolonia.Popup({ closeButton: false, closeOnClick: false, offset: 15 }).setHTML(`<div style="font-family:sans-serif;font-size:12px;"><strong>${escapeHtml(entity.id)}</strong><br/><span style="color:#666">${escapeHtml(entity.type)}</span></div>`);
|
|
216
|
+
el.addEventListener("mouseenter", () => {
|
|
217
|
+
marker.setPopup(popup);
|
|
218
|
+
popup.addTo(map);
|
|
219
|
+
});
|
|
220
|
+
el.addEventListener("mouseleave", () => {
|
|
221
|
+
popup.remove();
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
if (onEntityClick) {
|
|
225
|
+
el.addEventListener("click", (e) => {
|
|
226
|
+
e.stopPropagation();
|
|
227
|
+
onEntityClick(entity);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
markersRef.current.push(marker);
|
|
231
|
+
});
|
|
232
|
+
if (fitToMarkers && markersRef.current.length > 0) {
|
|
233
|
+
const allCoords = entities.map(getEntityLocation).filter((c) => c !== null);
|
|
234
|
+
if (allCoords.length === 1 && allCoords[0]) {
|
|
235
|
+
map.flyTo({ center: allCoords[0], zoom: 14 });
|
|
236
|
+
} else if (allCoords.length > 1) {
|
|
237
|
+
const bounds = allCoords.reduce(
|
|
238
|
+
(b, coord) => [[Math.min(b[0][0], coord[0]), Math.min(b[0][1], coord[1])], [Math.max(b[1][0], coord[0]), Math.max(b[1][1], coord[1])]],
|
|
239
|
+
[[allCoords[0][0], allCoords[0][1]], [allCoords[0][0], allCoords[0][1]]]
|
|
240
|
+
);
|
|
241
|
+
map.fitBounds(bounds, { padding: 30 });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}, [entities, onEntityClick, showPopups, fitToMarkers]);
|
|
246
|
+
(0, import_react2.useEffect)(() => {
|
|
247
|
+
if (mapLoadedRef.current) {
|
|
248
|
+
doUpdate();
|
|
249
|
+
} else {
|
|
250
|
+
pendingUpdateRef.current = true;
|
|
251
|
+
const handler = () => doUpdate();
|
|
252
|
+
window.addEventListener("geonicdb-map-ready", handler, { once: true });
|
|
253
|
+
return () => window.removeEventListener("geonicdb-map-ready", handler);
|
|
254
|
+
}
|
|
255
|
+
}, [doUpdate]);
|
|
256
|
+
const defaultStyle = { width: "100%", height: "100%", minHeight: "400px" };
|
|
257
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
258
|
+
"div",
|
|
259
|
+
{
|
|
260
|
+
ref: mapContainerRef,
|
|
261
|
+
className,
|
|
262
|
+
style: { ...defaultStyle, ...customStyle }
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/sdk/react/index.ts", "../../../src/sdk/react/hooks.ts", "../../../src/sdk/react/GeonicDbMap.tsx"],
|
|
4
|
+
"sourcesContent": ["// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Geolonia Inc.\n// See LICENSE in src/sdk/ for full license text.\n\n/**\n * @geolonia/geonicdb-sdk/react \u2014 React hooks and map component\n *\n * Requires React 18 or 19 as an optional peer dependency.\n * Import only from this subpath to avoid pulling React into core consumers.\n */\nexport { useLdEntities, useTemporalData } from './hooks.js';\nexport { GeonicDbMap } from './GeonicDbMap.js';\nexport type { GeonicDbMapProps } from './GeonicDbMap.js';\nexport type {\n LdEntity,\n LdAttribute,\n LdProperty,\n LdGeoProperty,\n LdRelationship,\n LdQueryOptions,\n NgsiEntity,\n NgsiAttribute,\n GeoJsonPoint,\n GeoJsonPolygon,\n GeoJsonGeometry,\n} from './types.js';\n", "// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Geolonia Inc.\n// See LICENSE in src/sdk/ for full license text.\n\n/**\n * NGSI-LD React hooks \u2014 built on top of GeonicDB SDK core.\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport type GeonicDB from '../index.js';\nimport type { GetEntitiesParams, GetTemporalEntitiesParams } from '../types.js';\nimport type { LdEntity, LdQueryOptions } from './types.js';\n\n/**\n * Fetch NGSI-LD entities using the GeonicDB SDK client.\n *\n * The official SDK core handles DPoP / auth transparently.\n */\nexport function useLdEntities(\n client: GeonicDB,\n options?: LdQueryOptions,\n): {\n entities: LdEntity[];\n loading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [entities, setEntities] = useState<LdEntity[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n // \u30EA\u30AF\u30A8\u30B9\u30C8\u4E16\u4EE3\u30AB\u30A6\u30F3\u30BF\u3002deps \u5909\u5316\u30FBrefetch\u30FB\u30A2\u30F3\u30DE\u30A6\u30F3\u30C8\u6642\u306B\u5897\u3084\u3057\u3001\n // \u53E4\u3044 in-flight \u30EC\u30B9\u30DD\u30F3\u30B9\u304C\u65B0\u3057\u3044\u7D50\u679C\u3092\u4E0A\u66F8\u304D\u3059\u308B race \u3068\u3001\n // \u30A2\u30F3\u30DE\u30A6\u30F3\u30C8\u5F8C\u306E setState \u3092\u9632\u3050\u3002\n const genRef = useRef(0);\n\n const params: GetEntitiesParams = {};\n if (options?.type) params.type = options.type;\n if (options?.limit) params.limit = options.limit;\n if (options?.attrs) params.attrs = options.attrs.join(',');\n if (options?.q) params.q = options.q;\n if (options?.scopeQ) params.scopeQ = options.scopeQ;\n\n const paramsKey = JSON.stringify(params);\n\n const fetchEntities = useCallback(async () => {\n const gen = ++genRef.current;\n setLoading(true);\n setError(null);\n try {\n const data = await client.getEntities(params);\n if (genRef.current === gen) setEntities(data as LdEntity[]);\n } catch (err) {\n if (genRef.current === gen) setError(err instanceof Error ? err : new Error('Unknown error'));\n } finally {\n if (genRef.current === gen) setLoading(false);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, paramsKey]);\n\n useEffect(() => {\n void fetchEntities();\n // \u30A2\u30F3\u30DE\u30A6\u30F3\u30C8 / deps \u5909\u5316\u3067 in-flight \u3092\u7121\u52B9\u5316\u3059\u308B\n return () => {\n genRef.current++;\n };\n }, [fetchEntities]);\n\n return { entities, loading, error, refetch: fetchEntities };\n}\n\n/**\n * Fetch NGSI-LD temporal (time-series) data using the GeonicDB SDK client.\n */\nexport function useTemporalData(\n client: GeonicDB | undefined,\n options: GetTemporalEntitiesParams & { enabled?: boolean },\n): {\n data: Record<string, unknown>[];\n loading: boolean;\n error: Error | null;\n refetch: () => Promise<void>;\n} {\n const [data, setData] = useState<Record<string, unknown>[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // useLdEntities \u3068\u540C\u3058\u304F\u3001\u53E4\u3044 in-flight \u30EC\u30B9\u30DD\u30F3\u30B9\u3068\u30A2\u30F3\u30DE\u30A6\u30F3\u30C8\u5F8C setState \u3092\u9632\u3050\u4E16\u4EE3\u30AB\u30A6\u30F3\u30BF\u3002\n const genRef = useRef(0);\n\n const enabled = options.enabled !== false;\n\n // Serialize all options as a stable cache key so that any param change triggers re-fetch.\n const optionsKey = JSON.stringify(options);\n\n const fetchData = useCallback(async () => {\n const gen = ++genRef.current;\n if (!enabled || !client) {\n setData([]);\n return;\n }\n\n setLoading(true);\n setError(null);\n try {\n if (options.id) {\n const entity = await client.getTemporalEntity(options.id);\n if (genRef.current === gen) setData([entity]);\n } else {\n const entities = await client.getTemporalEntities(options);\n if (genRef.current === gen) setData(entities);\n }\n } catch (err) {\n if (genRef.current === gen) setError(err instanceof Error ? err : new Error('Unknown error'));\n } finally {\n if (genRef.current === gen) setLoading(false);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client, optionsKey]);\n\n useEffect(() => {\n void fetchData();\n // \u30A2\u30F3\u30DE\u30A6\u30F3\u30C8 / deps \u5909\u5316\u3067 in-flight \u3092\u7121\u52B9\u5316\u3059\u308B\n return () => {\n genRef.current++;\n };\n }, [fetchData]);\n\n return { data, loading, error, refetch: fetchData };\n}\n", "// SPDX-License-Identifier: MIT\n// Copyright (c) 2026 Geolonia Inc.\n// See LICENSE in src/sdk/ for full license text.\n\n/**\n * GeonicDbMap \u2014 Geolonia embed-based map component for GeonicDB entities.\n *\n * Ported from geonicdb-demo-app/packages/shared/src/components/GeonicDbMap.tsx.\n * @geolonia/embed is an optional peer dependency (required only when using this component).\n */\n\nimport { useEffect, useRef, useCallback, type CSSProperties } from 'react';\nimport type { Map as EmbedMap, Marker as EmbedMarker } from '@geolonia/embed';\nimport type { LdEntity, NgsiEntity, GeoJsonPoint } from './types.js';\n\nexport interface GeonicDbMapProps {\n center?: [number, number];\n zoom?: number;\n styleUrl?: string;\n /** Entities to display. Accepts both LdEntity (NGSI-LD) and NgsiEntity (NGSIv2). */\n entities?: (LdEntity | NgsiEntity)[];\n onEntityClick?: (entity: LdEntity | NgsiEntity) => void;\n onMapClick?: (lngLat: [number, number]) => void;\n className?: string;\n style?: CSSProperties;\n showPopups?: boolean;\n fitToMarkers?: boolean;\n}\n\nconst DEFAULT_STYLE = 'geolonia/basic';\n\nfunction escapeHtml(s: string): string {\n return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n}\n\nfunction getEntityLocation(entity: LdEntity | NgsiEntity): [number, number] | null {\n const location = entity.location;\n if (!location || typeof location === 'string') return null;\n\n // NGSI-LD GeoProperty normalized: { type: \"GeoProperty\", value: { type: \"Point\", coordinates: [...] } }\n const ldLoc = location as { type?: string; value?: GeoJsonPoint };\n if (ldLoc.type === 'GeoProperty' && ldLoc.value?.type === 'Point' && Array.isArray(ldLoc.value.coordinates)) {\n return ldLoc.value.coordinates;\n }\n\n // NGSIv2 normalized: { type: \"geo:json\", value: { type: \"Point\", coordinates: [...] } }\n const v2Loc = location as { type?: string; value?: GeoJsonPoint };\n if (v2Loc.value?.type === 'Point' && Array.isArray(v2Loc.value.coordinates)) {\n return v2Loc.value.coordinates;\n }\n\n // keyValues: { type: \"Point\", coordinates: [...] }\n const kv = location as unknown as GeoJsonPoint;\n if (kv.type === 'Point' && Array.isArray(kv.coordinates)) {\n return kv.coordinates;\n }\n\n return null;\n}\n\nfunction getMarkerColor(entityType: string): string {\n const colors: Record<string, string> = {\n Room: '#3b82f6',\n ParkingSpot: '#22c55e',\n AirQualitySensor: '#f59e0b',\n TrafficFlow: '#ef4444',\n EnvironmentSensor: '#8b5cf6',\n EnvSensor: '#8b5cf6',\n TemperatureSensor: '#f97316',\n GeoFeature: '#0ea5e9',\n };\n return colors[entityType] ?? '#6b7280';\n}\n\nexport function GeonicDbMap({\n center = [139.7671, 35.6812],\n zoom = 13,\n styleUrl,\n entities = [],\n onEntityClick,\n onMapClick,\n className = '',\n style: customStyle,\n showPopups = true,\n fitToMarkers = false,\n}: GeonicDbMapProps) {\n const mapContainerRef = useRef<HTMLDivElement>(null);\n const mapRef = useRef<EmbedMap | null>(null);\n const markersRef = useRef<EmbedMarker[]>([]);\n const mapLoadedRef = useRef(false);\n const pendingUpdateRef = useRef(false);\n\n useEffect(() => {\n if (!mapContainerRef.current) return;\n\n mapLoadedRef.current = false;\n\n // Dynamic import to avoid bundling @geolonia/embed for non-map consumers.\n // @geolonia/embed is an optional peer dependency.\n void import('@geolonia/embed').then(({ Map: GeoMap }) => {\n if (!mapContainerRef.current) return;\n\n const map = new GeoMap({\n container: mapContainerRef.current,\n style: styleUrl ?? DEFAULT_STYLE,\n center,\n zoom,\n });\n\n mapRef.current = map;\n\n const onReady = () => {\n mapLoadedRef.current = true;\n if (pendingUpdateRef.current) {\n pendingUpdateRef.current = false;\n window.dispatchEvent(new CustomEvent('geonicdb-map-ready'));\n }\n };\n\n map.on('load', onReady);\n map.once('idle', onReady);\n\n if (onMapClick) {\n (map as unknown as { on: (e: string, cb: (e: { lngLat: { lng: number; lat: number } }) => void) => void }).on('click', (e) => {\n onMapClick([e.lngLat.lng, e.lngLat.lat]);\n });\n }\n });\n\n return () => {\n if (mapRef.current) {\n mapRef.current.remove();\n mapRef.current = null;\n mapLoadedRef.current = false;\n }\n };\n // center/zoom are intentionally excluded: the map is initialized once per styleUrl change.\n // Subsequent center/zoom updates should use imperative map methods (flyTo/jumpTo),\n // not re-mount the map. Markers/entities are updated via the doUpdate effect below.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [styleUrl]);\n\n const doUpdate = useCallback(() => {\n const map = mapRef.current;\n if (!map) return;\n\n markersRef.current.forEach((marker) => marker.remove());\n markersRef.current = [];\n\n void import('@geolonia/embed').then(({ Marker, geolonia }) => {\n entities.forEach((entity) => {\n const coords = getEntityLocation(entity);\n if (!coords) return;\n\n const color = getMarkerColor(entity.type);\n const el = document.createElement('div');\n el.style.cssText = `width:24px;height:24px;background-color:${color};border:2px solid white;border-radius:50%;cursor:pointer;box-shadow:0 2px 4px rgba(0,0,0,0.3);`;\n\n const marker = new Marker({ element: el }).setLngLat(coords).addTo(map);\n\n if (showPopups && geolonia?.Popup) {\n const popup = new geolonia.Popup({ closeButton: false, closeOnClick: false, offset: 15 })\n .setHTML(`<div style=\"font-family:sans-serif;font-size:12px;\"><strong>${escapeHtml(entity.id)}</strong><br/><span style=\"color:#666\">${escapeHtml(entity.type)}</span></div>`);\n el.addEventListener('mouseenter', () => { marker.setPopup(popup); popup.addTo(map); });\n el.addEventListener('mouseleave', () => { popup.remove(); });\n }\n\n if (onEntityClick) {\n el.addEventListener('click', (e) => { e.stopPropagation(); onEntityClick(entity); });\n }\n\n markersRef.current.push(marker);\n });\n\n if (fitToMarkers && markersRef.current.length > 0) {\n const allCoords = entities.map(getEntityLocation).filter((c): c is [number, number] => c !== null);\n if (allCoords.length === 1 && allCoords[0]) {\n map.flyTo({ center: allCoords[0], zoom: 14 });\n } else if (allCoords.length > 1) {\n const bounds = allCoords.reduce(\n (b, coord) => [[Math.min(b[0][0], coord[0]), Math.min(b[0][1], coord[1])], [Math.max(b[1][0], coord[0]), Math.max(b[1][1], coord[1])]] as [[number, number], [number, number]],\n [[allCoords[0]![0], allCoords[0]![1]], [allCoords[0]![0], allCoords[0]![1]]] as [[number, number], [number, number]]\n );\n map.fitBounds(bounds, { padding: 30 });\n }\n }\n });\n }, [entities, onEntityClick, showPopups, fitToMarkers]);\n\n useEffect(() => {\n if (mapLoadedRef.current) {\n doUpdate();\n } else {\n pendingUpdateRef.current = true;\n const handler = () => doUpdate();\n window.addEventListener('geonicdb-map-ready', handler, { once: true });\n return () => window.removeEventListener('geonicdb-map-ready', handler);\n }\n }, [doUpdate]);\n\n const defaultStyle: CSSProperties = { width: '100%', height: '100%', minHeight: '400px' };\n\n return (\n <div\n ref={mapContainerRef}\n className={className}\n style={{ ...defaultStyle, ...customStyle }}\n />\n );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["import_react"]
|
|
7
|
+
}
|