16

GeoHash

 3 years ago
source link: https://studygolang.com/articles/31017
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

对9个base32进行排序

[wt3mf9qe, wt3mf9qg, wt3mf9qs, wt3mf9qt, wt3mf9qu, wt3mf9qv, wt3mf9r5, wt3mf9rh, wt3mf9rj]

geohash base32图
7vaU3ue.png!mobile

image.png

对9个二进制编码进行排序

1110011001000111001101110010011011001101, 1110011001000111001101110010011011001111, 1110011001000111001101110010011011011000, 1110011001000111001101110010011011011001, 1110011001000111001101110010011011011010, 1110011001000111001101110010011011011011, 1110011001000111001101110010011011100101, 1110011001000111001101110010011011110000, 1110011001000111001101110010011011110001

geohash 二进制图
zymUv26.png!mobile

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))
    }
}

有疑问加站长微信联系

iiUfA3j.png!mobile

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK