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))
}
}
}
这个示例展示了如何:
- 打开PBF文件
- 创建解码器
- 读取文件头信息
- 逐个解码文件中的实体(节点/路径/关系)
- 根据不同类型处理实体
编码示例
以下是一个使用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)
}
}
这个示例展示了如何:
- 创建PBF文件
- 设置并写入文件头
- 创建并编码节点和路径
- 关闭编码器确保所有数据写入文件
更多关于golang OpenStreetMap PBF格式编码解码插件库pbf的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于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)
}
}
}
}
性能优化技巧
- 批量处理:将元素收集到批次中再处理,减少IO操作
- 内存复用:对于大规模处理,考虑复用对象减少GC压力
- 选择性解码:如果只需要特定数据,可以跳过不需要的元素
// 批量处理示例
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{}) {
// 批量处理逻辑
}
注意事项
- PBF文件通常很大,确保有足够的内存
- 处理过程中注意错误处理,特别是IO错误
- 对于生产环境,考虑使用更专业的库如
osm
(github.com/paulmach/osm)
pbf
库提供了基本的PBF编解码功能,适合简单的OSM数据处理任务。对于更复杂的需求,可能需要使用功能更全面的库。