はじめに
2025年10月12日に行われたFOSS4G Japanの参加レポートでも書こうかと思ったのですが、気になる技術ネタがありすぎたので、参加レポートは書かずに、使ってみた系の話を書こうと思います。(参加レポートは、他のインフルエンス力高めな方々がきっと書いてくれるから大丈夫)
さて、色々と面白い発表があったのですが、今回は以下の発表で触れられていたFusedを触ってみることにしました。
Pythonで触れるからFusedを選んだだけで、決してCEOの発表だからとかそういう忖度ではない、ええ、決して。
Fusedって何?
ぶっちゃけ私もまだよく分かっていないです。が、PSS様のページで紹介されているので載せておきます。
なお、2~3時間程度触ってみた雑感としては、「自前のGISデータをブラウザ上で可視化、分析できるツール。AIとのチャット欄があって自然言語でもデータを処理してくれる。」って感じです。
自然言語での処理の一例として、以下のような使い方があります。
① チャット欄でAIさんに指示を投げる
② AIさんがPythonのスクリプトを書き換える
③ returnで戻されたGeoDataframeが地図上で可視化される

もちろん、Pythonコードは編集できるので、「AIに頼らず自分で書くぜ」というのも可能です。
AIに書かせるにしろ、自分で書くにしろ、とにかく「returnでGeoDataframeを返す」ことを意識していれば良さそうです。
WebAPIを立ててみる
ここまでお伝えしたように、FusedではPythonスクリプトから返されたGeoDataframeを使って可視化や分析を行います。実はこの時、返されたGeoDataframeを使ったWebAPIも同時に立てられています。
ということで、今回はWebAPIを立てるところまでの手順をまとめておきます。
なお、以下を満たしていることが前提条件です。
- geopandasを触ったことがある
- AWSアカウントを持っている
Fusedにログインする
Fusedにアクセスし、右上の「Log In」をクリックします。

多分、新規アカウント作成を求められますが、普通にGoogleアカウントで作れます。
APIの仕組みを把握する
とりあえず、「あー、そんな感じでAPIが作られるのね」を体感してみましょう。ログインしたら、以下のような画面になると思います。(なってなかったら、画面左のツールバーにUDF builderがあるのでそれを選択してください。)

真ん中のコードを見ると、ちゃんとDataframeを返してますねー。
@fused.udf
def udf(name: str = "world"):
import pandas as pd
return pd.DataFrame({"hello": [name]})
とりあえず、使ってみたところ「@fused.udfでデコったudf()内で返されたDataframe」がFused上で色々と使えるようになる仕様っぽいです。
なので、udf2とかに名前変えると上手く動かなくなります。
さて、初めて開いた時にコードが保存されているのかよく分からないので、念の為に左上の「Save all」を押しておきましょう。

Saveした時点でWebAPIは作られているので、一回見てみましょう。
左のツールバーから「Canvas」を開くと、なんかウィンドウっぽいのが見えると思います。ウィンドウを選択状態にすると「...」が表示されるので、「Share Link」をクリックします。

これがWebAPIのURLになっています。とりあえず、最初は以下のような表が出ると思います。

まだ、ピンとこないかもしれないので、先ほどの表のURLの後ろに&name=FOSS4Gと追加してみましょう。すると表が以下のように変わるはずです。

さて、Dataframeを返していたPythonコードを見直してみましょう。
@fused.udf
def udf(name: str = "world"):
import pandas as pd
return pd.DataFrame({"hello": [name]})
もうお分かりですね。関数の引数であるnameがURLのパラメータで指定されたため、Dataframeの中身が変わり、「FOSS4G」と書かれた表が出力されるようになっています。
自分のGISデータを使ってみる
WebAPIがどんな感じで立てられるのかは、多少理解できたと思うので、自前のデータを使ってみましょう。本当はFusedにファイルをアップロードするのが一番楽だと思うのですが、アップロードを試みると「課金しろ」って言われます。

技術検証の段階で、以下の金額を払う元気がある人は、人柱になって記事を書いてください。(2週間の無料期間はあるようです。)

Amazon S3の設定
私の懐はそんなに元気ないので、Amazon S3を使うことにします。Amazon S3のバケットの作成方法については、この記事では説明しないです。
まあ、バケット名を設定するくらいで、あとはデフォルトのまま作ればOKです。強いて言えば、「パブリックアクセスをすべてブロック」になっていることを確認してもらえればOKです。
S3バケットを作ったら、利用したいGISデータをS3バケットに入れておいてください。なお、後でもう一回言いますが、Fusedに課金しない方はセキュアなデータは絶対に使わないでください。
お試しなので、国土数値情報などのオープンデータを使うのが良いと思います。
以下のようにS3にGISデータ(検証してないけど、geojsonが無難かな)が入っている状態になっていればOKです。

さて、ここからがS3関連で一番重要な話です。S3のバケットポリシーを設定しましょう。バケットポリシーはFusedのユーザープロフィールから生成できるのですが、そのまま使うのは危険なので、多少安全にしたコードを以下に貼ります。(your-bucket-nameの部分は、ご自身で作成したバケット名に合わせてください。)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::926411091187:role/rt-production-basic-tier"
},
"Action": [
"s3:ListBucket",
"s3:GetObjectAttributes",
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::your-bucket-name",
"arn:aws:s3:::your-bucket-name/*"
]
}
]
}
注意すべき項目が2つあります。1つ目は、以下の部分です。
"Principal": {
"AWS": "arn:aws:iam::926411091187:role/rt-production-basic-tier"
}
FusedのDocsにも記載されていますが、本来はFusedから発行されるYOUR_ENV_NAMEを使って以下のような記述にするのが適切です。
"Principal": {
"AWS": [
"arn:aws:iam::926411091187:role/rt-production-YOUR_ENV_NAME",
"arn:aws:iam::926411091187:role/ec2_job_task_role-v2-production-YOUR_ENV_NAME",
"arn:aws:iam::926411091187:role/fused_server_role_prod_us_west_2"
]
}
ただし、このYOUR_ENV_NAMEは有料プランでしか発行されません。FusedのFree Tierのページに以下の注意書きがあります。
All users on the free tier share the same compute resources. Do not share sensitive data that others might be able to access on the free tier.
上記でも書かれているように、「Free Tier」の人は同じコンピューティングリソースを共用する( = 全員が"arn:aws:iam::926411091187:role/rt-production-basic-tier"を使う)ことになります。
そんな共用のリソースにアクセス権を付与するわけですから、セキュアなデータは絶対に入れないでください。
2点目は許可するアクションです。FusedのDocsやテンプレートは、以下のように書かれています。
Docsくん「PutもDeleteもOKやで」
"Action": [
"s3:ListBucket",
"s3:GetObjectAttributes",
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
]
User Profileで作れるテンプレートくん「S3の操作なら全部OKやで」
"Action": "s3:*"
いや、怖すぎワロタ(ワロエナイ
(なんで怖いのか分からない人は生成AIに聞いてください。)
というわけで、バケットポリシーの設定は慎重に行いましょう。
S3のGISデータをFusedで利用する
ようやく本題です。でも、ここまで理解できたなら難しくないです。Fusedに戻って、コードを以下のように書き換えてみましょう。(もちろん、your-bucket-nameとファイル名はご自身のものに書き換えてください。)
@fused.udf
def udf(
path: str = "s3://your-bucket-name/N03-20250101_14.geojson",
):
import geopandas as gpd
@fused.cache
def get_geojson(path):
return gpd.read_file(path)
gdf = get_geojson(path)
return gdf
Mapタブに切り替えれば、地図上に読み込んだデータが表示されます。

表示できていれば、正常に動いています。あとは、最初と同じ手順でAPIのURLにアクセスしてみましょう。読み込んだデータのGeoDataframeが表示されるはずです。

もし、表形式のhtmlではなく、geojsonが欲しい場合は、urlのdtype_out_vector=htmlの部分をdtype_out_vector=geojsonに変えてみてください。ちゃんとgeojsonで出力されます。(まあ、テキストで見てもわけわからんですが)

出力できるフォーマット一覧の正しい確認方法はよく分からないのですが、とりあえずdtype_out_vector=html2みたいに明らかに間違ったパラメータを与えると以下のような記述が確認できるので、"expected"に記載されているやつなら出力できるようです。
{
"detail": [
{
"type": "literal_error",
"loc": [
"body",
"dtype_out_vector"
],
"msg": "Input should be 'parquet', 'geojson', 'json', 'feather', 'csv', 'mvt', 'html', 'excel' or 'xml'",
"input": "html2",
"ctx": {
"expected": "'parquet', 'geojson', 'json', 'feather', 'csv', 'mvt', 'html', 'excel' or 'xml'"
}
}
]
}
おわりに
昨日初めて知ったFusedだったんですが、とりあえず、「geopandasがデフォルトで入っているAPIサーバー」くらいのノリで使うことはできそうでした。ただし、本番運用するには$20の課金はほぼ必須なので、コストに見合った旨味をもう少し引き出す必要はありそうですね。
「こう使うと嬉しいよ」的な情報があればコメントなり、記事にしてもらえると嬉しいです。