golang OpenStreetMap PBF格式编码解码插件库pbf的使用

golang OpenStreetMap PBF格式编码解码插件库pbf的使用

概述

pbf是一个基于Golang的OpenStreetMap PBF编码器/解码器,附带一个方便的命令行工具pbf

pbf命令行工具安装

可以使用go install命令安装pbf命令行工具:

$ go install m4o.io/pbf/cmd/pbf

pbf info命令

pbf命令行工具可以用来获取OpenStreetMap PBF文件的摘要和扩展信息:

$ pbf info -i testdata/greater-london.osm.pbf
BoundingBox: [-0.511482, 51.28554, 0.335437, 51.69344]
RequiredFeatures: OsmSchema-V0.6, DenseNodes
OptionalFeatures: 
WritingProgram: Osmium (http://wiki.openstreetmap.org/wiki/Osmium)
Source: 
OsmosisReplicationTimestamp: 2014-03-24T21:55:02Z
OsmosisReplicationSequenceNumber: 0
OsmosisReplicationBaseURL: 

JSON格式输出

添加-j选项可以获取JSON格式的输出:

$ pbf info -j -i testdata/greater-london.osm.pbf | jq
{
  "BoundingBox": {
    "Left": -0.511482,
    "Right": 0.33543700000000004,
    "Top": 51.69344,
    "Bottom": 51.285540000000005
  },
  "RequiredFeatures": [
    "OsmSchema-V0.6",
    "DenseNodes"
  ],
  "OptionalFeatures": null,
  "WritingProgram": "Osmium (http://wiki.openstreetmap.org/wiki/Osmium)",
  "Source": "",
  "OsmosisReplicationTimestamp": "2014-03-24T14:55:02-07:00",
  "OsmosisReplicationSequenceNumber": 0,
  "OsmosisReplicationBaseURL": ""
}

扩展信息

使用-e选项可以获取PBF文件的扩展信息。这会扫描整个文件,可能需要很长时间,同时会在stderr上显示进度条:

$ pbf info -e -i testdata/greater-london.osm.pbf
BoundingBox: [-0.511482, 51.28554, 0.335437, 51.69344]
RequiredFeatures: OsmSchema-V0.6, DenseNodes
OptionalFeatures: 
WritingProgram: Osmium (http://wiki.openstreetmap.org/wiki/Osmium)
Source: 
OsmosisReplicationTimestamp: 2014-03-24T21:55:02Z
OsmosisReplicationSequenceNumber: 0
OsmosisReplicationBaseURL: 
NodeCount: 2,729,006
WayCount: 459,055
RelationCount: 12,833

从stdin读取

pbf也可以从标准输入读取OpenStreetMap PBF文件:

$ cat testdata/greater-london.osm.pbf | pbf info -e

这种情况下不会显示进度条,因为无法预先知道PBF文件的大小。

Golang代码示例

以下是一个使用pbf库解码PBF文件的Golang示例代码:

package main

import (
	"fmt"
	"log"
	"os"

	"m4o.io/pbf"
)

func main() {
	// 打开PBF文件
	file, err := os.Open("testdata/greater-london.osm.pbf")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	// 创建PBF解码器
	decoder := pbf.NewDecoder(file)
	
	// 读取文件头信息
	header, err := decoder.Header()
	if err != nil {
		log.Fatal(err)
	}

	// 打印头信息
	fmt.Printf("BoundingBox: [%f, %f, %f, %f]\n",
		header.BoundingBox.Left,
		header.BoundingBox.Bottom,
		header.BoundingBox.Right,
		header.BoundingBox.Top)
	fmt.Printf("RequiredFeatures: %v\n", header.RequiredFeatures)
	fmt.Printf("OptionalFeatures: %v\n", header.OptionalFeatures)
	fmt.Printf("WritingProgram: %s\n", header.WritingProgram)

	// 解码PBF文件内容
	for {
		// 获取下一个实体(节点/路径/关系)
		entity, err := decoder.Decode()
		if err != nil {
			log.Fatal(err)
		}
		if entity == nil {
			break // 文件结束
		}

		// 根据实体类型处理
		switch e := entity.(type) {
		case *pbf.Node:
			// 处理节点
			fmt.Printf("Node: ID=%d, Lat=%f, Lon=%f\n", e.ID, e.Lat, e.Lon)
		case *pbf.Way:
			// 处理路径
			fmt.Printf("Way: ID=%d, NodeCount=%d\n", e.ID, len(e.Nodes))
		case *pbf.Relation:
			// 处理关系
			fmt.Printf("Relation: ID=%d, MemberCount=%d\n", e.ID, len(e.Members))
		}
	}
}

这个示例展示了如何:

  1. 打开PBF文件
  2. 创建解码器
  3. 读取文件头信息
  4. 逐个解码文件中的实体(节点/路径/关系)
  5. 根据不同类型处理实体

编码示例

以下是一个使用pbf库编码PBF文件的Golang示例代码:

package main

import (
	"log"
	"os"

	"m4o.io/pbf"
)

func main() {
	// 创建输出文件
	file, err := os.Create("output.osm.pbf")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	// 创建PBF编码器
	encoder := pbf.NewEncoder(file)
	
	// 设置文件头
	header := &pbf.Header{
		BoundingBox: &pbf.BoundingBox{
			Left:   -0.511482,
			Right:  0.335437,
			Top:    51.69344,
			Bottom: 51.28554,
		},
		RequiredFeatures: []string{"OsmSchema-V0.6", "DenseNodes"},
		WritingProgram:   "My PBF Writer",
	}
	
	// 写入文件头
	if err := encoder.EncodeHeader(header); err != nil {
		log.Fatal(err)
	}

	// 创建并编码一个节点
	node := &pbf.Node{
		ID:  12345,
		Lat: 51.5074,
		Lon: -0.1278,
		Tags: map[string]string{
			"name": "London",
			"place": "city",
		},
	}
	if err := encoder.Encode(node); err != nil {
		log.Fatal(err)
	}

	// 创建并编码一条路径
	way := &pbf.Way{
		ID: 67890,
		Nodes: []int64{12345, 12346, 12347},
		Tags: map[string]string{
			"highway": "residential",
			"name": "Main Street",
		},
	}
	if err := encoder.Encode(way); err != nil {
		log.Fatal(err)
	}

	// 确保所有数据写入文件
	if err := encoder.Close(); err != nil {
		log.Fatal(err)
	}
}

这个示例展示了如何:

  1. 创建PBF文件
  2. 设置并写入文件头
  3. 创建并编码节点和路径
  4. 关闭编码器确保所有数据写入文件

更多关于golang OpenStreetMap PBF格式编码解码插件库pbf的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang OpenStreetMap PBF格式编码解码插件库pbf的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


OpenStreetMap PBF格式编码解码库pbf的使用指南

OpenStreetMap PBF(Protocolbuffer Binary Format)是OSM数据的高效二进制格式。在Go语言中,pbf库是一个常用的PBF格式编解码工具。

安装pbf库

go get github.com/maguro/pbf

基本使用

解码PBF文件

package main

import (
	"fmt"
	"os"
	"github.com/maguro/pbf"
)

func main() {
	// 打开PBF文件
	file, err := os.Open("map.osm.pbf")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	// 创建解码器
	decoder := pbf.NewDecoder(file)
	
	// 开始解码
	err = decoder.Start(runtime.GOMAXPROCS(-1)) // 使用所有CPU核心
	if err != nil {
		panic(err)
	}

	// 处理解码后的数据
	for {
		// 获取下一个元素
		if v, err := decoder.Decode(); err == nil {
			switch v := v.(type) {
			case *pbf.Node:
				fmt.Printf("Node: ID=%d, Lat=%f, Lon=%f\n", v.ID, v.Lat, v.Lon)
			case *pbf.Way:
				fmt.Printf("Way: ID=%d, NodeIDs=%v\n", v.ID, v.NodeIDs)
			case *pbf.Relation:
				fmt.Printf("Relation: ID=%d, Members=%v\n", v.ID, v.Members)
			}
		} else {
			if err == pbf.EOF {
				break // 文件结束
			}
			panic(err)
		}
	}
}

编码PBF文件

package main

import (
	"os"
	"github.com/maguro/pbf"
)

func main() {
	// 创建输出文件
	file, err := os.Create("output.osm.pbf")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	// 创建编码器
	encoder := pbf.NewEncoder(file)
	defer encoder.Close()

	// 添加节点
	node := &pbf.Node{
		ID:  12345,
		Lat: 51.5074,
		Lon: -0.1278,
		Tags: map[string]string{
			"name": "London",
			"place": "city",
		},
	}
	if err := encoder.Encode(node); err != nil {
		panic(err)
	}

	// 添加路径
	way := &pbf.Way{
		ID: 54321,
		NodeIDs: []int64{12345, 12346, 12347},
		Tags: map[string]string{
			"highway": "residential",
			"name": "Main Street",
		},
	}
	if err := encoder.Encode(way); err != nil {
		panic(err)
	}
}

高级特性

并行处理

// 使用worker池处理PBF数据
func processPBF(filename string) error {
	file, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	decoder := pbf.NewDecoder(file)
	if err := decoder.Start(4); err != nil { // 使用4个worker
		return err
	}

	var wg sync.WaitGroup
	wg.Add(4) // 与worker数一致

	for i := 0; i < 4; i++ {
		go func() {
			defer wg.Done()
			for {
				v, err := decoder.Decode()
				if err != nil {
					if err == pbf.EOF {
						return
					}
					fmt.Printf("Error: %v\n", err)
					continue
				}
				// 处理元素
				processElement(v)
			}
		}()
	}

	wg.Wait()
	return nil
}

func processElement(v interface{}) {
	// 元素处理逻辑
}

过滤特定元素

// 只处理特定类型的元素
func filterElements(filename string) {
	file, err := os.Open(filename)
	if err != nil {
		panic(err)
	}
	defer file.Close()

	decoder := pbf.NewDecoder(file)
	if err := decoder.Start(runtime.GOMAXPROCS(-1)); err != nil {
		panic(err)
	}

	for {
		v, err := decoder.Decode()
		if err != nil {
			if err == pbf.EOF {
				break
			}
			panic(err)
		}

		switch v := v.(type) {
		case *pbf.Node:
			// 只处理有特定标签的节点
			if v.Tags["amenity"] == "restaurant" {
				fmt.Printf("Restaurant: %+v\n", v)
			}
		case *pbf.Way:
			// 只处理高速公路
			if v.Tags["highway"] != "" {
				fmt.Printf("Highway way: %+v\n", v)
			}
		}
	}
}

性能优化技巧

  1. 批量处理:将元素收集到批次中再处理,减少IO操作
  2. 内存复用:对于大规模处理,考虑复用对象减少GC压力
  3. 选择性解码:如果只需要特定数据,可以跳过不需要的元素
// 批量处理示例
func batchProcessing(decoder *pbf.Decoder) {
	const batchSize = 1000
	var batch []interface{}

	for {
		v, err := decoder.Decode()
		if err != nil {
			if err == pbf.EOF {
				processBatch(batch) // 处理最后一批
				break
			}
			panic(err)
		}

		batch = append(batch, v)
		if len(batch) >= batchSize {
			processBatch(batch)
			batch = batch[:0] // 清空批次
		}
	}
}

func processBatch(batch []interface{}) {
	// 批量处理逻辑
}

注意事项

  1. PBF文件通常很大,确保有足够的内存
  2. 处理过程中注意错误处理,特别是IO错误
  3. 对于生产环境,考虑使用更专业的库如osm(github.com/paulmach/osm)

pbf库提供了基本的PBF编解码功能,适合简单的OSM数据处理任务。对于更复杂的需求,可能需要使用功能更全面的库。

回到顶部