GeonicDB の製品紹介スライドデッキ。全画面プレゼン(Google スライド風)に、実際の GeonicDB(ステージング)へ接続して動くインタラクティブなライブデモを内蔵。TypeScript + Vite で構成し、公式 SDK @geolonia/geonicdb-sdk の使い方が一目で追える「お手本アプリ」を兼ねています。
- Vite + TypeScript(strict)。地図は Geolonia Maps(CDN)、データは公式 SDK(npm)。
src/main.tsがエントリ。src/deck/(スライドエンジン)、src/demos/(各ライブデモ)、src/lib/(SDK クライアント・型付き設定・DOM/イベントの小物)。- SDK 初期化は
src/lib/client.tsのcreateClient()に集約。
npm install # 初回のみ
cp .env.example .env # API キーを設定(下記)
npm run dev # → http://localhost:8745| コマンド | 内容 |
|---|---|
npm run dev |
開発サーバー(ポート 8745 固定) |
npm run build |
型チェック(tsc --noEmit)+ 本番ビルド(dist/) |
npm run preview |
ビルド成果物の確認サーバー(8745) |
npm run typecheck |
型チェックのみ |
ライブデモ用の API キーは origin 制限付きのため、
http://localhost:8745またはhttps://geolonia.github.ioから開く必要があります(ポート 8745 固定)。
デッキ全体で 1 つの統合 API キー(統合ポリシー geonicdb-livedeck-deck)を共用します。
| 変数 | 用途 |
|---|---|
VITE_GEONICDB_KEY |
全デモ共通の統合キー(統合ポリシー geonicdb-livedeck-deck。GET/POST/WS を型別に許可・origin 制限+DPoP) |
VITE_GEOLONIA_API_KEY |
Geolonia Maps(任意。未設定なら YOUR-API-KEY) |
非秘密の設定(接続先・テナント・各デモのエンティティ)は src/lib/config.ts に直書きしています。
direnv 利用者は
.envの代わりに.envrcにexport VITE_GEONICDB_KEY=…を書いてもよい。.worktrees/配下のワークツリーで作業する場合は、source_env ../..だけの.envrcを置けば親の環境変数を継承できる(いずれも gitignore 済み)。
本番デプロイ(GitHub Pages)ではキーをリポジトリシークレットからビルド時に注入します(.github/workflows/deploy.yml)。必要なシークレット:
| シークレット名 | 対応する env | 必須 |
|---|---|---|
GEONICDB_KEY |
VITE_GEONICDB_KEY |
✅(全デモ共通の統合キー) |
VITE_GEOLONIA_API_KEY |
VITE_GEOLONIA_API_KEY |
任意(未設定なら YOUR-API-KEY にフォールバック。*.github.io で動作) |
いずれかの GeonicDB キーが未設定だと、そのデモが
AuthenticationError(空キー)で動かない。新しいライブデモを足したら deploy.yml の env とこの表も更新すること。
- → / Space / PageDown: 次へ ← / PageUp: 戻る Home / End: 先頭・末尾
- F: 全画面 Esc: 解除
- ページ移動は矢印ボタンとキーボードのみ(スライド本体クリック・スワイプでは移動しません)
本編(タイトル → 会社紹介 → Context Broker → 標準準拠 → AI Native → 競合比較 → 各ライブデモ → ユースケース)と、Appendix(全機能カタログ・管理機能・セキュリティ・信頼性・クエリパラメータ・用語集)+クロージングで構成。スライド順序は index.html の <section class="slide"> の並びで決まり、ライブデモは .slide--dual / .slide--map / .slide--tmp / .slide--fb / .slide--ai / .slide--shelter / .slide--collab / .slide--msg のクラスで識別する(番号がずれても各デモが自分のスライドを自動追従)。
いずれも https://geonicdb.geolonia.com(テナント miya)へ DPoP 認証で接続します。
- いずれも読み取りのみ。
AedLocationの地図表示+ NGSI-LDgeorel=near検索、同じ内容の環境センサーを NGSIv2 と NGSI-LD の両形式で取得(env-sensor-001/urn:ngsi-ld:EnvironmentSensor:001)してプロトコル差を対比、WeatherObservedの Temporal API 履歴など。デモデータは特定地域を想起させない中立的な内容にしている。 - 認可: 統合キー
geonicdb-livedeck-deck(GET + WS。origin 制限・DPoP 必須)。
- フォーム送信でカスタムデータモデル
Feedbackの NGSI-LD エンティティを作成 → WebSocket で受信し件数を集計。送信前はデフォルトで最新の回答エンティティを表示。 - 右はタブ切替: 「NGSI-LD エンティティ」(注釈付き JSON)、「カスタムデータモデル」(
GET /custom-data-models/Feedbackの実データ)、「集計結果」(関心・所属・地域の 3 つの円グラフ。WebSocket でリアルタイム更新)。 - 各項目を NGSI-LD の構文要素にマッピング: 所属/期待度 → Property(
observedAtメタデータ)、関心/地域 → Relationship(urn:ngsi-ld:UseCase:*/urn:ngsi-ld:AdministrativeArea:*)、会場位置 → GeoProperty。 - 認可: 統合キー
geonicdb-livedeck-deck(GET|WS +Feedbackへの POST、/custom-data-models/Feedbackの GET)。
- 高松市の指定避難所(
EvacuationArea)を地図に表示し、Temporal API で固定期間(2026-06-26 の24時間)の受入状況を取得 → 混雑度で色分け。タイムスライダー/再生で時間変化を再生、避難所クリックで受入率の推移をポップアップ表示。 - 認可: 統合キー
geonicdb-livedeck-deck(EvacuationAreaの GET / temporal GET)。 - データ: 位置・収容人数は高松市オープンデータ(CC BY 4.0)。混雑度は Temporal API のデモ用合成データ(実受入実績ではない旨を画面に明記)。詳細は「セットアップ §4」。
- カスタムデータモデル
Feedback(role・expectation・interestedIn・region・location)でサーバ側バリデーション。
- 地図にポイント/ライン/ポリゴンを描くと、地物が NGSI-LD エンティティ(
type=geonicdb-livedeck-MapFeature、locationは GeoProperty)として作成され、WebSocket で全クライアントの地図にリアルタイム反映される(=共同編集)。参加者ごとに色を割り当て。表示は直近1週間に作成された地物のみ。 - 認可: 統合キー
geonicdb-livedeck-deck(geonicdb-livedeck-MapFeatureの GET|POST + WS)。 - 地図の初期表示は広島県尾道市周辺(
src/lib/config.tsのdemos.collab)。
- 「ランダム投稿」で 名前+メッセージ(100字まで)を NGSI-LD エンティティ(
type=geonicdb-livedeck-Message)として作成 → WebSocket で全員に配信。登壇中のキーボード入力を避けるため、名前・本文はダミーから無作為に選ぶ。 - サーバ側の ReactiveCore Rules(
geonicdb-livedeck-message-log)が作成を検知して ログ(type=geonicdb-livedeck-MessageLog)を自動生成。メッセージ / ログをタブで切替。 - 認可: 統合キー
geonicdb-livedeck-deck(Message の GET|POST、MessageLog の GET、WS を統合ポリシーに含む)。
ライブデモが使う XACML ポリシー・API キー・デモ用データは geonic CLI で作成します。
前提: 対象テナント(例 miya)の tenant_admin として認証済み(geonic auth login → geonic profile use <profile>)。以下は -s <tenant> でテナントを明示する例です。
デッキ全体で 1 つのキー geonicdb-livedeck-deck を使う(テナントの API キー上限対策)。ポリシー geonicdb-livedeck-deck に、各デモが必要とする型別 GET/POST・WS・パスをまとめて許可する。新デモを足すときは新キーを作らず、この 1 ポリシーに権限を追記する(geonic me policies update geonicdb-livedeck-deck @patch.json)。
このキーは公開バンドルに埋め込まれるため、最小権限を保つ(破壊系 PUT/PATCH/DELETE は一切許可しない・append-only)。読み取りも実使用分だけに絞る:
allow-read-types(型別 GET)は型別クエリで読む型のみ列挙する。EnvironmentSensor(dual: by-id 取得)とWeatherObserved(temporal 取得)は型別 GET しないため含めず、allow-get-pathsの個別パスだけで許可する。allow-get-pathsの/custom-data-models/**は実際に読む/custom-data-models/Feedbackに限定する。
# 統合ポリシー(全デモの型別 GET/POST + WS + 必要パスを 1 つに)
cat > deck-policy.json <<'JSON'
{
"policyId": "geonicdb-livedeck-deck",
"description": "geonicdb-livedeck: 全デモ統合キー用ポリシー(WS + 各デモの型別 GET/POST + 必要パス)",
"target": { "resources": [
{"attributeId":"path","matchValue":"/ngsi-ld/**","matchFunction":"glob"},
{"attributeId":"path","matchValue":"/v2/**","matchFunction":"glob"},
{"attributeId":"path","matchValue":"/custom-data-models/**","matchFunction":"glob"}
]},
"ruleCombiningAlgorithm": "first-applicable",
"rules": [
{"ruleId":"allow-stream","effect":"Permit","target":{"actions":[{"attributeId":"method","matchValue":"WS"}]}},
{"ruleId":"allow-read-types","effect":"Permit","target":{
"resources":[{"attributeId":"entityType","matchValue":"^(AedLocation|EvacuationArea|Feedback|geonicdb-livedeck-MapFeature|geonicdb-livedeck-Message|geonicdb-livedeck-MessageLog)$","matchFunction":"string-regexp"}],
"actions":[{"attributeId":"method","matchValue":"GET"}]}},
{"ruleId":"allow-write-types","effect":"Permit","target":{
"resources":[{"attributeId":"entityType","matchValue":"^(Feedback|geonicdb-livedeck-MapFeature|geonicdb-livedeck-Message)$","matchFunction":"string-regexp"}],
"actions":[{"attributeId":"method","matchValue":"POST"}]}},
{"ruleId":"allow-get-paths","effect":"Permit","target":{
"resources":[
{"attributeId":"path","matchValue":"/v2/entities","matchFunction":"glob"},
{"attributeId":"path","matchValue":"/v2/entities/env-sensor-001","matchFunction":"glob"},
{"attributeId":"path","matchValue":"/ngsi-ld/v1/entities/*AedLocation*","matchFunction":"glob"},
{"attributeId":"path","matchValue":"/ngsi-ld/v1/entities/*EnvironmentSensor*","matchFunction":"glob"},
{"attributeId":"path","matchValue":"/ngsi-ld/v1/entities/*EvacuationArea*","matchFunction":"glob"},
{"attributeId":"path","matchValue":"/ngsi-ld/v1/temporal/entities","matchFunction":"glob"},
{"attributeId":"path","matchValue":"/ngsi-ld/v1/temporal/entities/*WeatherObserved*","matchFunction":"glob"},
{"attributeId":"path","matchValue":"/ngsi-ld/v1/temporal/entities/*EvacuationArea*","matchFunction":"glob"},
{"attributeId":"path","matchValue":"/custom-data-models/Feedback","matchFunction":"glob"}
],
"actions":[{"attributeId":"method","matchValue":"GET"}]}},
{"ruleId":"deny-others","effect":"Deny"}
]
}
JSON
geonic -s miya me policies create @deck-policy.json
# key(出力された gdb_… を .env の VITE_GEONICDB_KEY / CI シークレット GEONICDB_KEY へ)
geonic -s miya me api-keys create \
--name geonicdb-livedeck-deck \
--policy geonicdb-livedeck-deck \
--origins "http://localhost:8745,https://geolonia.github.io" \
--dpop-required以下 §1〜§6 は各デモが必要とする 権限の内訳(統合ポリシーに含める型・アクション)とデモ用データの作成手順。 かつては per-demo にキーを分けていたが、API キー上限のため 1 キーに統合した(#37)。
読み取り系デモに個別のポリシー・キーは作らない。§0 の統合ポリシーの以下のルールでカバーする:
allow-read-types— 型別クエリ GET(?type=…):AedLocation/EvacuationAreaallow-get-paths— ID 指定・temporal・NGSIv2 の個別パス GET:/ngsi-ld/v1/entities/*EnvironmentSensor*(標準API デモの by-id 取得)/ngsi-ld/v1/temporal/entities(避難所デモの型指定一括取得)・*WeatherObserved*・*EvacuationArea*/v2/entities・/v2/entities/env-sensor-001(NGSIv2 側)
ID 指定 GET は entityType が認可に乗らないためパスで許可する。
EnvironmentSensorとWeatherObservedは 型別クエリ GET をしないのでallow-read-typesには含めない(§0 の最小権限の方針)。
権限は §0 の統合ポリシーでカバーする: WS(allow-stream)+ Feedback の GET/POST(allow-read-types / allow-write-types)+
「カスタムデータモデル」タブが読む /custom-data-models/Feedback の GET(allow-get-paths)。
# カスタムデータモデル Feedback(関心・地域は Relationship、位置は GeoProperty)
geonic -s miya custom-data-models create '{
"type":"Feedback","domain":"Survey",
"propertyDetails":{
"role":{"ngsiType":"Property","valueType":"string","required":true},
"expectation":{"ngsiType":"Property","valueType":"number","required":true},
"interestedIn":{"ngsiType":"Relationship","required":true},
"region":{"ngsiType":"Relationship","required":true},
"location":{"ngsiType":"GeoProperty","required":true}
}
}'# 標準APIデモ(dual)用: 同じ内容の環境センサーを NGSI-LD 側にも用意(NGSIv2 側は下記の注参照)
geonic -s miya entities create '{
"@context":"https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld",
"id":"urn:ngsi-ld:EnvironmentSensor:001","type":"EnvironmentSensor",
"name":{"type":"Property","value":"サンプル環境センサー 001"},
"temperature":{"type":"Property","value":24.3,"unitCode":"CEL"},
"relativeHumidity":{"type":"Property","value":58},
"co2":{"type":"Property","value":612},
"location":{"type":"GeoProperty","value":{"type":"Point","coordinates":[139.767,35.681]}}
}'
# 地図デモ用 AedLocation(NGSI-LD)
geonic -s miya entities create '{"id":"urn:ngsi-ld:AedLocation:1","type":"AedLocation","name":{"type":"Property","value":"…"},"location":{"type":"GeoProperty","value":{"type":"Point","coordinates":[134.045,34.341]}}}'
# 時系列デモ用 WeatherObserved(observedAt 付きの配列)
geonic -s miya temporal entities create @weather-temporal.json避難所の位置・収容人数は高松市オープンデータ「指定緊急避難場所・指定避難所」
(https://github.com/takamatsu-city/opendata data/evacuation_space、CC BY 4.0)から
中心部の約 30 件を EvacuationArea(municipalityCode: "372013")として投入。
混雑度(occupancy)は Temporal API のデモ用合成データ(固定期間 2026-06-26 の 24 時間・1 時間刻み。
相対期間だと古くなるため固定)で、実際の受入実績ではない。
# 通常エンティティ(位置・収容人数・出典/ライセンス)
geonic -s miya entities create @shelter-001.json
# 混雑度の時系列(occupancy を observedAt 付き配列で。固定期間 2026-06-26T00:00Z〜2026-06-27T00:00Z)
geonic -s miya temporal entities create @shelter-001-temporal.jsonこのデモも統合キー(§0)で動く。必要な権限(
EvacuationAreaの GET と temporal GET)は 統合ポリシーのallow-read-types/allow-get-pathsに含まれている(§1 参照)。 AED マップと同じ高松市域のオープンデータなので、出典・ライセンスを明示すれば地域名の使用は可 (AED デモと同じ扱い)。
地図に描いた地物(ポイント/ライン/ポリゴン)を geonicdb-livedeck-MapFeature として作成し、WebSocket で全員に配信する。
権限(WS + geonicdb-livedeck-MapFeature の GET/POST)は §0 の統合ポリシーの
allow-stream / allow-read-types / allow-write-types に含まれている。個別ポリシー・キーは作らない。
地物は自由形状(GeoProperty に Point / LineString / Polygon)なのでカスタムデータモデルは使わない。 表示は直近1週間に作成された地物のみ(クライアント側で
drawnAt/id 埋め込み時刻でフィルタ)。
geonicdb-livedeck-Message の作成を ReactiveCore Rules で検知し、createEntity アクションで
geonicdb-livedeck-MessageLog を自動生成する。
geonic -s miya rules create '{
"ruleId": "geonicdb-livedeck-message-log",
"name": "geonicdb-livedeck: メッセージ作成時にログを生成",
"conditions": [
{"type":"eventType","eventTypes":["create"]},
{"type":"entityType","entityTypes":["geonicdb-livedeck-Message"]}
],
"actions": [{
"type":"createEntity",
"entityId":"urn:ngsi-ld:geonicdb-livedeck-MessageLog:${entity.id}",
"entityType":"geonicdb-livedeck-MessageLog",
"attributes":{"action":"message.created","author":"${attribute.name.value}",
"target":"${entity.id}","summary":"${attribute.name.value} さんがメッセージを投稿しました"}
}]
}'権限(
geonicdb-livedeck-Messageの GET|POST、geonicdb-livedeck-MessageLogの GET、WS)は §0 の統合ポリシーgeonicdb-livedeck-deckに含める。クライアントは共通のcreateClient()(統合キー)で接続。 メッセージ本文は 100 字まで(クライアント側で制限。登壇時はダミーからランダム投稿)。
標準APIデモ(dual)は「同じデータを両プロトコルで見せる」ことでプロトコル差を強調する。GeonicDB は NGSIv2 と NGSI-LD を別空間で保持するため、同内容を 2 件用意する: NGSI-LD 側は上記
urn:ngsi-ld:EnvironmentSensor:001、NGSIv2 側env-sensor-001は NGSIv2 API(PUT /v2/entities/env-sensor-001/attrs、ヘッダーFiware-Service: miya)で同じ内容にする。デモデータは実在の顧客データと誤認させないよう、特定の地域名を名前・URL・scope 等に含めない中立的な内容にすること。
| パス | 役割 |
|---|---|
index.html |
デッキ本体(全スライドのマークアップ・大型インライン SVG) |
src/main.ts |
エントリ。各デモを登録 → デッキ起動 |
src/deck/slides.ts |
スライドエンジン(ナビゲーション・背景同期・スケーリング) |
src/demos/dual.ts |
標準API(NGSIv2 / NGSI-LD 二面取得)デモ |
src/demos/map.ts |
ジオクエリの地図デモ(Geolonia Maps + near 検索) |
src/demos/temporal.ts |
時系列(Temporal API)デモ |
src/demos/shelter.ts |
避難所の混雑(地図 + Temporal API・自治体ユースケース)デモ |
src/demos/collab.ts |
共同編集 GIS(作図 + WebSocket・民間ユースケース)デモ |
src/demos/messaging.ts |
メッセージング + ReactiveCore Rules ログ(民間ユースケース)デモ |
src/demos/feedback.ts |
NGSI-LD フィードバック(カスタムデータモデル + WS)デモ |
src/demos/aiNative.ts |
AI ネイティブ(スクリプト化アニメ・ライブ API なし) |
src/lib/client.ts |
GeonicDB SDK クライアントの生成を集約 |
src/lib/config.ts |
型付き設定(非秘密値+ env からのキー) |
src/lib/dom.ts / slidechange.ts |
型安全な DOM ヘルパ・型付き slidechange イベント |
src/styles/styles.css |
テーマ・レイアウト・アニメーション |
public/ |
sw.js・manifest.webmanifest・assets/(ロゴ・地図スタイル・スプライト・画像)。ビルドで dist/ 直下へコピー |
vite.config.ts / tsconfig.json / .env.example |
ビルド・型・環境変数の設定 |