store common tags as single unicode char

master
Oliver Tonnhofer 2013-06-05 09:37:28 +02:00
parent bd3024b88c
commit 618deea4a9
4 changed files with 324 additions and 22 deletions

View File

@ -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
}

277
cache/binary/tags.go vendored Normal file
View File

@ -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")
}

41
cache/binary/tags_test.go vendored Normal file
View File

@ -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)
}
}

View File

@ -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
}