Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions codec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import (
"encoding/binary"
"math"
)

// an empty byte slice used for zeroing
var zeros = make([]byte, 64)

// node encoding
type encoding []byte

type encodingMetadata struct {
EntranceType uint8
AccessibilityType uint8
}

// encode lat/lon as 64 bit floats packed in to 8 bytes,
// each float is then truncated to 6 bytes because we don't
// need the additional precision (> 8 decimal places)
func (e encoding) setCoords(lat float64, lon float64) {
binary.BigEndian.PutUint64(e[0:8], math.Float64bits(lat))
binary.BigEndian.PutUint64(e[6:14], math.Float64bits(lon))
copy(e[12:14], zeros)
}

// decode lat/lon as truncated 64 bit floats from the first 12 bytes
func (e encoding) getCoords() (float64, float64) {
buffer := make([]byte, 8)

copy(buffer, e[:6])
lat := math.Float64frombits(binary.BigEndian.Uint64(buffer))

copy(buffer, e[6:12])
lon := math.Float64frombits(binary.BigEndian.Uint64(buffer))

return lat, lon
}

// leftmost two bits are for the entrance, next two bits are accessibility
// remaining 4 rightmost bits are reserved for future use.
func (e encoding) setMetadata(meta encodingMetadata) int {
if meta.EntranceType > 0 {
e[12] = ((meta.EntranceType & 0b00000011) << 6) // values [0,1,2] (stored in leftmost two bits)
e[12] |= ((meta.AccessibilityType & 0b00000011) << 4) // values [0,1,2] (stored in next two bits)

// 13 byte encoding
return 13
}

// 12 byte encoding
return 12
}

func (e encoding) getMetadata() encodingMetadata {
meta := encodingMetadata{}

if len(e) > 12 {
meta.EntranceType = (e[12] & 0b11000000) >> 6
meta.AccessibilityType = (e[12] & 0b00110000) >> 4
}

return meta
}
51 changes: 51 additions & 0 deletions entrance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

import (
"strings"

"github.com/qedus/osmpbf"
)

const (
EntranceNone uint8 = iota
EntranceNormal
EntranceMain
)

const (
WheelchairAccessibleNo uint8 = iota
WheelchairAccessibleImplicitYes
WheelchairAccessibleExplicitYes
)

// determine if the node is for an entrance
// https://wiki.openstreetmap.org/wiki/Key:entrance
func entranceType(node *osmpbf.Node) uint8 {
if val, ok := node.Tags["entrance"]; ok {
var norm = strings.ToLower(val)
switch norm {
case "main":
return EntranceMain
case "yes", "home", "staircase":
return EntranceNormal
}
}
return EntranceNone
}

// determine if the node is accessible for wheelchair users
// https://wiki.openstreetmap.org/wiki/Key:entrance
func accessibilityType(node *osmpbf.Node) uint8 {
if val, ok := node.Tags["wheelchair"]; ok {
var norm = strings.ToLower(val)
switch norm {
case "yes":
return WheelchairAccessibleExplicitYes
case "no":
return WheelchairAccessibleNo
default:
return WheelchairAccessibleImplicitYes
}
}
return WheelchairAccessibleNo
}
80 changes: 15 additions & 65 deletions pbf2json.go
Original file line number Diff line number Diff line change
Expand Up @@ -463,38 +463,6 @@ func onRelation(relation *osmpbf.Relation, centroid map[string]string, bounds *g
fmt.Println(string(json))
}

// determine if the node is for an entrance
// https://wiki.openstreetmap.org/wiki/Key:entrance
func isEntranceNode(node *osmpbf.Node) uint8 {
if val, ok := node.Tags["entrance"]; ok {
var norm = strings.ToLower(val)
switch norm {
case "main":
return 2
case "yes", "home", "staircase":
return 1
}
}
return 0
}

// determine if the node is accessible for wheelchair users
// https://wiki.openstreetmap.org/wiki/Key:entrance
func isWheelchairAccessibleNode(node *osmpbf.Node) uint8 {
if val, ok := node.Tags["wheelchair"]; ok {
var norm = strings.ToLower(val)
switch norm {
case "yes":
return 2
case "no":
return 0
default:
return 1
}
}
return 0
}

// queue a leveldb write in a batch
func cacheQueueNode(batch *leveldb.Batch, node *osmpbf.Node) {
id, val := nodeToBytes(node)
Expand Down Expand Up @@ -573,28 +541,21 @@ func cacheLookupWayNodes(db *leveldb.DB, wayid int64) ([]map[string]string, erro
return cacheLookupNodes(db, way)
}

// decode bytes to a 'latlon' type object
// decode bytes to a 'latlon' type object (map[string]string)
func bytesToLatLon(data []byte) map[string]string {
buf := make([]byte, 8)
latlon := make(map[string]string, 4)
enc := encoding(data)

// first 6 bytes are the latitude
// buf = append(buf, data[0:6]...)
copy(buf, data[:6])
lat64 := math.Float64frombits(binary.BigEndian.Uint64(buf[:8]))
lat64, lon64 := enc.getCoords()
latlon["lat"] = strconv.FormatFloat(lat64, 'f', 7, 64)

// next 6 bytes are the longitude
// buf = append(buf[:0], data[6:12]...)
copy(buf, data[6:12])
lon64 := math.Float64frombits(binary.BigEndian.Uint64(buf[:8]))
latlon["lon"] = strconv.FormatFloat(lon64, 'f', 7, 64)

// check for the bitmask byte which indicates things like an
// entrance and the level of wheelchair accessibility
if len(data) > 12 {
latlon["entrance"] = fmt.Sprintf("%d", (data[12]&0xC0)>>6)
latlon["wheelchair"] = fmt.Sprintf("%d", (data[12]&0x30)>>4)
meta := enc.getMetadata()
if meta.EntranceType > 0 {
latlon["entrance"] = fmt.Sprintf("%d", meta.EntranceType)
latlon["wheelchair"] = fmt.Sprintf("%d", meta.AccessibilityType)
}

return latlon
Expand All @@ -604,27 +565,16 @@ func bytesToLatLon(data []byte) map[string]string {
func nodeToBytes(node *osmpbf.Node) (string, []byte) {
stringid := strconv.FormatInt(node.ID, 10)

buf := make([]byte, 14)
// encode lat/lon as 64 bit floats packed in to 8 bytes,
// each float is then truncated to 6 bytes because we don't
// need the additional precision (> 8 decimal places)

binary.BigEndian.PutUint64(buf, math.Float64bits(node.Lat))
binary.BigEndian.PutUint64(buf[6:], math.Float64bits(node.Lon))

// generate a bitmask for relevant tag features
isEntrance := isEntranceNode(node)
if isEntrance == 0 {
return stringid, buf[:12]
}
enc := make(encoding, 14)
enc.setCoords(node.Lat, node.Lon)

// leftmost two bits are for the entrance, next two bits are accessibility
// remaining 4 rightmost bits are reserved for future use.
bitmask := isEntrance << 6
bitmask |= isWheelchairAccessibleNode(node) << 4
buf[12] = bitmask
len := enc.setMetadata(encodingMetadata{
EntranceType: entranceType(node),
AccessibilityType: accessibilityType(node),
})

return stringid, buf[:13]
// return variable length encoding
return stringid, enc[:len]
}

func idSliceToBytes(ids []int64) []byte {
Expand Down