私は最近、ミニマリストのふりをしながらアプリを構築する練習をしました。そのためには、実績のある友人である PostGIS を使用せずに GPS ポイントをフィルタリングできる必要がありました。
空間データにインデックスを付ける最も一般的な 3 つの方法、Geohash、H3、S2 について簡単に説明しました。
これら 3 つのツールはすべて、ズームインするにつれて地球をより小さな単位に細分化する形状にスライスすることで機能します。
これらの種類のインデックスがどのように機能するかを調べるための優れた方法は、https://geohash.softeng.co/ です。各セルをクリックすると拡大表示され、より深いレベルが表示されます。世界で私のお気に入りの場所の 1 つは、GeoHash ddk6p5
です。これらを使用して GPS データに「インデックス」を付けることは、関心のあるレベルごとにテキスト列を追加するだけで簡単です。たとえば、次のようなテーブルを作成できます。
CREATE TABLE IF NOT EXISTS places ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, latitude REAL, longitude REAL, geohash_level_2 TEXT, geohash_level_4 TEXT ); INSERT INTO places (name,latitude,longitude,geohash_level_2,geohash_level_4) VALUES('mt shavano', 38.618840, -106.239364, '9w', '9wu7'); INSERT INTO places (name,latitude,longitude,geohash_level_2,geohash_level_4) VALUES('blanca peak', 37.578047, -105.485796, '9w', '9wsv'); INSERT INTO places (name,latitude,longitude,geohash_level_2,geohash_level_4) VALUES('mt princeton', 38.749148, -106.242578, '9w', '9wuk'); INSERT INTO places (name,latitude,longitude,geohash_level_2,geohash_level_4) VALUES('la soufriere', 13.336299, -61.177146, 'dd', 'ddk7');
Sqlime は、これらのクエリを試すのに最適な場所です。
次に、コロラド州南部の場所を見つけるために、地図の「9w」の部分をカバーするハッシュを見つけます。
SELECT * FROM places WHERE geohash_level_2 = '9w';
カリブ海南部の場所を見つけるには、以下を使用できます:
SELECT * FROM places WHERE geohash_level_2 = 'dd';
このアプローチでは、データベースにどのレベルのインデックスを保持する必要があるかを事前に計画する必要があります。 それ以外の場合は、特定のエリアまたはマップ ビューポートでどのオブジェクトが見つかったかをすばやく見つける必要がある単純なアプリケーションに最適です。
LIKE クエリをサポートするデータベースを使用すると、複数の列の使用を減らすことができます。また、H3 セル番号は完全に重複していないため、「で始まる」タイプのクエリは H3 では機能しないことにも注意してください。
最後に、これらのインデックスの優れた特徴は、セル番号が優れたリアルタイム イベント トピックになることです。 したがって、上記の例では、コロラド州南部に対して「9w」というイベント トピックを作成できます。その後、新しい場所が追加されると、新しい場所ごとに「9w」トピックでイベントを発行します。
これらの各インデックス タイプのサンプル JavaScript (deno) コードを次に示します。
GeoJSON 出力はここで表示できることに注意してください: https://geojson.io/
import { geocoordinateToGeohash } from 'npm:geocoordinate-to-geohash' import { geohashToPolygonFeature } from 'npm:geohash-to-geojson' import geohash from 'npm:ngeohash' const lat = 38.618840 const lng = -106.239364 const hash2 = geohash.encode(lat, lng, 2) console.log('mt shavano hash at level 2', hash2) const feat2 = geohashToPolygonFeature(hash2) console.log('mt shavano hash at level 2 bounds', JSON.stringify(feat2)) // About a city block in size in CO (size changes as you head towards poles) const hash7 = geohash.encode(lat, lng, 7) console.log('mt shavano hash at level 4', hash7) const feat7 = geohashToPolygonFeature(hash7) console.log('mt shavano hash at level 4 bounds', JSON.stringify(feat7))
import h3 from 'npm:h3-js' import geojson2h3 from "npm:geojson2h3" const lat = 38.618840 const lng = -106.239364 // Level 2 (~1/3 Colorado Size) const h3IndexL2 = h3.latLngToCell(lat, lng, 2); console.log('mt shavano cell at level 2', h3IndexL2) const featureL2 = geojson2h3.h3ToFeature(h3IndexL2) console.log('mt shavano cell at level 2 bounds', JSON.stringify(featureL2)) // Level 4 (~City of Salida Size) const h3IndexL4 = h3.latLngToCell(lat, lng, 4); console.log('mt shavano cell at level 4', h3IndexL4) const featureL4 = geojson2h3.h3ToFeature(h3IndexL4) console.log('mt shavano cell at level 4 bounds', JSON.stringify(featureL4))
// This might be a better choice : https://www.npmjs.com/package/@radarlabs/s2 import s2 from 'npm:s2-geometry' const S2 = s2.S2 const lat = 38.618840 const lng = -106.239364 const key2 = S2.latLngToKey(lat, lng, 2) const id2 = S2.keyToId(key2) const feature2 = cellCornersToFeatureCollection(lat, lng, 2) console.log('mt shavano key at level 2', key2) console.log('mt shavano cell id at level 2', id2) console.log('mt shavano cell at level 2 corners', JSON.stringify(feature2)) const key4 = S2.latLngToKey(lat, lng, 4) const id4 = S2.keyToId(key4) const feature4 = cellCornersToFeatureCollection(lat, lng, 4) console.log('mt shavano key at level 4', key4) console.log('mt shavano cell id at level 4', id4) console.log('mt shavano cell at level 4 corners', JSON.stringify(feature4)) function cellCornersToFeatureCollection(lat, lng, level) { const ll = S2.L.LatLng(lat, lng) const cell = S2.S2Cell.FromLatLng(ll, level) const corners = cell.getCornerLatLngs() const coordinates = corners.map((pair) => { return [pair.lng, pair.lat] }) return { "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [coordinates] }, "properties": {} } ] } }
以上が地理空間インデックス作成サンプラーの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。