GEO
このドキュメントで説明するGEO型は、Dorisに実在するデータ型ではありません。String/Varchar型で保存する地理空間データの形式と、関連する関数の使い方を指します。
地理空間型は、地理空間データの保存と操作に使用される特殊なデータ型です。点、線、ポリゴンなどの幾何オブジェクトを表現できます。主な用途は次のとおりです。
- 地理的位置情報(経度や緯度など)を保存する。
- 空間クエリ(距離の計算、領域への包含判定、交差判定など)をサポートする。
- 地理空間分析(バッファ分析、経路計画など)を実行する。 地理情報システムは、地図サービス、物流の配送計画、位置情報に基づくソーシャルネットワーク、気象観測などで広く利用されています。大量の空間データを効率的に保存し、低遅延の空間計算をサポートすることが重要です。
主なエンコーディング技術
S2 Geometryライブラリ
S2 GeometryはGoogleが開発した球面幾何のエンコーディングシステムです。球面を平面に投影することで、地球全体の地理空間データを効率的にインデックス化します。
基本原理
- 球面投影: 地球の球面を正六面体の6つの面に投影し、3Dの球面データを2Dの平面データに変換します。
- 階層的なグリッド分割: 各面を四辺形のグリッド(セル)に再帰的に分割します。各セルはさらに4つの小さなセルに分割でき、30レベルの精度を持つ階層構造を形成します。レベルが高いほどセルの面積は小さくなり、精度は高くなります。
- 64ビットエンコーディング: 各セルに一意の64ビットIDを割り当てます。このIDを使って空間上の位置をすばやく特定し、空間的な関係を判定できます。
- ヒルベルト曲線による順序付け: ヒルベルト空間充填曲線を使用してセルをエンコードします。空間的に隣接するセルに連続したIDを割り当て、範囲クエリの性能を最適化します。
利点
- 高精度と滑らかな精度の切り替え: 30レベルの階層により、地球全体(レベル0)からセンチメートル単位(レベル30)まで精度を滑らかに切り替え、さまざまな用途に対応できます。
- 広域の範囲クエリでの効率: 大陸や国をまたぐ地域分析など、大規模な空間クエリに適しており、大幅な性能低下を招きません。
- 効率的な空間関係の計算: 複雑な幾何演算を避け、セルIDを使って包含や交差などの関係をすばやく判定できます。
GeoHashエンコーディング
GeoHashは正距円筒図法に基づくジオコーディング方式で、経度と緯度を文字列に変換して空間インデックスを実現します。
基本原理
- 平面投影: 地球の球面を平面として近似し、経度と緯度を二分割することで領域を再帰的に分割します。
- 長方形のグリッド分割: 地表を精度の異なる長方形のセルに分割します。文字列の長さが精度を決定し(最大12文字)、文字が増えるごとに精度が約10倍になります。
- Zオーダー曲線によるエンコーディング: 経度と緯度の二進数のビットを交互に取り出してZオーダー曲線を形成し、2Dの座標を1Dの文字列に変換します。
特徴
- インデックス化の容易さ: 文字列のプレフィックスマッチにより、隣接する領域をすばやく検索できます(同じプレフィックスを持つGeoHashコードは、空間的に隣接する領域に対応します)。
- 制限:
- 精度レベルの制約: 最大12レベルで、レベル間の精度差が大きいため、高精度で滑らかな分割を必要とする用途には対応しにくい。
- Zオーダー曲線の不連続性: 曲線の飛びによって、空間的に隣接する領域のコードが連続しない場合があり、範囲クエリの精度に影響する。
- 大規模クエリでの効率の低さ: 地球全体の範囲を検索する場合、多数の離散的なセルを走査する必要があり、性能が低下する。
総合的な比較と選定
S2 GeometryライブラリとGeoHashの特徴を総合的に比較し、地理空間処理のサードパーティー依存ライブラリとしてS2 Geometryライブラリを選択しました。主な理由は次のとおりです。
- 広域の範囲クエリへの適合性: S2の階層的なグリッド設計は大規模な空間分析に適しています。一方、GeoHashでは複数の地域にまたがるクエリが性能上のボトルネックになります。
- 精度と滑らかな切り替え: S2の30レベルの階層では、地球全体からセンチメートル単位まで精度を滑らかに切り替えられます。さまざまな用途の精度要件に対応でき、GeoHashの12レベルの分割より優れています。
- 空間的な連続性: ヒルベルト曲線は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)は二点を結ぶ線分を表します。 - 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: ビッグエンディアン(ネットワークバイト順)
- 0x01: リトルエンディアン(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);
浮動小数点形式のクエリ
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形式での保存
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形式のクエリ
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)の保存には、それぞれ浮動小数点数を使用します。
create table simple_circle(id int, X double,Y double, R double)
INSERT INTO simple_circle VALUES(1,1.0,1.0,2);
円のクエリ
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型の主な用途と使用方法
地球上の二点間の距離の計算
北京から上海までの距離 北京(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 |
+----------------------------------------------------------+

地球の球面上にある地域の面積の計算
ニューヨークの面積の推定 ニューヨークの地域をポリゴンで大まかに囲み、面積を計算します。
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 |
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
