Avant, pendant le processus de recherche de contenu basé sur Lucene, j'ai appris que Lucene peut récupérer des informations textuelles et des informations numériques, et la distance spatiale semble être implémentée dans le code source. Au cours des six derniers mois, je suis entré en contact avec Solr, qui a une recherche à distance spatiale (latitude et longitude). Récemment, j'ai appris la mise en œuvre et j'ai appris qu'il existe une technologie relativement courante pour réaliser la recherche à distance spatiale - Geohash. Permettez-moi de présenter Geohash ci-dessous.
Geohash Fesure
Par conséquent, lorsque nous effectuons une recherche à distance, nous n'avons qu'à préfixer la correspondance de GeoHash. Les raisons spécifiques sont introduites plus tard.
Principe de Geohash
L'explication la plus simple de GeoHash est de convertir une information de localisation en un codage de chaîne triable et comparable. Le processus de mise en œuvre suivant est décrit en détail ci-dessous:
Tout d'abord, nous divisons la latitude (-90, 90) en deux intervalles (-90, 0) et (0, 90). Si la valeur de latitude de la position des coordonnées est dans le premier intervalle, le codage est 0, sinon le codage est 1. Nous utilisons 40.222012 comme exemple. Étant donné que 40.222012 appartient à (0, 90), le codage est 1. Ensuite, nous continuons à diviser (0, 90) en deux intervalles (0, 45) et (45, 90), tandis que 40.222012 est situé en (0, 45), donc l'encodage est 0, et ainsi de suite. Nous avons divisé 20 fois et finalement calculé que le codage de 40.222012 est 10111001001101000110.
La même méthode est utilisée pour la longitude et le codage de 116.248283 est obtenu comme 11010010101010101010101010101010101010101010101.
Ensuite, nous fusions les encodages de latitude et de longitude. Le nombre impair est la latitude et même le nombre est une longitude. Le codage résultant est 111001110100100110001101100110110011001101100110000110110 (il a besoin d'attention particulière ici, le nombre impaire et même le nombre mentionné ici sont les indices du tableau de valeur, à partir de 0);
Enfin, la base32 est codée. La décimale correspondant à la chaîne binaire est respectivement de 28, 29, 4, 24, 27, 6, 1, 22. La conversion en base32 est wx4sv61q, donc (40.222012, 116.248283) est codée en WX4SV61Q. (La figure suivante introduit la correspondance de la base32)
L'emplacement correspondant du code WX4SV61Q sur la carte est le suivant:
Ici, la longueur d'encodage de notre Geohash est de 8, et la précision est de 19 mètres. Le tableau suivant répertorie la précision correspondant à différentes longueurs de codage:
D'après la précision ci-dessus, nous pouvons voir que si vous souhaitez sélectionner un élément à moins de 2 km de moi (40.222012, 116.248283), nous n'avons qu'à trouver le GeoHash correspondant aux coordonnées de l'élément avec WX4SV comme préfixe.
Extension de Geohash
Jusqu'à présent, nous avons une certaine compréhension des indices spatiaux, mais l'introduction ci-dessus ne peut pas atteindre l'une des situations suivantes:
Nous pouvons voir sur la figure que le point rouge est plus proche du point vert au-dessus et plus loin du point vert ci-dessous, mais le point rouge est le même que la chaîne codée du point vert ci-dessous, et est à la fois WX4G0. L'idée de résoudre des problèmes de limite tels que Geohash est très simple. Lorsque nous recherchons ou interrogeons, nous correspondons aux huit zones environnantes, ce qui peut bien résoudre le problème des limites. Ensuite, nous mettrons en œuvre Geohash en Java.
Implémentation Java
Avant la mise en œuvre, nous définissons d'abord un BlansBean et les utilisons pour représenter les informations de latitude et de longitude:
/ ** * @ Description: Stocker les informations de latitude et de longitude * / package com.lulei.geo.bean; classe publique LocationBean {public static final double minlat = -90; Public statique final double maxlat = 90; public statique final double minlng = -180; public statique final double maxlng = 180; Double privé lat; // latitude [-90,90] Double LNG privé; // Longitude [-180,180] Public LocationBean (double lat, double lng) {this.lat = lat; this.lng = lng; } public double getlat () {return lat; } public void setlat (double lat) {this.lat = lat; } public double getlng () {return lng; } public void setlng (double lng) {this.lng = lng; }} Ensuite, nous écrivons une classe pour implémenter GeoHash. Dans le processus de mise en œuvre de GeoHash, nous devons définir certaines constantes et des informations de latitude et de longitude, comme suit:
classe publique Geohash {emplacement privé de l'emplacement de l'emplacement; / ** * 1 2500 km; 2 630 km; 3 78 km; 4 30 km * 5 2,4 km; 6 610m; 7 76m; 8 19m * / private int hashlength = 8; // latitude et la longitude sont converties en longueur geohash private int latlength = 20; // latitude et la longitude sont converties en longueur binaire privé int lngnglefther = 20; // la longitude et la longitude sont converties en longueur binaire double minlat privé; // la taille de l'unité de chaque grille latitude double minlng privé; // chute de chaque longitude est effondre 'F', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'}; } Lors de l'instanciation de Geohash, nous devons attribuer certaines propriétés:
public geohash (double lat, double lng) {location = new locationbean (lat, lng); setMinlatlng (); } public int gethashLength () {return hashLength; } / ** * @Author: Lulei * @Description: Définissez l'unité minimale de latitude et de la longitude * / private void setMinlatlng () {minlat = locationbean.maxlat - locationbean.minlat; pour (int i = 0; i <latLength; i ++) {minlat / = 2.0; } minlng = locationbean.maxlng - locationbean.minlng; for (int i = 0; i <lngLength; i ++) {minlng / = 2.0; }} Lorsque nous utilisons GeoHash, nous devons définir la longueur finale du codage, nous écrivons donc une méthode pour définir la longueur de Geohash
public boolean sethashLength (int longueur) {if (longueur <1) {return false; } HashLength = longueur; latLength = (longueur * 5) / 2; if (longueur% 2 == 0) {lnglength = latLength; } else {lnglong = latLength + 1; } setminlatlng (); Retour Vrai; } Avec ces paramètres, nous devons convertir la longitude et la latitude en encodages binaires correspondants
Boolean privé [] gethasharray (double valeur, double min, double max, int le long) {if (valeur <min || value> max) {return null; } if (longueur <1) {return null; } boolean [] result = new boolean [longueur]; pour (int i = 0; i <length; i ++) {double mid = (min + max) / 2.0; if (valeur> mid) {result [i] = true; min = mid; } else {result [i] = false; max = mid; }} Retour Résultat; } Après avoir obtenu le codage binaire de la latitude et de la longitude respectivement, nous devons fusionner deux cordes binaires en une
Boolean privé [] fusionner (boolean [] latarray, booléen [] lngarray) {if (latArray == null || lngArray == null) {return null; } boolean [] result = new boolean [lngArray.length + latArray.length]; Arrays.filt (résultat, false); for (int i = 0; i <lngArray.length; i ++) {result [2 * i] = lngArray [i]; } pour (int i = 0; i <latArray.length; i ++) {result [2 * i + 1] = latArray [i]; } Retour Résultat; } Enfin, nous avons besoin de la conversion de base32 de la conversion binaire obtenue
/ ** * @param lat * @param lng * @return * @Author: Lulei * @description: obtenez la chaîne de latitude et de la longitude privée de base32 * / private getGeoHashBase32 (double lat, double lng); if (bools == null) {return null; } StringBuffer sb = new StringBuffer (); for (int i = 0; i <bools.length; i = i + 5) {boolean [] base32 = new boolean [5]; pour (int j = 0; j <5; j ++) {base32 [j] = bools [i + j]; } char cha = getBase32Char (base32); if ('' == cha) {return null; } sb.append (cha); } return sb.toString (); } / ** * @param base32 * @return * @author: Lulei * @description: convertir le binaire cinq bits à la base32 * / private char getBase32char (boolean [] base32) {if (base32 == null || base32.Length! = 5) {return ''; } int num = 0; for (boolean bool: base32) {num << = 1; if (bool) {num + = 1; }} Retour Chars [num% chars.length]; } Pour la question de savoir comment obtenir la valeur Geohash des huit zones environnantes, nous pouvons effectuer la transformation suivante. Nous connaissons déjà la latitude et la longitude du point de courant, et nous connaissons également la largeur de longitude et de latitude dans chaque zone. Si la longitude est ajoutée ou soustraite, nous pouvons être situés à la longitude des zones gauche et droite de la zone. Si la latitude est ajoutée ou soustraite, nous pouvons obtenir la latitude des parties supérieure et inférieure de la zone, afin que nous puissions obtenir les coordonnées d'un point dans les huit zones autour de la zone. Nous calculons les coordonnées de ces huit points, qui est le code Geohash correspondant aux huit zones.
Liste publique <string> getGeoHashBase32For9 () {double Leftlat = location.getlat () - minlat; Double RightLat = Location.GetLat () + Minlat; double uplng = location.getlng () - minlng; double downlng = location.getlng () + minlng; List <string> base32for9 = new ArrayList <string> (); // les 3 chaînes sur le gauche = GetGeoHashBase32 (LeftLat, Uplng); if (! (dufUp == null || "" .equals (dufUp))) {base32for.Add (sauf); } String LeftMid = GetGeoHashBase32 (LeftLat, Location.getlng ()); if (! (LeftMid == null || "" .equals (LeftMID))) {Base32For9.Add (LeftMID); } String Leftdown = GetGeoHashBase32 (LeftLat, Downlng); if (! (La gauche == NULL || "" .Equals (gauche))) {Base32For9.Add (gauche); } // Les 3 chaînes au milieu de haut en bas au milieu = GetGeoHashBase32 (location.getlat (), uplng); if (! (midup == null || "" .equals (midup))) {base32for9.add (midup); } String midmid = getGeoHashBase32 (location.getlat (), location.getlng ()); if (! (midmid == null || "" .equals (midMid))) {base32for.Add (midmid); } String middown = getGeoHashBase32 (location.getlat (), downlng); if (! (middown == null || "" .equals (middown))) {base32for9.add (middown); } // 3 chaînes à droite de la droite de haut en bas = GetGeoHashBase32 (droite, uplng); if (! (justup == null || "" .equals (justup))) {base32for9.add (justup); } String rightmid = getGeoHashBase32 (droite, uplng); if (! (justup == null || "" .equals (justup))) {base32for9.add (justup); } String RightMid = GetGeoHashBase32 (droite, emplacement.getlng ()); if (! (droitemid == null || "" .equals (droiteMID))) {base32for.Add (droiteMID); } String rightdown = getGeoHashBase32 (droitelat, downlng); if (! (droite == null || "" .equals (droite))) {base32for.Add (droite); } return Base32For9; } Résultats en cours d'exécution
Code complet
Il y a déjà un code LOACATIONNAGE complet dans le blog ci-dessus, donc je ne l'écrirai pas ici.
/ ** * @ Description: Geohash réalise la conversion de latitude et de la longitude * / package com.lulei.geo; import java.util.arraylist; import java.util.arrays; Importer java.util.list; import com.lulei.geo.bean.locationBean; import com.lulei.util.jsonutil; classe publique Geohash {emplacement privé de l'emplacement de l'emplacement; / ** * 1 2500 km; 2 630 km; 3 78 km; 4 30 km * 5 2,4 km; 6 610m; 7 76m; 8 19m * / private int hashlength = 8; // latitude et la longitude sont converties en longueur de geohash private int latlength = 20; // latitude et la longitude sont converties en longueur binaire privé int lngnglefther = 20; // latitude et la longitude sont converties en longueur binaire privée double minlat; // Taille de l'unité de chaque Double Minlng privé de latitude; //Fallen of each longitude private static final char[] CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', «s», «t», «u», «v», «w», «x», «y», «z»}; public geohash (double lat, double lng) {location = new locationbean (lat, lng); setMinlatlng (); } public int gethashLength () {return hashLength; } / ** * @Author: Lulei * @Description: Définissez l'unité minimale de latitude et de la longitude * / private void setMinlatlng () {minlat = locationbean.maxlat - locationbean.minlat; pour (int i = 0; i <latLength; i ++) {minlat / = 2.0; } minlng = locationbean.maxlng - locationbean.minlng; for (int i = 0; i <lngLength; i ++) {minlng / = 2.0; }} / ** * @return * @Author: Lulei * @description: Trouvez le point de coordonnées et les points environnants * / Liste publique <string> GetGeoHashBase32For9 () {double Leftlat = location.getlat () - minelat; Double RightLat = Location.GetLat () + Minlat; double uplng = location.getlng () - minlng; double downlng = location.getlng () + minlng; List <string> base32for9 = new ArrayList <string> (); // les 3 chaînes sur le gauche = GetGeoHashBase32 (LeftLat, Uplng); if (! (dufUp == null || "" .equals (dufUp))) {base32for.Add (sauf); } String LeftMid = GetGeoHashBase32 (LeftLat, Location.getlng ()); if (! (LeftMid == null || "" .equals (LeftMID))) {Base32For9.Add (LeftMID); } String Leftdown = GetGeoHashBase32 (LeftLat, Downlng); if (! (La gauche == NULL || "" .Equals (gauche))) {Base32For9.Add (gauche); } // Les 3 chaînes de haut en bas au milieu du milieu = GetGeoHashBase32 (location.getlat (), uplng); if (! (midup == null || "" .equals (midup))) {base32for9.add (midup); } String midmid = getGeoHashBase32 (location.getlat (), location.getlng ()); if (! (midmid == null || "" .equals (midMid))) {base32for.Add (midmid); } String middown = getGeoHashBase32 (location.getlat (), downlng); if (! (middown == null || "" .equals (middown))) {base32for9.add (middown); } // 3 chaînes de haut en bas sur le côté droit droite = GetGeoHashBase32 (droite, uplng); if (! (justup == null || "" .equals (justup))) {base32for9.add (justup); } String RightMid = GetGeoHashBase32 (droite, emplacement.getlng ()); if (! (droitemid == null || "" .equals (droiteMID))) {base32for.Add (droiteMID); } String rightdown = getGeoHashBase32 (droitelat, downlng); if (! (droite == null || "" .equals (droite))) {base32for.Add (droite); } return Base32For9; } / ** * @param longueur * @return * @author: Lulei * @Description: définissez la latitude et la longitude sur la longueur de Geohash * / public booléen sethashlength (int longueur) {if (longueur <1) {return false; } HashLength = longueur; latLength = (longueur * 5) / 2; if (longueur% 2 == 0) {lnglength = latLength; } else {lnglong = latLength + 1; } setminlatlng (); Retour Vrai; } / ** * @return * @author: LULEI * @Description: Obtenez la chaîne de base32 de latitude et de longitude * / chaîne publique GetGeoHashBase32 () {return getGeoHashBase32 (emplacement.getlat (), location.getlng ()); } / ** * @param lat * @param lng * @return * @author: Lulei * @description: obtenez la chaîne de latitude et la chronditude privée de la base32 * / private getGeoHashBase32 (double lat, double lng); if (bools == null) {return null; } StringBuffer sb = new StringBuffer (); for (int i = 0; i <bools.length; i = i + 5) {boolean [] base32 = new boolean [5]; pour (int j = 0; j <5; j ++) {base32 [j] = bools [i + j]; } char cha = getBase32Char (base32); if ('' == cha) {return null; } sb.append (cha); } return sb.toString (); } / ** * @param base32 * @return * @author: Lulei * @description: convertir le binaire cinq bits à la base32 * / private char getBase32char (boolean [] base32) {if (base32 == null || base32.Length! = 5) {return ''; } int num = 0; for (boolean bool: base32) {num << = 1; if (bool) {num + = 1; }} Retour Chars [num% chars.length]; } / ** * @param lat * @param lng * @return * @Author: Lulei * @Description: Obtenez la chaîne géo-binaire de coordonnées * / private booléen [] getGeobinary (double lat, double lng) {Boolean [] LATEARray = GetHasharray (lat, locationBean.Minlat, employée. Boolean [] lngArray = Gethasharray (lng, locationbean.minlng, locationbean.maxlng, lnglonghength); Retour Merge (LatArray, lngarray); } / ** * @param latarray * @param lngarray * @return * @author: Lulei * @description: fusion latitude et longitude binaire * / private booléen [] fusionner (booléen [] latarray, booléen [] lngarray) {if (latarray == null || lngarray == null) {retour null; } boolean [] result = new boolean [lngArray.length + latArray.length]; Arrays.filt (résultat, false); for (int i = 0; i <lngArray.length; i ++) {result [2 * i] = lngArray [i]; } pour (int i = 0; i <latArray.length; i ++) {result [2 * i + 1] = latArray [i]; } Retour Résultat; } / ** * @param valeur * @param min * @param max * @return * @author: Lulei * @Description: convertir les nombres en chaîne binaire GeoHash * / private booléen [] gethasharray (double valeur, double min, double max, int le long) {if (valeur <min || value> max) {return null; } if (longueur <1) {return null; } boolean [] result = new boolean [longueur]; pour (int i = 0; i <length; i ++) {double mid = (min + max) / 2.0; if (valeur> mid) {result [i] = true; min = mid; } else {result [i] = false; max = mid; }} Retour Résultat; } public static void main (String [] args) {// TODO Méthode générée automatiquement Stume Geohash G = new GeoHash (40.222012, 116.248283); System.out.println (g.getGeoHashBase32 ()); System.out.println (jsonutil.parsejson (g.getGeoHashBase32For9 ())); }}Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.