GeoHash

golang才是未来 · · 814 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

对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;

/**
 * 类描述 <p>
 * @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<Character> latCodes = new ArrayList<Character>();
        List<Character> lonCodes = new ArrayList<Character>();
        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<GeoHash> collect = Arrays.stream(adjacent).collect(Collectors.toList());
        collect.add(geoHash);

        GeometryFactory factory = new GeometryFactory();
        Map<String, Geometry> 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<String, Geometry> sortMap = new TreeMap<>(Comparator.naturalOrder());
        sortMap.putAll(map);

        List<Map<String, Object>> featureList = new ArrayList<>();
        for (Map.Entry<String, Geometry> entry : sortMap.entrySet()) {
            String key = entry.getKey();
            String binaryString = GeoHash.fromGeohashString(key).toBinaryString();
            Map<String, Object> map1 = new HashMap<>();
            map1.put("the_geom", entry.getValue());
            map1.put("base32", key);
            map1.put("binary", binaryString);
            featureList.add(map1);
        }
        Map<String,  Class<?>> 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))
    }
}


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:golang才是未来

查看原文:GeoHash

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

814 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传