日本には、意外とたくさんのオープンデータがありますよね。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の主な機能で、後ほど一部の機能を使って実際にカタログを作成していきます。
| できること | 具体例 | 主なコマンド |
|---|---|---|
| カタログを作る | カタログを初期化し、データを登録する |
init、add
|
| データ形式を変換する | GeoJSON→GeoParquet、GeoTIFF→COG | add |
| 外部から取り込む | ArcGISやWFSなどからデータを取得する | extract |
| 表示用のファイルを作る | PMTiles、縮小画像を生成する | add --pmtiles --thumbnails |
| 説明文を作る | STACとmetadata.yamlからREADMEを生成する | readme |
| 検査・修復する | rashidで検査し、対応できる不備を修正する |
check、check --fix
|
| 公開先とやり取りする | S3などへ転送し、更新を反映・取得する |
push、pull、sync
|
| 更新履歴を管理する | データの版や公開先との差を確認する |
version、status
|
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が並びます。
検索欄に west と入力すると、西側のCollectionだけになります。名前、説明文、キーワードが検索対象です。
西側のCollectionを開くと、PMTilesを使った公園2件の地図と、GeoParquetの属性表が表示されます。列の定義もcollection.jsonから読み取って表示されます。
おわりに
ということで、2つのGeoJSONから、2つのCollection、分析用GeoParquet、地図表示用PMTilesを持つカタログを作りました。その後、rashidの検査を通し、Browserで一覧の絞り込みと地図・属性表の表示まで出来ましたね!
Portolanは、GISデータを共通の形で配布するための選択肢として検討できそうです。静的ファイルを直接使える形式に整え、README.mdとAGENTS.mdで意味や扱い方も渡す。データと、使うための知識をまとめて配布するところに可能性を感じました!
皆さんも使ってみてください!



