2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PortolanでAI-Readyな地理空間情報カタログを作ってみた

2
Posted at

image.png

日本には、意外とたくさんのオープンデータがありますよね。G空間情報センターe-Stat国土交通データプラットフォーム、各自治体の公開サイトなど、GISで使えるデータの入手先もいろいろあります。

ただ、いくつものサイトを使い分けようとすると、ファイル形式や説明項目、利用条件をサイトごとに確認することになります。検索やダウンロードの方法もそれぞれ違いますし、データの中身を把握するまでにも時間がかかるので、分析に入る前の段階でひと手間かかりますよね。

GISデータはどれも「位置情報に関わる」データなのですから、地図上の場所や、分野・施設名といった言葉から横断的に探して、そのまま分析に使えたらうれしいところです。

ところが、公開する側から見ると、これは簡単ではありません。メタデータの項目はデータの種類や配信元によって異なるので、横断的に検索できるようにするには、それらを取り込むデータベースと検索用APIを構築し、運用し続ける必要があります。

ファイル形式や説明の書き方もまちまちなので、ダウンロードしてすぐ分析に使える状態にするには、変換や説明の整備も要ります。

さらに近年はAIエージェントの活用も重視されていて、列の意味や座標系、利用条件までAIが読み取れる「AI-Ready」な整備も求められています。配信する組織の体制や予算を考えると、ここまでそろえるのは大変です。

実は、こうした地理データの説明を共通化する仕様として、STACがあります。STACには、JSONファイルを静的に配信するStaticなSTACと、サーバーを用意するDynamicなSTACの2種類があります(STACの配信方法)。

Staticなら運用は軽く済みますが、柔軟な検索をしたければ、やはりDynamicな方、つまりSTAC APIの実装とデータベースの構築が必要になります。

そこで今回試してみたのが、最近登場した静的なファイルのまま、検索・表示・分析しやすい形にそろえるPortolanです。

STACを土台に、分析用のGeoParquet、地図表示用のPMTiles、AI向けの AGENTS.md などを組み合わせる考え方です。

もちろん、静的に配信するにしてもメタデータの整備には一定の手間がかかります。それでも、検索サーバーを立てて運用し続けるよりは軽く済むでしょう。

今回はダミーのGeoJSONを2つ用意して、西と東の2つのCollectionからなるカタログを作ります。公式ツールのPortolan CLIでの変換、検査ツールのrashidでの検証、Portolan Browserでの閲覧、そして説明文を手がかりにした集計まで、ひと通り試してみます!

Portolanって何?

Portolanは、STACを土台に、地理空間データの形式・説明文・配信方法をそろえる仕様とツール群です(公式仕様)。

カタログとデータを静的ファイルとして配信し、利用者が対応ツールで直接読みます。検索専用のAPIを必須としない構成です。

今回の例では、適当な矩形を公園に見立てて2つの公園データを配信する、という想定でカタログを整理してみました!

catalog.json が入口、西と東の collection.json がそれぞれの公園データの説明になります。公園の形状や属性は、Collectionが参照するGeoParquetに入ります。

STAC自体はある程度知っている前提で話を進めていきます!
STACについて詳しくは以下などからどうぞ!
Python製STAC APIの「stac-fastapi」で衛星データ管理のためのAPIを実装してみる!!!

ファイル 役割
catalog.json Collectionへ案内する入口
collection.json データの説明、利用条件、範囲、列の定義、実データへのリンク
GeoParquet 公園の形状と属性を収めた実データ
PMTiles / styles/default.json 地図表示用のタイルと描画設定
README.md / AGENTS.md 人間とAIに意味・使い方を伝える文書

ベクトルデータにはGeoParquetが必須で、地図表示用のPMTilesは推奨です。
データ形式の仕様

今回はベクトルデータで検証していきますが、Portolanの仕様としてはラスターデータの登録もできます。そもそもSTACは衛星画像のカタログ化から始まった仕様なので、ラスターはむしろ本来の得意分野です。

STACと比較して何が良いの?

Portolanの利点は、STACでデータを配る際の作り方を具体化している点です。

STACには、前述の通りメタデータをJSONファイルとして置く静的カタログと、要求に応じて返す動的カタログがあります。動的カタログはSTAC APIを提供することが多く、場所や日時などでデータを検索できます(STAC API)。データの保管先にはデータベースがよく選定されますが、STAC APIの仕様が特定のデータベースを要求するわけではありません。

構成 配信するもの 検索の考え方
静的STAC 相互にリンクしたJSONと実データ 利用側がリンクをたどる。検索には利用ツールや別の索引が必要
STAC API 条件に応じた検索結果 サーバー側が条件を処理して結果を返す
Portolan 静的STACと、直接読みやすいデータ・説明文 BrowserやDuckDBなどの利用側で、ファイルを読み、絞り込む

例えばBrowserは、Collectionの説明を読み込んで一覧にし、名前や説明文で絞り込めます。各地域のGeoParquetをDuckDBで読めば、公園の分類や樹木数もそのまま集計できます。PMTilesは地図を表示するときの読み込みを助けます。

通常のSTACでも、これらの形式や説明文は用意できます。ただ、Portolanでは、その組み合わせと検査方法が共通化されています。

ただし、ファイルを置けば自由な日本語検索まで自動で完成する、というものではありません。検索対象となる項目の整備と、それを処理する閲覧・分析ツールは必要です。公開側の検索サービスを軽くできる可能性がある、と捉えるのがよさそうです。

人間もAIも見やすいカタログ

特に面白いのが、同じデータに3つの入り口を用意するという考え方です。

入り口 主な読み手 内容
collection.json ソフトウェア データへのリンク、形式、範囲、列の定義
README.md 人間 データの意味、出典、ライセンス、利用上の注意
AGENTS.md AIエージェント ファイルの使い分け、確認済みの集計例、座標系の注意、期待する結果

AGENTS.mdは、データの公開者が、使い方の知識をエージェントに渡す場所です。各Catalog・CollectionにREADME.mdとAGENTS.mdを置くことが求められています。
作成時の要件

今回なら、「西に2件、東に1件で合計3件」「分析には各Collectionの .parquet を使う」「緯度・経度の度をメートルと解釈しない」「樹木数の合計は60」と記載できます。単にファイルの場所を教えるだけでなく、どれを読み、結果をどう確かめるかも伝わります。

READMEで意味を理解し、AGENTS.mdで扱い方を確認し、JSONから実データにたどり着く。この情報が同じCollectionにまとまります。

便利なエコシステム

ツール 用途
Portolan CLI データの取り込み、変換、カタログ生成、検査
rashid Portolanの仕様に沿っているかを検査するツール。CLIからも呼び出される
Portolan Browser カタログ、地図、属性表の閲覧
portolan-skills AIエージェント向けのカタログ作成・利用手順

CLIには、次の機能があります。表は2026年9月時点のCLIの主な機能で、後ほど一部の機能を使って実際にカタログを作成していきます。

できること 具体例 主なコマンド
カタログを作る カタログを初期化し、データを登録する initadd
データ形式を変換する GeoJSON→GeoParquet、GeoTIFF→COG add
外部から取り込む ArcGISやWFSなどからデータを取得する extract
表示用のファイルを作る PMTiles、縮小画像を生成する add --pmtiles --thumbnails
説明文を作る STACとmetadata.yamlからREADMEを生成する readme
検査・修復する rashidで検査し、対応できる不備を修正する checkcheck --fix
公開先とやり取りする S3などへ転送し、更新を反映・取得する pushpullsync
更新履歴を管理する データの版や公開先との差を確認する versionstatus

Portolan CLIで add --pmtiles を使う場合は、Tippecanoeが必要です。
(Tippecanoeの導入方法は公式READMEのInstallationに記載されています。)

CLIを使って、サンプルデータでカタログを作ってみる

環境を用意する

Pythonのuvを用意し、空の作業フォルダで環境を作ります。

uv --no-config venv --python 3.13
uv --no-config pip install \
  'portolan-cli[thumbnails]==0.8.0' 'rashid==0.1.8'
source .venv/bin/activate

2つのGeoJSONは適当に生成する

カタログを初期化し、西側・東側のフォルダを作ります。フォルダ名がそのままCollectionのIDになります。

portolan init catalog --id parks-demo --title 'Parks Demo' \
  --description 'Three fictional parks for learning Portolan.' \
  --auto --license CC0-1.0
mkdir -p catalog/west/.portolan catalog/east/.portolan

catalog/west/west.geojson に、西側の公園2件を保存します。東京付近に置いた架空の公園です。

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {"name": "Park A", "category": "garden", "trees": 10},
      "geometry": {"type": "Polygon", "coordinates": [[[139.735,35.665],[139.739,35.665],[139.739,35.669],[139.735,35.669],[139.735,35.665]]]}
    },
    {
      "type": "Feature",
      "properties": {"name": "Park B", "category": "garden", "trees": 20},
      "geometry": {"type": "Polygon", "coordinates": [[[139.743,35.665],[139.747,35.665],[139.747,35.669],[139.743,35.669],[139.743,35.665]]]}
    }
  ]
}

catalog/east/east.geojson には、東側の公園1件を保存します。

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {"name": "Park C", "category": "playground", "trees": 30},
      "geometry": {"type": "Polygon", "coordinates": [[[139.751,35.665],[139.755,35.665],[139.755,35.669],[139.751,35.669],[139.751,35.665]]]}
    }
  ]
}

合計3地物で、属性情報には架空の樹木数などを入れています。

データの説明を記入する

catalog/.portolan/metadata.yaml を次の内容にします。READMEの生成元であり、providersやlicenseなど仕様が要求する項目をcollection.jsonへ書き込むための、人が書く入力です。

title: Parks Demo
description: Three fictional parks for learning Portolan.
license: CC0-1.0
providers:
  - name: Demo Author
    roles: [producer, licensor, host]
    url: http://localhost:8768/README.md
contact:
  name: Demo Author
  url: http://localhost:8768/README.md
processing_notes: 'Source: hand-written GeoJSON. All parks and tree counts are fictional.'

Collectionごとの名前と説明は、それぞれのフォルダに置きます。ほかの項目はルートから引き継がれます。URLは後で起動するローカル配信先です。

catalog/west/.portolan/metadata.yaml:

title: Parks (west)
description: Two fictional parks in the west area, Park A and Park B.

catalog/east/.portolan/metadata.yaml:

title: Parks (east)
description: One fictional park in the east area, Park C.

2つのCollectionを作る

西側・東側のGeoJSONを、それぞれ登録します。

portolan add catalog/west/west.geojson --portolan-dir catalog --thumbnails
portolan add catalog/east/east.geojson --portolan-dir catalog --thumbnails

GeoParquetへの変換、Collection JSON、縮小画像、READMEとAGENTS.mdの雛形の生成をCLIが行います。変換元のGeoJSONは、残すと検査で未登録ファイルとして指摘されるため削除します。

rm catalog/west/west.geojson catalog/east/east.geojson

地図表示用のPMTilesを作る

PMTilesの座標はタイルの格子に合わせて丸められます。公園を拡大して表示できるよう、今回は最大ズームを14に設定してから生成します。

cd catalog
portolan config set pmtiles.max_zoom 14
portolan add west/west.parquet east/east.parquet --pmtiles
cd ..

西側の west.pmtiles と東側の east.pmtiles が生成されます。各Collectionの styles/default.json もCLIが作り、collection.jsonにPMTilesへのリンクとスタイルへの参照を登録します。

GeoParquetは分析用、PMTilesは地図表示用です。PMTilesでは表示のために形状が簡略化されるので、座標を使う分析にはGeoParquetを使います。

生成物は次の構成になります。管理用ファイルは省略しています。

catalog/
├── catalog.json
├── README.md
├── AGENTS.md
├── west/
│   ├── collection.json
│   ├── README.md
│   ├── AGENTS.md
│   ├── west.parquet
│   ├── west.pmtiles
│   ├── west.thumb.jpg
│   └── styles/
│       └── default.json
└── east/
    ├── collection.json
    ├── README.md
    ├── AGENTS.md
    ├── east.parquet
    ├── east.pmtiles
    ├── east.thumb.jpg
    └── styles/
        └── default.json

READMEとAGENTS.mdを整える

README.mdは add が作り、readme がSTACとmetadata.yamlから再生成します。手で編集せず、直したいことはmetadata.yamlに書いて再生成します。

cd catalog
portolan readme
cd ..

AGENTS.mdは add が見出しだけの雛形を作るので、本文は人が書きます。今回の west/AGENTS.md には、例えば次の内容を入れました。

- 公園は全体で3件、樹木数の合計は60です。
- 分析には`collection.json``assets`が指す`.parquet`を使います。
- 地図表示には `.pmtiles``styles/default.json` を使います。表示用の形状は簡略化されるので、座標を使う分析にはGeoParquetを使ってください。
- 座標系はEPSG:4326です。
- 公園は架空のデータで、ライセンスはCC0-1.0です。

ルートのAGENTS.mdからは、2つのCollectionの文書へ案内します。カタログ全体と各Collectionの両方に、読み手のための入口を用意します。

rashidで検査する

rashidを直接実行し、JSONの構造だけでなく、参照するデータの実体も検査します。

rashid check catalog --schema --data --json

結果は、エラー0件・警告0件・終了コード0でした。

{
  "passed": true,
  "files_checked": 3,
  "error_count": 0,
  "warning_count": 0
}

上はrashidの出力の抜粋です。検査した3ファイルは、Catalogと2つのCollectionのJSONです。--data では、参照するデータのサイズ、内容の一致、形式なども確認します。

Portolan CLIから検査したい場合は、代わりに portolan check を使えます。カタログの検査には内部でrashidを利用しています。
公式リファレンス

portolan check catalog --strict --schema --json

--strict を付けると、警告が残っている場合も失敗として扱います。

Portolan Browserでカタログを見てみる

ローカルで配信する

作業フォルダで適当な静的HTTPサーバーを起動します。

http-server catalog -a 127.0.0.1 -p 8768 --cors -c-1

別ターミナルでPortolan Browserを、起動します。

cd browser
bun install --ignore-scripts
SB_catalogUrl=http://127.0.0.1:8768/catalog.json \
bun run start --host 127.0.0.1 --port 8083

Collectionを検索し、データを開く

http://127.0.0.1:8083/ を開くと、西側・東側の2つのCollectionが並びます。

image.png

検索欄に west と入力すると、西側のCollectionだけになります。名前、説明文、キーワードが検索対象です。

image.png

西側のCollectionを開くと、PMTilesを使った公園2件の地図と、GeoParquetの属性表が表示されます。列の定義もcollection.jsonから読み取って表示されます。

image.png

おわりに

ということで、2つのGeoJSONから、2つのCollection、分析用GeoParquet、地図表示用PMTilesを持つカタログを作りました。その後、rashidの検査を通し、Browserで一覧の絞り込みと地図・属性表の表示まで出来ましたね!

Portolanは、GISデータを共通の形で配布するための選択肢として検討できそうです。静的ファイルを直接使える形式に整え、README.mdとAGENTS.mdで意味や扱い方も渡す。データと、使うための知識をまとめて配布するところに可能性を感じました!

皆さんも使ってみてください!

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?