GEO
このドキュメントで説明するGEO型は、Dorisの実際のデータ型ではなく、String/Varchar型をベースに格納されるデータの特定フォーマットと、関連する関数の使用方法です。
地理空間型は、地理空間データを格納・操作するためのデータベースの特殊なデータ型で、点、線、ポリゴンなどの幾何学的オブジェクトを表現できます。主な目的は以下の通りです:
- 地理的位置情報の格納(例:経度と緯度)
- 空間クエリのサポート(例:距離計算、領域包含、交差判定)
- 地理空間解析の処理(例:バッファ解析、経路計画) 地理情報システムは、地図サービス、物流スケジューリング、位置ベースのソーシャルネットワーキング、気象監視などで広く使用されています。コア要件は、大量の空間データを効率的に格納し、低レイテンシの空間計算をサポートすることです。
コアエンコーディング技術
S2 Geometry Library
S2 GeometryはGoogleが開発した球面幾何学エンコーディングシステムです。そのコア思想は、球面から平面への投影を通じて、グローバルな地理空間データの効率的なインデックス化を実現することです。
コア原理
- 球面投影:地球の球面を正六面体の6つの面に投影し、3D球面データを2D平面データに変換します。
- 階層グリッド分割:各面を再帰的に四角形グリッド(セル)に分割し、各セルはさらに4つの小さなサブセルに細分化でき、30レベルの精度を持つ階層構造を形成します(レベルが高いほど、セル面積が小さく、精度が高くなります)。
- 64ビットエンコーディング:各セルに一意の64ビットIDが割り当てられ、このIDを通じて空間位置を迅速に特定し、空間関係を判定できます。
- Hilbert曲線順序付け:Hilbert空間充填曲線を使用してセルをエンコードし、空間的に隣接するセルが連続するIDを持つようにして、範囲クエリのパフォーマンスを最適化します。
利点
- 高精度でスムーズな遷移:30段階の階層で、グローバル(レベル0)からセンチメートルレベル(レベル30)まての精度範囲を持ち、スムーズな遷移を保証して異なるシナリオのニーズを満たします。
- グローバル範囲クエリでの効率性:大規模な空間クエリ(例:大陸横断、国境を越えた地域解析)に適しており、パフォーマンスの大幅な劣化がありません。
- 効率的な空間関係計算:包含、交差などの関係をセルIDを通じて迅速に判定でき、複雑な幾何学的操作を回避できます。
GeoHashエンコーディング
GeoHashは正距円筒図法に基づく地理符号化手法で、経度と緯度を文字列に変換することで空間インデックスを実現します。
コア原理
- 平面投影:地球の球面を平面として近似し、経度と緯度の二進分割を通じて領域を再帰的に分割します。
- 矩形グリッド分割:地球表面を異なる精度の矩形セルに分割します。文字列の長さが精度を決定し(最大12文字)、文字が1つ追加されるごとに精度が約10倍向上します。
- Z次曲線エンコーディング:経度と緯度の二進ビットを交互に切り詰めることでZ次曲線を形成し、2D座標を1D文字列に変換します。
特徴
- インデックスの利便性:文字列のプレフィックスマッチングを通じて隣接領域を迅速にクエリできます(例:同じプレフィックスを持つGeoHashコードは空間的に隣接する領域に対応)。
- 制限事項:
- 限定的な精度レベル:最大12レベルで、レベル間の急激な遷移により、高精度でスムーズな分割のニーズを満たすことが困難です。
- Z次曲線の可変性:曲線のジャンプにより、空間的に隣接する領域が不連続なコードを持つ可能性があり、範囲クエリの精度に影響します。
- 大規模クエリでの低効率:グローバル範囲をクエリする際、大量の離散セルをスキャンする必要があり、パフォーマンスが低下します。
包括的比較と選択
S2 Geometry LibraryとGeoHashの特徴を包括的に比較した結果、地理空間処理のサードパーティ依存関係としてS2 Geometry Libraryを選択します。主な理由は以下の通りです:
- グローバル範囲クエリへの適応性:S2の階層グリッド設計は大規模な空間解析により適しており、GeoHashは地域横断クエリでパフォーマンスのボトルネックがあります。
- 精度とスムーズさ:S2の30レベル階層はグローバルからセンチメートルレベルまでのスムーズな遷移を実現でき、マルチシナリオの精度要件を満たし、GeoHashの12レベル分割よりも優秀です。
- 空間連続性:Hilbert曲線はZ次曲線よりも優れた空間連続性を持ち、範囲クエリでの冗長計算を削減できます。
WKTの紹介
WKT(Well-Known Text)は地理空間データを表現するための標準テキスト形式です。
定義
- テキスト形式:テキスト文字列で幾何学的オブジェクトの構造と座標を記述します。
- 特徴:人間が読みやすく、編集が容易で、手動入力や簡単なデータ交換に適しています。
構文構造
- 基本形式:GeometryType(CoordinateValues)
- 一般的な幾何学的型:
- Point:POINT(longitude, latitude)
例:POINT(112.46, 45.23)は点の経度と緯度を表します。 - LineString:LINESTRING(point1, point2)
例:LINESTRING(0 0, 1 1)は2点を結ぶ線分を表します。 - Polygon:POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))
- Point:POINT(longitude, latitude)
WKBの紹介
WKB(Well-Known Binary)は地理空間データを表現するための標準バイナリデータ形式です。
定義
- バイナリ形式:バイナリエンコーディングで幾何学的オブジェクトを表現し、WKTよりもコンパクトで効率的です。
- 特徴:コンピュータによる内部格納と送信に最適化されており、スペースを節約し、高速な解析が可能です。
エンコーディング構造
WKBは以下の部分で構成されます:
- バイトオーダー(1バイト):
- 0x00:Big Endian(ネットワークバイトオーダー)
- 0x01:Little Endian(Intel/AMDアーキテクチャで一般的)
- 幾何学型(4バイト整数):
- 1:Point
- 2:LineString
- 3:Polygon
- ...(その他の型)
- 座標値:
- Point:x, y(またはx, y, z)
- LineString:point1の座標、point2の座標
- Polygon:point1の座標、point2の座標...
例
01 01 00 00 00 00 00 00 00 00 F0 3F 00 00 00 00 00 00 00 40
└─┘ └─┘ └───────────────┘ └───────────────┘
│ │ │ │
Little Endian Point type x=1.0 y=2.0
GeoPoint タイプ
- StringまたはVarcharを使用したWKT形式の保存
CREATE TABLE simple_point ( id INT, wkt STRING) ;
INSERT INTO simple_point VALUES(1,'POINT(121.4737 31.2304)');
create table simple_point(id int, wkt VARCHAR(255);
INSERT INTO simple_point VALUES(1,'POINT(121.4737 31.2304)');
WKT形式のクエリ
select st_astext(st_geometryfromtext(wkt)) from simple_point;
+-------------------------------------+
| st_astext(st_geometryfromtext(wkt)) |
+-------------------------------------+
| POINT (121.4737 31.2304) |
+-------------------------------------+
- WKB形式を使用した保存
CREATE TABLE simple_point ( id INT, wkb STRING) ;
INSERT INTO simple_point VALUES(1,'\x01010000005f07ce19515e5e4097ff907efb3a3f40');
create table simple_point(id int, wkb VARCHAR(255);
INSERT INTO simple_point VALUES(1,'\x01010000005f07ce19515e5e4097ff907efb3a3f40');
WKB形式のクエリ
select st_astext(st_geometryfromwkb(wkb)) from simple_point;
+------------------------------------+
| st_astext(st_geometryfromwkb(wkb)) |
+------------------------------------+
| POINT (121.4737 31.2304) |
+------------------------------------+
- 浮動小数点数を使用した座標の保存(xは緯度、yは経度)
CREATE TABLE simple_point_double (id INT,x DOUBLE,y DOUBLE)
INSERT INTO simple_point_double VALUES(0,1,2);
Floating-Point形式の照会
select st_astext(st_point(x,y)) from simple_point_double;
+--------------------------+
| st_astext(st_point(x,y)) |
+--------------------------+
| POINT (1 2) |
+--------------------------+
GeoLine型
- StringまたはVarcharを使用したWKT形式の保存
CREATE TABLE simple_line ( id INT, wkt STRING)
INSERT INTO simple_line VALUES(1,'LINESTRING(116.4074 39.9042, 121.4737 31.2304)');
CREATE TABLE simple_line ( id INT, wkt VARCHAR(255))
INSERT INTO simple_line VALUES(1,'LINESTRING(116.4074 39.9042, 121.4737 31.2304)');
WKT形式のクエリ
select st_astext(st_linefromtext(wkt)) from simple_line;
+-------------------------------------------------+
| st_astext(st_linefromtext(wkt)) |
+-------------------------------------------------+
| LINESTRING (116.4074 39.9042, 121.4737 31.2304) |
+-------------------------------------------------+
- WKB形式を使用した格納
CREATE TABLE simple_line ( id INT, wkb STRING)
INSERT INTO simple_line VALUES(1,'\x010200000002000000fc1873d7121a5d4088855ad3bcf343405f07ce19515e5e4097ff907efb3a3f40');
CREATE TABLE simple_line ( id INT, wkb VARCHAR(255))
INSERT INTO simple_line VALUES(1,'\x010200000002000000fc1873d7121a5d4088855ad3bcf343405f07ce19515e5e4097ff907efb3a3f40');
WKBフォーマットのクエリ
select st_astext(st_geometryfromwkb(wkb)) from simple_line;
+-------------------------------------------------+
| st_astext(st_geometryfromwkb(wkb)) |
+-------------------------------------------------+
| LINESTRING (116.4074 39.9042, 121.4737 31.2304) |
+-------------------------------------------------+
GeoPolygon型
- StringまたはVarcharを使用したWKT形式の格納
CREATE TABLE simple_polygon ( id INT, wkt STRING)
INSERT INTO simple_polygon VALUES(1,'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))');
CREATE TABLE simple_polygon ( id INT, wkt VARCHAR(255))
INSERT INTO simple_polygon VALUES(1,'POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))');
WKT形式のクエリ
select st_astext(st_polygon(wkt)) from simple_polygon;
+------------------------------------------+
| st_astext(st_polygon(wkt)) |
+------------------------------------------+
| POLYGON ((10 0, 10 10, 0 10, 0 0, 10 0)) |
+------------------------------------------+
- WKB Format を使用した保存
CREATE TABLE simple_polygon_wkb ( id INT, wkb STRING)
INSERT INTO simple_polygon_wkb VALUES(1,'\x010300000001000000050000000000000000002440000000000000000000000000000024400000000000002440000000000000000000000000000024400000000000000000000000000000000000000000000024400000000000000000');
CREATE TABLE simple_polygon_wkb ( id INT, wkb VARCHAR(255))
INSERT INTO simple_polygon_wkb VALUES(1,'\x010300000001000000050000000000000000002440000000000000000000000000000024400000000000002440000000000000000000000000000024400000000000000000000000000000000000000000000024400000000000000000');
WKB Formatのクエリ
select st_astext(st_geometryfromwkb(wkb)) from simple_polygon_wkb;
+------------------------------------------+
| st_astext(st_geometryfromwkb(wkb)) |
+------------------------------------------+
| POLYGON ((10 0, 10 10, 0 10, 0 0, 10 0)) |
+------------------------------------------+
GeoMultiPolygon型
- StringまたはVarcharを使用したWKT形式の格納
CREATE TABLE simple_multipolygon ( id INT, wkt STRING)
INSERT INTO simple_multipolygon VALUES(1,'MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)),((20 20, 20 30, 30 30, 30 20, 20 20)))');
CREATE TABLE simple_multipolygon ( id INT, wkt VARCHAR(255))
INSERT INTO simple_multipolygon VALUES(1,'MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)), -- 第一个多边形((20 20, 20 30, 30 30, 30 20, 20 20)) -- 第二个多边形)');
WKT形式のクエリ
select st_astext(st_geometryfromtext(wkt)) from simple_multipolygon;
+----------------------------------------------------------------------------------------+
| st_astext(st_geometryfromtext(wkt)) |
+----------------------------------------------------------------------------------------+
| MULTIPOLYGON (((10 0, 10 10, 0 10, 0 0, 10 0)), ((30 20, 30 30, 20 30, 20 20, 30 20))) |
+----------------------------------------------------------------------------------------+
注意: GeoMultiPolygonのWKB形式変換はまだサポートされていません
GeoCircle型
格納方法(中心座標と半径を浮動小数点数で格納) 円はWKBおよびWKT形式に準拠していないため、中心座標 (x, y) と半径 (R) をそれぞれ格納するために3つの浮動小数点数が必要です:
create table simple_circle(id int, X double,Y double, R double)
INSERT INTO simple_circle VALUES(1,1.0,1.0,2);
Query circle
select st_astext(st_circle(X,Y,R)) from simple_circle;
+-----------------------------+
| st_astext(st_circle(X,Y,R)) |
+-----------------------------+
| CIRCLE ((1 1), 2) |
+-----------------------------+
制約
インデックス
DorisはGeoタイプを直接実装せず、WKTとWKBを使用して保存・変換するため、インデックス技術を通じたGEOタイプクエリのクエリ高速化はできません。
WKTをGEO出力に変換する際は、13桁の精度のみが保証されます:
mysql> SELECT ST_AsText(ST_GeometryFromText("POINT (1 3.1415926535897223)"));
+----------------------------------------------------------------+
| ST_AsText(ST_GeometryFromText("POINT (1 3.1415926535897223)")) |
+----------------------------------------------------------------+
| POINT (1 3.14159265358972) |
+----------------------------------------------------------------+
バイナリをGEO出力に変換する際は、13桁の精度のみが保証されます:
mysql> select ST_AsText(ST_GeomFromWKB(ST_AsBinary(ST_Point(24.7,3.141592653589793))));
+--------------------------------------------------------------------------+
| ST_AsText(ST_GeomFromWKB(ST_AsBinary(ST_Point(24.7,3.141592653589793)))) |
+--------------------------------------------------------------------------+
| POINT (24.7 3.1415926535898) |
+--------------------------------------------------------------------------+
DorisにおけるGeoタイプの一般的な用途と手法
地球上の2点間の距離計算
北京から上海までの距離 北京の座標 (116.4074, 39.9042) と上海の座標 (121.4737, 31.2304):
select ST_DISTANCE_SPHERE(116.4074, 39.9042, 121.4737, 31.2304);
+----------------------------------------------------------+
| ST_DISTANCE_SPHERE(116.4074, 39.9042, 121.4737, 31.2304) |
+----------------------------------------------------------+
| 1067311.8461903075 |
+----------------------------------------------------------+

北京からニューヨークまでの距離 北京の座標 (116.4074, 39.9042) とニューヨークの座標 (-74.0060, 40.7128):
select ST_DISTANCE_SPHERE(116.4074, 39.9042, -74.0060, 40.7128);
+----------------------------------------------------------+
| ST_DISTANCE_SPHERE(116.4074, 39.9042, -74.0060, 40.7128) |
+----------------------------------------------------------+
| 10989107.361809434 |
+----------------------------------------------------------+

地球の球面上の領域の面積計算
New Yorkの面積の推定 New York地域をポリゴンでおおまかに囲み、面積を計算します:
SELECT ST_AREA_SQUARE_KM(
ST_GeomFromText('POLYGON((
-74.2591 40.9155,
-73.8726 40.9147,
-73.7004 40.7506,
-73.9442 40.5840,
-74.0817 40.6437,
-74.1502 40.6110,
-74.0984 40.6550,
-74.0431 40.7290,
-74.0136 40.7903,
-73.9352 40.8448,
-74.2591 40.9155
))'));
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ST_AREA_SQUARE_KM( ST_GeomFromText('POLYGON((-74.2591 40.9155, -73.8726 40.9147, -73.7004 40.7506, -73.9442 40.5840, -74.0817 40.6437,-74.1502 40.6110,-74.0984 40.6550,-74.0431 40.7290,-74.0136 40.7903, -73.9352 40.8448, -74.2591 40.9155))' )) |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| 744.3806189617659 |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
