npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@geolonia/geonicdb-sdk

v0.16.0

Published

GeonicDB JavaScript SDK — NGSI-LD Context Broker client for browser and Node.js

Readme

@geolonia/geonicdb-sdk

GeonicDB JavaScript SDK — NGSI-LD Context Broker client for browser and Node.js.

Installation

npm install @geolonia/geonicdb-sdk

Quick Start

Node.js / TypeScript

import GeonicDB from '@geolonia/geonicdb-sdk';

const db = new GeonicDB({
  apiKey: 'your-api-key',
  tenant: 'your-tenant',
  baseUrl: 'https://your-geonicdb.example.com',
});

// Query entities
const rooms = await db.getEntities({ type: 'Room' });
console.log(rooms);

// Create entity
await db.createEntity({
  id: 'urn:ngsi-ld:Room:001',
  type: 'Room',
  temperature: { type: 'Property', value: 23.5 },
});

TypeScript

The SDK ships type definitions (geonicdb.d.ts) in the published package. Named imports of error classes and types resolve cleanly even under strict tsconfig settings (strict, verbatimModuleSyntax, etc.):

import GeonicDB, {
  AuthorizationError,
  AuthenticationError,
  type EntityEvent,
  type RefreshedCredentials,
} from '@geolonia/geonicdb-sdk';

try {
  await db.deleteEntity(entityId);
} catch (err) {
  if (err instanceof AuthorizationError) {
    // Type-safe branch on XACML denial.
  }
}

See Error Handling below for the full list of exported error classes.

Browser (script tag)

<script src="https://unpkg.com/@geolonia/geonicdb-sdk/geonicdb.iife.js"></script>
<script>
  var db = new GeonicDB({
    apiKey: 'your-api-key',
    tenant: 'your-tenant',
    baseUrl: 'https://your-geonicdb.example.com',
  });
  db.getEntities({ type: 'Room' }).then(console.log);
</script>

Authentication

API Key (DPoP + Proof of Work)

const db = new GeonicDB({
  apiKey: 'your-api-key',
  tenant: 'your-tenant',
  baseUrl: 'https://your-geonicdb.example.com',
});
// Authentication is automatic — DPoP key pair generation,
// PoW solving, and token exchange happen transparently.

Bearer JWT (Email/Password Login)

const db = new GeonicDB({
  tenant: 'your-tenant',
  baseUrl: 'https://your-geonicdb.example.com',
});

await db.login('[email protected]', 'password');
// Token refresh is automatic via refresh token.

Sessions are DPoP sender-constrained automatically (RFC 9449). After POST /auth/login succeeds, the SDK transparently exchanges the Bearer session for a DPoP-bound one, generating a non-extractable ECDSA P-256 key pair via Web Crypto and binding the access / refresh tokens to it. A token leaked from storage (e.g. localStorage XSS) therefore cannot be replayed from another origin. There is no DPoP option to set — you never deal with DPoP directly.

const result = await db.login('[email protected]', 'password');
// result.tokenType === 'DPoP' when Web Crypto is available.
// Subsequent requests use `Authorization: DPoP <token>` + per-request proof.
// The bind step and the RFC 9449 §8 `use_dpop_nonce` handshake are internal.

On runtimes without Web Crypto (subtle.generateKey, e.g. a non-HTTPS context), the session cannot be sender-constrained: the SDK silently keeps the plain Bearer session and login still succeeds (tokenType === 'Bearer').

First-login forced password change (#1532)

When an administrator issues a temporary password — either at creation (POST /admin/users with passwordResetRequired: true) or afterwards (POST /admin/users/{userId}/reset-password) — the user must set a new password on first login. Logging in with only the temporary password throws PasswordResetRequiredError; supply newPassword to set the new password and receive tokens in the same call (no re-login needed):

import { PasswordResetRequiredError, TemporaryPasswordExpiredError } from '@geolonia/geonicdb-sdk';

try {
  await db.login('[email protected]', temporaryPassword);
} catch (err) {
  if (err instanceof PasswordResetRequiredError) {
    // Prompt the user for a new password, then complete login in one call:
    await db.login('[email protected]', temporaryPassword, { newPassword });
  } else if (err instanceof TemporaryPasswordExpiredError) {
    // Ask an administrator to re-issue the temporary password.
  }
}

Keeping the user signed in across page reloads

By default a session is held in memory only — reloading the page loses it and forces the user to log in again. Pass { rememberSession: true } to keep the user signed in:

const db = new GeonicDB({ baseUrl, tenant });
await db.login('[email protected]', 'password', { rememberSession: true });

// On the next page load:
const db2 = new GeonicDB({ baseUrl, tenant });
if (await db2.restoreSession()) {
  // Session was rehydrated — boot the app.
} else {
  // No persisted session; show the login form.
}

// On logout, the persisted entry is cleared automatically.
await db2.logout();

Under the hood, the session is persisted to IndexedDB. The structured clone algorithm stores the DPoP CryptoKey as-is, preserving extractable: false: the key survives reloads, but JavaScript on the page still cannot read it back as JWK / PEM. Tokens leaked from any other storage remain unusable without this origin's IndexedDB.

Notes:

  • The IndexedDB store is keyed by tenant, so multi-tenant apps in the same origin keep separate sessions.
  • Persistence is best-effort: a failed IDB write (or no Web Crypto, so the session stays Bearer) does not break login — it just means no rehydrate.
  • Opt-in by design: shared-device flows should leave rememberSession off.
  • iOS Safari may evict IndexedDB under ITP after 7 days of no user interaction with the origin.

External Token

db.setCredentials({
  token: 'your-jwt-token',
  tokenType: 'Bearer',
  expiresIn: 3600,
  refreshToken: 'your-refresh-token',
});

Anonymous Mode (public viewers)

For public-facing apps such as GeoJSON viewers or BI dashboards that should serve unregistered visitors without forcing a login, pass anonymous: true to skip token acquisition entirely:

const db = new GeonicDB({
  baseUrl: 'https://your-geonicdb.example.com',
  tenant: 'public',
  anonymous: true, // no Authorization header, no /auth/nonce, no /oauth/token
});

// The server treats this as `role: 'anonymous'` and the XACML policy engine
// decides whether to permit. Tenant admins must register a custom policy that
// permits the anonymous role for the resources to expose — the default
// ANONYMOUS_DEFAULT_POLICY denies everything.
const entities = await db.getEntities({ type: 'GeoJSON' });
  • apiKey and anonymous: true cannot be combined (constructor throws).
  • db.login() upgrades the SDK out of anonymous mode (Bearer JWT).
  • db.setCredentials({ token, tokenType: 'Bearer', ... }) upgrades from anonymous. tokenType: 'DPoP' is rejected for anonymous-built instances (no DPoP key pair is generated for them) — use a non-anonymous SDK instance for DPoP credentials.
  • db.logout() reverts back to anonymous (db.isAnonymous() === true again).
  • WebSocket db.connect() is not supported in anonymous mode — call login() / setCredentials() first.

Entity CRUD

Automatic @context (#1583): the SDK sends requests as application/ld+json, which requires an inline JSON-LD @context in the body. When your body omits @context, the SDK injects the NGSI-LD core context automatically; a @context you provide is always preserved (never overwritten).

// Create
await db.createEntity({ id: 'urn:ngsi-ld:Room:001', type: 'Room', ... });

// Read
const entity = await db.getEntity('urn:ngsi-ld:Room:001');

// Query (NGSI-LD §5.7.2 のクエリパラメータを網羅)
const entities = await db.getEntities({
  type: 'Room',
  limit: 10,
  // scopeQ: '/OpenData;/OpenData/#',     // exact + descendants
  // georel: 'near;maxDistance==1000',
  // geometry: 'Point',
  // coordinates: '139.7,35.6',
  // attrs: 'name,location',
  // pick: 'name', omit: 'createdAt',
  // lang: 'ja',
  // orderBy: 'modifiedAt', orderDirection: 'desc',
  // count: true,
});

// Update
await db.updateEntity('urn:ngsi-ld:Room:001', { temperature: { type: 'Property', value: 25 } });

// Delete
await db.deleteEntity('urn:ngsi-ld:Room:001');

Types / Attributes Discovery

// getTypes()/getAttributes() は EntityTypeList/AttributeList オブジェクトを返す。
// 型名・属性名の一覧は typeList / attributeList から取得する。
const { typeList } = await db.getTypes();          // string[]
const roomType = await db.getType('Room');
const { attributeList } = await db.getAttributes(); // string[]
const tempAttr = await db.getAttribute('temperature');

Temporal API

const history = await db.getTemporalEntities({
  type: 'Room',
  timerel: 'between',
  timeAt: '2026-01-01T00:00:00Z',
  endTimeAt: '2026-01-02T00:00:00Z',
});

const entityHistory = await db.getTemporalEntity('urn:ngsi-ld:Room:001');

Batch Operations

// Create multiple entities
await db.batchCreate([
  { id: 'urn:ngsi-ld:Room:001', type: 'Room', ... },
  { id: 'urn:ngsi-ld:Room:002', type: 'Room', ... },
]);

// Upsert, Update, Delete
await db.batchUpsert(entities);
await db.batchUpdate(entities);
await db.batchDelete(['urn:ngsi-ld:Room:001', 'urn:ngsi-ld:Room:002']);

Generic API Request

// Access any API endpoint with automatic authentication
const result = await db.request('GET', '/ngsi-ld/v1/subscriptions');

WebSocket Event Streaming

db.subscribe({ entityTypes: ['Room'] });

db.on('entityUpdated', (event) => {
  // event.entity contains the complete NGSI-LD entity ({ id, type, ...attributes })
  console.log(`${event.entityId} updated:`, event.entity);
});

db.on('error', (err) => console.error(err));

await db.connect();

Events

| Event | Payload | Description | |-------|---------|-------------| | entityCreated | EntityEvent | Entity was created | | entityUpdated | EntityEvent | Entity was updated | | entityDeleted | EntityEvent | Entity was deleted | | open | — | WebSocket connection opened | | connected | — | WebSocket ready | | close | { code, reason } | WebSocket closed | | disconnected | — | Unexpected disconnect | | reconnecting | { attempt, delay } | Auto-reconnect starting | | error | Error | Error occurred | | tokenRefresh | RefreshedCredentials | Bearer token was refreshed | | cacheHit | CacheEvent | Request served from in-memory cache (304 received) | | cacheMiss | CacheEvent | Cache miss — fresh response fetched from origin | | cacheInvalidated | CacheEvent | Cache entry dropped via explicit clearCache(). WebSocket entity events do not trigger cache invalidation — see "Client-side Cache & Polling" |

Client-side Cache & Polling

The SDK includes an in-memory cache that handles ETag / If-None-Match negotiation transparently. Subsequent reads of an unchanged resource are served as 304 Not Modified — the SDK presents them to your code as a normal 200 response with the cached body.

// First call: fresh fetch, ETag stored
const rooms1 = await db.getEntities({ type: 'Room' });

// Second call: server returns 304, SDK returns the cached body
const rooms2 = await db.getEntities({ type: 'Room' });

// Concurrent calls to the same path are deduplicated:
const [a, b, c] = await Promise.all([
  db.getEntities({ type: 'Room' }),
  db.getEntities({ type: 'Room' }),
  db.getEntities({ type: 'Room' }),
]);  // → only one HTTP request hits the network

Cache is enabled by default; pass cache: false to opt out.

Persistence across reloads

In the browser the cache (validators + bodies) is persisted to IndexedDB so it survives a page reload. A reloaded SPA can therefore send If-None-Match on its very first request and get a 304 instead of always cold-starting into a full 200 body.

// First load: fetched in full and saved to IndexedDB.
await db.getEntities({ type: 'AedLocation' });

// After a page reload, restore the session — the cache rehydrates with it:
if (await db.restoreSession()) {
  await db.getEntities({ type: 'AedLocation' });
  // → If-None-Match sent; unchanged data comes back as 304 (near-zero bytes).
}
  • Persistence is tied to rememberSession: it is active only for sessions that themselves survive a reload (login({ rememberSession: true })restoreSession()), sharing the exact lifecycle of the persisted DPoP session.
  • Entries are namespaced by tenant and authenticated principal, so a different subject sharing the same origin/tenant (another tab, another SDK instance, a later login) never reads entries written by a previous principal. They are dropped on every credential change (login(), setCredentials(), logout()) and only read back by the same principal that wrote them, via restoreSession().
  • Capacity / LRU follow cacheMaxEntries, the same as the in-memory cache.
  • In non-browser runtimes (Node, CLI) without IndexedDB the cache stays in-memory only — fully backward compatible.
  • clearCache() flushes the persisted store as well as the in-memory cache.

Cache lifetime under WebSocket activity

WebSocket entity events (entityCreated / entityUpdated / entityDeleted) do not automatically invalidate the SDK cache. This is intentional:

  • Data endpoints respond with Cache-Control: private, no-cache, so every cached read already revalidates with the origin via If-None-Match.
  • If the entity actually changed, the server's freshly computed ETag will not match the client's If-None-Match → server returns 200 with the fresh body → SDK replaces the cache entry.
  • If the WebSocket event was noisy / unrelated to a particular cached query (or the data hasn't propagated to the read replica yet), the server returns 304 Not Modified → SDK serves the cached body with no body bytes transferred.

Deleting cache entries on every WebSocket event would force the next read to miss the cache and fetch a full 200 body — defeating the bandwidth-saving 304 path. To force a full reset (e.g. on auth change), call clearCache() explicitly.

Polling

db.poll(params, options) repeats getEntities() at an interval and reports back whether the data actually changed. Internally it leverages the cache's ETag negotiation, so unchanged ticks transfer almost no bytes.

const handle = db.poll({ type: 'Room' }, {
  interval: 5000,
  onData: (rooms) => render(rooms),
  onNoChange: () => {},  // server returned 304
  onError: (err) => console.error(err),
});

// Stop later
handle.stop();

API Reference

Constructor

| Method | Parameters | Returns | Description | |--------|-----------|---------|-------------| | new GeonicDB(options?) | GeonicDBOptions | GeonicDB | Create a new SDK instance |

GeonicDBOptions

| Property | Type | Required | Description | |----------|------|----------|-------------| | apiKey | string | No | API key (or use data-api-key attribute on script tag) | | tenant | string | No | Tenant name (or use data-tenant attribute on script tag) | | baseUrl | string | No | API base URL (auto-detected from script src if omitted) | | wsEndpoint | string | No | WebSocket endpoint URL (auto-detected from baseUrl if omitted) | | debug | boolean | No | Enable debug logging to console (default: false) | | cache | boolean | No | Enable in-memory cache with ETag/304 + request dedup (default: true) | | cacheMaxEntries | number | No | LRU cache capacity when cache is enabled (default: 1000) | | anonymous | boolean | No | Anonymous mode — skip token acquisition and send no Authorization header. Cannot be combined with apiKey. See Anonymous Mode (default: false) |

Authentication

| Method | Parameters | Returns | Description | |--------|-----------|---------|-------------| | login(email, password, options?) | string, string, LoginOptions? | Promise<LoginResponse> | Login with email and password. Upgrades the SDK out of anonymous mode. The session is DPoP sender-constrained (RFC 9449) automatically and transparently — no option to set; silently falls back to Bearer when Web Crypto is unavailable. Pass { rememberSession: true } to keep the user signed in across page reloads (persisted to IndexedDB) | | restoreSession() | — | Promise<boolean> | Rehydrate a session previously persisted via login({ rememberSession: true }). Returns true if a session was found and loaded, false otherwise. Call at app startup before showing the login form | | setCredentials(opts) | CredentialsOptions | this | Set credentials externally (e.g. from a login API response). Upgrades the SDK out of anonymous mode when tokenType: 'Bearer'. Throws when tokenType: 'DPoP' is passed to an anonymous-built instance — anonymous instances do not generate a DPoP key pair | | logout() | — | void | Clear all credentials and disconnect WebSocket. Reverts to anonymous mode if anonymous: true was set at construction | | isAnonymous() | — | boolean | Whether explicit anonymous mode is active. Returns the explicit mode flag — not derived from credential presence — so a refresh failure that clears _token does NOT silently flip this back to true |

Entity CRUD

| Method | Parameters | Returns | Description | |--------|-----------|---------|-------------| | createEntity(entity) | Record<string, unknown> | Promise<{ created: true }> | Create a new entity | | getEntity(entityId) | string | Promise<Record<string, unknown>> | Get a single entity by ID | | getEntities(params?) | GetEntitiesParams | Promise<Record<string, unknown>[]> | Query entities with optional filters | | count(params?) | CountEntitiesParams | Promise<number> | Count entities matching the given filters | | updateEntity(entityId, attrs) | string, Record<string, unknown> | Promise<{ updated: true }> | Update entity attributes (partial) | | deleteEntity(entityId) | string | Promise<{ deleted: true }> | Delete an entity |

GetEntitiesParams

| Property | Type | Description | |----------|------|-------------| | type | string | Entity type filter | | limit | number | Max results | | offset | number | Pagination offset | | q | string | NGSI-LD query expression | | options | string | NGSI-LD representation options (comma-separated): sysAttrs (include createdAt/modifiedAt), keyValues, concise, normalized |

CountEntitiesParams

| Property | Type | Description | |----------|------|-------------| | type | string | Entity type filter | | q | string | NGSI-LD query expression |

Types / Attributes Discovery

| Method | Parameters | Returns | Description | |--------|-----------|---------|-------------| | getTypes() | — | Promise<EntityTypeList> | List all entity types ({ id, type, typeList }) | | getType(typeName) | string | Promise<Record<string, unknown>> | Get details for a specific entity type | | getAttributes() | — | Promise<AttributeList> | List all attributes ({ id, type, attributeList }) | | getAttribute(attrName) | string | Promise<Record<string, unknown>> | Get details for a specific attribute |

Temporal API

| Method | Parameters | Returns | Description | |--------|-----------|---------|-------------| | getTemporalEntities(params?) | GetTemporalEntitiesParams | Promise<Record<string, unknown>[]> | Query temporal entities | | getTemporalEntity(entityId) | string | Promise<Record<string, unknown>> | Get temporal representation of a single entity |

GetTemporalEntitiesParams

| Property | Type | Description | |----------|------|-------------| | type | string | Entity type filter | | id | string | Entity ID | | idPattern | string | Entity ID pattern (regex) | | attrs | string | Attribute names to retrieve | | q | string | NGSI-LD query expression | | timeproperty | string | Time property (observedAt, createdAt, modifiedAt) | | timeAt | string | Start time (ISO 8601) | | endTimeAt | string | End time (ISO 8601) | | timerel | string | Temporal relation (before, after, between) | | limit | number | Max results | | offset | number | Pagination offset | | options | string | NGSI-LD representation options (comma-separated): sysAttrs, temporalValues, aggregatedValues |

Batch Operations

| Method | Parameters | Returns | Description | |--------|-----------|---------|-------------| | batchCreate(entities) | Record<string, unknown>[] | Promise<Record<string, unknown>> | Create multiple entities | | batchUpsert(entities) | Record<string, unknown>[] | Promise<Record<string, unknown>> | Upsert multiple entities | | batchUpdate(entities) | Record<string, unknown>[] | Promise<Record<string, unknown>> | Update multiple entities | | batchDelete(entityIds) | string[] | Promise<Record<string, unknown>> | Delete multiple entities by ID |

Generic Request

| Method | Parameters | Returns | Description | |--------|-----------|---------|-------------| | request(method, path, body?) | string, string, unknown | Promise<unknown> | Authenticated request with automatic JSON parsing | | requestRaw(method, path, body?) | string, string, unknown | Promise<Response> | Authenticated request returning raw Response (for accessing headers) |

Cache & Polling

| Method | Parameters | Returns | Description | |--------|-----------|---------|-------------| | poll(params, options) | GetEntitiesParams \| undefined, PollOptions<T> | PollHandle | ETag-based polling — fires onData only when the data actually changes. options is required (onData must be specified to receive data) | | clearCache() | — | void | Drop all cached responses |

PollOptions<T>

| Property | Type | Description | |----------|------|-------------| | interval | number | Polling interval in milliseconds (default: 5000) | | onData | (data: T) => void | Called whenever the server returns fresh data (200) | | onNoChange | () => void | Called when the server returns 304 Not Modified | | onError | (err: Error) => void | Called on any fetch/parse failure |

PollHandle

| Method | Parameters | Returns | Description | |--------|-----------|---------|-------------| | stop() | — | void | Stop the polling timer |

CacheEvent

| Property | Type | Description | |----------|------|-------------| | key | string | Cache key (METHOD:path) | | path | string | Request path |

WebSocket

| Method | Parameters | Returns | Description | |--------|-----------|---------|-------------| | connect() | — | Promise<void> | Establish WebSocket connection | | reconnect() | — | Promise<void> | Force-reconnect (preserves subscriptions) | | disconnect() | — | void | Disconnect WebSocket | | isConnected() | — | boolean | Check if WebSocket is currently open | | subscribe(options?) | SubscribeOptions | void | Subscribe to entity events |

SubscribeOptions

| Property | Type | Description | |----------|------|-------------| | entityTypes | string[] | Entity types to subscribe to | | idPattern | string | Entity ID regex pattern |

Event Handling

| Method | Parameters | Returns | Description | |--------|-----------|---------|-------------| | on(event, listener) | string, Function | this | Register an event listener | | off(event, listener) | string, Function | this | Remove an event listener |

Error Handling

All async methods throw typed errors extending GeonicDBError. Use instanceof to distinguish error types.

import { GeonicDBError, AuthenticationError, NotFoundError } from '@geolonia/geonicdb-sdk';

try {
  await db.getEntity('urn:ngsi-ld:Room:404');
} catch (err) {
  if (err instanceof AuthenticationError) {
    // 401 — re-login needed
  } else if (err instanceof NotFoundError) {
    // 404 — entity not found
  } else if (err instanceof GeonicDBError) {
    console.error(err.message, err.statusCode);
  } else {
    console.error(err);
  }
}

| Class | Status | Description | |-------|--------|-------------| | GeonicDBError | — | Base class (statusCode property) | | AuthenticationError | 401 | Invalid credentials or expired token | | AuthorizationError | 403 | Insufficient permissions | | NotFoundError | 404 | Resource not found | | ConflictError | 409 | Entity already exists | | ValidationError | 422 | Bad request payload | | RateLimitError | 429 | Rate limited (retryAfter property) | | NetworkError | — | Fetch failure, DNS error, timeout |

React Hooks (@geolonia/geonicdb-sdk/react)

Optional React integration, shipped as a separate subpath so the core SDK stays framework-agnostic. Requires React 18 or 19 (declared as an optional peer dependency — core consumers are unaffected).

import GeonicDB from '@geolonia/geonicdb-sdk';
import { useLdEntities, useTemporalData, GeonicDbMap } from '@geolonia/geonicdb-sdk/react';

const db = new GeonicDB({ baseUrl, tenant });

function Rooms() {
  const { entities, loading, error, refetch } = useLdEntities(db, { type: 'Room', limit: 100 });
  if (loading) return <p>Loading…</p>;
  if (error) return <p>{error.message}</p>;
  return <GeonicDbMap entities={entities} apiKey="YOUR-GEOLONIA-KEY" />;
}

| Export | Type | Description | |--------|------|-------------| | useLdEntities(client, options?) | hook | NGSI-LD エンティティ取得。{ entities, loading, error, refetch } を返す。options: type / limit / attrs / q / scopeQ | | useTemporalData(client, options) | hook | Temporal API 取得。{ data, loading, error, refetch } を返す。options.id 指定時は単一エンティティ、options.enabled === false で取得スキップ | | GeonicDbMap | component | Geolonia 地図にエンティティをマーカー描画。@geolonia/embed(optional peer)を動的 import |

両 hook は in-flight リクエストを世代管理しており、options 変更やアンマウント時に古いレスポンスが新しい結果を上書きしない。GeonicDbMap のポップアップは entity.id / entity.type を HTML エスケープして描画する。

NGSIv2 Client (@geolonia/geonicdb-sdk/ngsi-v2)

NGSIv2 (/v2/entities) を叩く軽量クライアント。NGSI-LD を使うコア SDK とは独立した サブパスで、NGSIv2 のみを使う利用者向け。

import { createNgsiV2Client } from '@geolonia/geonicdb-sdk/ngsi-v2';

const client = createNgsiV2Client({ baseUrl: 'https://geonicdb.example.com', apiKey: 'KEY', service: 'mytenant' });
const rooms = await client.getEntities({ type: 'Room', limit: 20 });

| Method | Description | |--------|-------------| | createNgsiV2Client(config) | NgsiV2Client を生成。config: baseUrl(必須)/ apiKey / service | | getEntities(options?) | エンティティ一覧(type / q / limit / offset / attrs / options / georel / geometry / coords) | | getEntity(id, options?) | 単一エンティティ取得 | | createEntity(entity) | エンティティ作成 | | updateEntityAttributes(id, attrs) | 属性の部分更新 | | deleteEntity(id) | エンティティ削除 |

Geospatial query (georel / geometry / coords)

NGSIv2 の地理空間クエリに対応。georelgeometrycoords は 3 つ揃えて指定する(部分指定は Orion 側で 400)。

| Option | Type | Description | |--------|------|-------------| | georel | string | 地理的関係。複合形も可(例: near;maxDistance:1000, coveredBy) | | geometry | 'point' \| 'line' \| 'polygon' \| 'box' | ジオメトリ種別 | | coords | string | WGS84 座標。複数点はセミコロン区切り(例: 35.6,139.7) |

// 指定座標から半径 1km 以内
const nearby = await client.getEntities({
  type: 'PublicFacility',
  georel: 'near;maxDistance:1000',
  geometry: 'point',
  coords: '35.6,139.7',
});

// ポリゴン内に含まれるエンティティ
const inArea = await client.getEntities({
  georel: 'coveredBy',
  geometry: 'polygon',
  coords: '0,0;1,0;1,1;0,1;0,0',
});

License

MIT