J'ai récemment fait un exercice en essayant de créer une application tout en faisant semblant d'être minimaliste. Cela exigeait que je puisse filtrer les points GPS sans utiliser mon ami PostGIS.
J'ai fait un tour rapide des trois méthodes les plus populaires pour indexer des données spatiales : Geohash, H3 et S2.
Ces trois outils fonctionnent en découpant le globe en formes qui se subdivisent en unités de plus en plus petites à mesure que vous zoomez.
Un excellent moyen d'explorer le fonctionnement de ces types d'index est sur https://geohash.softeng.co/. Lorsque vous cliquez sur chaque cellule, un zoom avant vous montrera un niveau plus profond. L'un de mes endroits préférés au monde est GeoHash ddk6p5
Les utiliser pour « indexer » les données GPS est aussi simple que d'ajouter une colonne de texte pour chaque niveau qui vous intéresse. Par exemple, je pourrais créer un tableau comme celui-ci :
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 est un bon endroit pour essayer ces requêtes.
Ensuite, pour trouver des endroits dans le sud du Colorado, je trouverais quel hachage couvre la partie de la carte qui est « 9w » :
SELECT * FROM places WHERE geohash_level_2 = '9w';
Pour trouver des endroits dans le sud des Caraïbes, je pourrais utiliser :
SELECT * FROM places WHERE geohash_level_2 = 'dd';
Cette approche nécessite une certaine planification dès le départ quant aux niveaux d'index que vous devez conserver dans votre base de données. Sinon, cela fonctionne très bien pour les applications simples où vous devez trouver rapidement quels objets se trouvent dans une zone ou une fenêtre de carte spécifique.
Disposer d'une base de données prenant en charge les requêtes LIKE peut aider à réduire l'utilisation de plusieurs colonnes. Notez également que les numéros de cellules H3 ne se chevauchent pas parfaitement, donc les requêtes de type « commence par » ne fonctionneront pas avec H3.
Enfin, une caractéristique intéressante de ces index est que les numéros de cellules constitueraient d'excellents sujets d'événements en temps réel. Ainsi, dans mes exemples ci-dessus, je pourrais créer un sujet d'événement appelé « 9w » pour le sud du Colorado. Ensuite, à mesure que de nouveaux lieux sont ajoutés, j'émets un événement dans le sujet « 9w » pour chaque nouveau lieu.
Voici un exemple de code JavaScript (deno) pour chacun de ces types d'index :
Notez que la sortie GeoJSON peut être consultée ici : 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": {} } ] } }
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!