Gogeo
To achieve rapid spatial analysis of large-scale vector data by leveraging the high concurrency of the Go language and the vector analysis capabilities of the GDAL library.
Install / Use
/learn @GrainArc/GogeoREADME
Gogeo - High-Performance GIS Spatial Analysis Library for Go
Gogeo is a high-performance Go GIS spatial analysis library built on GDAL/OGR, designed for large-scale geospatial data processing. It provides comprehensive spatial analysis capabilities through parallel computing, tile-based processing, and precision control.
✨ Key Features
🚀 High-Performance Parallel Computing
- Tile-based Processing: Automatically splits large datasets into tiles for parallel processing
- Multi-threaded Worker Pool: Configurable concurrent worker threads
- Memory Optimization: Smart memory management and resource cleanup
- Progress Monitoring: Real-time progress callbacks and user cancellation support
🎯 Complete Spatial Analysis Operations
- Clip: Clip one layer with another layer
- Erase: Remove overlapping parts from input layer
- Identity: Preserve input features and add overlapping attributes
- Intersect: Calculate intersection of two layers
- SymDifference: Calculate symmetric difference of two layers
- Union: Calculate union of two layers
- Update: Update one layer with another layer
📁 Comprehensive Data I/O Support
- PostGIS Database: Read from and write to PostGIS databases
- Shapefile: Support for ESRI Shapefile format
- File Geodatabase: Support for ESRI File Geodatabase (.gdb)
- Format Conversion: Convert between different geospatial formats
- Layer Management: List layers, get layer information, and metadata
🔧 Advanced Features
- Geometry Precision Control: Configurable geometry precision grid
- Field Management: Smart field mapping and conflict resolution
- Spatial Indexing: Automatic spatial index optimization for query performance
- Boundary Processing: Intelligent boundary feature deduplication
- Resource Management: Automatic cleanup with finalizers
📦 Installation
Prerequisites
Ensure GDAL development libraries are installed on your system:
Ubuntu/Debian:
sudo apt-get update
sudo apt-get install libgdal-dev gdal-bin
CentOS/RHEL:
sudo yum install gdal-devel gdal
macOS:
brew install gdal
Windows: Download and install OSGeo4W or GDAL Windows binaries
Install Gogeo
go get github.com/GrainArc/Gogeo
🚀 Quick Start
Basic Usage Example
package main
import (
"fmt"
"log"
"path/filepath"
"runtime"
"time"
"github.com/GrainArc/Gogeo" // 根据您的实际包路径调整
)
func main() {
// 设置输入文件路径
shpFile1 := "data/layer1.shp" // 第一个shapefile路径
shpFile2 := "data/layer2.shp" // 第二个shapefile路径
outputFile := "output/intersection_result.shp" // 输出文件路径
fmt.Println("开始空间相交分析测试...")
fmt.Printf("输入文件1: %s\n", shpFile1)
fmt.Printf("输入文件2: %s\n", shpFile2)
fmt.Printf("输出文件: %s\n", outputFile)
// 执行空间相交分析
err := performSpatialIntersectionTest(shpFile1, shpFile2, outputFile)
if err != nil {
log.Fatalf("空间相交分析失败: %v", err)
}
fmt.Println("空间相交分析完成!")
}
func performSpatialIntersectionTest(shpFile1, shpFile2, outputFile string) error {
startTime := time.Now()
// 1. 读取第一个shapefile
fmt.Println("正在读取第一个shapefile...")
reader1, err := Gogeo.NewFileGeoReader(shpFile1)
if err != nil {
return fmt.Errorf("创建第一个文件读取器失败: %v", err)
}
layer1, err := reader1.ReadShapeFile()
if err != nil {
return fmt.Errorf("读取第一个shapefile失败: %v", err)
}
// 打印第一个图层信息
fmt.Println("第一个图层信息:")
layer1.PrintLayerInfo()
// 2. 读取第二个shapefile
fmt.Println("\n正在读取第二个shapefile...")
reader2, err := Gogeo.NewFileGeoReader(shpFile2)
if err != nil {
layer1.Close()
return fmt.Errorf("创建第二个文件读取器失败: %v", err)
}
layer2, err := reader2.ReadShapeFile()
if err != nil {
layer1.Close()
return fmt.Errorf("读取第二个shapefile失败: %v", err)
}
// 打印第二个图层信息
fmt.Println("第二个图层信息:")
layer2.PrintLayerInfo()
// 3. 配置并行相交分析参数
config := &Gogeo.ParallelGeosConfig{
TileCount: 4, // 4x4分块
MaxWorkers: runtime.NumCPU(), // 使用所有CPU核心
BufferDistance: 0.001, // 分块缓冲距离
IsMergeTile: true, // 合并分块结果
ProgressCallback: progressCallback, // 进度回调函数
PrecisionConfig: &Gogeo.GeometryPrecisionConfig{
Enabled: true,
GridSize: 0.0001, // 几何精度网格大小
PreserveTopo: true, // 保持拓扑
KeepCollapsed: false, // 不保留退化几何
},
}
// 4. 选择字段合并策略
strategy := Gogeo.MergeWithPrefix // 使用前缀区分字段来源
fmt.Printf("\n开始执行空间相交分析...")
fmt.Printf("分块配置: %dx%d, 工作线程: %d\n",
config.TileCount, config.TileCount, config.MaxWorkers)
fmt.Printf("字段合并策略: %s\n", strategy.String())
// 5. 执行空间相交分析
result, err := Gogeo.SpatialIntersectionAnalysis(layer1, layer2, strategy, config)
if err != nil {
return fmt.Errorf("空间相交分析执行失败: %v", err)
}
analysisTime := time.Since(startTime)
fmt.Printf("\n相交分析完成! 耗时: %v\n", analysisTime)
fmt.Printf("结果要素数量: %d\n", result.ResultCount)
// 6. 将结果写出为shapefile
fmt.Println("正在写出结果到shapefile...")
writeStartTime := time.Now()
// 获取输出文件的图层名称(不含扩展名)
layerName := getFileNameWithoutExt(outputFile)
err = Gogeo.WriteShapeFileLayer(result.OutputLayer, outputFile, layerName, true)
if err != nil {
result.OutputLayer.Close()
return fmt.Errorf("写出shapefile失败: %v", err)
}
writeTime := time.Since(writeStartTime)
totalTime := time.Since(startTime)
fmt.Printf("结果写出完成! 耗时: %v\n", writeTime)
fmt.Printf("总耗时: %v\n", totalTime)
fmt.Printf("输出文件: %s\n", outputFile)
// 7. 验证输出文件
err = verifyOutputFile(outputFile)
if err != nil {
fmt.Printf("警告: 输出文件验证失败: %v\n", err)
} else {
fmt.Println("输出文件验证成功!")
}
// 清理资源
result.OutputLayer.Close()
return nil
}
// progressCallback 进度回调函数
func progressCallback(complete float64, message string) bool {
// 显示进度信息
fmt.Printf("\r进度: %.1f%% - %s", complete*100, message)
// 如果进度完成,换行
if complete >= 1.0 {
fmt.Println()
}
// 返回true继续执行,返回false取消执行
return true
}
// getFileNameWithoutExt 获取不含扩展名的文件名
func getFileNameWithoutExt(filePath string) string {
fileName := filepath.Base(filePath)
return fileName[:len(fileName)-len(filepath.Ext(fileName))]
}
// verifyOutputFile 验证输出文件
func verifyOutputFile(filePath string) error {
// 读取输出文件验证
reader, err := Gogeo.NewFileGeoReader(filePath)
if err != nil {
return fmt.Errorf("无法读取输出文件: %v", err)
}
layer, err := reader.ReadShapeFile()
if err != nil {
return fmt.Errorf("无法读取输出图层: %v", err)
}
defer layer.Close()
// 打印输出图层信息
fmt.Println("\n输出图层信息:")
layer.PrintLayerInfo()
// 检查要素数量
featureCount := layer.GetFeatureCount()
if featureCount == 0 {
return fmt.Errorf("输出文件中没有要素")
}
fmt.Printf("验证通过: 输出文件包含 %d 个要素\n", featureCount)
return nil
}
// 高级测试函数:测试不同的字段合并策略
func testDifferentStrategies(shpFile1, shpFile2 string) error {
strategies := []Gogeo.FieldMergeStrategy{
Gogeo.UseTable1Fields,
Gogeo.UseTable2Fields,
Gogeo.MergePreferTable1,
Gogeo.MergePreferTable2,
Gogeo.MergeWithPrefix,
}
config := &Gogeo.ParallelGeosConfig{
TileCount: 2,
MaxWorkers: runtime.NumCPU(),
BufferDistance: 0.001,
IsMergeTile: true,
ProgressCallback: progressCallback,
}
for i, strategy := range strategies {
fmt.Printf("\n=== 测试策略 %d: %s ===\n", i+1, strategy.String())
outputFile := fmt.Sprintf("output/test_strategy_%d.shp", i+1)
// 读取图层
layer1, err := Gogeo.ReadShapeFileLayer(shpFile1)
if err != nil {
return err
}
layer2, err := Gogeo.ReadShapeFileLayer(shpFile2)
if err != nil {
layer1.Close()
return err
}
// 执行分析
result, err := Gogeo.SpatialIntersectionAnalysis(layer1, layer2, strategy, config)
if err != nil {
return fmt.Errorf("策略 %s 执行失败: %v", strategy.String(), err)
}
// 写出结果
layerName := fmt.Sprintf("strategy_%d", i+1)
err = Gogeo.WriteShapeFileLayer(result.OutputLayer, outputFile, layerName, true)
if err != nil {
result.OutputLayer.Close()
return fmt.Errorf("策略 %s 写出失败: %v", strategy.String(), err)
}
fmt.Printf("策略 %s 完成,结果要素: %d,输出: %s\n",
strategy.String(), result.ResultCount, outputFile)
result.OutputLayer.Close()
}
return nil
}
// 性能测试函数
func performanceTest(shpFile1, shpFile2 string) error {
fmt.Println("\n=== 性能测试 ===")
// 测试不同的分块配置
tileConfigs := []int{2, 4, 8}
for _, tileCount := range tileConfigs {
fmt.Printf("\n--- 测试分块配置: %dx%d ---\n", tileCount, tileCount)
config := &Gogeo.ParallelGeosConfig{
TileCount: tileCount,
MaxWorkers: runtime.NumCPU(),
BufferDistance: 0.001,
IsMergeTile: true,
ProgressCallback: nil, // 性能测试时不显示进度
}
startTime := time.Now()
// 读取图层
layer1, err := Gogeo.ReadShapeFileLayer(shpFile1)
if err != nil {
return err
}
layer2, err := Gogeo.ReadShapeFileLayer(shpFile2)
if err != nil {
layer1.Close()
return err
}
// 执行分析
result, err := Gogeo.SpatialIntersectionAnalysis(layer1, layer2,
Gogeo.MergePreferTable1, config)
if err != nil {
return err
}
duration := time.Since(startTime)
fmt.Printf("分块 %dx%d: 耗时 %v, 结果要素 %d\n",
tileCount, tileCount, duration,
