DatabricksのビルトインH3関数の動作を実践を通じて理解する

以前サンプルノートブックを動かしましたが、理解が表層的でした。なので、きちんと動かして理解したいと思いました。

https://qiita.com/taka_yayoi/items/0f5423a93631f3fcd749

全部は動かしませんが、主要なところを。

# h3_coverash3

[h3\_coverash3\(geographyExpr, resolutionExpr\)](https://docs.databricks.com/en/sql/language-manual/functions/h3_coverash3.html)

> 入力された線形、領域ジオグラフィを完全にカバーし、指定された解像度の六角形、五角形の最小セットに対応するH3セルID(BIGINT表現)のARRAYを返却します。

分かるような分からないような。なので、目で確認します。可視化は[Mosaic](https://github.com/databrickslabs/mosaic)を使います。

```py
%pip install "databricks-mosaic==0.4.0" 
```

```py
# -- mosaicのセットアップ
import mosaic as mos

mos.enable_mosaic(spark, dbutils)

# -- databricks + spark 関数のインポート
from pyspark.sql import functions as F
from pyspark.sql.functions import col, udf
from pyspark.sql.types import *

# -- その他のインポート
import os
import pathlib
import requests
import warnings
```

ジオグラフィを用意します。

```py
# ジオグラフィ(ポリゴン)
geography = "POLYGON((-122.4194 37.7749,-118.2437 34.0522,-74.0060 40.7128,-122.4194 37.7749))"

geo_df = spark.sql(
    f"SELECT '{geography}' AS polygon"
)
display(geo_df)
```
![Screenshot 2024-02-12 at 10.50.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/45eddd2a-fd23-5ccf-ece9-33f07e858f20.png)

これで形状を理解するのは困難なので地図上に表示します。`%%mosaic_kepler`マジックコマンドでお手軽にプロットできます。

```py
%%mosaic_kepler
geo_df "polygon" "geometry"
```

なるほど。
![Screenshot 2024-02-12 at 10.51.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/3c66b3c9-e012-6f01-9c59-cee1c9620ad3.png)

それでは`h3_coverash3`を使います。二つ目の引数は解像度です。`explode`を使っているのはセルIDをレコードに分解するためです。

```py
h3_df = spark.sql(
    f"SELECT h3_coverash3('{geography}', 0) AS H3_cells"
).select(F.explode(col("H3_cells")).alias("H3_cells"))
display(h3_df)
```
H3セルIDが返ってきます。
![Screenshot 2024-02-12 at 10.52.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/b8722e24-9354-3f4c-9299-f3f3cce85789.png)

これも可視化します。

```py
%%mosaic_kepler
h3_df "H3_cells" "h3" 10
```

分かってきました。まさしく、指定された解像度でジオメトリを完全にカバーするポリゴンのセルIDを返却すると言うことですね。
![Screenshot 2024-02-12 at 10.53.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/bf94aed4-7f01-415d-c15e-75f2d974c25b.png)

なので、解像度を上げると…。

```py
h3_df = spark.sql(
    f"SELECT h3_coverash3('{geography}', 1) AS H3_cells"
).select(F.explode(col("H3_cells")).alias("H3_cells"))
display(h3_df)
```

より多くのセルが返却されます。
![Screenshot 2024-02-12 at 10.55.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/34c26f70-bc79-9edc-9a7a-06a926b72fa1.png)

これを可視化すると。
```py
%%mosaic_kepler
h3_df "H3_cells" "h3" 10
```

より解像度の高いポリゴンでジオメトリを表現できる訳です。
![Screenshot 2024-02-12 at 10.56.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/7f70662b-ff36-0814-1203-947731c6691c.png)

# h3_longlatash3

[h3\_longlatash3\(longitudeExpr, latitudeExpr, resolutionExpr\)](https://docs.databricks.com/en/sql/language-manual/functions/h3_longlatash3.html)

これはシンプル。指定された緯度経度に対応するセルIDを返却します。

```py
lon = -122.4783
lat = 37.8199

# ジオメトリ(ポイント)
geometry = f"POINT({lon} {lat})"

geo_df = spark.sql(
    f"SELECT '{geometry}' AS point"
)
display(geo_df)
```
![Screenshot 2024-02-12 at 10.57.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/26f02d9d-8c5c-fadd-dc29-7de8c18e53ff.png)

```py
%%mosaic_kepler
geo_df "point" "geometry"
```
![Screenshot 2024-02-12 at 10.58.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/57d5e005-ca44-c3cb-53c8-85969eb6f053.png)

```py
h3_df = spark.sql(
    f"SELECT h3_longlatash3({lon}, {lat}, 13) AS H3_cells"
)
display(h3_df)
```
```
635714569676958015
```

```py
%%mosaic_kepler
h3_df "H3_cells" "h3"
```

ちゃんとヘキサゴンになってます。
![Screenshot 2024-02-12 at 10.59.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/45cc33dd-0ef7-bd70-fd0c-d862d6b63d23.png)

[h3\_pointash3\(geographyExpr, resolutionExpr\)](https://docs.databricks.com/en/sql/language-manual/functions/h3_pointash3.html)はジオグラフィ形式で受け取ると言うことで。

# h3_polyfillash3

> 入力領域ジオグラフィに包含される、指定された解像度の六角形、五角形に対応するH3セルID(BIGINT)のARRAYを返却します。

[h3_coverash3](#h3_coverash3)と若干違います。

```py
# ジオグラフィ(ポリゴン)
geography = "POLYGON((-122.4194 37.7749,-118.2437 34.0522,-74.0060 40.7128,-122.4194 37.7749))"

geo_df = spark.sql(
    f"SELECT '{geography}' AS polygon"
)
display(geo_df)
```
```
POLYGON((-122.4194 37.7749,-118.2437 34.0522,-74.0060 40.7128,-122.4194 37.7749))
```

上と同じジオグラフィで。

```py
%%mosaic_kepler
geo_df "polygon" "geometry"
```
![Screenshot 2024-02-12 at 11.03.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/c478251d-f45d-e1cc-5764-7a6ae5e144f1.png)

```py
h3_df = spark.sql(
    f"SELECT h3_polyfillash3('{geography}', 0) AS H3_cells"
).select(F.explode(col("H3_cells")).alias("H3_cells"))
display(h3_df)
```
```
クエリー結果が返されませんでした
```

これも分かってきました。解像度が0なのがミソ。1にします。

```py
h3_df = spark.sql(
    f"SELECT h3_polyfillash3('{geography}', 1) AS H3_cells"
).select(F.explode(col("H3_cells")).alias("H3_cells"))
display(h3_df)
```
返ってきました。
```
581698825698148351
```

```py
%%mosaic_kepler
h3_df "H3_cells" "h3"
```
![Screenshot 2024-02-12 at 11.05.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/d48cffbf-4e05-71f7-560a-2d04ebab0322.png)

説明の通り、ジオグラフィに**内包**されるセルを返却すると言うことですね。解像度が低いとセルが大きくてジオグラフィに入りきらなかったのでした。さらに解像度を上げると…。

```py
h3_df = spark.sql(
    f"SELECT h3_polyfillash3('{geography}', 2) AS H3_cells"
).select(F.explode(col("H3_cells")).alias("H3_cells"))
display(h3_df)
```
```
H3_cells
586146350232502271
586147449744130047
586198577034821631
586152397546455039
586199676546449407
586153497058082815
586142501941805055
586201325813891071
```

```py
%%mosaic_kepler
h3_df "H3_cells" "h3"
```
![Screenshot 2024-02-12 at 11.07.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/445a4379-4d03-11ab-b70a-c24581a05526.png)

```py
h3_df = spark.sql(
    f"SELECT h3_polyfillash3('{geography}', 3) AS H3_cells"
).select(F.explode(col("H3_cells")).alias("H3_cells"))
display(h3_df)
```
なるほど。
![Screenshot 2024-02-12 at 11.08.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/9e8509f5-f207-e40d-70b9-ed121a9e8ce9.png)


# h3_ischildof

[h3\_ischildof\(h3CellId1Expr, h3CellId2Expr\)](https://docs.databricks.com/en/sql/language-manual/functions/h3_ischildof.html)

> 最初のH3セルIDが2番目のH3セルIDと同じあるいは子供関係である場合にtrueを返却します。

動作確認します。

```sql
%sql
SELECT h3_ischildof(608693241318998015, 599686042433355775)
```
```
h3_ischildof(608693241318998015, 599686042433355775)
true
```

これも目で確認します。

```py
h3_df = spark.sql(
  "SELECT col1 AS H3_cells FROM VALUES 608693241318998015, 599686042433355775"
)
display(h3_df)
```
```
H3_cells
608693241318998015
599686042433355775
```
```py
%%mosaic_kepler
h3_df "H3_cells" "h3"
```

デフォルト表示だと(当然ですが)被ってしまうので、サイドメニューのcoverageを調整します。
![Screenshot 2024-02-12 at 11.10.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/8006fb92-0246-07e7-431b-754efd58caf8.png)

# h3_hexring

[h3\_hexring\(h3CellIdExpr, kExpr\)](https://docs.databricks.com/en/sql/language-manual/functions/h3_hexring.html)

> オリジンのH3セルを中心とし、オリジンH3セルからkのグリッド距離を持つ空洞六角形リングを形成するH3セルIDの配列を返却します。

これも目で見てみましょう。

```py
cell_id = 599686042433355775

h3_df = spark.sql(
    f"SELECT {cell_id} AS H3_cells"
)
display(h3_df)
```
```
H3_cells
599686042433355775
```

```py
%%mosaic_kepler
h3_df "H3_cells" "h3"
```

サンノゼ。
![Screenshot 2024-02-12 at 11.12.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/e6835eca-e302-c79f-b954-0b3fb7bd9f9f.png)

```py
h3_df = spark.sql(
    f"SELECT h3_hexring({cell_id}, 1) AS H3_cells"
).select(F.explode(col("H3_cells")).alias("H3_cells"))
display(h3_df)
```
```
H3_cells
599686014516068351
599686030622195711
599686044580839423
599686038138388479
599686043507097599
599686015589810175
```
```
%%mosaic_kepler
h3_df "H3_cells" "h3"
```

説明通り。
![Screenshot 2024-02-12 at 11.13.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/4a9df65b-c9bb-9514-ce07-ca73e705c23a.png)

`k`を増やせばこの通り。
![Screenshot 2024-02-12 at 11.14.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/964603c1-62fe-dd95-bc62-31bbaf00bba7.png)

# h3_compact

[h3\_compact\(h3CellIdsExpr\)](https://docs.databricks.com/en/sql/language-manual/functions/h3_compact.html)

> 入力されたH3セルIDのセットを可能な限りコンパクトにします。

```py
h3_df = spark.sql("SELECT col1 AS H3_cells FROM VALUES 599686042433355775,599686030622195711,599686044580839423,599686038138388479,599686043507097599,599686015589810175,599686014516068351,599686034917163007,599686029548453887,599686032769679359,599686198125920255,599686040285872127,599686041359613951,599686039212130303,599686023106002943,599686027400970239,599686013442326527,599686012368584703,599686018811035647")
display(h3_df)
```
```
H3_cells
599686042433355775
599686030622195711
599686044580839423
599686038138388479
599686043507097599
599686015589810175
599686014516068351
599686034917163007
599686029548453887
599686032769679359
599686198125920255
599686040285872127
599686041359613951
599686039212130303
599686023106002943
599686027400970239
599686013442326527
599686012368584703
599686018811035647
```

```py
%%mosaic_kepler
h3_df "H3_cells" "h3"
```
![Screenshot 2024-02-12 at 13.22.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/23292e18-4bf2-b11e-5460-91894da92abc.png)

```py
h3_df = spark.sql("SELECT h3_compact(ARRAY(599686042433355775,599686030622195711,599686044580839423,599686038138388479,599686043507097599,599686015589810175,599686014516068351,599686034917163007,599686029548453887,599686032769679359,599686198125920255,599686040285872127,599686041359613951,599686039212130303,599686023106002943,599686027400970239,599686013442326527,599686012368584703,599686018811035647)) AS H3_cells").select(F.explode(col("H3_cells")).alias("H3_cells"))
display(h3_df)
```
```
H3_cells
599686030622195711
599686015589810175
599686014516068351
599686034917163007
599686029548453887
599686032769679359
599686198125920255
599686023106002943
599686027400970239
599686013442326527
599686012368584703
599686018811035647
595182446027210751
```

```py
%%mosaic_kepler
h3_df "H3_cells" "h3"
```

なるほど。
![Screenshot 2024-02-12 at 13.22.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/02f9c15b-8af1-b8c8-3455-4a030feb0994.png)



H3面白い。

### はじめてのDatabricks

[はじめてのDatabricks](https://qiita.com/taka_yayoi/items/8dc72d083edb879a5e5d)

### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)