@geolonia/geonicdb-sdk 0.11.1 → 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 +49 -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/README.md
CHANGED
|
@@ -558,6 +558,55 @@ try {
|
|
|
558
558
|
| `RateLimitError` | 429 | Rate limited (`retryAfter` property) |
|
|
559
559
|
| `NetworkError` | — | Fetch failure, DNS error, timeout |
|
|
560
560
|
|
|
561
|
+
## React Hooks (`@geolonia/geonicdb-sdk/react`)
|
|
562
|
+
|
|
563
|
+
Optional React integration, shipped as a separate subpath so the core SDK stays
|
|
564
|
+
framework-agnostic. Requires React 18 or 19 (declared as an **optional peer
|
|
565
|
+
dependency** — core consumers are unaffected).
|
|
566
|
+
|
|
567
|
+
```tsx
|
|
568
|
+
import GeonicDB from '@geolonia/geonicdb-sdk';
|
|
569
|
+
import { useLdEntities, useTemporalData, GeonicDbMap } from '@geolonia/geonicdb-sdk/react';
|
|
570
|
+
|
|
571
|
+
const db = new GeonicDB({ baseUrl, tenant });
|
|
572
|
+
|
|
573
|
+
function Rooms() {
|
|
574
|
+
const { entities, loading, error, refetch } = useLdEntities(db, { type: 'Room', limit: 100 });
|
|
575
|
+
if (loading) return <p>Loading…</p>;
|
|
576
|
+
if (error) return <p>{error.message}</p>;
|
|
577
|
+
return <GeonicDbMap entities={entities} apiKey="YOUR-GEOLONIA-KEY" />;
|
|
578
|
+
}
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
| Export | Type | Description |
|
|
582
|
+
|--------|------|-------------|
|
|
583
|
+
| `useLdEntities(client, options?)` | hook | NGSI-LD エンティティ取得。`{ entities, loading, error, refetch }` を返す。`options`: `type` / `limit` / `attrs` / `q` / `scopeQ` |
|
|
584
|
+
| `useTemporalData(client, options)` | hook | Temporal API 取得。`{ data, loading, error, refetch }` を返す。`options.id` 指定時は単一エンティティ、`options.enabled === false` で取得スキップ |
|
|
585
|
+
| `GeonicDbMap` | component | Geolonia 地図にエンティティをマーカー描画。`@geolonia/embed`(optional peer)を動的 import |
|
|
586
|
+
|
|
587
|
+
両 hook は in-flight リクエストを世代管理しており、`options` 変更やアンマウント時に古いレスポンスが新しい結果を上書きしない。`GeonicDbMap` のポップアップは `entity.id` / `entity.type` を HTML エスケープして描画する。
|
|
588
|
+
|
|
589
|
+
## NGSIv2 Client (`@geolonia/geonicdb-sdk/ngsi-v2`)
|
|
590
|
+
|
|
591
|
+
NGSIv2 (`/v2/entities`) を叩く軽量クライアント。NGSI-LD を使うコア SDK とは独立した
|
|
592
|
+
サブパスで、NGSIv2 のみを使う利用者向け。
|
|
593
|
+
|
|
594
|
+
```ts
|
|
595
|
+
import { createNgsiV2Client } from '@geolonia/geonicdb-sdk/ngsi-v2';
|
|
596
|
+
|
|
597
|
+
const client = createNgsiV2Client({ baseUrl: 'https://geonicdb.example.com', apiKey: 'KEY', service: 'mytenant' });
|
|
598
|
+
const rooms = await client.getEntities({ type: 'Room', limit: 20 });
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
| Method | Description |
|
|
602
|
+
|--------|-------------|
|
|
603
|
+
| `createNgsiV2Client(config)` | `NgsiV2Client` を生成。`config`: `baseUrl`(必須)/ `apiKey` / `service` |
|
|
604
|
+
| `getEntities(options?)` | エンティティ一覧(`type` / `q` / `limit` / `offset` / `attrs` / `options`) |
|
|
605
|
+
| `getEntity(id, options?)` | 単一エンティティ取得 |
|
|
606
|
+
| `createEntity(entity)` | エンティティ作成 |
|
|
607
|
+
| `updateEntityAttributes(id, attrs)` | 属性の部分更新 |
|
|
608
|
+
| `deleteEntity(id)` | エンティティ削除 |
|
|
609
|
+
|
|
561
610
|
## License
|
|
562
611
|
|
|
563
612
|
MIT
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
21
|
+
|
|
22
|
+
// src/sdk/ngsi-v2/index.ts
|
|
23
|
+
var index_exports = {};
|
|
24
|
+
__export(index_exports, {
|
|
25
|
+
NgsiV2Client: () => NgsiV2Client,
|
|
26
|
+
createNgsiV2Client: () => createNgsiV2Client
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
var NgsiV2Client = class {
|
|
30
|
+
constructor(config) {
|
|
31
|
+
__publicField(this, "baseUrl");
|
|
32
|
+
__publicField(this, "headers");
|
|
33
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
34
|
+
this.headers = {
|
|
35
|
+
"Content-Type": "application/json",
|
|
36
|
+
Accept: "application/json"
|
|
37
|
+
};
|
|
38
|
+
if (config.service) this.headers["Fiware-Service"] = config.service;
|
|
39
|
+
if (config.servicePath) this.headers["Fiware-ServicePath"] = config.servicePath;
|
|
40
|
+
}
|
|
41
|
+
async request(method, path, body) {
|
|
42
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
43
|
+
method,
|
|
44
|
+
headers: this.headers,
|
|
45
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
46
|
+
});
|
|
47
|
+
if (!res.ok) {
|
|
48
|
+
const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
|
|
49
|
+
const description = typeof err["description"] === "string" ? err["description"] : void 0;
|
|
50
|
+
const error = typeof err["error"] === "string" ? err["error"] : void 0;
|
|
51
|
+
throw new Error(description ?? error ?? `HTTP ${res.status}`);
|
|
52
|
+
}
|
|
53
|
+
if (res.status === 201 || res.status === 204) return void 0;
|
|
54
|
+
return res.json();
|
|
55
|
+
}
|
|
56
|
+
/** List entities */
|
|
57
|
+
async getEntities(options) {
|
|
58
|
+
const sp = new URLSearchParams();
|
|
59
|
+
if (options?.type) sp.set("type", options.type);
|
|
60
|
+
if (options?.q) sp.set("q", options.q);
|
|
61
|
+
if (options?.limit !== void 0) sp.set("limit", String(options.limit));
|
|
62
|
+
if (options?.offset !== void 0) sp.set("offset", String(options.offset));
|
|
63
|
+
if (options?.attrs) sp.set("attrs", options.attrs);
|
|
64
|
+
if (options?.options) sp.set("options", options.options);
|
|
65
|
+
const qs = sp.toString();
|
|
66
|
+
return this.request("GET", `/v2/entities${qs ? "?" + qs : ""}`);
|
|
67
|
+
}
|
|
68
|
+
/** Get single entity by ID */
|
|
69
|
+
async getEntity(entityId, options) {
|
|
70
|
+
const sp = new URLSearchParams();
|
|
71
|
+
if (options?.attrs) sp.set("attrs", options.attrs);
|
|
72
|
+
if (options?.options) sp.set("options", options.options);
|
|
73
|
+
const qs = sp.toString();
|
|
74
|
+
return this.request("GET", `/v2/entities/${encodeURIComponent(entityId)}${qs ? "?" + qs : ""}`);
|
|
75
|
+
}
|
|
76
|
+
/** Create entity */
|
|
77
|
+
async createEntity(entity) {
|
|
78
|
+
await this.request("POST", "/v2/entities", entity);
|
|
79
|
+
}
|
|
80
|
+
/** Update entity attributes (partial) */
|
|
81
|
+
async updateEntityAttributes(entityId, attrs) {
|
|
82
|
+
await this.request("PATCH", `/v2/entities/${encodeURIComponent(entityId)}/attrs`, attrs);
|
|
83
|
+
}
|
|
84
|
+
/** Delete entity */
|
|
85
|
+
async deleteEntity(entityId) {
|
|
86
|
+
await this.request("DELETE", `/v2/entities/${encodeURIComponent(entityId)}`);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
function createNgsiV2Client(config) {
|
|
90
|
+
return new NgsiV2Client(config);
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/sdk/ngsi-v2/index.ts"],
|
|
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 * NGSIv2 basic client \u2014 bundled per ADR\u8AD6\u70B92=a (\u6BBF\u88C1\u53EF\u78BA\u5B9A)\n *\n * Scope: basic entity CRUD only. No subscriptions, no batch ops.\n * For NGSI-LD, use the core GeonicDB client instead.\n */\n\nexport interface NgsiV2ClientConfig {\n baseUrl: string;\n /** FIWARE service (tenant) */\n service?: string;\n /** FIWARE service path */\n servicePath?: string;\n}\n\nexport interface NgsiV2Entity {\n id: string;\n type: string;\n [key: string]: string | NgsiV2Attribute | undefined;\n}\n\nexport interface NgsiV2Attribute {\n type: string;\n value: string | number | boolean | object | null;\n metadata?: Record<string, { type: string; value: unknown }>;\n}\n\nexport interface NgsiV2QueryOptions {\n type?: string;\n q?: string;\n limit?: number;\n offset?: number;\n attrs?: string;\n options?: string;\n}\n\nexport class NgsiV2Client {\n private baseUrl: string;\n private headers: Record<string, string>;\n\n constructor(config: NgsiV2ClientConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, '');\n this.headers = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n };\n if (config.service) this.headers['Fiware-Service'] = config.service;\n if (config.servicePath) this.headers['Fiware-ServicePath'] = config.servicePath;\n }\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const res = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers: this.headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n if (!res.ok) {\n const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` })) as Record<string, unknown>;\n const description = typeof err['description'] === 'string' ? err['description'] : undefined;\n const error = typeof err['error'] === 'string' ? err['error'] : undefined;\n throw new Error(description ?? error ?? `HTTP ${res.status}`);\n }\n if (res.status === 201 || res.status === 204) return undefined as T;\n return res.json() as Promise<T>;\n }\n\n /** List entities */\n async getEntities(options?: NgsiV2QueryOptions): Promise<NgsiV2Entity[]> {\n const sp = new URLSearchParams();\n if (options?.type) sp.set('type', options.type);\n if (options?.q) sp.set('q', options.q);\n if (options?.limit !== undefined) sp.set('limit', String(options.limit));\n if (options?.offset !== undefined) sp.set('offset', String(options.offset));\n if (options?.attrs) sp.set('attrs', options.attrs);\n if (options?.options) sp.set('options', options.options);\n const qs = sp.toString();\n return this.request<NgsiV2Entity[]>('GET', `/v2/entities${qs ? '?' + qs : ''}`);\n }\n\n /** Get single entity by ID */\n async getEntity(entityId: string, options?: { attrs?: string; options?: string }): Promise<NgsiV2Entity> {\n const sp = new URLSearchParams();\n if (options?.attrs) sp.set('attrs', options.attrs);\n if (options?.options) sp.set('options', options.options);\n const qs = sp.toString();\n return this.request<NgsiV2Entity>('GET', `/v2/entities/${encodeURIComponent(entityId)}${qs ? '?' + qs : ''}`);\n }\n\n /** Create entity */\n async createEntity(entity: NgsiV2Entity): Promise<void> {\n await this.request<void>('POST', '/v2/entities', entity);\n }\n\n /** Update entity attributes (partial) */\n async updateEntityAttributes(entityId: string, attrs: Record<string, NgsiV2Attribute>): Promise<void> {\n await this.request<void>('PATCH', `/v2/entities/${encodeURIComponent(entityId)}/attrs`, attrs);\n }\n\n /** Delete entity */\n async deleteEntity(entityId: string): Promise<void> {\n await this.request<void>('DELETE', `/v2/entities/${encodeURIComponent(entityId)}`);\n }\n}\n\nexport function createNgsiV2Client(config: NgsiV2ClientConfig): NgsiV2Client {\n return new NgsiV2Client(config);\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwCO,IAAM,eAAN,MAAmB;AAAA,EAIxB,YAAY,QAA4B;AAHxC,wBAAQ;AACR,wBAAQ;AAGN,SAAK,UAAU,OAAO,QAAQ,QAAQ,OAAO,EAAE;AAC/C,SAAK,UAAU;AAAA,MACb,gBAAgB;AAAA,MAChB,QAAQ;AAAA,IACV;AACA,QAAI,OAAO,QAAS,MAAK,QAAQ,gBAAgB,IAAI,OAAO;AAC5D,QAAI,OAAO,YAAa,MAAK,QAAQ,oBAAoB,IAAI,OAAO;AAAA,EACtE;AAAA,EAEA,MAAc,QAAW,QAAgB,MAAc,MAA4B;AACjF,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI,IAAI;AAAA,MAChD;AAAA,MACA,SAAS,KAAK;AAAA,MACd,MAAM,SAAS,SAAY,KAAK,UAAU,IAAI,IAAI;AAAA,IACpD,CAAC;AACD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,MAAM,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,EAAE,OAAO,QAAQ,IAAI,MAAM,GAAG,EAAE;AAC1E,YAAM,cAAc,OAAO,IAAI,aAAa,MAAM,WAAW,IAAI,aAAa,IAAI;AAClF,YAAM,QAAQ,OAAO,IAAI,OAAO,MAAM,WAAW,IAAI,OAAO,IAAI;AAChE,YAAM,IAAI,MAAM,eAAe,SAAS,QAAQ,IAAI,MAAM,EAAE;AAAA,IAC9D;AACA,QAAI,IAAI,WAAW,OAAO,IAAI,WAAW,IAAK,QAAO;AACrD,WAAO,IAAI,KAAK;AAAA,EAClB;AAAA;AAAA,EAGA,MAAM,YAAY,SAAuD;AACvE,UAAM,KAAK,IAAI,gBAAgB;AAC/B,QAAI,SAAS,KAAM,IAAG,IAAI,QAAQ,QAAQ,IAAI;AAC9C,QAAI,SAAS,EAAG,IAAG,IAAI,KAAK,QAAQ,CAAC;AACrC,QAAI,SAAS,UAAU,OAAW,IAAG,IAAI,SAAS,OAAO,QAAQ,KAAK,CAAC;AACvE,QAAI,SAAS,WAAW,OAAW,IAAG,IAAI,UAAU,OAAO,QAAQ,MAAM,CAAC;AAC1E,QAAI,SAAS,MAAO,IAAG,IAAI,SAAS,QAAQ,KAAK;AACjD,QAAI,SAAS,QAAS,IAAG,IAAI,WAAW,QAAQ,OAAO;AACvD,UAAM,KAAK,GAAG,SAAS;AACvB,WAAO,KAAK,QAAwB,OAAO,eAAe,KAAK,MAAM,KAAK,EAAE,EAAE;AAAA,EAChF;AAAA;AAAA,EAGA,MAAM,UAAU,UAAkB,SAAuE;AACvG,UAAM,KAAK,IAAI,gBAAgB;AAC/B,QAAI,SAAS,MAAO,IAAG,IAAI,SAAS,QAAQ,KAAK;AACjD,QAAI,SAAS,QAAS,IAAG,IAAI,WAAW,QAAQ,OAAO;AACvD,UAAM,KAAK,GAAG,SAAS;AACvB,WAAO,KAAK,QAAsB,OAAO,gBAAgB,mBAAmB,QAAQ,CAAC,GAAG,KAAK,MAAM,KAAK,EAAE,EAAE;AAAA,EAC9G;AAAA;AAAA,EAGA,MAAM,aAAa,QAAqC;AACtD,UAAM,KAAK,QAAc,QAAQ,gBAAgB,MAAM;AAAA,EACzD;AAAA;AAAA,EAGA,MAAM,uBAAuB,UAAkB,OAAuD;AACpG,UAAM,KAAK,QAAc,SAAS,gBAAgB,mBAAmB,QAAQ,CAAC,UAAU,KAAK;AAAA,EAC/F;AAAA;AAAA,EAGA,MAAM,aAAa,UAAiC;AAClD,UAAM,KAAK,QAAc,UAAU,gBAAgB,mBAAmB,QAAQ,CAAC,EAAE;AAAA,EACnF;AACF;AAEO,SAAS,mBAAmB,QAA0C;AAC3E,SAAO,IAAI,aAAa,MAAM;AAChC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Generated by dts-bundle-generator v9.5.1
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* NGSIv2 basic client — bundled per ADR論点2=a (殿裁可確定)
|
|
5
|
+
*
|
|
6
|
+
* Scope: basic entity CRUD only. No subscriptions, no batch ops.
|
|
7
|
+
* For NGSI-LD, use the core GeonicDB client instead.
|
|
8
|
+
*/
|
|
9
|
+
export interface NgsiV2ClientConfig {
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
/** FIWARE service (tenant) */
|
|
12
|
+
service?: string;
|
|
13
|
+
/** FIWARE service path */
|
|
14
|
+
servicePath?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface NgsiV2Entity {
|
|
17
|
+
id: string;
|
|
18
|
+
type: string;
|
|
19
|
+
[key: string]: string | NgsiV2Attribute | undefined;
|
|
20
|
+
}
|
|
21
|
+
export interface NgsiV2Attribute {
|
|
22
|
+
type: string;
|
|
23
|
+
value: string | number | boolean | object | null;
|
|
24
|
+
metadata?: Record<string, {
|
|
25
|
+
type: string;
|
|
26
|
+
value: unknown;
|
|
27
|
+
}>;
|
|
28
|
+
}
|
|
29
|
+
export interface NgsiV2QueryOptions {
|
|
30
|
+
type?: string;
|
|
31
|
+
q?: string;
|
|
32
|
+
limit?: number;
|
|
33
|
+
offset?: number;
|
|
34
|
+
attrs?: string;
|
|
35
|
+
options?: string;
|
|
36
|
+
}
|
|
37
|
+
export declare class NgsiV2Client {
|
|
38
|
+
private baseUrl;
|
|
39
|
+
private headers;
|
|
40
|
+
constructor(config: NgsiV2ClientConfig);
|
|
41
|
+
private request;
|
|
42
|
+
/** List entities */
|
|
43
|
+
getEntities(options?: NgsiV2QueryOptions): Promise<NgsiV2Entity[]>;
|
|
44
|
+
/** Get single entity by ID */
|
|
45
|
+
getEntity(entityId: string, options?: {
|
|
46
|
+
attrs?: string;
|
|
47
|
+
options?: string;
|
|
48
|
+
}): Promise<NgsiV2Entity>;
|
|
49
|
+
/** Create entity */
|
|
50
|
+
createEntity(entity: NgsiV2Entity): Promise<void>;
|
|
51
|
+
/** Update entity attributes (partial) */
|
|
52
|
+
updateEntityAttributes(entityId: string, attrs: Record<string, NgsiV2Attribute>): Promise<void>;
|
|
53
|
+
/** Delete entity */
|
|
54
|
+
deleteEntity(entityId: string): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
export declare function createNgsiV2Client(config: NgsiV2ClientConfig): NgsiV2Client;
|
|
57
|
+
|
|
58
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
|
|
5
|
+
// src/sdk/ngsi-v2/index.ts
|
|
6
|
+
var NgsiV2Client = class {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
__publicField(this, "baseUrl");
|
|
9
|
+
__publicField(this, "headers");
|
|
10
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
11
|
+
this.headers = {
|
|
12
|
+
"Content-Type": "application/json",
|
|
13
|
+
Accept: "application/json"
|
|
14
|
+
};
|
|
15
|
+
if (config.service) this.headers["Fiware-Service"] = config.service;
|
|
16
|
+
if (config.servicePath) this.headers["Fiware-ServicePath"] = config.servicePath;
|
|
17
|
+
}
|
|
18
|
+
async request(method, path, body) {
|
|
19
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
20
|
+
method,
|
|
21
|
+
headers: this.headers,
|
|
22
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
23
|
+
});
|
|
24
|
+
if (!res.ok) {
|
|
25
|
+
const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` }));
|
|
26
|
+
const description = typeof err["description"] === "string" ? err["description"] : void 0;
|
|
27
|
+
const error = typeof err["error"] === "string" ? err["error"] : void 0;
|
|
28
|
+
throw new Error(description ?? error ?? `HTTP ${res.status}`);
|
|
29
|
+
}
|
|
30
|
+
if (res.status === 201 || res.status === 204) return void 0;
|
|
31
|
+
return res.json();
|
|
32
|
+
}
|
|
33
|
+
/** List entities */
|
|
34
|
+
async getEntities(options) {
|
|
35
|
+
const sp = new URLSearchParams();
|
|
36
|
+
if (options?.type) sp.set("type", options.type);
|
|
37
|
+
if (options?.q) sp.set("q", options.q);
|
|
38
|
+
if (options?.limit !== void 0) sp.set("limit", String(options.limit));
|
|
39
|
+
if (options?.offset !== void 0) sp.set("offset", String(options.offset));
|
|
40
|
+
if (options?.attrs) sp.set("attrs", options.attrs);
|
|
41
|
+
if (options?.options) sp.set("options", options.options);
|
|
42
|
+
const qs = sp.toString();
|
|
43
|
+
return this.request("GET", `/v2/entities${qs ? "?" + qs : ""}`);
|
|
44
|
+
}
|
|
45
|
+
/** Get single entity by ID */
|
|
46
|
+
async getEntity(entityId, options) {
|
|
47
|
+
const sp = new URLSearchParams();
|
|
48
|
+
if (options?.attrs) sp.set("attrs", options.attrs);
|
|
49
|
+
if (options?.options) sp.set("options", options.options);
|
|
50
|
+
const qs = sp.toString();
|
|
51
|
+
return this.request("GET", `/v2/entities/${encodeURIComponent(entityId)}${qs ? "?" + qs : ""}`);
|
|
52
|
+
}
|
|
53
|
+
/** Create entity */
|
|
54
|
+
async createEntity(entity) {
|
|
55
|
+
await this.request("POST", "/v2/entities", entity);
|
|
56
|
+
}
|
|
57
|
+
/** Update entity attributes (partial) */
|
|
58
|
+
async updateEntityAttributes(entityId, attrs) {
|
|
59
|
+
await this.request("PATCH", `/v2/entities/${encodeURIComponent(entityId)}/attrs`, attrs);
|
|
60
|
+
}
|
|
61
|
+
/** Delete entity */
|
|
62
|
+
async deleteEntity(entityId) {
|
|
63
|
+
await this.request("DELETE", `/v2/entities/${encodeURIComponent(entityId)}`);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
function createNgsiV2Client(config) {
|
|
67
|
+
return new NgsiV2Client(config);
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
NgsiV2Client,
|
|
71
|
+
createNgsiV2Client
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/sdk/ngsi-v2/index.ts"],
|
|
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 * NGSIv2 basic client \u2014 bundled per ADR\u8AD6\u70B92=a (\u6BBF\u88C1\u53EF\u78BA\u5B9A)\n *\n * Scope: basic entity CRUD only. No subscriptions, no batch ops.\n * For NGSI-LD, use the core GeonicDB client instead.\n */\n\nexport interface NgsiV2ClientConfig {\n baseUrl: string;\n /** FIWARE service (tenant) */\n service?: string;\n /** FIWARE service path */\n servicePath?: string;\n}\n\nexport interface NgsiV2Entity {\n id: string;\n type: string;\n [key: string]: string | NgsiV2Attribute | undefined;\n}\n\nexport interface NgsiV2Attribute {\n type: string;\n value: string | number | boolean | object | null;\n metadata?: Record<string, { type: string; value: unknown }>;\n}\n\nexport interface NgsiV2QueryOptions {\n type?: string;\n q?: string;\n limit?: number;\n offset?: number;\n attrs?: string;\n options?: string;\n}\n\nexport class NgsiV2Client {\n private baseUrl: string;\n private headers: Record<string, string>;\n\n constructor(config: NgsiV2ClientConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, '');\n this.headers = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n };\n if (config.service) this.headers['Fiware-Service'] = config.service;\n if (config.servicePath) this.headers['Fiware-ServicePath'] = config.servicePath;\n }\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const res = await fetch(`${this.baseUrl}${path}`, {\n method,\n headers: this.headers,\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n if (!res.ok) {\n const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` })) as Record<string, unknown>;\n const description = typeof err['description'] === 'string' ? err['description'] : undefined;\n const error = typeof err['error'] === 'string' ? err['error'] : undefined;\n throw new Error(description ?? error ?? `HTTP ${res.status}`);\n }\n if (res.status === 201 || res.status === 204) return undefined as T;\n return res.json() as Promise<T>;\n }\n\n /** List entities */\n async getEntities(options?: NgsiV2QueryOptions): Promise<NgsiV2Entity[]> {\n const sp = new URLSearchParams();\n if (options?.type) sp.set('type', options.type);\n if (options?.q) sp.set('q', options.q);\n if (options?.limit !== undefined) sp.set('limit', String(options.limit));\n if (options?.offset !== undefined) sp.set('offset', String(options.offset));\n if (options?.attrs) sp.set('attrs', options.attrs);\n if (options?.options) sp.set('options', options.options);\n const qs = sp.toString();\n return this.request<NgsiV2Entity[]>('GET', `/v2/entities${qs ? '?' + qs : ''}`);\n }\n\n /** Get single entity by ID */\n async getEntity(entityId: string, options?: { attrs?: string; options?: string }): Promise<NgsiV2Entity> {\n const sp = new URLSearchParams();\n if (options?.attrs) sp.set('attrs', options.attrs);\n if (options?.options) sp.set('options', options.options);\n const qs = sp.toString();\n return this.request<NgsiV2Entity>('GET', `/v2/entities/${encodeURIComponent(entityId)}${qs ? '?' + qs : ''}`);\n }\n\n /** Create entity */\n async createEntity(entity: NgsiV2Entity): Promise<void> {\n await this.request<void>('POST', '/v2/entities', entity);\n }\n\n /** Update entity attributes (partial) */\n async updateEntityAttributes(entityId: string, attrs: Record<string, NgsiV2Attribute>): Promise<void> {\n await this.request<void>('PATCH', `/v2/entities/${encodeURIComponent(entityId)}/attrs`, attrs);\n }\n\n /** Delete entity */\n async deleteEntity(entityId: string): Promise<void> {\n await this.request<void>('DELETE', `/v2/entities/${encodeURIComponent(entityId)}`);\n }\n}\n\nexport function createNgsiV2Client(config: NgsiV2ClientConfig): NgsiV2Client {\n return new NgsiV2Client(config);\n}\n"],
|
|
5
|
+
"mappings": ";;;;;AAwCO,IAAM,eAAN,MAAmB;AAAA,EAIxB,YAAY,QAA4B;AAHxC,wBAAQ;AACR,wBAAQ;AAGN,SAAK,UAAU,OAAO,QAAQ,QAAQ,OAAO,EAAE;AAC/C,SAAK,UAAU;AAAA,MACb,gBAAgB;AAAA,MAChB,QAAQ;AAAA,IACV;AACA,QAAI,OAAO,QAAS,MAAK,QAAQ,gBAAgB,IAAI,OAAO;AAC5D,QAAI,OAAO,YAAa,MAAK,QAAQ,oBAAoB,IAAI,OAAO;AAAA,EACtE;AAAA,EAEA,MAAc,QAAW,QAAgB,MAAc,MAA4B;AACjF,UAAM,MAAM,MAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI,IAAI;AAAA,MAChD;AAAA,MACA,SAAS,KAAK;AAAA,MACd,MAAM,SAAS,SAAY,KAAK,UAAU,IAAI,IAAI;AAAA,IACpD,CAAC;AACD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,MAAM,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,EAAE,OAAO,QAAQ,IAAI,MAAM,GAAG,EAAE;AAC1E,YAAM,cAAc,OAAO,IAAI,aAAa,MAAM,WAAW,IAAI,aAAa,IAAI;AAClF,YAAM,QAAQ,OAAO,IAAI,OAAO,MAAM,WAAW,IAAI,OAAO,IAAI;AAChE,YAAM,IAAI,MAAM,eAAe,SAAS,QAAQ,IAAI,MAAM,EAAE;AAAA,IAC9D;AACA,QAAI,IAAI,WAAW,OAAO,IAAI,WAAW,IAAK,QAAO;AACrD,WAAO,IAAI,KAAK;AAAA,EAClB;AAAA;AAAA,EAGA,MAAM,YAAY,SAAuD;AACvE,UAAM,KAAK,IAAI,gBAAgB;AAC/B,QAAI,SAAS,KAAM,IAAG,IAAI,QAAQ,QAAQ,IAAI;AAC9C,QAAI,SAAS,EAAG,IAAG,IAAI,KAAK,QAAQ,CAAC;AACrC,QAAI,SAAS,UAAU,OAAW,IAAG,IAAI,SAAS,OAAO,QAAQ,KAAK,CAAC;AACvE,QAAI,SAAS,WAAW,OAAW,IAAG,IAAI,UAAU,OAAO,QAAQ,MAAM,CAAC;AAC1E,QAAI,SAAS,MAAO,IAAG,IAAI,SAAS,QAAQ,KAAK;AACjD,QAAI,SAAS,QAAS,IAAG,IAAI,WAAW,QAAQ,OAAO;AACvD,UAAM,KAAK,GAAG,SAAS;AACvB,WAAO,KAAK,QAAwB,OAAO,eAAe,KAAK,MAAM,KAAK,EAAE,EAAE;AAAA,EAChF;AAAA;AAAA,EAGA,MAAM,UAAU,UAAkB,SAAuE;AACvG,UAAM,KAAK,IAAI,gBAAgB;AAC/B,QAAI,SAAS,MAAO,IAAG,IAAI,SAAS,QAAQ,KAAK;AACjD,QAAI,SAAS,QAAS,IAAG,IAAI,WAAW,QAAQ,OAAO;AACvD,UAAM,KAAK,GAAG,SAAS;AACvB,WAAO,KAAK,QAAsB,OAAO,gBAAgB,mBAAmB,QAAQ,CAAC,GAAG,KAAK,MAAM,KAAK,EAAE,EAAE;AAAA,EAC9G;AAAA;AAAA,EAGA,MAAM,aAAa,QAAqC;AACtD,UAAM,KAAK,QAAc,QAAQ,gBAAgB,MAAM;AAAA,EACzD;AAAA;AAAA,EAGA,MAAM,uBAAuB,UAAkB,OAAuD;AACpG,UAAM,KAAK,QAAc,SAAS,gBAAgB,mBAAmB,QAAQ,CAAC,UAAU,KAAK;AAAA,EAC/F;AAAA;AAAA,EAGA,MAAM,aAAa,UAAiC;AAClD,UAAM,KAAK,QAAc,UAAU,gBAAgB,mBAAmB,QAAQ,CAAC,EAAE;AAAA,EACnF;AACF;AAEO,SAAS,mBAAmB,QAA0C;AAC3E,SAAO,IAAI,aAAa,MAAM;AAChC;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geolonia/geonicdb-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "GeonicDB JavaScript SDK — NGSI-LD Context Broker client for browser and Node.js",
|
|
5
5
|
"main": "geonicdb.cjs",
|
|
6
6
|
"module": "geonicdb.mjs",
|
|
7
7
|
"types": "geonicdb.d.ts",
|
|
8
8
|
"browser": "geonicdb.iife.js",
|
|
9
|
+
"sideEffects": false,
|
|
9
10
|
"exports": {
|
|
10
11
|
".": {
|
|
11
12
|
"import": {
|
|
@@ -16,12 +17,46 @@
|
|
|
16
17
|
"types": "./geonicdb.d.ts",
|
|
17
18
|
"default": "./geonicdb.cjs"
|
|
18
19
|
}
|
|
20
|
+
},
|
|
21
|
+
"./react": {
|
|
22
|
+
"import": {
|
|
23
|
+
"types": "./react/index.d.ts",
|
|
24
|
+
"default": "./react/index.mjs"
|
|
25
|
+
},
|
|
26
|
+
"require": {
|
|
27
|
+
"types": "./react/index.d.ts",
|
|
28
|
+
"default": "./react/index.cjs"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"./ngsi-v2": {
|
|
32
|
+
"import": {
|
|
33
|
+
"types": "./ngsi-v2/index.d.ts",
|
|
34
|
+
"default": "./ngsi-v2/index.mjs"
|
|
35
|
+
},
|
|
36
|
+
"require": {
|
|
37
|
+
"types": "./ngsi-v2/index.d.ts",
|
|
38
|
+
"default": "./ngsi-v2/index.cjs"
|
|
39
|
+
}
|
|
19
40
|
}
|
|
20
41
|
},
|
|
21
42
|
"files": [
|
|
22
43
|
"geonicdb.*",
|
|
44
|
+
"react/",
|
|
45
|
+
"ngsi-v2/",
|
|
23
46
|
"LICENSE"
|
|
24
47
|
],
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"react": "^18 || ^19",
|
|
50
|
+
"@geolonia/embed": "^1.0.0"
|
|
51
|
+
},
|
|
52
|
+
"peerDependenciesMeta": {
|
|
53
|
+
"react": {
|
|
54
|
+
"optional": true
|
|
55
|
+
},
|
|
56
|
+
"@geolonia/embed": {
|
|
57
|
+
"optional": true
|
|
58
|
+
}
|
|
59
|
+
},
|
|
25
60
|
"license": "MIT",
|
|
26
61
|
"publishConfig": {
|
|
27
62
|
"access": "public"
|
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
|