MySQL ST_PointFromGeoHash で得られる点はどこの点

 ST_GeoHashで得たハッシュ文字列を ST_PointFromGeoHashでPOINTに変換する際に選ばれる点は、どこの点なのかという疑問が、先日発生しました。

MySQL の GeoHash系関数に、当該GeoHashの範囲を得られる何か(ST_PolyFromGeoHashとかST_MaxLatFromGeoHashとかのようなもの)があれば簡単にこの問いには答えられるのですが、あいにくそれらの関数はないため、とりあえず状況証拠から想像する作戦を採ることにしました。

概観してみる

 まずは、GeoHashの桁数に応じて PointFromGeoHash の値がどのように変化するのかを概観してみることにします。

mysql> set @pstr='POINT(35.123456789012 136.987654321098)';
Query OK, 0 rows affected (0.00 sec)
mysql> WITH keta (k) AS (
    -> VALUES ROW(10),ROW(9),ROW(8),ROW(7),ROW(6),ROW(5),ROW(4),ROW(3),ROW(2),ROW(1))
    -> SELECT ST_GeoHash(ST_GeomFromText(@pstr,4326), keta.k) gh,
    ->        ST_AsText(ST_PointFromGeoHash(ST_GeoHash(ST_GeomFromText(@pstr,4326), keta.k),4326)) pnt,
    ->        ST_latFromGeoHash(ST_GeoHash(ST_GeomFromText(@pstr,4326), keta.k)) lat,
    ->        keta.k
    -> FROM keta
    -> ORDER BY keta.k DESC;
+------------+----------------------------+-----------+----+
| gh         | pnt                        | lat       | k  |
+------------+----------------------------+-----------+----+
| xn1rv4p2gw | POINT(35.123455 136.98765) | 35.123455 | 10 |
| xn1rv4p2g  | POINT(35.12344 136.98765)  |  35.12344 |  9 |
| xn1rv4p2   | POINT(35.1234 136.9877)    |   35.1234 |  8 |
| xn1rv4p    | POINT(35.124 136.988)      |    35.124 |  7 |
| xn1rv4     | POINT(35.126 136.98)       |    35.126 |  6 |
| xn1rv      | POINT(35.13 137)           |     35.13 |  5 |
| xn1r       | POINT(35 137)              |        35 |  4 |
| xn1        | POINT(34 137)              |        34 |  3 |
| xn         | POINT(37 141)              |        37 |  2 |
| x          | POINT(22 158)              |        22 |  1 |
+------------+----------------------------+-----------+----+
10 rows in set (0.00 sec)

 ついでに、ST_PointFromGeoHash で得られる緯度と、ST_LatFromGeoHash で得られる緯度が同じものになることの確認もしています。

 さて、まず緯度に注目してみると、GeoHashの桁数が短くなるのに対して、緯度の値は上に行ったり下に行ったりしていることがわかります。緯度も同様。なんだか不思議な動きです。

端っこの値ではない

 6桁の GeoHash を例にとって、これが上下左右いずれかの端っこであるか否かを確認してみることにします。
6桁のPOINTは、これ。

| xn1rv4     | POINT(35.126 136.98) 

 まず緯度方向に微少量揺らした結果:

mysql> SELECT ST_GeoHash(ST_GeomFromText('POINT(35.1259 136.98)',4326),6) h UNION ALL
    -> SELECT ST_GeoHash(ST_GeomFromText('POINT(35.126  136.98)',4326),6) h UNION ALL
    -> SELECT ST_GeoHash(ST_GeomFromText('POINT(35.1261 136.98)',4326),6) h;
+--------+
| h      |
+--------+
| xn1rv4 |
| xn1rv4 |
| xn1rv4 |
+--------+
3 rows in set (0.01 sec)

 次に、経度方向に微少量揺らした結果:

mysql> SELECT ST_GeoHash(ST_GeomFromText('POINT(35.126  136.979)',4326),6) h UNION ALL
    -> SELECT ST_GeoHash(ST_GeomFromText('POINT(35.126  136.98)',4326),6) h  UNION ALL
    -> SELECT ST_GeoHash(ST_GeomFromText('POINT(35.126  136.981)',4326),6) h;
+--------+
| h      |
+--------+
| xn1rv4 |
| xn1rv4 |
| xn1rv4 |
+--------+
3 rows in set (0.00 sec)

 いずれも結果のGeoHashに変化がありませんね。得られたPOINTの少し上も下も、少し右も左も、同じGeoHashであることがわかります。ということは、得られたPOINTは、端っこではない、ということです。

 というところで「端っこでないなら、まぁ中心なんだろうな」と想像したところで、実験はおしまい。