package cache import ( bin "encoding/binary" "github.com/jmhodges/levigo" "goposm/binary" "goposm/element" "path/filepath" ) type OSMCache struct { Dir string Coords *DeltaCoordsCache Ways *WaysCache Nodes *NodesCache Relations *RelationsCache } func (c *OSMCache) Close() { if c.Coords != nil { c.Coords.close() c.Coords = nil } if c.Nodes != nil { c.Nodes.close() c.Nodes = nil } if c.Ways != nil { c.Ways.close() c.Ways = nil } if c.Relations != nil { c.Relations.close() c.Relations = nil } } func NewOSMCache(dir string) (*OSMCache, error) { cache := &OSMCache{Dir: dir} var err error cache.Coords, err = NewDeltaCoordsCache(filepath.Join(dir, "coords")) if err != nil { return nil, err } cache.Nodes, err = NewNodesCache(filepath.Join(dir, "nodes")) if err != nil { cache.Close() return nil, err } cache.Ways, err = NewWaysCache(filepath.Join(dir, "ways")) if err != nil { cache.Close() return nil, err } cache.Relations, err = NewRelationsCache(filepath.Join(dir, "relations")) if err != nil { cache.Close() return nil, err } return cache, nil } type Cache struct { db *levigo.DB wo *levigo.WriteOptions ro *levigo.ReadOptions } func (c *Cache) open(path string) error { opts := levigo.NewOptions() opts.SetCache(levigo.NewLRUCache(1024 * 1024 * 50)) opts.SetCreateIfMissing(true) db, err := levigo.Open(path, opts) if err != nil { return err } c.db = db c.wo = levigo.NewWriteOptions() c.ro = levigo.NewReadOptions() return nil } func (c *Cache) close() { c.db.Close() } type NodesCache struct { Cache } func NewNodesCache(path string) (*NodesCache, error) { cache := NodesCache{} err := cache.open(path) if err != nil { return nil, err } return &cache, err } type CoordsCache struct { Cache } func NewCoordsCache(path string) (*CoordsCache, error) { cache := CoordsCache{} err := cache.open(path) if err != nil { return nil, err } return &cache, err } type WaysCache struct { Cache toWrite chan []element.Way } func NewWaysCache(path string) (*WaysCache, error) { cache := WaysCache{} cache.toWrite = make(chan []element.Way) go cache.wayWriter() err := cache.open(path) if err != nil { return nil, err } return &cache, err } type RelationsCache struct { Cache } func NewRelationsCache(path string) (*RelationsCache, error) { cache := RelationsCache{} err := cache.open(path) if err != nil { return nil, err } return &cache, err } func (p *CoordsCache) PutCoord(node *element.Node) error { keyBuf := make([]byte, 8) bin.PutVarint(keyBuf, int64(node.Id)) data, err := binary.MarshalCoord(node) if err != nil { panic(err) } return p.db.Put(p.wo, keyBuf, data) } func (p *CoordsCache) PutCoords(nodes []element.Node) error { batch := levigo.NewWriteBatch() defer batch.Close() keyBuf := make([]byte, 8) for _, node := range nodes { bin.PutVarint(keyBuf, int64(node.Id)) data, err := binary.MarshalCoord(&node) if err != nil { panic(err) } batch.Put(keyBuf, data) } return p.db.Write(p.wo, batch) } func (p *CoordsCache) GetCoord(id int64) (*element.Node, error) { keyBuf := make([]byte, 8) bin.PutVarint(keyBuf, int64(id)) data, err := p.db.Get(p.ro, keyBuf) if err != nil { return nil, err } if data == nil { return nil, nil } node, err := binary.UnmarshalCoord(id, data) if err != nil { panic(err) } return node, nil } func (p *NodesCache) PutNode(node *element.Node) error { if node.Tags == nil { return nil } keyBuf := make([]byte, 8) bin.PutVarint(keyBuf, int64(node.Id)) data, err := binary.MarshalNode(node) if err != nil { panic(err) } return p.db.Put(p.wo, keyBuf, data) } func (p *NodesCache) PutNodes(nodes []element.Node) (int, error) { batch := levigo.NewWriteBatch() defer batch.Close() keyBuf := make([]byte, 8) var n int for _, node := range nodes { if node.Tags == nil { continue } bin.PutVarint(keyBuf, int64(node.Id)) data, err := binary.MarshalNode(&node) if err != nil { panic(err) } batch.Put(keyBuf, data) n += 1 } return n, p.db.Write(p.wo, batch) } func (p *NodesCache) GetNode(id int64) (*element.Node, error) { keyBuf := make([]byte, 8) bin.PutVarint(keyBuf, int64(id)) data, err := p.db.Get(p.ro, keyBuf) if err != nil { return nil, err } if data == nil { return nil, nil } node, err := binary.UnmarshalNode(data) if err != nil { panic(err) } return node, nil } func (p *WaysCache) PutWay(way *element.Way) error { keyBuf := make([]byte, 8) bin.PutVarint(keyBuf, int64(way.Id)) data, err := binary.MarshalWay(way) if err != nil { panic(err) } return p.db.Put(p.wo, keyBuf, data) } func (p *WaysCache) PutWays(ways []element.Way) error { batch := levigo.NewWriteBatch() defer batch.Close() keyBuf := make([]byte, 8) for _, way := range ways { bin.PutVarint(keyBuf, int64(way.Id)) data, err := binary.MarshalWay(&way) if err != nil { panic(err) } batch.Put(keyBuf, data) } return p.db.Write(p.wo, batch) } func (p *WaysCache) _PutWays(ways []element.Way) { p.toWrite <- ways } func (p *WaysCache) wayWriter() { for ways := range p.toWrite { batch := levigo.NewWriteBatch() defer batch.Close() keyBuf := make([]byte, 8) for _, way := range ways { bin.PutVarint(keyBuf, int64(way.Id)) data, err := binary.MarshalWay(&way) if err != nil { panic(err) } batch.Put(keyBuf, data) } _ = p.db.Write(p.wo, batch) } } func (p *WaysCache) GetWay(id int64) (*element.Way, error) { keyBuf := make([]byte, 8) bin.PutVarint(keyBuf, int64(id)) data, err := p.db.Get(p.ro, keyBuf) if err != nil { return nil, err } if data == nil { return nil, nil } way, err := binary.UnmarshalWay(data) if err != nil { panic(err) } return way, nil } func (p *WaysCache) Iter() chan *element.Way { way := make(chan *element.Way) go func() { ro := levigo.NewReadOptions() ro.SetFillCache(false) it := p.db.NewIterator(ro) defer it.Close() it.SeekToFirst() for it = it; it.Valid(); it.Next() { ways, err := binary.UnmarshalWay(it.Value()) if err != nil { panic(err) } way <- ways } close(way) }() return way } func (p *RelationsCache) PutRelation(relation *element.Relation) error { keyBuf := make([]byte, 8) bin.PutVarint(keyBuf, int64(relation.Id)) data, err := binary.MarshalRelation(relation) if err != nil { panic(err) } return p.db.Put(p.wo, keyBuf, data) } func (p *RelationsCache) PutRelations(rels []element.Relation) error { batch := levigo.NewWriteBatch() defer batch.Close() keyBuf := make([]byte, 8) for _, rel := range rels { bin.PutVarint(keyBuf, int64(rel.Id)) data, err := binary.MarshalRelation(&rel) if err != nil { panic(err) } batch.Put(keyBuf, data) } return p.db.Write(p.wo, batch) } func (p *RelationsCache) Iter() chan *element.Relation { rel := make(chan *element.Relation) go func() { ro := levigo.NewReadOptions() ro.SetFillCache(false) it := p.db.NewIterator(ro) defer it.Close() it.SeekToFirst() for it = it; it.Valid(); it.Next() { relation, err := binary.UnmarshalRelation(it.Value()) if err != nil { panic(err) } rel <- relation } close(rel) }() return rel } func (p *RelationsCache) GetRelation(id int64) (*element.Relation, error) { keyBuf := make([]byte, 8) bin.PutVarint(keyBuf, int64(id)) data, err := p.db.Get(p.ro, keyBuf) if err != nil { return nil, err } if data == nil { return nil, nil } relation, err := binary.UnmarshalRelation(data) if err != nil { panic(err) } return relation, err } func (p *Cache) Close() { p.db.Close() }