6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DataBricksからQGISで可視化する

6
Last updated at Posted at 2025-12-28

image.png

はじめに

DataBricksは、巨大データの収集整理からAIによる解析までドカンと一気通貫で行える統合プラットフォーム…いわばデータの巨大ショッピングモールです(雑)。

image.png

位置データに関しては、億単位の地物や移動体データもとに物流ルートの最適化や店舗戦略予測などなどが可能とされており、最新のMosaic AIのサポートで自然言語での分析やAI構築の自動化が加速しています。

これらはDataSharingというオープンな規格で様々なツールやアプリケーションへの接続が可能なため、ベンダーロックインのリスクを下げ、データの民主化と意思決定を迅速化するとされています。詳しくはこちらなどを御覧ください

さて先日某MTGで「DataBricksとかどうすか?」と話題に上がった際、そういえば気にしていたもののあまり触っていたこともあり、まずはQGISからどのようにアプローチできるか検証を行いました。

なお、数カ月まえからスキマ時間に検証を行っていたところ、Databricks DBSQL Connectorというプラグインが最近ローンチされたので「あーハイ、そうですか、ワイの検証も終了かあ」と思いつつ試してみたのですが、このQiitaを書いているときのv1.0ではまず、urllib3が.1.25以下だとプラグインが起動しません(最新版QGISなら1.25以上なのでおそらく大丈夫)。また、DataBricksへの接続時にユーザーアカウントの全てのカタログ・スキーマ・テーブルを確認するようで、大きめのデータを扱う際なかなか接続できないという状況にありました。
わたしの設定やアプローチがおかしいのかもしれませんが、このプラグインはDataBricks側である程度ミニマムなデータに加工してから、QGISで読むには良いのかもしれません。しかし今後バージョンアップでさらに使いやすくなるかもしれないので、期待大ですね。

image.png

そのため、現状ではやや回り道かもしれませんが、

  1. DataBricks謹製のdatabricks-sql-connectorライブラリをQGISに追加
  2. QGISからOverTure Mapsのデータに関するリクエストをDatabricksに送る
  3. その結果をQGISで見る

ということをやってみます。なお、🤖とケンカしながら試行錯誤して検証しているところもありますので、ご了承くださいませ

また、QGISにdatabricks-sql-connectorライブラリのインストールを行いますので、At your risk でお試しください。

環境はMacOSのQGIS3.44.6-Solothurnで、筆者はビリヤニが好きです。

事前準備

まずDataBricksのwebページから各種事前準備を行います。

DataBricksのフリートライアルでサインアップをします。サインアップから14日間は無料で利用することが可能ですし、登録時に恐怖のキャッシュカード登録が不要なのはありがたいです。

なお、この手引きでは、右上のユーザーアイコン→環境設定→言語「日本語」での表示で説明します。

image.png

接続情報の取得

QGISからの接続には、DataBricksの

  • ホスト名
  • HTTPパス
  • トークン
    情報が必要になります。

この手順にしたがって、「接続の詳細」をみクリックすると、サーバーのホスト名およびHTTPパスが確認できます。←のコピーボタンを押して、どこぞにメモしておきましょう

image.png

トークンはこちらの手順に従って取得してください。
なお、新規トークン生成時の一度しかトークンは表示されないのでご注意を
image.png

これで、三種の神器が揃いました。

サンプルデータの登録

次に、今回はDataBricksにバンドルされている世界の超弩級地図データOverture Mapsの「Place」データを用います。このレコードは5400万件ほどあり、ライセンスはおもにユルめのCDLA Permissive 2.0です。

アカウントのワークスペースに登録するために、左のMarketPlace→🔎overture maps→Overture Maps - Placesをクリック

image.png

右上の「即時アクセス権を取得」をクリック
image.png

利用規約を確認の上、右上の「即時アクセス権を取得」をもう一度クリック

image.png

左メニューの「カタログ」にcarto_overture_maps_placesが追加されていればOKです。これで旅の準備が完了です。

image.png

QGISとの接続

databricks-sql-connectorのインストール

それでは、DataBricksと接続するためのPythonライブラリである、databricks-sql-connectorをインストールします。

GISのPythonコンソールで以下を実行してください。実行後、ライブラリを反映させるためにQGISを再起動してください。

from pip._internal import main as pip_main
pip_main(['install', '--user', 'databricks-sql-connector'])

接続確認

では上記で得た三種の神器

  • ホスト名
  • HTTPパス
  • トークン

を下記コード上に入れ替え、QGISのPythonコンソールで実行してください。

from databricks import sql

HOSTNAME="XXXXXXXXXX.cloud.databricks.com"
HTTP_PATH="/sql/1.0/warehouses/XXXXXXXXXXXXXXXXXX"
TOKEN="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

# Shinjuku Sta
LON0, LAT0 = 139.7006, 35.6896

Q = f"""
WITH p AS (
  SELECT
    names.primary AS name,
    CAST((bbox.xmin + bbox.xmax)/2 AS DOUBLE) AS lon,
    CAST((bbox.ymin + bbox.ymax)/2 AS DOUBLE) AS lat
  FROM place
  WHERE names.primary IS NOT NULL AND bbox IS NOT NULL
),
r AS (
  SELECT
    name, lon, lat,
    SQRT(POWER(lon - {LON0}, 2) + POWER(lat - {LAT0}, 2)) AS d
  FROM p
  WHERE lon BETWEEN {LON0}-0.05 AND {LON0}+0.05
    AND lat BETWEEN {LAT0}-0.05 AND {LAT0}+0.05
)
SELECT name, lon, lat, d
FROM r
ORDER BY d
LIMIT 10
"""

with sql.connect(server_hostname=HOSTNAME, http_path=HTTP_PATH, access_token=TOKEN) as c, c.cursor() as cur:
    cur.execute("USE CATALOG `carto_overture_maps_places`"); cur.execute("USE SCHEMA `carto`")
    cur.execute(Q)
    print("name\tlon\tlat\td")
    for name, lon, lat, d in cur.fetchall():
        print(f"{name}\t{lon:.6f}\t{lat:.6f}\t{d:.6f}")


データのバージョンなどによって多少異なるかもしれませんが、新宿駅(139.7006, 35.6896)周辺のPOI10件と距離が抽出されます。

name	lon	lat	d
Public Tokyo Mens	139.700623	35.689613	0.000026
A+Tokyo Mens	139.700623	35.689613	0.000026
Plaza ルミネ新宿	139.700592	35.689644	0.000045
ネイルステーション	139.700592	35.689651	0.000052
NewDays	139.700653	35.689613	0.000055
United Tokyo Mens Shinjuku	139.700623	35.689659	0.000063
SNIDEL Lumine Shinjuku 2	139.700623	35.689667	0.000070
Lacoste	139.700623	35.689671	0.000074
Maison de Fleur	139.700623	35.689674	0.000078
Maison Special Mens	139.700623	35.689674	0.000078

databricks-sql-connectorのアンインストール

万が一databricks-sql-connectorによる不具合が疑われる場合は、QGISのPythonコンソールでアンインストールが可能です

from pip._internal import main as pip_main
pip_main(["uninstall", "-y", "databricks-sql-connector"])

QGISからリクエストを投げる

ではこれでDataBricksのデータ資源がQGISから読み込めます。もうFTPから巨大ZIPのダウンロードして解凍()して属性テーブルで検索するなんで手間からおさらばです。

たとえば、DataBricksに東京都23区の区域内におけるPlaceがあるデータをH3Indexの解像度9で集計してというSQLを投げて、QGISで表示させるコードはこんな感じになります。
私の環境(Macbook Air M4 2025 32GB)だと8秒ほどで処理が終わりました。

import time
from databricks import sql
from qgis.core import (
    QgsVectorLayer, QgsProject, QgsFeature, QgsGeometry, QgsField,
    QgsGraduatedSymbolRenderer, QgsRendererRange, QgsSymbol, QgsStyle
)
from qgis.PyQt.QtCore import QVariant
from qgis.PyQt.QtGui import QColor

T_ALL = time.perf_counter()

# ============================================================
# Databricks 接続
# ============================================================
HOSTNAME  = "XXXXXXXXXXXXXXXXXXXX.cloud.databricks.com"
HTTP_PATH = "/sql/1.0/warehouses/XXXXXXXXXXXXXXXXXXXX"
TOKEN     = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

CATALOG = "carto_overture_maps_places"
SCHEMA  = "carto"

# ============================================================
# 検索・処理パラメータ
# ============================================================
H3_RES  = 9
LIMIT_N = 20000

# 東京23区 bbox
LON_MIN, LON_MAX = 139.56, 139.92
LAT_MIN, LAT_MAX = 35.53,  35.82

# ============================================================
# SQL
# ============================================================
QUERY = f"""
WITH base AS (
  SELECT
    CAST((bbox.xmin + bbox.xmax)/2 AS DOUBLE) AS lon,
    CAST((bbox.ymin + bbox.ymax)/2 AS DOUBLE) AS lat
  FROM place
  WHERE bbox IS NOT NULL
    AND bbox.xmax >= {LON_MIN} AND bbox.xmin <= {LON_MAX}
    AND bbox.ymax >= {LAT_MIN} AND bbox.ymin <= {LAT_MAX}
),
h3 AS (
  SELECT h3_longlatash3(lon, lat, {H3_RES}) AS h3
  FROM base
)
SELECT
  h3_boundaryaswkt(h3) AS wkt,
  COUNT(*) AS cnt
FROM h3
GROUP BY 1
ORDER BY cnt DESC
LIMIT {LIMIT_N}
"""

# ============================================================
# Databricks 実行
# ============================================================
t0 = time.perf_counter()
with sql.connect(server_hostname=HOSTNAME, http_path=HTTP_PATH, access_token=TOKEN) as conn, conn.cursor() as cur:
    cur.execute(f"USE CATALOG `{CATALOG}`")
    cur.execute(f"USE SCHEMA `{SCHEMA}`")
    cur.execute(QUERY)
    rows = cur.fetchall()
print(f"[TIME] Databricks SQL: {time.perf_counter() - t0:.2f}s")

# ============================================================
# QGIS レイヤ生成
# ============================================================
t0 = time.perf_counter()
layer = QgsVectorLayer("Polygon?crs=EPSG:4326", f"Tokyo23_Place_H3_r{H3_RES}", "memory")
prov = layer.dataProvider()
prov.addAttributes([QgsField("cnt", QVariant.Int)])
layer.updateFields()

feats = []
for wkt, cnt in rows:
    if not wkt:
        continue
    g = QgsGeometry.fromWkt(wkt)
    if g.isEmpty():
        continue
    f = QgsFeature(layer.fields())
    f.setGeometry(g)
    f["cnt"] = int(cnt)
    feats.append(f)

prov.addFeatures(feats)
print(f"[TIME] Layer build: {time.perf_counter() - t0:.2f}s")

# ============================================================
# QGIS表示
# ============================================================
t0 = time.perf_counter()

classes = 7
tmp = QgsGraduatedSymbolRenderer()
tmp.setClassAttribute("cnt")
tmp.setMode(QgsGraduatedSymbolRenderer.Quantile)
tmp.updateClasses(layer, classes)

ranges = []
for i, r in enumerate(tmp.ranges()):
    sym = QgsSymbol.defaultSymbol(layer.geometryType())

    # 白 → 濃赤(不透明)
    t = i / max(1, classes - 1)
    color = QColor(
        int(255 - 75 * t),  # R
        int(255 * (1 - t)), # G
        int(255 * (1 - t)), # B
        255                 # αは固定
    )
    sym.setColor(color)

    # 最大値レンジだけ強調
    if i == classes - 1:
        sym.setOpacity(1.0)

    label = f"{int(r.lowerValue())}{int(r.upperValue())}"
    ranges.append(QgsRendererRange(r.lowerValue(), r.upperValue(), sym, label))

renderer = QgsGraduatedSymbolRenderer("cnt", ranges)
renderer.setMode(QgsGraduatedSymbolRenderer.Quantile)
layer.setRenderer(renderer)

# レイヤ全体を 70% 透明に
layer.setOpacity(0.8)

print(f"[TIME] Renderer: {time.perf_counter() - t0:.2f}s")

# ============================================================
# 表示
# ============================================================
QgsProject.instance().addMapLayer(layer)
iface.mapCanvas().setExtent(layer.extent())
iface.mapCanvas().refresh()

print(f"[TIME] TOTAL: {time.perf_counter() - T_ALL:.2f}s")

image.png

また、上記コードを日本の矩形でH3indexの解像度4だと18秒ほどで表示されます。

image.png

このほかにも、DataBricksのMarketplaceにあるcarto_overture_maps_transportationデータを用いて東京都23区矩形でのH3あたりの道路長なども10秒ほどで計算することが可能です。

image.png

おわりに

このように、DataBricksからQGISにも様々なデータが持ってこれる事がわかりました。
下図はとりあえず全世界のPlaceをチューニングした集計でやってみると5400万件30秒ほどで出してくれるのでなかなかですね、もっとDBに詳しいひとがいじるともっと早いかもしれません。
また、DataBricksのAIへもリクエストを投げることができそうなのですが、ちょっとややこしいのでそれはまたこんどの機会に

image.png

6
0
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
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?