Antes, durante o processo de pesquisa de conteúdo baseado em Lucene, aprendi que o Lucene pode recuperar informações de texto e informações numéricas, e a distância espacial parece ter sido implementada no código-fonte. Nos últimos seis meses, entrei em contato com Solr, que tem uma pesquisa de distância espacial (latitude e longitude). Recentemente, aprendi sobre a implementação e aprendi que existe uma tecnologia relativamente comum para realizar a pesquisa de distância espacial - Geohash. Deixe -me apresentar o Geohash abaixo.
Recursos geohash
Portanto, quando fazemos pesquisa a distância, precisamos apenas prefixar o GeoHash. Os motivos específicos são introduzidos posteriormente.
Princípio Geohash
A explicação mais simples do GeoHash é converter uma informação de localização em uma codificação de string classificável e comparável. O seguinte processo de implementação é descrito em detalhes abaixo:
Primeiro, dividimos a latitude (-90, 90) em dois intervalos (-90, 0) e (0, 90). Se o valor de latitude da posição de coordenadas estiver no primeiro intervalo, a codificação será 0, caso contrário, a codificação será 1. Usamos 40.222012 como exemplo. Como 40.2222012 pertence a (0, 90), a codificação é 1. Então continuamos a dividir (0, 90) em dois intervalos (0, 45) e (45, 90), enquanto 40.2222012 está localizado em (0, 45), a codificação é 0, e assim por diante. Dividimos 20 vezes e, finalmente, calculamos que a codificação de 40.222012 é 10111001101000110.
O mesmo método é usado para longitude e a codificação de 116.248283 é obtida como 110100101010101010101010101010101010101010101010101.
Em seguida, mesclamos as codificações de latitude e longitude. O número ímpar é a latitude e o número par é de longitude. A codificação resultante é 111001110100100110001101100110110011001101100110000110110 (precisa de atenção especial aqui, o número ímpar e o número par mencionado aqui são os subscritos da matriz de valor, a partir de 0);
Finalmente, a base32 é codificada. O decimal correspondente à corda binária é 28, 29, 4, 24, 27, 6, 1, 22, respectivamente. A conversão para Base32 é WX4SV61Q, então (40.2222012, 116.248283) é codificada como WX4SV61Q. (A figura a seguir apresenta a correspondência de base32)
O local correspondente do código WX4SV61Q no mapa é o seguinte:
Aqui, o comprimento da codificação do nosso Geohash é de 8 e a precisão é de 19 metros. A tabela a seguir lista a precisão correspondente a diferentes comprimentos de codificação:
A partir da precisão acima, podemos ver que, se você deseja selecionar um item a 2 km de mim (40.222012, 116.248283), precisamos encontrar apenas o geohash correspondente às coordenadas do item com o WX4SV como o prefixo.
Extensão Geohash
Até agora, temos um certo entendimento dos índices espaciais, mas a introdução acima não pode alcançar uma das seguintes situações:
Podemos ver pela figura que o ponto vermelho está mais próximo do ponto verde acima e mais longe do ponto verde abaixo, mas o ponto vermelho é o mesmo que a corda codificada do ponto verde abaixo e é ambos WX4G0. A idéia de resolver problemas de limite, como o Geohash, é muito simples. Quando pesquisamos ou consultamos, combinamos as oito áreas ao redor, o que pode resolver bem o problema dos limites. Em seguida, implementaremos o Geohash em Java.
Implementação de Java
Antes da implementação, primeiro definimos um local e o usamos para representar as informações de latitude e longitude:
/ ***@Descrição: armazenar latitude e longitude Informações*/ pacote com.lulei.geo.bean; classe pública LocationBean {public static final duplo minlat = -90; public estático final duplo maxlat = 90; public estático final duplo minlng = -180; public estático final duplo maxlng = 180; private duplo lat; // latitude [-90,90] LNG duplo privado; // longitude [-180,180] Localização pública (duplo lat, duplo lng) {this.lat = lat; this.lng = lng; } public duplo getLat () {return lat; } public void setLat (duplo lat) {this.lat = lat; } public duplo getlng () {return lng; } public void Setlng (duplo lng) {this.lng = lng; }} Então escrevemos uma classe para implementar o Geohash. No processo de implementação de Geohash, precisamos definir algumas constantes e informações de latitude e longitude, como segue:
classe pública Geohash {Localização Private Location; /** * 1 2500 km; 2 630 km; 3 78km; 4 30km * 5 2,4 km; 6 610m; 7 76m; 8 19m */ private int hashlength = 8; // latitude e longitude são convertidas em comprimento geohash privado int latlength = 20; // latitude e longitude são convertidas em comprimento binário privado int lngle comprimento = 20; //Longitude and longitude are converted to binary length private double minLat;//Unit size of each grid latitude private double minLng;//Fall of each longitude are collapsed 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'}; } Ao instantar o Geohash, precisamos atribuir algumas propriedades:
public Geohash (Double Lat, Double LNG) {Location = new LocationBean (Lat, LNG); setMinlatlng (); } public int gethashLength () {return hashLength; } / *** @Author: lulei* @Description: Defina a unidade mínima de latitude e longitude* / private void setMinlatlng () {minlat = locationBean.maxlat - locationBean.Minlat; for (int i = 0; i <comprimento latl; i ++) {minlat /= 2.0; } minlng = localBean.maxlng - locationBean.Minlng; for (int i = 0; i <lnglength; i ++) {minlng /= 2.0; }} Quando usamos o Geohash, precisamos definir o comprimento final da codificação, por isso escrevemos um método para definir o comprimento do geohash
public boolean sethashlength (int length) {if (comprimento <1) {return false; } hashLength = length; Latlength = (comprimento * 5) / 2; if (comprimento % 2 == 0) {lnglesten = Latlength; } else {lnglength = Latlength + 1; } setMinlatlng (); retornar true; } Com essas configurações, precisamos converter longitude e latitude em codificações binárias correspondentes
Privado booleano [] Gethasharray (valor duplo, duplo min, max duplo, int length) {if (valor <min || value> max) {return null; } if (comprimento <1) {return null; } boolean [] resultado = novo booleano [comprimento]; for (int i = 0; i <comprimento; i ++) {duplo mid = (min+max) / 2.0; if (value> mid) {resultado [i] = true; min = médio; } else {resultado [i] = false; max = médio; }} Retornar resultado; } Depois de obter a codificação binária de latitude e longitude, respectivamente, precisamos mesclar duas cordas binárias em uma
Privado booleano [] mescla (boolean [] latAray, boolean [] lngarray) {if (latArray == null || lngarray == null) {return null; } boolean [] resultado = new boolean [lngarray.length + latarray.length]; Arrays.fill (resultado, falso); for (int i = 0; i <lngarray.length; i ++) {resultado [2 * i] = lngarray [i]; } para (int i = 0; i <latRray.length; i ++) {resultado [2 * i+1] = latRray [i]; } resultado de retorno; } Finalmente, precisamos basear32 conversão da conversão binária obtida
/ ** * @param lat * @param lng * @return * @author: lulei * @description: obtenha a sequência base32 de latitude e longitude */ private string getGeohashbase32 (duplo Lat, duplo lng) {boolean [] bools = getGeobinary (lat, lng); if (bools == null) {return null; } StringBuffer sb = new StringBuffer (); for (int i = 0; i <bools.length; i = i + 5) {boolean [] base32 = novo booleano [5]; for (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: converta binário de cinco bits em 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; }} retorna chars [num % chars.length]; } Para a questão de como obter o valor geohash das oito áreas circundantes, podemos fazer a seguinte transformação. Já conhecemos a latitude e a longitude do ponto atual, e também conhecemos a largura de longitude e latitude em cada área. Se a longitude for adicionada ou subtraída, podemos estar localizados na longitude das áreas esquerda e direita da área. Se a latitude for adicionada ou subtraída, podemos obter a latitude das partes superior e inferior da área, para que possamos obter as coordenadas de um ponto nas oito áreas ao redor da área. Calculamos as coordenadas desses oito pontos, que é o código Geohash correspondente às oito áreas.
Lista pública <String> getGeoHashBase32for9 () {duplo leftlat = location.getLat () - minlat; dupla direitaLAT = Location.getLat () + minlat; duplo uplng = local.getlng () - minlng; duplo downlng = local.getlng () + minlng; List <String> base32for9 = new ArrayList <String> (); // as 3 cordas no leftup = getGeoHashBase32 (LeftLat, uplng); if (! (leftup == null || "" .equals (leftup))) {base32for9.add (leftup); } String leftMid = getGeoHashBase32 (leftlat, location.getlng ()); if (! (LeftMid == null || "" .equals (esquerdmid))) {base32for9.add (esquerda); } String leftDown = getGeoHashBase32 (Leftlat, Downlng); if (! (LeftDown == null || "" .equals (leftdown))) {base32for9.add (leftdown); } // As 3 cordas no meio de cima a inferior midup = getGeohashbase32 (location.getLat (), uplng); if (! (midup == null || "" .equals (midup))) {base32for9.add (midup); } String midmid = getGeoHashBase32 (location.getlat (), location.getlng ()); if (! (midmid == null || "" .equals (midmid))) {base32for9.add (midmid); } String middown = getGeoHashBase32 (location.getlat (), downlng); if (! (middown == null || "" .equals (middown))) {base32for9.add (middown); } // 3 strings à direita de cima para baixo à direita = getGeoHashBase32 (Rightlat, uplng); if (! (RightUp == NULL || "" .Equals (RightUp))) {base32for9.add (RightUp); } String RightMid = getGeoHashBase32 (RightLat, Uplng); if (! (RightUp == NULL || "" .Equals (RightUp))) {base32for9.add (RightUp); } String RightMid = getGeoHashBase32 (RightLat, Location.getLng ()); if (! (RightMid == NULL || "" .Equals (RightMid))) {base32for9.add (RightMid); } String RightDown = getGeoHashBase32 (RightLat, Downlng); if (! (Rightdown == NULL || "" .Equals (Rightdown))) {base32for9.add (Rightdown); } retornar base32for9; } Resultados de execução
Código completo
Já existe um código completo do LoacationBean no blog acima, então não vou escrever aqui.
/ ***@Descrição: Geohash percebe a conversão de latitude e longitude*/ package com.lulei.geo; importar java.util.arraylist; importar java.util.arrays; importar java.util.list; importação com.lulei.geo.bean.LocationBean; importação com.lulei.util.jsonutil; classe pública Geohash {Localização Private Location; /** * 1 2500 km; 2 630km; 3 78km; 4 30km * 5 2,4km; 6 610m; 7 76m; 8 19m */ private int hashlength = 8; // latitude e longitude são convertidas em comprimento geohash privado int latlength = 20; // latitude e longitude são convertidas em comprimento binário privado int lngle comprimento = 20; // Latitude e longitude são convertidos em minlat duplo privado de comprimento binário; // tamanho unitário de cada latitude private duplo minlng; //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: Defina a unidade mínima de latitude e longitude* / private void setMinlatlng () {minlat = locationBean.maxlat - locationBean.Minlat; for (int i = 0; i <comprimento latl; i ++) {minlat /= 2.0; } minlng = localBean.maxlng - locationBean.Minlng; for (int i = 0; i <lnglength; i ++) {minlng /= 2.0; }} / ** * @return * @author: lulei * @Description: Encontre o ponto de nove coordenadas e os pontos circundantes * / list public <String> getGeoHashBase32For9 () {duplo leftLat = location.getLat () - minlat; dupla direitaLAT = Location.getLat () + minlat; duplo uplng = local.getlng () - minlng; duplo downlng = local.getlng () + minlng; List <String> base32for9 = new ArrayList <String> (); // as 3 cordas no leftup = getGeoHashBase32 (LeftLat, uplng); if (! (leftup == null || "" .equals (leftup))) {base32for9.add (leftup); } String leftMid = getGeoHashBase32 (leftlat, location.getlng ()); if (! (LeftMid == null || "" .equals (esquerdmid))) {base32for9.add (esquerda); } String leftDown = getGeoHashBase32 (Leftlat, Downlng); if (! (LeftDown == null || "" .equals (leftdown))) {base32for9.add (leftdown); } // As 3 cordas de cima para baixo no meio -médio = getGeoHashBase32 (location.getLat (), uplng); if (! (midup == null || "" .equals (midup))) {base32for9.add (midup); } String midmid = getGeoHashBase32 (location.getlat (), location.getlng ()); if (! (midmid == null || "" .equals (midmid))) {base32for9.add (midmid); } String middown = getGeoHashBase32 (location.getlat (), downlng); if (! (middown == null || "" .equals (middown))) {base32for9.add (middown); } // 3 strings de cima para baixo no lado direito da direita = getGeoHashBase32 (Rightlat, uplng); if (! (RightUp == NULL || "" .Equals (RightUp))) {base32for9.add (RightUp); } String RightMid = getGeoHashBase32 (RightLat, Location.getLng ()); if (! (RightMid == NULL || "" .Equals (RightMid))) {base32for9.add (RightMid); } String RightDown = getGeoHashBase32 (RightLat, Downlng); if (! (Rightdown == NULL || "" .Equals (Rightdown))) {base32for9.add (Rightdown); } retornar base32for9; } / ** * @param Comprimento * @return * @Author: lulei * @Description: Defina a latitude e a longitude para o comprimento geohash * / public boolean sethashlength (int length) {if (comprimento <1) {return false; } hashLength = length; Latlength = (comprimento * 5) / 2; if (comprimento % 2 == 0) {lnglesten = Latlength; } else {lnglength = Latlength + 1; } setMinlatlng (); retornar true; } / ** * @return * @Author: lulei * @description: obtenha a sequência base32 de latitude e longitude * / public String getGeohashbase32 () {return getGeohashbase32 (location.getlat (), location.getlng ()); } / ** * @param lat * @param lng * @return * @author: lulei * @description: obtenha a sequência base32 de latitude e longitude * / private string getGeohashbase32 (duplo lat, duplo lng) {boolean [] bools = getGeobinary (lat, lng); if (bools == null) {return null; } StringBuffer sb = new StringBuffer (); for (int i = 0; i <bools.length; i = i + 5) {boolean [] base32 = novo booleano [5]; for (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: converta binário de cinco bits em 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; }} retorna chars [num % chars.length]; } / ** * @param lat * @param lng * @return * @author: lulei * @description: obtenha a sequência binária geográfica de coordenadas * / private boolean [] getGeobinary (lat duplo, duplo lng) {boolean [] latarray = gethasharray (latBeanBean.MinLAT) {boolean [] latarray = gethasharray (latBean.MinLAT) {boolean [] LatARray = boolean [] lngarray = gethasharray (lng, locationbean.Minlng, locationBean.maxlng, lngle comprimento); Merge de retorno (Latarray, Lngarray); } / ** * @param latRray * @param lngarray * @return * @author: lulei * @description: mescle latitude e longitude binária * / private boolean [] mescle (boolean [] latArray, boolean [] lngarray) {if (latarray == null | } boolean [] resultado = new boolean [lngarray.length + latarray.length]; Arrays.fill (resultado, falso); for (int i = 0; i <lngarray.length; i ++) {resultado [2 * i] = lngarray [i]; } para (int i = 0; i <latRray.length; i ++) {resultado [2 * i+1] = latRray [i]; } resultado de retorno; } / ** * @param valor * @param min * @param max * @return * @author: lulei * @description: convert os números em string binária geohash * / private boolean [] gethasharray (valor duplo, duplo min, duplo max, int) {if (valor <min | } if (comprimento <1) {return null; } boolean [] resultado = novo booleano [comprimento]; for (int i = 0; i <comprimento; i ++) {duplo mid = (min+max) / 2.0; if (value> mid) {resultado [i] = true; min = médio; } else {resultado [i] = false; max = médio; }} Retornar resultado; } public static void main (string [] args) {// ToDO Method Auto-Generated Stub Geohash G = New Geohash (40.222012, 116.248283); System.out.println (g.getgeohashbase32 ()); System.out.println (jsonutil.parsejson (g.getgeohashbase32for9 ())); }}O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.