Wherobots CloudでOverture Mapsのデータを空間検索
Wherobotsについて
みなさんは、WherobotsというGeospatialなサービスを知っていますでしょうか?
Wherobotsは、地理空間データの解析・処理・AI活用を可能にするクラウドベースのプラットフォームです。地球規模のデータをスケーラブルに扱える点が強みで、リモートセンシングや最適化、リスク分析など、多様なユースケースに対応します。また、衛星画像解析や変化検出、交通最適化などを手軽に実行可能です。UIとCLIの両方を提供し、TypeScript向けのSDKも利用できます。無料プランで基本的な機能を試した後、プロフェッショナルプラン以上ではより高度な機能へアクセス可能です。Apache Sedonaの開発者が設立した背景もあり、オープンソースとの深い関連性がある点も特徴的です。最近、2150万ドルの資金調達が報じられるなど、アメリカ発のサービスとして海外では注目を集めつつある一方で、日本ではまだほとんど知られていない状況です。
今後、日本でも利用が広がるツールになる可能性を秘めているので、ぜひチェックしてみてください!
AWS re:Invent 2024での出会い
Wherobotsとの出会いは、今年のAWS re:Inventに参加する前、 CARTOが昨年のAWS re:Inventで開催した「Geo Party」が今年も開催されるという案内が届きました。その案内で、今年はWherobotsとCARTOが共同開催することを知り、Wherobotsの存在を知ることになりました。
AWS re:Invent 2024当日、会場のEXPOホールで実際にWherobotsのブースを訪れました。話を伺うと、Wherobotsが提供する地理空間データ解析プラットフォームの詳細や、技術面での強みについて丁寧に説明してくれました。そこでの出会いが、今回の記事を書くきっかけとなりました。
Overture Maps Foundationに参加
Wherobotsは、今年からOverture Maps Foundationにも参加しています。
Wherobotsのプロダクト
Wherobotsには4つのプロダクトがあります。
1. Wherobots Cloud
クラウド上で地理空間データのETL(抽出、変換、ロード)、分析、AI処理を行う統合プラットフォームです。ユーザーはJupyter Notebook上でSpatial SQL、Python、Javaなどを用いた開発・分析が可能で、地理空間データの前処理から高度なモデル適用まで一貫して実行できます。現在はAWS上でのワークロードに対応しており、将来的にはAzureやGCPなど他クラウド環境への展開も計画中です。
2. WherobotsDB
地理空間データ向けに最適化された、クラウドネイティブのサーバーレス分析エンジンです。小規模な解析から世界規模の地理空間クエリまで、最大20倍の速度で実行可能とされ、従来のクラウド分析エンジンに比べてコスト削減も見込めます。さまざまな地理データフォーマットや投影法への対応、オンデマンドでの自動スケーリングによって、柔軟なワークロード処理を実現します。
3. WherobotsAI
リモートセンシングデータ専用のコンピュータビジョン機能を提供するモジュールです。衛星画像からのセグメンテーション、変化検出、物体検出、土地利用分類、気候分析など、多面的な解析が可能となり、インフラモニタリングや自然災害予測、農業最適化など、幅広い分野での応用が期待されます。
4. Wherobots Spatial Catalog
膨大な地理空間データセットを統合的に管理・提供するカタログ機能です。ユーザーは、キーワード検索やメタデータフィルタリングによって必要なデータを容易に発見し、Wherobots CloudやWherobotsDBとの連携によってスムーズに分析へと活用できます。公共データ、商用データ、顧客固有データのいずれも統合的に扱うことが可能で、データ駆動型の意思決定を支援します。
今回の記事では、Wherobots Cloudについて試します。Wherobots Cloudを利用することで、他の機能とも連携が可能です。
Wherobots Cloudの料金
今回は、Communityバージョンを利用します。他にもAWS Marketplaceで高機能なProfessionalバージョンを利用することも可能です。2024年12月現在、Professionalバージョンは初回30日間に限り、最大400ドル分まで無料で試せます。
事前準備
アカウント作成
はじめに、アカウントを作成します。
「Try Wherobots」をクリックします。
アカウント情報を入力 → 「Create Account」をクリックします。
Organization名を入力 → 「Submit」をクリックします。
ログイン後、ダッシュボードが表示されます。
Notebookの起動
次に、位置情報データの処理に利用するNotebookを起動します。
「Start」をクリックします。
Notebookを起動後 →「Open」をクリックします。
Notebookが表示されます。
Overture Mapsのデータを空間検索
WherobotsDBを利用
WherobotsDBを利用するために「SedonaContext」オブジェクトを作成します。
from sedona.spark import *
config = SedonaContext.builder().getOrCreate()
sedona = SedonaContext.create(config)
Wherobots Spatial CatalogのOpen Data Catalogを確認
Open Data Catalogでは、Overture MapsやFoursquareのデータをプリセットで利用できます。
Open Data Catalogの一覧を表示します。
sedona.sql("SHOW SCHEMAS IN wherobots_open_data").show()
+--------------------+
| namespace|
+--------------------+
| overture|
| overture_2024_02_15|
| overture_2024_05_16|
| overture_2024_07_22|
|overture_2024_01_...|
|overture_2024_05_...|
|overture_2023_07_...|
|overture_2024_06_...|
|overture_2024_09_...|
|overture_2024_08_...|
|overture_2024_10_...|
|overture_2024_03_...|
|overture_2024_04_...|
|overture_2023_10_...|
|overture_2023_11_...|
|overture_2024_06_...|
|overture_2024_07_...|
|overture_2024_02_...|
|overture_2023_12_...|
|foursquare_2024_1...|
+--------------------+
only showing top 20 rows
Overture Mapsデータベースのテーブルを表示します。
sedona.sql("SHOW tables IN wherobots_open_data.overture").show(truncate=False)
+---------+-----------------------------+-----------+
|namespace|tableName |isTemporary|
+---------+-----------------------------+-----------+
|overture |admins_administrativeBoundary|false |
|overture |admins_locality |false |
|overture |buildings_building |false |
|overture |places_place |false |
|overture |transportation_connector |false |
|overture |transportation_segment |false |
+---------+-----------------------------+-----------+
Overture Mapsの「places_place」テーブルのスキーマを表示します。
sedona.table("wherobots_open_data.overture.places_place").printSchema()
root
|-- id: string (nullable = true)
|-- updatetime: string (nullable = true)
|-- version: integer (nullable = true)
|-- names: map (nullable = true)
| |-- key: string
| |-- value: array (valueContainsNull = true)
| | |-- element: map (containsNull = true)
| | | |-- key: string
| | | |-- value: string (valueContainsNull = true)
|-- categories: struct (nullable = true)
| |-- main: string (nullable = true)
| |-- alternate: array (nullable = true)
| | |-- element: string (containsNull = true)
|-- confidence: double (nullable = true)
|-- websites: array (nullable = true)
| |-- element: string (containsNull = true)
|-- socials: array (nullable = true)
| |-- element: string (containsNull = true)
|-- emails: array (nullable = true)
| |-- element: string (containsNull = true)
|-- phones: array (nullable = true)
| |-- element: string (containsNull = true)
|-- brand: struct (nullable = true)
| |-- names: map (nullable = true)
| | |-- key: string
| | |-- value: array (valueContainsNull = true)
| | | |-- element: map (containsNull = true)
| | | | |-- key: string
| | | | |-- value: string (valueContainsNull = true)
| |-- wikidata: string (nullable = true)
|-- addresses: array (nullable = true)
| |-- element: map (containsNull = true)
| | |-- key: string
| | |-- value: string (valueContainsNull = true)
|-- sources: array (nullable = true)
| |-- element: map (containsNull = true)
| | |-- key: string
| | |-- value: string (valueContainsNull = true)
|-- bbox: struct (nullable = true)
| |-- minx: double (nullable = true)
| |-- maxx: double (nullable = true)
| |-- miny: double (nullable = true)
| |-- maxy: double (nullable = true)
|-- geometry: geometry (nullable = true)
|-- geohash: string (nullable = true)
Spatial SQLで空間検索
Spatial SQLを利用することでさまざまな空間検索が実行できます。
Overture Mapsの「places_place」テーブルから「places」ビューを作成します。
sedona.table("wherobots_open_data.overture.places_place").createOrReplaceTempView("places")
名前・カテゴリー・ポイント座標の属性とポイントデータを取得します。
sedona.sql("SELECT categories.main AS category, names.common[0].value AS name, geometry FROM places LIMIT 20").show(truncate=False)
+---------------------+----------------------------------+------------------------------+
|category |name |geometry |
+---------------------+----------------------------------+------------------------------+
|taiwanese_restaurant |台湾料理四季紅 |POINT (136.885079 35.343057) |
|japanese_restaurant |いろ川 |POINT (139.848961 35.739373) |
|farm |株式会社阿部農園 |POINT (139.0049956 37.7329319)|
|train_station |岩波駅 |POINT (138.919082 35.215899) |
|japanese_restaurant |甲南そば |POINT (135.27482 34.728534) |
|restaurant |Cuatro |POINT (138.8529754 35.1067167)|
|smoothie_juice_bar |ゴクゴク 横浜ワールドポーターズ店|POINT (139.6385154 35.4541338)|
|hotel |Curation Hotel |POINT (139.0785116 35.1066403)|
|asian_restaurant |集来軒 |POINT (135.470227 34.717611) |
|professional_services|株式会社CDF |POINT (135.1936619 34.6962007)|
|public_plaza |深見歴史の森スポーツ広場 |POINT (139.464748 35.491567) |
|pet_store |うさぎ専門店ちゅらうさぎ |POINT (138.2966425 34.8537383)|
|japanese_restaurant |無添くら寿司横浜長津田店 |POINT (139.497489 35.521179) |
|health_and_medical |大滝漢方堂 |POINT (136.5 36.05) |
|japanese_restaurant |焼き鳥酒場 ちょりちょり |POINT (139.5350146 36.0305335)|
|eat_and_drink |味のじゅん天 |POINT (140.386382 37.399886) |
|japanese_restaurant |まご茶亭 |POINT (139.071774 35.09486) |
|gym |スマートフィット100大塚店 |POINT (140.408632 36.383624) |
|NULL |法隆寺駅 |POINT (135.739107 34.601617) |
|bar |布施酒場かい |POINT (135.564394 34.663048) |
+---------------------+----------------------------------+------------------------------+
指定ポイントから半径10km以内にある「hiking_trail」を取得します。
sedona.sql("""
SELECT names.common[0].value AS name, categories.main AS category, geometry FROM places WHERE ST_DistanceSphere(ST_GeomFromWKT('POINT (139.7645 35.6811)'), geometry) < 10000 AND categories.main = 'hiking_trail' LIMIT 20
""").show(truncate=False)
+-----------------------------+------------+------------------------------+
|name |category |geometry |
+-----------------------------+------------+------------------------------+
|玉川上水旧水路幡ヶ谷緑道 |hiking_trail|POINT (139.678356 35.676208) |
|向日葵の小径 |hiking_trail|POINT (139.756216 35.674825) |
|荒木坂 |hiking_trail|POINT (139.7393195 35.7115361)|
|目黒川緑道池尻大橋駅側入口 |hiking_trail|POINT (139.6854 35.651325) |
|芝浦アイランド 遊歩道 |hiking_trail|POINT (139.750809 35.63663) |
|大塚バラロード |hiking_trail|POINT (139.72557 35.729693) |
|解剖坂 |hiking_trail|POINT (139.759613 35.721601) |
|足立の平成五色桜 |hiking_trail|POINT (139.770207 35.759781) |
|芸術の散歩道 |hiking_trail|POINT (139.77332 35.7163) |
|玉川上水旧水路世田谷緑道 |hiking_trail|POINT (139.662694 35.670916) |
|三段坂 |hiking_trail|POINT (139.769306 35.718932) |
|飛鳥大坂 |hiking_trail|POINT (139.737485 35.750156) |
|目黒川東海禅寺裏遊歩道 |hiking_trail|POINT (139.739045 35.615981) |
|山王男坂 |hiking_trail|POINT (139.740707 35.674686) |
|レインボープロムナード 台場口|hiking_trail|POINT (139.775662 35.635127) |
|大横川の桜並木 |hiking_trail|POINT (139.794909 35.671448) |
|白鷺坂 |hiking_trail|POINT (139.736717 35.723212) |
|三平坂 |hiking_trail|POINT (139.731583 35.758) |
|玉川上水旧水路初台緑道 |hiking_trail|POINT (139.687152 35.680788) |
|レインボープロムナード 芝浦口|hiking_trail|POINT (139.759193 35.637868) |
+-----------------------------+------------+------------------------------+
空間検索結果のDataFrameを作成します。
trails_df = sedona.sql("""
SELECT names.common[0].value AS name, categories.main AS category, geometry FROM places WHERE ST_DistanceSphere(ST_GeomFromWKT('POINT (139.7645 35.6811)'), geometry) < 10000 AND categories.main = 'hiking_trail'
""")
空間検索結果を可視化
SedonaKeplerやSedonaPyDeckを利用し空間検索結果を可視化できます。
SedonaKeplerで空間検索結果を可視化します。
SedonaKepler.create_map(trails_df, "Hiking Trails")
Overture MapsのデータをPMTilesに変換
WherobotsDBを利用
WherobotsDBを利用するために「SedonaContext」オブジェクトを作成します。
from sedona.spark import *
config = SedonaContext.builder().getOrCreate()
sedona = SedonaContext.create(config)
範囲指定したデータを抽出
東京周辺の範囲を指定します。
from sedona.sql.st_constructors import ST_GeomFromText
from sedona.sql.st_predicates import ST_Intersects
region_wkt = "POLYGON ((139.7543 35.7044, 139.7371 35.6921, 139.7377 35.6758, 139.7498 35.6600, 139.7734 35.6658, 139.7841 35.6793, 139.7845 35.7023, 139.7543 35.7044))"
Overture Mapsの「buildings」テーブルのDataFrameを作成します。
import pyspark.sql.functions as f
buildings_df = (
sedona.table("wherobots_open_data.overture_2024_02_15.buildings_building")
.select(
f.col("geometry"),
f.lit("buildings").alias("layer"),
f.element_at(f.col("sources"), 1).dataset.alias("source")
)
)
buildings_df.show()
+--------------------+---------+--------------------+
| geometry| layer| source|
+--------------------+---------+--------------------+
|POLYGON ((-49.438...|buildings|Microsoft ML Buil...|
|POLYGON ((-49.438...|buildings|Google Open Build...|
|POLYGON ((-49.438...|buildings|Microsoft ML Buil...|
|POLYGON ((-49.438...|buildings|Google Open Build...|
|POLYGON ((-49.441...|buildings|Google Open Build...|
|POLYGON ((-49.440...|buildings|Google Open Build...|
|POLYGON ((-49.441...|buildings|Microsoft ML Buil...|
|POLYGON ((-49.441...|buildings|Google Open Build...|
|POLYGON ((-49.440...|buildings|Microsoft ML Buil...|
|POLYGON ((-49.442...|buildings|Google Open Build...|
|POLYGON ((-49.442...|buildings|Google Open Build...|
|POLYGON ((-49.442...|buildings|Google Open Build...|
|POLYGON ((-49.442...|buildings|Google Open Build...|
|POLYGON ((-49.442...|buildings|Microsoft ML Buil...|
|POLYGON ((-49.442...|buildings|Google Open Build...|
|POLYGON ((-49.438...|buildings|Google Open Build...|
|POLYGON ((-49.438...|buildings|Google Open Build...|
|POLYGON ((-49.439...|buildings|Google Open Build...|
|POLYGON ((-49.439...|buildings|Google Open Build...|
|POLYGON ((-49.438...|buildings|Microsoft ML Buil...|
+--------------------+---------+--------------------+
only showing top 20 rows
Overture Mapsの「roads」テーブルのDataFrameを作成します。
roads_df = (
sedona.table("wherobots_open_data.overture_2024_02_15.transportation_segment")
.select(
f.col("geometry"),
f.lit("roads").alias("layer"),
f.element_at(f.col("sources"), 1).dataset.alias("source")
)
)
roads_df.show()
+--------------------+-----+-------------+
| geometry|layer| source|
+--------------------+-----+-------------+
|LINESTRING (7.034...|roads|OpenStreetMap|
|LINESTRING (7.037...|roads|OpenStreetMap|
|LINESTRING (7.032...|roads|OpenStreetMap|
|LINESTRING (7.033...|roads|OpenStreetMap|
|LINESTRING (7.031...|roads|OpenStreetMap|
|LINESTRING (7.031...|roads|OpenStreetMap|
|LINESTRING (7.031...|roads|OpenStreetMap|
|LINESTRING (7.033...|roads|OpenStreetMap|
|LINESTRING (7.034...|roads|OpenStreetMap|
|LINESTRING (7.030...|roads|OpenStreetMap|
|LINESTRING (7.037...|roads|OpenStreetMap|
|LINESTRING (7.037...|roads|OpenStreetMap|
|LINESTRING (7.041...|roads|OpenStreetMap|
|LINESTRING (7.051...|roads|OpenStreetMap|
|LINESTRING (7.037...|roads|OpenStreetMap|
|LINESTRING (7.050...|roads|OpenStreetMap|
|LINESTRING (7.054...|roads|OpenStreetMap|
|LINESTRING (7.051...|roads|OpenStreetMap|
|LINESTRING (7.052...|roads|OpenStreetMap|
|LINESTRING (7.052...|roads|OpenStreetMap|
+--------------------+-----+-------------+
only showing top 20 rows
Overture Mapsの「buildings」と「roads」を組み合わせた東京周辺のDataFrameを作成します。
features_df = roads_df.union(buildings_df)
if filter:
features_df = features_df.filter(ST_Intersects(f.col("geometry"), ST_GeomFromText(f.lit(region_wkt))))
features_df.count()
38957
ベクトルタイルを作成
抽出したデータのベクトルタイルを作成します。
from wherobots import vtiles
tiles_df = vtiles.generate(features_df)
tiles_df.show(3, 150, True)
-RECORD 0----------------------------------------------------------------------------------------------------------------------------------------------------------
tile | {56, 25, 6}
features | [1A DA B8 02 0A 05 72 6F 61 64 73 12 16 12 02 00 00 18 02 22 0E 09 A4 36 82 0D 22 01 01 01 00 01 00 01 01 12 10 12 02 00 00 18 02 22 08 09 98 36 F2...
-RECORD 1----------------------------------------------------------------------------------------------------------------------------------------------------------
tile | {14552, 6453, 14}
features | [1A 8E 01 0A 05 72 6F 61 64 73 12 3E 12 02 00 00 18 02 22 36 09 FE 0A 8A 08 AA 01 01 0F 03 27 1E 8F 03 04 2D 06 27 22 83 02 14 9D 01 06 31 06 35 1E...
-RECORD 2----------------------------------------------------------------------------------------------------------------------------------------------------------
tile | {29104, 12900, 15}
features | [1A 90 20 0A 05 72 6F 61 64 73 12 14 12 02 00 00 18 02 22 0C 09 D4 23 D6 3D 1A 05 56 03 38 29 0E 12 43 12 02 00 00 18 02 22 3B 09 88 21 D0 3A C2 01...
only showing top 3 rows
ベクトルタイルをPMTilesに変換
ベクトルタイルをPMTilesに変換します。
import os
full_tiles_path = os.getenv("USER_S3_PATH") + "tiles.pmtiles"
vtiles.write_pmtiles(tiles_df, full_tiles_path, features_df=features_df)
PMTilesのデータを可視化
LeafmapでPMTilesのデータを可視化します。
vtiles.show_pmtiles(full_tiles_path)
簡素化したPMTilesのデータを可視化
大量なデータを簡素化して可視化します。
sample_tiles_path = os.getenv("USER_S3_PATH") + "sampleTiles.pmtiles"
vtiles.generate_quick_pmtiles(features_df, sample_tiles_path)
Discussion