Раньше во время процесса поиска контента на основе Lucene я узнал, что Lucene может получить текстовую информацию и численную информацию, и пространственное расстояние, по-видимому, реализовано в исходном коде. За последние шесть месяцев я вступил в контакт с Solr, который имеет пространственный поиск на расстоянии (широта и долгота). Недавно я узнал о реализации и узнал, что существует относительно распространенная технология для реализации поиска пространственного расстояния - Geohash. Позвольте мне представить Geohash ниже.
Геохаш функции
Поэтому, когда мы выполняем поиск на расстоянии, нам нужно только префикс соответствовать Geohash. Конкретные причины введены позже.
Принцип геохаша
Самое простое объяснение Geohash - преобразовать информацию о местоположении в сортируемое и сопоставимое кодирование строки. Следующий процесс реализации подробно описан ниже:
Сначала мы делим широту (-90, 90) на два интервала (-90, 0) и (0, 90). Если значение широты позиции координат находится в первом интервале, кодирование равно 0, в противном случае кодирование равно 1. Мы используем 40.222012 в качестве примера. Поскольку 40.222012 принадлежит (0, 90), кодирование составляет 1. Затем мы продолжаем разделять (0, 90) на два интервала (0, 45) и (45, 90), в то время как 40,222012 расположены в (0, 45), поэтому кодирование равно 0 и так далее. Мы разделились 20 раз и, наконец, рассчитываем, что кодирование 40,222012 составляет 10111001001101000110.
Тот же метод используется для долготы, а кодирование 116,248283 получается как 1101001010101010101010101010101010101010101010101011.
Затем мы объединяем кодирования широты и долготы. Нечетное число - широта, а четное число - это долгота. Полученное кодирование - 11100111010011110001101111101111111110111111110011110110 (здесь требуется особое внимание, нечетное число и даже число, упомянутые здесь, являются подписки массива значений, начиная с 0);
Наконец, Base32 кодируется. Десятичная, соответствующая двоичной строке, составляет 28, 29, 4, 24, 27, 6, 1, 22, соответственно. Преобразование в Base32 составляет wx4sv61q, поэтому (40.222012, 116.248283) кодируется как wx4sv61q. (Следующий рисунок представляет собой соответствие Base32)
Соответствующее местоположение кода wx4sv61q на карте следующим образом:
Здесь длина кодирования нашего геохаша составляет 8, а точность - 19 метров. В следующей таблице перечислены точность, соответствующая различной длине кодирования:
Из приведенной выше точности мы видим, что если вы хотите выбрать элемент в пределах 2 км от меня (40.222012, 116.248283), нам нужно только найти Geohash, соответствующий координатам элемента с WX4SV в качестве префикса.
Расширение Geohash
До сих пор у нас есть определенное понимание пространственных индексов, но вышеупомянутое введение не может достичь одной из следующих ситуаций:
Из рисунка мы видим, что красная точка ближе к зеленой точке выше и дальше от зеленой точки внизу, но красная точка такая же, как кодированная строка зеленой точки внизу, и оба WX4G0. Идея решения граничных задач, таких как Geohash, очень проста. Когда мы ищем или запросите, мы сопоставляем окружающие восемь областей, которые могут хорошо решить граничную проблему. Далее мы будем внедрить Geohash в Java.
Реализация Java
Перед реализацией мы сначала определяем локацию и используем его для представления информации о широте и долготе:
/ ***@Описание: хранить информацию о широте и долготе*/ пакет com.lulei.geo.bean; открытый класс местоположение {public static final double minlat = -90; публичный статический окончательный двойной макслат = 90; общественный статический окончательный двойной minlng = -180; общественный статический окончательный двойной maxlng = 180; частное двойное lat; // latitude [-90,90] частное двойное LNG; // долгота [-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; }} Затем мы пишем класс для реализации Geohash. В процессе реализации Geohash нам необходимо определить некоторую константу и широту и информацию о долготе, следующим образом:
открытый класс Geohash {частное местоположение местоположение; /** * 1 2500 км; 2 630 км; 3 78 км; 4 30 км * 5 2,4 км; 6 610 м; 7 76 м; 8 19m */ private int hashlength = 8; // широта и долгота преобразуются в длину Geohash Private int latlength = 20; // широта и долгота преобразуются в бинарную длину частной int lnglength = 20; // долгота и долгота преобразуются в двоичную длину частную двойную двойную минлат; // размер единицы каждой сетки. '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: установить минимальную единицу широты и долготы* / private void setminlatlng () {minlat = locationbean.maxlat - locationbean.minlat; for (int i = 0; i <latlength; i ++) {minlat /= 2,0; } minlng = locationbean.maxlng - locationbean.minlng; for (int i = 0; i <lnglength; i ++) {minlng /= 2,0; }} Когда мы используем Geohash, нам нужно установить конечную длину кодирования, поэтому мы пишем метод для установки длины Geohash
public boolean sethashlength (int length) {if (длина <1) {return false; } hashlength = длина; latlength = (длина * 5) / 2; if (длина % 2 == 0) {lnglength = latlength; } else {lnglength = latlength + 1; } setminlatlng (); вернуть истину; } С этими настройками нам нужно преобразовать долготу и широту в соответствующие двоичные кодировки
Private Boolean [] gethasharray (двойное значение, двойное мин, двойное максимум, int length) {if (value <min || value> max) {return null; } if (длина <1) {return null; } boolean [] result = new Boolean [длина]; for (int i = 0; i <length; i ++) {double mid = (min+max) / 2,0; if (value> mid) {result [i] = true; мин = средняя; } else {result [i] = false; max = mid; }} return result; } После получения бинарной кодирования широты и долготы соответственно нам нужно объединить две бинарные строки в один
Private Boolean [] merge (boolean [] latarray, boolean [] lngarray) {if (latarray == null || lngarray == null) {return null; } boolean [] result = new Boolean [lngarray.length + latarray.length]; Arrays.fill (результат, false); for (int i = 0; i <lngarray.length; i ++) {result [2 * i] = lngarray [i]; } for (int i = 0; i <latarray.length; i ++) {result [2 * i+1] = latarray [i]; } return Result; } Наконец, нам необходимо основать 32 преобразование полученного бинарного преобразования
/ ** * @param lat * @param lng * @return * @author: lulei * @description: получить строку базы32 широты и долготы */ private string getgeohashbase32 (double lat, двойной LNG) {boolean [] bools = getgeobasian (lat, 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]; for (int j = 0; j <5; j ++) {base32 [j] = bools [i+j]; } char cha = getBase32Chr (base32); if ('' == cha) {return null; } sb.append (cha); } вернуть sb.toString (); } / ** * @param base32 * @return * @author: lulei * @description: преобразовать пятибитный двоичный в Base32 * / private char getbase32ch (boolean [] base32) {if (base32 == null || base32.length! = 5) {return ''; } int num = 0; for (boolean bool: base32) {num << = 1; if (bool) {num += 1; }} return chars [num % chars.length]; } Что касается вопроса о том, как получить стоимость геохаша восьми окружающих областей, мы можем сделать следующее преобразование. Мы уже знаем широту и долготу текущей точки, и мы также знаем долготу и ширину широты в каждой области. Если долгота добавлена или вычтена, мы можем быть расположены в долготе левой и правой площади области. Если широта добавлена или вычтена, мы можем получить широту верхних и нижних частей площади, чтобы мы могли получить координаты точки в восьми областях вокруг области. Мы рассчитываем координаты этих восьми точек, который является кодом геохаша, соответствующим восьми областям.
public list <string> getGeOhashbase32for9 () {Double Leathlat = location.getLat () - minlat; double rightlat = location.getlat () + minlat; Double uplng = location.getlng () - minlng; double downlng = location.getlng () + minlng; Список <string> base32for9 = new ArrayList <string> (); // 3 строки на Leftup = getGeOhashbase32 (Leathlat, Uplng); if (! (Leftup == null || ". } String leftmid = getGeOhashbase32 (левш, location.getlng ()); if (! (LeftMid == null || "" .equals (левый))) {base32for9.add (левый); } String deflown = getGeOhashbase32 (левша, Downlng); if (! (Унижение == null || ". } // 3 строки в середине сверху вниз midup = getGeOhashbase32 (location.getlat (), uplng); if (! (midup == null || ". } String mid Mid = getGeOhashbase32 (location.getLat (), location.getlng ()); if (! (Mid Mid == null || "" .equals (Mid Mid))) {base32for9.add (Mid Mid); } String middown = getGeOhashbase32 (location.getLat (), downlng); if (! (middown == null || "" .equals (middown))) {base32for9.add (middown); } // 3 строки справа от сверху вниз rustup = getGeOhashbase32 (rightlat, uplng); if (! (Rightup == null || "" .equals (raightUp))) {base32for9.add (rugeUp); } String rightmid = getGeohashbase32 (rightlat, uplng); if (! (Rightup == null || "" .equals (raightUp))) {base32for9.add (rugeUp); } String rightmid = getGeOhashbase32 (rightlat, location.getlng ()); if (! (rightmid == null || ". } String rightdown = getGeOhashbase32 (rightlat, downlng); if (! (rightdown == null || ". } return base32for9; } Результаты бега
Полный код
В приведенном выше блоге уже есть полный код подвески, поэтому я не буду писать его здесь.
/ ***@Описание: Geohash реализует преобразование широты и долготы*/ package com.lulei.geo; импортировать java.util.arraylist; импортировать java.util.arrays; импортировать java.util.list; Импорт com.lulei.geo.bean.locationbean; импорт com.lulei.util.jsonutil; открытый класс Geohash {частное местоположение местоположение; /** * 1 2500 км; 2 630 км; 3 78 км; 4 30 км * 5 2,4 км; 6 610 м; 7 76 м; 8 19m */ private int hashlength = 8; // широта и долгота преобразуются в длину Geohash Private int latlength = 20; // широта и долгота преобразуются в двоичную длину // широта и долгота преобразуются в двоичную длину частную двойную двойную минлат; // размер единицы каждого частного двойного двойного манипуляции; //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: установить минимальную единицу широты и долготы* / private void setminlatlng () {minlat = locationbean.maxlat - locationbean.minlat; for (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: Найдите девять координат и окружающие точки * / public <string> getgeohashbase32for9 () {Double Leathlat = location.getLat () - minlat; double rightlat = location.getlat () + minlat; Double uplng = location.getlng () - minlng; double downlng = location.getlng () + minlng; Список <string> base32for9 = new ArrayList <string> (); // 3 строки на Leftup = getGeOhashbase32 (Leathlat, Uplng); if (! (Leftup == null || ". } String leftmid = getGeOhashbase32 (левш, location.getlng ()); if (! (LeftMid == null || "" .equals (левый))) {base32for9.add (левый); } String deflown = getGeOhashbase32 (левша, Downlng); if (! (Унижение == null || ". } // 3 строки сверху донизу в среднем среднем = getGeOhashbase32 (location.getlat (), uplng); if (! (midup == null || ". } String mid Mid = getGeOhashbase32 (location.getLat (), location.getlng ()); if (! (Mid Mid == null || "" .equals (Mid Mid))) {base32for9.add (Mid Mid); } String middown = getGeOhashbase32 (location.getLat (), downlng); if (! (middown == null || "" .equals (middown))) {base32for9.add (middown); } // 3 строки сверху вниз с правой стороны вправо = getGeOhashbase32 (rightlat, uplng); if (! (Rightup == null || "" .equals (raightUp))) {base32for9.add (rugeUp); } String rightmid = getGeOhashbase32 (rightlat, location.getlng ()); if (! (rightmid == null || ". } String rightdown = getGeOhashbase32 (rightlat, downlng); if (! (rightdown == null || ". } return base32for9; } / ** * @param length * @return * @author: lulei * @description: установить широту и долготу на длину Geohash * / public boolean sethashlength (int length) {if (длина <1) {return false; } hashlength = длина; latlength = (длина * 5) / 2; if (длина % 2 == 0) {lnglength = latlength; } else {lnglength = latlength + 1; } setMinlatlng (); вернуть истину; } / ** * @return * @author: lulei * @description: получить строку базы32 широты и долготы * / public String getgeohashbase32 () {return getgeohashbase32 (location.getlat (), location.getlng ()); } / ** * @param lat * @param lng * @return * @author: lulei * @description: получить строку базы32 широты и долготы * / private string getgeohashbase32 (double lat, двойной lng) {boolean [] bools = getgeobasian (lat, 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]; for (int j = 0; j <5; j ++) {base32 [j] = bools [i+j]; } char cha = getBase32Chr (base32); if ('' == cha) {return null; } sb.append (cha); } вернуть sb.toString (); } / ** * @param base32 * @return * @author: lulei * @description: преобразовать пятибитный двоичный в Base32 * / private char getbase32ch (boolean [] base32) {if (base32 == null || base32.length! = 5) {return ''; } int num = 0; for (boolean bool: base32) {num << = 1; if (bool) {num += 1; }} return chars [num % chars.length]; } / ** * @param lat * @param lng * @return * @author: lulei * @description: получить бинарную строку координат * / private boolean [] Getegeobinary (Double lat, Double Lng) {Boolean [] latarray = Gethasharray (late, locationbean.minlat, location.maxlat, lathaxlate, lathaxlat, lathaxlat. Boolean [] lngarray = gethasharray (lng, locationbean.minlng, locationbean.maxlng, lnglength); возврат слияния (Latarray, Lngarray); } / ** * @param latarray * @param lngarray * @return * @author: lulei * @description: Merge Latitude and Longitude Binary * / Private Boolean [] Merge (Boolean [] latarray, boolean [] lngarray) {if (latarray = null || lngarray == null) {null) } boolean [] result = new Boolean [lngarray.length + latarray.length]; Arrays.fill (результат, false); for (int i = 0; i <lngarray.length; i ++) {result [2 * i] = lngarray [i]; } for (int i = 0; i <latarray.length; i ++) {result [2 * i+1] = latarray [i]; } return Result; } / ** * @param value * @param min * @param max * @return * @author: lulei * @description: преобразовать числа в бинарную строку Geohash * / private boolean [] gethasharray (двойное значение, двойное мин, двойное максимум, int length) {if (value <min || value> max) {return null; } if (длина <1) {return null; } boolean [] result = new Boolean [длина]; for (int i = 0; i <length; i ++) {double mid = (min+max) / 2,0; if (value> mid) {result [i] = true; мин = средняя; } else {result [i] = false; max = mid; }} return result; } public static void main (string [] args) {// todo Автопогенерированный метод geohash g = new Geohash (40.222012, 116.248283); System.out.println (g.getgeohashbase32 ()); System.out.println (jsonutil.parsejson (g.getgeohashbase32for9 ())); }}Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.