store common tags as single unicode char
parent
bd3024b88c
commit
618deea4a9
|
@ -63,7 +63,7 @@ func UnmarshalCoord(id int64, data []byte) (node *element.Node, err error) {
|
|||
func MarshalNode(node *element.Node) ([]byte, error) {
|
||||
pbfNode := &Node{}
|
||||
pbfNode.FromWgsCoord(node.Long, node.Lat)
|
||||
pbfNode.Tags = node.TagsAsArray()
|
||||
pbfNode.Tags = TagsAsArray(node.Tags)
|
||||
return proto.Marshal(pbfNode)
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ func UnmarshalNode(data []byte) (node *element.Node, err error) {
|
|||
|
||||
node = &element.Node{}
|
||||
node.Long, node.Lat = pbfNode.WgsCoord()
|
||||
node.TagsFromArray(pbfNode.Tags)
|
||||
node.Tags = TagsFromArray(pbfNode.Tags)
|
||||
return node, nil
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ func MarshalWay(way *element.Way) ([]byte, error) {
|
|||
pbfWay := &Way{}
|
||||
deltaPack(way.Refs)
|
||||
pbfWay.Refs = way.Refs
|
||||
pbfWay.Tags = way.TagsAsArray()
|
||||
pbfWay.Tags = TagsAsArray(way.Tags)
|
||||
return proto.Marshal(pbfWay)
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ func UnmarshalWay(data []byte) (way *element.Way, err error) {
|
|||
way = &element.Way{}
|
||||
deltaUnpack(pbfWay.Refs)
|
||||
way.Refs = pbfWay.Refs
|
||||
way.TagsFromArray(pbfWay.Tags)
|
||||
way.Tags = TagsFromArray(pbfWay.Tags)
|
||||
return way, nil
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ func MarshalRelation(relation *element.Relation) ([]byte, error) {
|
|||
pbfRelation.MemberTypes[i] = Relation_MemberType(m.Type)
|
||||
pbfRelation.MemberRoles[i] = m.Role
|
||||
}
|
||||
pbfRelation.Tags = relation.TagsAsArray()
|
||||
pbfRelation.Tags = TagsAsArray(relation.Tags)
|
||||
return proto.Marshal(pbfRelation)
|
||||
}
|
||||
|
||||
|
@ -151,6 +151,6 @@ func UnmarshalRelation(data []byte) (relation *element.Relation, err error) {
|
|||
relation.Members[i].Role = pbfRelation.MemberRoles[i]
|
||||
}
|
||||
//relation.Nodes = pbfRelation.Node
|
||||
relation.TagsFromArray(pbfRelation.Tags)
|
||||
relation.Tags = TagsFromArray(pbfRelation.Tags)
|
||||
return relation, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,277 @@
|
|||
package binary
|
||||
|
||||
// Serialize tags to a array of interleaved key and value strings.
|
||||
// Common tags like building=yes are serialized to a single unicode
|
||||
// char to save a few bytes.
|
||||
|
||||
// Common tags are encoded as a single unicode char from the Unicode
|
||||
// Private Use Area http://en.wikipedia.org/wiki/Private_Use_(Unicode)
|
||||
// between U+E000 and U+F8FF. They take three bytes in UTF-8 encoding.
|
||||
|
||||
// For example: building=yes will need 4 bytes instead of 13 bytes.
|
||||
// (building=8 + yes=3 + 2x1 for string length, vs. 3+1)
|
||||
|
||||
import (
|
||||
"goposm/element"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type codepoint rune
|
||||
type tag struct {
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
var tagsToCodePoint = map[string]map[string]codepoint{}
|
||||
var codePointToTag = map[codepoint]tag{}
|
||||
|
||||
const minCodePoint = codepoint('\uE000')
|
||||
|
||||
var nextCodePoint = codepoint('\uE000')
|
||||
|
||||
func addTagCodePoint(key, value string) {
|
||||
valMap, ok := tagsToCodePoint[key]
|
||||
if !ok {
|
||||
tagsToCodePoint[key] = map[string]codepoint{value: nextCodePoint}
|
||||
} else {
|
||||
if _, ok := valMap[value]; ok {
|
||||
panic("duplicate entry for tag codepoints: " + key + " " + value)
|
||||
}
|
||||
valMap[value] = nextCodePoint
|
||||
}
|
||||
|
||||
codePointToTag[nextCodePoint] = tag{key, value}
|
||||
nextCodePoint += 1
|
||||
}
|
||||
|
||||
func TagsFromArray(arr []string) element.Tags {
|
||||
if len(arr) == 0 {
|
||||
return element.Tags{}
|
||||
}
|
||||
result := make(element.Tags)
|
||||
for i := 0; i < len(arr); i += 1 {
|
||||
if r, size := utf8.DecodeRuneInString(arr[i]); size >= 3 &&
|
||||
codepoint(r) >= minCodePoint &&
|
||||
codepoint(r) < nextCodePoint {
|
||||
tag, ok := codePointToTag[codepoint(r)]
|
||||
if !ok {
|
||||
panic("missing tag for codepoint")
|
||||
}
|
||||
result[tag.Key] = tag.Value
|
||||
} else {
|
||||
result[arr[i]] = arr[i+1]
|
||||
i++
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func TagsAsArray(tags element.Tags) []string {
|
||||
if len(tags) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := make([]string, 0, 2*len(tags))
|
||||
for key, val := range tags {
|
||||
if valMap, ok := tagsToCodePoint[key]; ok {
|
||||
if codePoint, ok := valMap[val]; ok {
|
||||
result = append(result, string(codePoint))
|
||||
continue
|
||||
}
|
||||
}
|
||||
result = append(result, key, val)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func init() {
|
||||
//
|
||||
// DO NOT EDIT, REMOVE, REORDER ANY OF THE FOLLOWING LINES!
|
||||
//
|
||||
// most used tags for ways
|
||||
//
|
||||
addTagCodePoint("building", "yes")
|
||||
addTagCodePoint("highway", "residential")
|
||||
addTagCodePoint("highway", "service")
|
||||
addTagCodePoint("wall", "no")
|
||||
addTagCodePoint("highway", "unclassified")
|
||||
addTagCodePoint("waterway", "stream")
|
||||
addTagCodePoint("highway", "track")
|
||||
addTagCodePoint("natural", "water")
|
||||
addTagCodePoint("oneway", "yes")
|
||||
addTagCodePoint("highway", "footway")
|
||||
addTagCodePoint("highway", "tertiary")
|
||||
addTagCodePoint("access", "private")
|
||||
addTagCodePoint("highway", "path")
|
||||
addTagCodePoint("highway", "secondary")
|
||||
addTagCodePoint("landuse", "forest")
|
||||
addTagCodePoint("building", "house")
|
||||
addTagCodePoint("bridge", "yes")
|
||||
addTagCodePoint("surface", "asphalt")
|
||||
addTagCodePoint("natural", "wood")
|
||||
addTagCodePoint("foot", "yes")
|
||||
addTagCodePoint("landuse", "residential")
|
||||
addTagCodePoint("surface", "paved")
|
||||
addTagCodePoint("highway", "primary")
|
||||
addTagCodePoint("surface", "unpaved")
|
||||
addTagCodePoint("landuse", "grass")
|
||||
addTagCodePoint("building", "residential")
|
||||
addTagCodePoint("service", "parking_aisle")
|
||||
addTagCodePoint("oneway", "no")
|
||||
addTagCodePoint("railway", "rail")
|
||||
addTagCodePoint("bicycle", "yes")
|
||||
addTagCodePoint("service", "driveway")
|
||||
addTagCodePoint("amenity", "parking")
|
||||
addTagCodePoint("area", "yes")
|
||||
addTagCodePoint("barrier", "fence")
|
||||
addTagCodePoint("tracktype", "grade2")
|
||||
addTagCodePoint("natural", "coastline")
|
||||
addTagCodePoint("tracktype", "grade3")
|
||||
addTagCodePoint("intermittent", "yes")
|
||||
addTagCodePoint("landuse", "farmland")
|
||||
addTagCodePoint("building", "hut")
|
||||
addTagCodePoint("boundary", "administrative")
|
||||
addTagCodePoint("lit", "yes")
|
||||
addTagCodePoint("highway", "cycleway")
|
||||
addTagCodePoint("landuse", "meadow")
|
||||
addTagCodePoint("waterway", "river")
|
||||
addTagCodePoint("natural", "wetland")
|
||||
addTagCodePoint("highway", "trunk")
|
||||
addTagCodePoint("surface", "gravel")
|
||||
addTagCodePoint("tracktype", "grade1")
|
||||
addTagCodePoint("barrier", "wall")
|
||||
addTagCodePoint("building", "garage")
|
||||
addTagCodePoint("highway", "living_street")
|
||||
addTagCodePoint("highway", "motorway")
|
||||
addTagCodePoint("tracktype", "grade4")
|
||||
addTagCodePoint("landuse", "farm")
|
||||
addTagCodePoint("leisure", "pitch")
|
||||
addTagCodePoint("surface", "ground")
|
||||
addTagCodePoint("tunnel", "yes")
|
||||
addTagCodePoint("highway", "motorway_link")
|
||||
addTagCodePoint("bicycle", "no")
|
||||
addTagCodePoint("highway", "road")
|
||||
addTagCodePoint("natural", "scrub")
|
||||
addTagCodePoint("highway", "steps")
|
||||
addTagCodePoint("foot", "designated")
|
||||
addTagCodePoint("waterway", "ditch")
|
||||
addTagCodePoint("admin_level", "8")
|
||||
addTagCodePoint("tracktype", "grade5")
|
||||
addTagCodePoint("access", "yes")
|
||||
addTagCodePoint("building", "apartments")
|
||||
addTagCodePoint("leisure", "swimming_pool")
|
||||
addTagCodePoint("junction", "roundabout")
|
||||
addTagCodePoint("highway", "pedestrian")
|
||||
addTagCodePoint("barrier", "hedge")
|
||||
addTagCodePoint("bicycle", "designated")
|
||||
addTagCodePoint("leisure", "park")
|
||||
addTagCodePoint("service", "alley")
|
||||
addTagCodePoint("landuse", "farmyard")
|
||||
addTagCodePoint("building", "industrial")
|
||||
addTagCodePoint("waterway", "riverbank")
|
||||
addTagCodePoint("building", "roof")
|
||||
addTagCodePoint("surface", "dirt")
|
||||
addTagCodePoint("waterway", "drain")
|
||||
addTagCodePoint("surface", "grass")
|
||||
addTagCodePoint("amenity", "school")
|
||||
addTagCodePoint("power", "line")
|
||||
addTagCodePoint("landuse", "industrial")
|
||||
addTagCodePoint("landuse", "reservoir")
|
||||
addTagCodePoint("water", "intermittent")
|
||||
addTagCodePoint("highway", "trunk_link")
|
||||
addTagCodePoint("segregated", "no")
|
||||
addTagCodePoint("horse", "no")
|
||||
addTagCodePoint("wood", "deciduous")
|
||||
addTagCodePoint("highway", "primary_link")
|
||||
addTagCodePoint("foot", "no")
|
||||
addTagCodePoint("lit", "no")
|
||||
addTagCodePoint("surface", "concrete")
|
||||
addTagCodePoint("building", "garages")
|
||||
addTagCodePoint("amenity", "place_of_worship")
|
||||
addTagCodePoint("religion", "christian")
|
||||
addTagCodePoint("waterway", "canal")
|
||||
addTagCodePoint("landuse", "orchard")
|
||||
addTagCodePoint("surface", "paving_stones")
|
||||
addTagCodePoint("leisure", "garden")
|
||||
addTagCodePoint("service", "spur")
|
||||
addTagCodePoint("living_street", "yes")
|
||||
addTagCodePoint("access", "permissive")
|
||||
addTagCodePoint("sport", "soccer")
|
||||
addTagCodePoint("frequency", "0")
|
||||
addTagCodePoint("landuse", "cemetery")
|
||||
addTagCodePoint("wood", "mixed")
|
||||
addTagCodePoint("motorcar", "no")
|
||||
addTagCodePoint("access", "no")
|
||||
addTagCodePoint("man_made", "pier")
|
||||
addTagCodePoint("oneway", "-1")
|
||||
addTagCodePoint("sport", "tennis")
|
||||
addTagCodePoint("noexit", "yes")
|
||||
addTagCodePoint("service", "yard")
|
||||
addTagCodePoint("wood", "coniferous")
|
||||
addTagCodePoint("natural", "cliff")
|
||||
addTagCodePoint("leisure", "playground")
|
||||
addTagCodePoint("cycleway", "lane")
|
||||
addTagCodePoint("surface", "cobblestone")
|
||||
addTagCodePoint("landuse", "vineyard")
|
||||
addTagCodePoint("frequency", "16.7")
|
||||
//
|
||||
// most used tags for nodes
|
||||
//
|
||||
addTagCodePoint("power", "tower")
|
||||
addTagCodePoint("natural", "tree")
|
||||
addTagCodePoint("highway", "bus_stop")
|
||||
addTagCodePoint("power", "pole")
|
||||
addTagCodePoint("place", "locality")
|
||||
addTagCodePoint("highway", "turning_circle")
|
||||
addTagCodePoint("highway", "crossing")
|
||||
addTagCodePoint("place", "village")
|
||||
addTagCodePoint("place", "hamlet")
|
||||
addTagCodePoint("highway", "traffic_signals")
|
||||
addTagCodePoint("barrier", "gate")
|
||||
// addTagCodePoint("admin_level", "8")
|
||||
// addTagCodePoint("amenity", "place_of_worship")
|
||||
// addTagCodePoint("amenity", "school")
|
||||
// addTagCodePoint("religion", "christian")
|
||||
addTagCodePoint("amenity", "bench")
|
||||
addTagCodePoint("man_made", "survey_point")
|
||||
addTagCodePoint("amenity", "restaurant")
|
||||
// addTagCodePoint("amenity", "parking")
|
||||
addTagCodePoint("natural", "peak")
|
||||
addTagCodePoint("railway", "level_crossing")
|
||||
addTagCodePoint("type", "broad_leaved")
|
||||
// addTagCodePoint("building", "yes")
|
||||
// addTagCodePoint("foot", "yes")
|
||||
// addTagCodePoint("bicycle", "yes")
|
||||
addTagCodePoint("highway", "street_lamp")
|
||||
addTagCodePoint("tourism", "information")
|
||||
addTagCodePoint("wheelchair", "yes")
|
||||
addTagCodePoint("building", "entrance")
|
||||
addTagCodePoint("public_transport", "stop_position")
|
||||
addTagCodePoint("amenity", "fuel")
|
||||
// addTagCodePoint("noexit", "yes")
|
||||
addTagCodePoint("barrier", "bollard")
|
||||
addTagCodePoint("amenity", "post_box")
|
||||
addTagCodePoint("natural", "rock")
|
||||
// addTagCodePoint("landuse", "forest")
|
||||
addTagCodePoint("shelter", "yes")
|
||||
addTagCodePoint("emergency", "fire_hydrant")
|
||||
addTagCodePoint("public_transport", "platform")
|
||||
addTagCodePoint("amenity", "grave_yard")
|
||||
addTagCodePoint("shop", "convenience")
|
||||
addTagCodePoint("power", "generator")
|
||||
addTagCodePoint("shop", "supermarket")
|
||||
addTagCodePoint("amenity", "bank")
|
||||
addTagCodePoint("amenity", "fast_food")
|
||||
addTagCodePoint("amenity", "cafe")
|
||||
//
|
||||
// most used tags for rels
|
||||
//
|
||||
addTagCodePoint("type", "multipolygon")
|
||||
addTagCodePoint("type", "route")
|
||||
// addTagCodePoint("boundary", "administrative")
|
||||
addTagCodePoint("type", "restriction")
|
||||
addTagCodePoint("type", "boundary")
|
||||
addTagCodePoint("type", "site")
|
||||
// addTagCodePoint("admin_level", "8")
|
||||
addTagCodePoint("type", "associatedStreet")
|
||||
// addTagCodePoint("natural", "water")
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"goposm/element"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTagsAsAndFromArray(t *testing.T) {
|
||||
tags := element.Tags{"name": "foo", "highway": "residential", "oneway": "yes"}
|
||||
array := TagsAsArray(tags)
|
||||
|
||||
if len(array) != 4 {
|
||||
t.Fatal("invalid length", array)
|
||||
}
|
||||
|
||||
for i, expected := range []string{"name", "foo",
|
||||
string(tagsToCodePoint["highway"]["residential"]),
|
||||
string(tagsToCodePoint["oneway"]["yes"])} {
|
||||
if array[i] != expected {
|
||||
t.Fatal("invalid value", array, i, expected)
|
||||
}
|
||||
}
|
||||
|
||||
tags = TagsFromArray(array)
|
||||
if len(tags) != 3 {
|
||||
t.Fatal("invalid length", tags)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodePoints(t *testing.T) {
|
||||
// codepoints should never change, so check a few for sanity
|
||||
if c := tagsToCodePoint["building"]["yes"]; c != codepoint('\ue000') {
|
||||
t.Fatalf("%x\n", c)
|
||||
}
|
||||
if c := tagsToCodePoint["surface"]["grass"]; c != codepoint('\ue052') {
|
||||
t.Fatalf("%x\n", c)
|
||||
}
|
||||
if c := tagsToCodePoint["type"]["associatedStreet"]; c != codepoint('\ue0a5') {
|
||||
t.Fatalf("%x\n", c)
|
||||
}
|
||||
}
|
|
@ -58,19 +58,3 @@ type Relation struct {
|
|||
OSMElem
|
||||
Members []Member
|
||||
}
|
||||
|
||||
func (elem *OSMElem) TagsFromArray(arr []string) {
|
||||
result := make(Tags)
|
||||
for i := 0; i < len(arr); i += 2 {
|
||||
result[arr[i]] = arr[i+1]
|
||||
}
|
||||
elem.Tags = result
|
||||
}
|
||||
|
||||
func (elem *OSMElem) TagsAsArray() []string {
|
||||
result := make([]string, 0, 2*len(elem.Tags))
|
||||
for key, val := range elem.Tags {
|
||||
result = append(result, key, val)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue