Golang で OpenStreetMap ファイル（osm.pbf）の読み込み（osmpbf 利用）

こんにちは。
Golang で [osm.pbf](http://wiki.openstreetmap.org/wiki/PBF_Format) フォーマットの OpenStreetMap データファイルの読み込みを行ないました。[qedus/osmpbf](https://github.com/qedus/osmpbf) （を小修正したもの）を利用しました[^1] [^2] [^3]。[pbf-parser-comparison](https://github.com/pelias/pbf-parser-comparison) (GitHub) にもあるように確かに処理速度が速いように見えます。

[^1]: 他にも Go 用ライブラリがあるようです。例えば、[paulmach/osm/osmpbf](https://github.com/paulmach/osm/osmpbf),  [thomersch/gosmparse](https://github.com/thomersch/gosmparse), [maguro/pbf](https://github.com/maguro/pbf), [omniscale/imposm3](https://github.com/omniscale/imposm3), [qedus/osmpbf](https://github.com/qedus/osmpbf),  [missinglink/pbf](https://github.com/missinglink/pbf)。処理速度とメモリ消費量を実験して選ぶと良いかもしれません。
[^2]: C++ 用には、[osmcode/libosmium](https://github.com/osmcode/libosmium) （Python 用バインディングもあります）、[CanalTP/libosmpbfreader](https://github.com/CanalTP/libosmpbfreader) があるようです。
[^3]: Rust 用には、[b-r-u/osmpbf](https://github.com/b-r-u/osmpbf), [TeXitoi/osmpbfreader-rs](https://github.com/TeXitoi/osmpbfreader-rs), [crate/osmpbfreader](https://docs.rs/crate/osmpbfreader/), [oleksandromelchuk/rust-osm-reader](https://gitlab.com/oleksandromelchuk/rust-osm-reader) があるようです。

* ただしこれは、読込む osm.pbf ファイルには注意が必要で、[osmconvert](http://wiki.openstreetmap.org/wiki/Osmconvert) でクリッピングして作ったファイル（例えば[bbbike.org](http://download.bbbike.org/osm/)の extraction 機能で作られたファイル）を読み込むとメモリ消費量が大きいです。[osmosis](http://wiki.openstreetmap.org/wiki/Osmosis)や[osmium-tool](http://osmcode.org/osmium-tool/)で作られたファイルではこの問題は無いように見えます。

```

$ wget https://gist.github.com/AlekSi/d4369aa13cf1fc5ddfac3e91b67b2f7b/raw/8604f36a7357adfbd6b5292c2ea4972d9d0bfd3d/greater-london-140324.osm.pbf 
$ ls -ldh greater-london-140324.osm.pbf 
-rw-r--r--  1 kkdd  staff    33M 10 10 10:10 greater-london-140324.osm.pbf
$
$ go run osmpbf_progressbar.go -ncpu 4 greater-london-140324.osm.pbf 
33728 / 33728 [====================================================] 100.00 % 1s
Nodes: 2,729,006, Ways: 459,055, Relations: 12,833
$ go run osmpbf_progressbar.go -ncpu 1 greater-london-140324.osm.pbf 
33728 / 33728 [====================================================] 100.00 % 4s
Nodes: 2,729,006, Ways: 459,055, Relations: 12,833
```

```osmpbf_progressbar.go
package main

import (
    "os"
    "io"
    "fmt"
    "log"
    "flag"
    "runtime"
    "github.com/cheggaaa/pb"
    "github.com/dustin/go-humanize"
    "github.com/kkdd/osmpbf"
)

func main() {
    ncpu := flag.Int("ncpu", 1, "number of CPU")
    flag.Parse()
    runtime.GOMAXPROCS(*ncpu)
    for _, file := range flag.Args() {
        worker(file)
    }
}

func worker(file string) { 
    f, err := os.Open(file)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    stat, _ := f.Stat()
    filesiz := int(stat.Size()/1024)

    d := osmpbf.NewDecoder(f)
    err = d.Start(runtime.GOMAXPROCS(-1))
    if err != nil {
        log.Fatal(err)
    }

    var nc, wc, rc, i int64
    progressbar := pb.New(filesiz).SetUnits(pb.U_NO)
    progressbar.Start()
    for i = 0; ; i++ {
        if v, err := d.Decode(); err == io.EOF {
            break
        } else if err != nil {
            log.Fatal(err)
        } else {
            switch v := v.(type) {
            case *osmpbf.Node:
                nc++
            case *osmpbf.Way:
                wc++
            case *osmpbf.Relation:
                rc++
            default:
                log.Fatalf("unknown type %T\n", v)
            }
        }
        if i % 131072 == 0 {
            progressbar.Set(int(d.GetTotalReadSize()/1024))
        }
    }
    progressbar.Set(filesiz)
    progressbar.Finish()
    fmt.Printf("Nodes: %s, Ways: %s, Relations: %s\n", humanize.Comma(nc), humanize.Comma(wc), humanize.Comma(rc))
}
```