对9个base32进行排序
[wt3mf9qe, wt3mf9qg, wt3mf9qs, wt3mf9qt, wt3mf9qu, wt3mf9qv, wt3mf9r5, wt3mf9rh, wt3mf9rj]
geohash base32图
image.png
对9个二进制编码进行排序
1110011001000111001101110010011011001101, 1110011001000111001101110010011011001111, 1110011001000111001101110010011011011000, 1110011001000111001101110010011011011001, 1110011001000111001101110010011011011010, 1110011001000111001101110010011011011011, 1110011001000111001101110010011011100101, 1110011001000111001101110010011011110000, 1110011001000111001101110010011011110001
geohash 二进制图
image.png
总结:geohash是空间z曲线,可以看出值序是从左下角到右上角的总体是有序的。
在固定精度的geohash曲线上
geohash可以保证一个格子一定落在空间Z曲线上某一个位置(在小范围geohash不能对空间坐标进行排序,大范围下看小范围是有序的)
geohash空间Z曲线上某一点可以保证其临近坐标在Z曲线上都是相邻的
距离保留度:
将二维空间对象映射成一维曲线后,两个对象在二维区间上如果是相近的,那么,在一维曲线中也应当是相近的。一个更优的曲线,理应更大程度上在一维曲线中维持对象在二维空间上的距离,或者说,应该有更高的距离保留度。对于查询而言,更高的距离保留度,也往往意味着更高的Caching命中率。
经Geohash编码后,其顺序与Z-Order编码保持一致,因此,也可以将Geohash理解成Z-Order算法的一种编码机制。Geohash编码带来的显著优势是:以字符串字典顺序表达Z-Order顺序,利用字符串的前缀匹配规则,也可快速实现多边形区域的重叠计算,但在编码效率上却并无任何优势,如sfcurve项目中提供的Z2编码算法,性能要远高于Geohash算法。
http://www.nosqlnotes.com/technotes/hbase/hbase-spatial-index/
package geotool;
import ch.hsr.geohash.BoundingBox;
import ch.hsr.geohash.GeoHash;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.Transaction;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.*;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.util.*;
import java.util.stream.Collectors;
/**
* 类描述
* @since 2019/10/9 9:47
*/
public class GeoHashEx {
public static void main(String[] args) throws IOException {
double lat = 30.549608;
double lon = 114.376971;
// Geohash编码字符的长度(最大为12)
int precision = 8;
GeoHash geoHash = GeoHash.withCharacterPrecision(lat, lon, precision);
// 使用给定的经纬度坐标生成的二进制编码
String binaryCode = geoHash.toBinaryString();
System.out.println("经纬度坐标: (" + lat + ", " + lon + ")");
System.out.println("二进制编码:" + binaryCode);
// 使用给定的经纬度坐标生成的Geohash字符编码
String hashCode = geoHash.toBase32();
System.out.println("Geohash编码:" + hashCode);
// 从二进制的编码中分离出经度和纬度分别对应的二进制编码
char[] binaryCodes = binaryCode.toCharArray();
List latCodes = new ArrayList();
List lonCodes = new ArrayList();
for (int i = 0; i < binaryCodes.length; i++) {
if (i % 2 == 0) {
lonCodes.add(binaryCodes[i]);
} else {
latCodes.add(binaryCodes[i]);
}
}
// 纬度对应的二进制编码
StringBuilder latCode = new StringBuilder();
// 经度对应的二进制编码
StringBuilder lonCode = new StringBuilder();
for (Character ch : latCodes) {
latCode.append(ch);
}
for (Character ch : lonCodes) {
lonCode.append(ch);
}
System.out.println("纬度二进制编码:" + latCode.toString());
System.out.println("经度二进制编码:" + lonCode.toString());
GeoHash[] adjacent = geoHash.getAdjacent();
List collect = Arrays.stream(adjacent).collect(Collectors.toList());
collect.add(geoHash);
GeometryFactory factory = new GeometryFactory();
Map map = new HashMap<>();
for (GeoHash hash : collect) {
BoundingBox boundingBox = hash.getBoundingBox();
Polygon polygon = factory.createPolygon(new Coordinate[]{
new Coordinate(boundingBox.getMaxLon(), boundingBox.getMaxLat()),
new Coordinate(boundingBox.getMaxLon(), boundingBox.getMinLat()),
new Coordinate(boundingBox.getMinLon(), boundingBox.getMinLat()),
new Coordinate(boundingBox.getMinLon(), boundingBox.getMaxLat()),
new Coordinate(boundingBox.getMaxLon(), boundingBox.getMaxLat()),
});
map.put(hash.toBase32(), polygon);
}
Map sortMap = new TreeMap<>(Comparator.naturalOrder());
sortMap.putAll(map);
List> featureList = new ArrayList<>();
for (Map.Entry entry : sortMap.entrySet()) {
String key = entry.getKey();
String binaryString = GeoHash.fromGeohashString(key).toBinaryString();
Map map1 = new HashMap<>();
map1.put("the_geom", entry.getValue());
map1.put("base32", key);
map1.put("binary", binaryString);
featureList.add(map1);
}
Map> fields = new HashMap<>();
fields.put("the_geom", Polygon.class);
fields.put("base32", String.class);
fields.put("binary", String.class);
GeoUtils.createShpefile(featureList, fields, "F:/pop2pop/b.shp");
}
}
golang
package main
import (
"fmt"
"github.com/jonas-p/go-shp"
"github.com/mmcloughlin/geohash"
"github.com/twpayne/go-geom"
"log"
"strconv"
)
func main() {
lat := 30.549608
lon := 114.376971
hash_base32 := geohash.EncodeWithPrecision(lat, lon, 8)
fmt.Println(hash_base32)
neighbors := geohash.Neighbors(hash_base32)
hashs := append(neighbors, hash_base32)
geomMap := make(map[string]*geom.Polygon, 9)
for _, hash := range hashs {
box := geohash.BoundingBox(hash)
polygon, _ := geom.NewPolygon(geom.XY).SetCoords([][]geom.Coord{
{
{box.MaxLng, box.MaxLat},
{box.MaxLng, box.MinLat},
{box.MinLng, box.MinLat},
{box.MinLng, box.MaxLat},
{box.MaxLng, box.MaxLat},
}})
geomMap[hash] = polygon
}
polygonMap := map[string]*shp.PolyLine{}
for key, multiPlygon := range geomMap {
coordsMultiPolygon := multiPlygon.Coords()
points := make([][]shp.Point, len(coordsMultiPolygon), len(coordsMultiPolygon))
for index, coordsPolygon := range coordsMultiPolygon {
points2 := make([]shp.Point, len(coordsPolygon), len(coordsPolygon))
for j, coord := range coordsPolygon {
x := coord.X()
y := coord.Y()
point := shp.Point{x, y}
points2[j] = point
}
points[index]=points2
}
polygonTemp := shp.NewPolyLine(points)
polygonMap[key] = polygonTemp
}
// points to write
fields := []shp.Field{
// String attribute field with length 25
shp.StringField("base_32", 25),
//shp.StringField("binary", 50),
}
// create and open a shapefile for writing points
shape, err := shp.Create("F:/pop2pop/polygons.shp", shp.POLYGON)
if err != nil {
log.Fatal(err)
}
defer shape.Close()
// setup fields for attributes
shape.SetFields(fields)
// write points and attributes
cursor := 0
for key, polygon := range polygonMap {
shape.Write(polygon)
// write attribute for object n for field 0 (NAME)
//toInt, _ := geohash.ConvertStringToInt(key)
shape.WriteAttribute(cursor, 0, key)
//shape.WriteAttribute(cursor, 1, toInt)
cursor++;
}
points2 := []shp.Point{
shp.Point{10.0, 10.0},
shp.Point{10.0, 15.0},
shp.Point{15.0, 15.0},
shp.Point{15.0, 10.0},
}
// fields to write
fields2 := []shp.Field{
// String attribute field with length 25
shp.StringField("NAME", 25),
}
// create and open a shapefile for writing points
shape2, err := shp.Create("F:/pop2pop/points.shp", shp.POINT)
if err != nil { log.Fatal(err) }
defer shape2.Close()
// setup fields for attributes
shape2.SetFields(fields2)
// write points and attributes
for n, point := range points2 {
shape2.Write(&point)
// write attribute for object n for field 0 (NAME)
shape2.WriteAttribute(n, 0, "Point " + strconv.Itoa(n + 1))
}
}