GanosBaseが提供する地理空間グリッドモデルは、六角形構造に基づいて地理空間データを効率的に処理できます。 物流、ソーシャルネットワーク、データ分析、緊急対応など、さまざまなシナリオで広く使用されています。 このモデルは、独自の六角形グリッドシステムを活用して、データの均一な分布と固定された隣接関係を実現し、空間データ分析や経路計画などの機能を最適化します。 GanosBaseが提供する地理空間グリッドモデルは、GeoSOTおよびH3グリッドタイプをサポートし、さまざまなエンコード方法、高性能クエリ、および集計分析機能を提供します。 ジオメトリデータやラスターデータと効果的に統合して、データ処理効率とストレージの費用対効果を大幅に向上させることができます。
背景
地理的グリッドは、地球の表面を表すポリゴングリッドユニットの集合であり、他のタイプの時空間データと統合して、地理的空間内の特徴の位置情報を表すために使用することができる。 地理的グリッドコンピューティングは、粗粒から細粒までの特定のサイズのポリゴングリッドに地球の表面を細分化することによって地球の表面を近似します。 目標は、グリッドユニットの誤差範囲内で地理的特徴の位置決め及び記述を統合することである。 各グリッドユニットは、グリッドとコードとの間の1対1の対応で符号化される。 3次元地理的グリッドは、緯度および経度だけでなく、細分および符号化範囲における高さも考慮する。
GanosBaseの地理的グリッドエンジンは現在、GeoSOTとH3の2種類の地理的グリッドをカバーしています。
GeoSOTは、中国が提案した一連の地理空間細分化理論であり、離散的なマルチスケールの地域位置識別システムに発展しました。
H3は、Uberによって開発された地球の表面をカバーする2次元の地理的グリッドであり、地球の表面を表すためにグローバルで統一された多層の六角形グリッドシステムを使用しています。
H3のユニークなデザインは、従来の正方形または三角形のグリッドと比較して、より均一な分布、固定された隣接関係、および無方向性を提供する六角形の構造にあります。 これにより、空間データ分析、パス計画、ジオコーディング、ジオフェンシングなどの分野で地理空間データの編成とクエリをより正確かつ効率的に行うことができます。 GanosBaseの地理的グリッド関数を使用して、さまざまな空間範囲をグリッドコードに変換でき、グリッドコードの空間範囲、階層、および親子グリッドを決定できます。 GanosBaseは、グリッドの階層関係を完全に利用して、より合理化されたグリッドの組み合わせで空間範囲を表現し、縮退したグリッドコンピューティングをサポートしています (下図を参照) 。 さらに、GanosBaseの自己開発の地理的グリッドインデックスは、効率的なグリッドコードのクエリと集計計算の高速化に使用できます。

シナリオ
H3地理的グリッド技術は、以下を含む多くのビジネスシナリオで広く使用されています。
物流および旅行サービス: ルート計画、エリアカバレッジ分析、配信範囲の描写、地理的グリッドに基づくホットスポットエリアの発見などの機能の開発。
データ分析: 地理的グリッドに基づいて、人口密度分析、モバイルユーザー行動分析、地理的市場セグメンテーション、およびその他のビッグデータ分析フィールドを提供します。
Internet of Things (IoT): スマートシティ、環境モニタリング、資産追跡などのリアルタイムモニタリングが必要なシナリオで、地理的グリッドに基づいてモニタリングデータの空間分布を分析します。
ソーシャルネットワーク: 地理的グリッドに基づいて、位置情報サービス (LBS) 、友人位置共有、およびイベント通知を含むソーシャルネットワーキングシナリオのためのアプリケーションを構築する。
緊急対応と公共サービス: 地理的グリッドに基づいて、災害分布分析、災害警報ヒートマップ、緊急資源分布、および緊急救助地域の描写を実施します。
要約すると、H3グリッドテクノロジーは、地理空間データをより適切に管理および利用するための強力なツールを企業および開発者に提供し、位置関連の意思決定の効率と精度を向上させます。
機能
GanosBase H3地理的グリッドには、グリッド入力 /出力、親子グリッド関係判断、グリッドパス分析、グリッド照会などのさまざまな機能が含まれています。 地理的グリッドは、GanosBase他のベクトルデータを使用した空間解析用のジオメトリタイプ。 特に、GanosBase H3地理グリッドは、より合理化されたグリッドの組み合わせを使用して空間範囲を表現し、エンコードによって発生するデータベースのストレージコストを削減し、劣化もサポートします。 GanosBase H3地理的グリッドの詳細については、「GeomGrid SQLリファレンス」をご参照ください。
技術的な利点
他のオープンソースH3製品と比較して、GanosBase H3は、次の技術的利点を提供します。
より豊富なエンコード方法をサポートし、GanosBaseの点、線、および面の型を直接H3エンコーディングに変換することを可能にします。
エンコード効率とグリッドクエリ効率の両方で、パフォーマンスを大幅に最適化します。
他のGanosBaseモデルとの共同クエリ分析をサポートし、ジオメトリタイプをH3エンコーディングに直接変換したり、H3とラスターモデルを用いたグリッドに基づいてピクセル統計を実行したりすることができます。
PolarDBが提供する基盤となる多型階層ストレージを活用して、大規模なデータポイントをエンコードしてOSSに保存することで、ストレージコストを大幅に削減できます。
ベストプラクティス
この事例は、実世界のシナリオデータを使用して、GanosBaseH3を用いた空間点データの保存、エンコーディング、クエリ、最終的な表示などの機能の使用方法を紹介します。
データインポート
GanosBase H3を使用する前に、次のステートメントを実行してGeomGrid拡張子を作成する必要があります。
CREATE EXTENSION ganos_geomgrid CASCADE;GeomGridは、H3コードを表すh3gridフィールドタイプを提供します。 次のSQL文は、h3grid型のデータテーブルFOIL2013を作成します。フィールドh3_lev13は13番目のレベルH3コードを表します。 H3グリッドの異なるレベルは、異なる解像度を有する。 ビジネスニーズに基づいて解像度を定義できます。 H3の各レベルに対応する空間解像度については、コミュニティドキュメントを参照してください。
-- Create a table to store foil point data, where h3_lev13 represents the 13th level code. CREATE TABLE FOIL2013 ( id text, lon float, lat float, h3_lev13 h3grid);データベースにデータをインポートします。 FOILファイルはCSV形式で保存されます。 CSVファイルからデータをプログラムで抽出し、SQL文を実行するか、FDWメソッドを使用してインポートしてデータベースに保存できます。 この例では、GanosBase FDWモジュールは、高速データインポートを実現するために使用されます。
テストデータファイルをOSSディレクトリにアップロードします。 詳細については、「オブジェクトのアップロード」をご参照ください。
テストデータベースにGanosBase FDW拡張機能を作成します。
CREATE EXTENSION ganos_fdw CASCADE;CSVファイルを管理するサーバーを作成します。 次のSQL文では、
formatの値は'CSV'であり、管理データ形式がCSVであることを示しています。datasourceパラメーターの詳細については、「OSSのファイルパス」をご参照ください。CREATE SERVER csvserver FOREIGN DATA WRAPPER ganos_fdw OPTIONS ( datasource 'OSS://<access_id>:<secrect_key>@[<Endpoint>]/<bucket>/path_to/file.csv', format 'CSV' ); CREATE USER MAPPING FOR CURRENT_USER SERVER csvserver OPTIONS (user '<access_id>', password '<secrect_key>');OSS上のCSVファイルを外部テーブルとしてデータベースにマップします。 外部テーブルのデータは、通常のテーブルのデータと同じ方法で照会できます。 次のSQL文を参照してください。
メダリオン、pickup_longitude、およびpickup_latitude列が選択され、マップされた外部テーブルの名前はtrip_data_1です。
CREATE FOREIGN TABLE trip_data_1 ( medallion varchar, pickup_longitude varchar, pickup_latitude varchar) SERVER csvserver OPTIONS ( layer 'trip_data_1' );外部テーブルを照会する:
SELECT * FROM trip_data_1;外部テーブルデータをFOIL2013テーブルにインポートします。
INSERT INTO FOIL2013 SELECT medallion as id ,cast (pickup_longitude as double precision) as lon, cast(pickup_latitude as double precision) as lat FROM trip_data_1;FOIL2013テーブルを照会して、CSVファイル内のデータが正常にインポートされたことを確認します。
SELECT * FROM FOIL2013;
オブジェクトのエンコーディング
データをインポートした後、ポイントデータをエンコードできます。 GanosBase H3は、緯度と経度の指定、標準H3文字列、整数型H3エンコード、バイナリ型H3エンコード、ポイント型からH3への直接変換など、複数のエンコード方法を提供します。 詳細については、以下のトピックをご参照ください。
この例では、ST_H3FromLatLng関数を使用し、緯度、経度、およびターゲットレベルを指定してH3エンコードを直接取得できます。 次のSQLステートメントは、FOILテーブルのlatフィールドとlonフィールドから13番目のレベルH3エンコーディングを生成し、h3_lev13列に格納してから、ST_AsText関数を使用して特定のH3エンコーディングを照会します。
-- Level 13
UPDATE FOIL2013 SET h3_lev13 = ST_H3FromLatLng(lat,lon,13);
-- Query
SELECT id,lon,lat,ST_AsText(h3_lev13) AS h3 FROM FOIL2013 LIMIT 100;グリッド集約
グリッドの典型的なユースケースは、ヒートマップなどのテーママップを取得するためのグリッドコードに基づく空間データの空間集約と統計分析です。 たとえば、次のSQL文は、FOIL23テーブルのh3_lev13列のH3コードに基づいて、各グリッド内のポイント数 (count(*)) をカウントします。
-- Aggregate by h3_lev13
CREATE TABLE h3_count_lev13 AS
SELECT ST_AsText(h3_lev13) AS h3code,count(*) FROM FOIL2013 GROUP BY h3_lev13;
-- Query the aggregation results
SELECT ST_AsText(h3_lev13), ST_AsText(geometry),count FROM h3_count_lev13 ORDER BY count DESC;グリッドクエリ
GanosBase H3は、H3エンコードに基づいてさまざまな操作を提供します。 たとえば、次のクエリでは、ST_GridDistance 関数を使用して、空間位置 (40.71481749、-73.99100368) と対応するグリッドとの間の距離が10未満であるFOIL23内のすべてのグリッドポイントを取得します。
SELECT * FROM foil2013
WHERE
ST_GridDistance(ST_H3FromLatLng(40.71481749,-73.99100368,13),h3_lev13)<10;グリッド可視化
GanosBaseは、幾何学的データと同様にH3グリッドの視覚化をサポートしています。つまり、H3グリッドをベクトルタイルに変換して、表示用にフロントエンドにレンダリングできます。 GanosBaseは、ネイティブのH3グリッドMVT機能とインデックス作成を提供し、H3グリッドとその関連統計を簡単にクエリおよび視覚化できるようにします。 特に、GanosBaseは、動的に生成されたH3グリッドの視覚化をサポートします。 たとえば、レベル10 H3グリッドを視覚化したいが、テーブルにレベル10 H3グリッドが格納されていない場合は、ST_AsMVTとST_AsMVTGeom(ST_H3FromLatLng(lat, lon, 10), ...) を使用して、レベル10 H3グリッドの視覚化結果を動的に生成できます。 しかしながら、これは、事前保存H3グリッドよりも効率的ではない。 次のセクションでは、主に事前保存されたH3グリッドをテーブルに視覚化する方法を紹介します。
H3グリッドインデックスを作成します。 インデックスの作成は視覚化に必須ではありませんが、視覚化効率を大幅に向上できます。
CREATE INDEX ON h3_count_lev13 USING GIST(h3_lev13);H3グリッドに基づいて
(14、4826、6157)として識別されたグリッドのベクトルタイルを取得する。SELECT ST_AsMVT(tile) FROM (SELECT ST_AsMVTGeom(h3_lev13, ST_Transform(ST_TileEnvelope(14, 4826, 6157), 4326)) AS grid, count FROM h3_count_lev13 WHERE h3_lev13 && ST_Transform(ST_TileEnvelope(14, 4826, 6157), 4326)) AS tile;結果を視覚化します。 以下のアニメーションは、フロントエンドのデータベースのH3グリッドから動的にクエリされたベクトルタイルのリアルタイムレンダリングを示しています。 グリッドの色は、グリッドに対応する統計値に基づいて動的に決定される。
説明フロントエンド部分には、PythonスクリプトとHTMLファイルのみが必要です。 開始するには、Pythonスクリプトを実行してブラウザを開き、localhost:5100と入力して結果を表示します。 Pythonスクリプトは、マップ上のユーザーのマウスの位置とズームレベルに基づいて、対応するSQLクエリを自動的に生成し、データベースのクエリ結果をwebページに表示します。 詳細については、「付録」をご参照ください。

結論
地理的グリッドは、モバイルオブジェクト関連のアプリケーションシナリオに不可欠なサポートであり、軌跡、ベクトル、ラスターなどのデータ型と統合すると、膨大なビジネス価値と想像力に富んだ可能性をもたらすことができます。 この記事では、GanosBase H3地理グリッドの関連機能に焦点を当てます。H3地理グリッドを使用して、ベストプラクティスに基づいてベクトルデータの集計、空間関係の判断、および視覚化を行う方法を紹介します。 モバイルオブジェクト (MOD) をサポートする世界初のデータベースとして、GanosBaseの関連機能は、輸送、ロジスティクス、旅行、自動車などの業界で顧客側で効果的に検証されています。 従来のミドルウェアまたはビジネスコードの実装と比較して、GanosBaseは、データベースシステムの最低レベルから大規模なモバイルオブジェクトに時空間処理フレームワークを提供し、コンピューティング効率と全体的なコストを大幅に改善します。 将来的には、GanosBaseは、モバイルオブジェクト指向シナリオ向けのより効率的なネイティブデータベース内分析機能を提供し、関連分野における空間情報アプリケーションの包括的な「オンライン化」を促進します。
付録
フロントエンドのビジュアライゼーション用のPythonスクリプト:
from quart import Quart, send_file, render_template import asyncpg import io import re ## Database connection parameters CONNECTION = {"host": "YOUR-HOST-NAME-OR-IP", "port": PORT_NO, "database": "DATABASE_NAME", "user": "USER_NAME", "password": "PASSWORD"} ## Target table name/field/ID TABLE = "h3_count_lev13" H3_COL = "h3_lev13" H3_GEOM_COL = "geometry" AGG_VAL_COL = "count" COL_SRID = 4326 app = Quart(__name__, template_folder='./') @app.before_serving async def create_db_pool(): app.db_pool = await asyncpg.create_pool(**CONNECTION) @app.after_serving async def close_db_pool(): await app.db_pool.close() @app.route("/") async def home(): sql = f''' SELECT ST_Extent(ST_Transform(ST_Envelope({H3_GEOM_COL}), 4326)) FROM {TABLE}; ''' async with app.db_pool.acquire() as connection: box = await connection.fetchval(sql) box = re.findall('BOX\((.*?) (.*?),(.*?) (.*?)\)', box)[0] min_x, min_y, max_x, max_y = list(map(float, box)) bounds = [[min_x, min_y], [max_x, max_y]] center = [(min_x + max_x) / 2, (min_y + max_y) / 2] return await render_template('./index.html', center=str(center), bounds=str(bounds)) @app.route("/h3_mvt/<int:z>/<int:x>/<int:y>") async def h3_mvt(z, x, y): sql = f''' SELECT ST_AsMVT(tile.*) FROM (SELECT ST_AsMVTGeom({H3_COL}, ST_Transform(ST_TileEnvelope($1,$2,$3),{COL_SRID}), 4096, 512, true) geometry, {AGG_VAL_COL} count FROM {TABLE} WHERE ({H3_COL} && ST_Transform(ST_TileEnvelope($1,$2,$3),{COL_SRID}))) tile''' async with app.db_pool.acquire() as connection: tile = await connection.fetchval(sql, z, x, y) return await send_file(io.BytesIO(tile), mimetype='application/vnd.mapbox-vector-tile') if __name__ == "__main__": app.run(port=5100)index.htmlファイルの内容:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>map viewer</title> <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no"> <link href="https://api.mapbox.com/mapbox-gl-js/v2.14.1/mapbox-gl.css" rel="stylesheet"> <script src="https://api.mapbox.com/mapbox-gl-js/v2.14.1/mapbox-gl.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/chroma-js/2.4.2/chroma.min.js"></script> </head> <body> <div id="map" style="position: absolute;left:0; top: 0; bottom: 0; width: 100%;cursor:pointer;"></div> <div class="counter" style="position: absolute;left:2%;font-size: 20px;padding: .1em .1em;text-shadow: 3px 3px 3px black;"> <span>Current grid count:</span> <span id="count">0</span> </div> <script> let YOUR_TOKEN = "pk.eyJ1Ijoia3pmaWxlIiwiYSI6ImNqbHZueXdlZjB2cG4zdnFucGl1OHJsMjkifQ.kW_Utrh8ETQltRk6fnpa_A" mapboxgl.accessToken = YOUR_TOKEN; const map = new mapboxgl.Map({ container: "map", style: "mapbox://styles/mapbox/navigation-night-v1", center: {{ center }}, zoom: 1 }) map.on("load", () => { map.fitBounds({{ bounds }}) map.on('mousemove', 'h3', (e) => { map.getCanvas().style.cursor = "default"; if (e.features.length > 0) document.getElementById('count').innerText = e.features[0].properties.count }) map.on('mouseleave', 'h3', () => { map.getCanvas().style.cursor = "grab"; document.getElementById('count').innerText = 0 }) map.addSource("h3_source", { type: "vector", tiles: [`${window.location.href}h3_mvt/{z}/{x}/{y}`], tileSize: 512 }); // make color map const MIN = 1 const MAX = 600 const STEP = 10 color_map = chroma.scale(["#536edb", "#5d96a5", "#68be70", "#91d54d", "#cddf37", "#fede28", "#fda938", "#fb7447", "#f75a40", "#f24734", "#e9352a", "#da2723", "#cb181d"]) .domain([MIN, MAX]); let colors = [] for (let i = MIN; i < MAX; i += STEP) colors.push(color_map(i).hex(), i) colors.push(color_map(MAX).hex()) map.addLayer({ id: "h3", type: "fill", source: "h3_source", "source-layer": "default", paint: { "fill-color": [ "step", ["get", "count"], ...colors ], "fill-opacity": 0.8 } }); }); </script> </body> </html>