golang实现GeoJSON与S2单元格互转及地图几何特性处理插件库S2 geojson的使用

Golang实现GeoJSON与S2单元格互转及地图几何特性处理插件库S2 geojson的使用

预览

preview

概述

在地图上绘制多边形或粘贴geoJSON,探索s2.RegionCoverer如何根据最小和最大级别用S2单元格覆盖它。放置标记并检查相应的S2单元格。

  • 使用区域覆盖器在leaflet地图上显示s2单元格
  • 根据最小和最大级别将geojson要素转换为单元格联合(仅支持多边形和点)
  • 绘制点和多边形
  • 检查点与圆与geoJSON要素的交集

快速开始

go run cmd/s2-geojson/main.go

Docker

docker run -p 8080:8080 --rm lmaroulis/s2-geojson

完整示例代码

下面是一个使用s2-geojson库进行GeoJSON与S2单元格互转的完整示例:

package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/golang/geo/s2"
	"github.com/pantrif/s2-geojson"
)

func main() {
	// 示例GeoJSON多边形
	geojsonStr := `{
		"type": "Polygon",
		"coordinates": [
			[
				[13.361263, 38.115556],
				[13.361263, 38.115556],
				[13.364945, 38.115556],
				[13.364945, 38.117057],
				[13.361263, 38.117057],
				[13.361263, 38.115556]
			]
		]
	}`

	// 将GeoJSON转换为S2单元格
	var geojsonPolygon map[string]interface{}
	if err := json.Unmarshal([]byte(geojsonStr), &geojsonPolygon); err != nil {
		log.Fatal(err)
	}

	// 创建区域覆盖器,设置最小和最大级别
	rc := &s2.RegionCoverer{
		MinLevel: 10,
		MaxLevel: 20,
	}

	// 将GeoJSON多边形转换为S2单元格联合
	cellUnion, err := s2geojson.GeoJSONToCellUnion(geojsonPolygon, rc)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("转换后的S2单元格:")
	for _, cellID := range cellUnion {
		fmt.Printf("CellID: %d, Token: %s\n", cellID, cellID.ToToken())
	}

	// 将S2单元格联合转换回GeoJSON
	convertedGeoJSON := s2geojson.CellUnionToGeoJSON(cellUnion)
	jsonData, err := json.MarshalIndent(convertedGeoJSON, "", "  ")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("\n转换回的GeoJSON:")
	fmt.Println(string(jsonData))
}

功能说明

  1. GeoJSON转S2单元格:

    • 使用GeoJSONToCellUnion函数将GeoJSON多边形或点转换为S2单元格联合
    • 可以设置RegionCoverer的MinLevel和MaxLevel来控制单元格的精度
  2. S2单元格转GeoJSON:

    • 使用CellUnionToGeoJSON函数将S2单元格联合转换回GeoJSON格式
    • 生成的GeoJSON可以在地图库中直接使用
  3. 几何特性处理:

    • 支持点和多边形的绘制与转换
    • 可以检查点与圆的交集关系

注意事项

  • 目前仅支持Polygons和Points类型的GeoJSON转换
  • 转换精度取决于设置的MinLevel和MaxLevel参数
  • 对于复杂多边形,可能需要调整RegionCoverer参数以获得更好的覆盖效果

许可证

该项目使用MIT许可证。


更多关于golang实现GeoJSON与S2单元格互转及地图几何特性处理插件库S2 geojson的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现GeoJSON与S2单元格互转及地图几何特性处理插件库S2 geojson的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang中GeoJSON与S2单元格互转及地图处理

概述

在Golang中处理地理空间数据时,Google的S2库和GeoJSON是常用的工具组合。S2库提供了高效的球面几何计算和空间索引能力,而GeoJSON则是地理空间数据的通用交换格式。

核心库介绍

1. S2 Geometry Library

S2库提供了球面几何计算和空间索引功能,特别适合处理地理空间数据。

import (
	"github.com/golang/geo/s2"
)

2. GeoJSON处理

可以使用geojson包来处理GeoJSON数据:

import (
	"github.com/paulmach/orb"
	"github.com/paulmach/orb/geojson"
)

GeoJSON与S2互转实现

1. GeoJSON转S2单元格

func GeoJSONToS2Cells(geoJSONStr string, level int) ([]s2.CellID, error) {
	// 解析GeoJSON
	fc, err := geojson.UnmarshalFeatureCollection([]byte(geoJSONStr))
	if err != nil {
		return nil, err
	}

	var cellIDs []s2.CellID
	
	// 遍历所有要素
	for _, feature := range fc.Features {
		// 根据几何类型处理
		switch geom := feature.Geometry.(type) {
		case orb.Point:
			// 点转S2单元格
			ll := s2.LatLngFromDegrees(geom.Lat(), geom.Lon())
			cellID := s2.CellIDFromLatLng(ll).Parent(level)
			cellIDs = append(cellIDs, cellID)
			
		case orb.Polygon:
			// 多边形转S2单元格
			loops := make([]*s2.Loop, 0, len(geom))
			for _, ring := range geom {
				points := make([]s2.Point, 0, len(ring))
				for _, coord := range ring {
					points = append(points, s2.PointFromLatLng(
						s2.LatLngFromDegrees(coord.Lat(), coord.Lon()),
					))
				}
				loops = append(loops, s2.LoopFromPoints(points))
			}
			
			// 创建多边形并获取覆盖的单元格
			polygon := s2.PolygonFromLoops(loops)
			rc := s2.RegionCoverer{MaxLevel: level, MinLevel: level}
			cellUnion := rc.Covering(polygon)
			cellIDs = append(cellIDs, cellUnion...)
		}
	}
	
	return cellIDs, nil
}

2. S2单元格转GeoJSON

func S2CellsToGeoJSON(cellIDs []s2.CellID) (*geojson.FeatureCollection, error) {
	fc := geojson.NewFeatureCollection()
	
	for _, cellID := range cellIDs {
		cell := s2.CellFromCellID(cellID)
		
		// 获取单元格的四个顶点坐标
		var coords []orb.Point
		for i := 0; i < 4; i++ {
			ll := s2.LatLngFromPoint(cell.Vertex(i))
			coords = append(coords, orb.Point{ll.Lng.Degrees(), ll.Lat.Degrees()})
		}
		// 闭合多边形
		coords = append(coords, coords[0])
		
		// 创建GeoJSON要素
		polygon := orb.Polygon{coords}
		feature := geojson.NewFeature(polygon)
		feature.Properties["s2_cell_id"] = cellID.String()
		fc.Append(feature)
	}
	
	return fc, nil
}

地图几何特性处理

1. 空间关系判断

// 判断点是否在多边形内
func PointInPolygon(point orb.Point, polygon orb.Polygon) bool {
	s2Polygon := convertOrbPolygonToS2(polygon)
	s2Point := s2.PointFromLatLng(s2.LatLngFromDegrees(point.Lat(), point.Lon()))
	return s2Polygon.ContainsPoint(s2Point)
}

func convertOrbPolygonToS2(polygon orb.Polygon) *s2.Polygon {
	loops := make([]*s2.Loop, 0, len(polygon))
	for _, ring := range polygon {
		points := make([]s2.Point, 0, len(ring))
		for _, coord := range ring {
			points = append(points, s2.PointFromLatLng(
				s2.LatLngFromDegrees(coord.Lat(), coord.Lon()),
			))
		}
		loops = append(loops, s2.LoopFromPoints(points))
	}
	return s2.PolygonFromLoops(loops)
}

2. 缓冲区计算

// 创建缓冲区
func CreateBuffer(geom orb.Geometry, distance float64) orb.Geometry {
	// 这里可以使用Turf.js的Go实现或其他缓冲区算法
	// 示例使用简单的近似方法
	switch g := geom.(type) {
	case orb.Point:
		// 点缓冲区转换为圆形多边形
		return createCircle(g, distance)
	case orb.LineString:
		// 线缓冲区转换为带状多边形
		return createLineBuffer(g, distance)
	case orb.Polygon:
		// 多边形缓冲区
		return createPolygonBuffer(g, distance)
	default:
		return nil
	}
}

使用s2-geojson插件库

s2-geojson是一个专门处理S2和GeoJSON互转的库:

import "github.com/paulmach/go.geojson/s2geojson"

// GeoJSON转S2
func GeoJSONToS2UsingLib(geoJSONStr string) ([]s2.CellID, error) {
	geom, err := geojson.UnmarshalGeometry([]byte(geoJSONStr))
	if err != nil {
		return nil, err
	}
	
	s2Geom, err := s2geojson.ParseGeometry(geom)
	if err != nil {
		return nil, err
	}
	
	// 获取覆盖的S2单元格
	coverer := &s2.RegionCoverer{MaxLevel: 15}
	return coverer.Covering(s2Geom), nil
}

// S2转GeoJSON
func S2ToGeoJSONUsingLib(cellIDs []s2.CellID) ([]byte, error) {
	features := make([]*geojson.Feature, 0, len(cellIDs))
	
	for _, cellID := range cellIDs {
		cell := s2.CellFromCellID(cellID)
		geom := s2geojson.FromCell(cell)
		
		feature := geojson.NewFeature(geom)
		feature.Properties["s2_cell_id"] = cellID.String()
		features = append(features, feature)
	}
	
	fc := geojson.NewFeatureCollection()
	fc.Features = features
	
	return fc.MarshalJSON()
}

性能优化建议

  1. 单元格级别选择:根据精度需求选择合适的S2级别,级别越高单元格越小但数量越多
  2. 批量处理:对于大量数据,考虑使用goroutine并行处理
  3. 内存优化:处理大型数据集时注意内存使用,可以分块处理

总结

通过结合S2库和GeoJSON处理库,可以在Golang中高效地处理地理空间数据。S2提供了强大的空间索引和几何计算能力,而GeoJSON则是通用的数据交换格式。根据具体需求,可以选择直接使用底层库或更高级的封装库如s2-geojson

回到顶部