refactoring of the mapping package

master
Oliver Tonnhofer 2013-05-17 15:44:50 +02:00
parent 81d818a0c4
commit 60a7d80993
4 changed files with 192 additions and 530 deletions

View File

@ -2,9 +2,7 @@ package mapping
import (
"encoding/json"
"flag"
"goposm/element"
"log"
"os"
)
@ -32,8 +30,25 @@ type Mapping struct {
Tables Tables `json:"tables"`
}
func (t *Table) Mappings() map[string][]string {
return t.Mapping
type ElementFilter func(elem element.OSMElem) bool
type TagTables map[string]map[string][]string
func NewMapping(filename string) (*Mapping, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
decoder := json.NewDecoder(f)
mapping := Mapping{}
err = decoder.Decode(&mapping)
if err != nil {
return nil, err
}
mapping.prepare()
return &mapping, nil
}
func (t *Table) ExtraTags() map[string]bool {
@ -52,12 +67,12 @@ func (m *Mapping) prepare() {
}
}
func (m *Mapping) mappings(tableType string, mappings map[string]map[string][]string) {
func (m *Mapping) mappings(tableType string, mappings TagTables) {
for name, t := range m.Tables {
if t.Type != tableType {
continue
}
for key, vals := range t.Mappings() {
for key, vals := range t.Mapping {
for _, v := range vals {
vals, ok := mappings[key]
if ok {
@ -97,8 +112,8 @@ func (m *Mapping) extraTags(tableType string, tags map[string]bool) {
}
}
func (m *Mapping) elemFilters() map[string][]elemFilter {
result := make(map[string][]elemFilter)
func (m *Mapping) ElementFilters() map[string][]ElementFilter {
result := make(map[string][]ElementFilter)
for name, t := range m.Tables {
if t.Filters == nil {
continue
@ -119,227 +134,3 @@ func (m *Mapping) elemFilters() map[string][]elemFilter {
}
return result
}
func (m *Mapping) NodeTagFilter() *TagFilter {
mappings := make(map[string]map[string][]string)
m.mappings("point", mappings)
tags := make(map[string]bool)
m.extraTags("point", tags)
return &TagFilter{mappings, tags}
}
func (m *Mapping) WayTagFilter() *TagFilter {
mappings := make(map[string]map[string][]string)
m.mappings("linestring", mappings)
m.mappings("polygon", mappings)
tags := make(map[string]bool)
m.extraTags("linestring", tags)
m.extraTags("polygon", tags)
return &TagFilter{mappings, tags}
}
func (m *Mapping) RelationTagFilter() *RelationTagFilter {
mappings := make(map[string]map[string][]string)
m.mappings("linestring", mappings)
m.mappings("polygon", mappings)
tags := make(map[string]bool)
m.extraTags("linestring", tags)
m.extraTags("polygon", tags)
tags["type"] = true // do not filter out type tag
return &RelationTagFilter{TagFilter{mappings, tags}}
}
func (m *Mapping) PointMatcher() *TagMatcher {
mappings := make(map[string]map[string][]string)
m.mappings("point", mappings)
filters := m.elemFilters()
return &TagMatcher{mappings, m.tables("point"), filters}
}
func (m *Mapping) LineStringMatcher() *TagMatcher {
mappings := make(map[string]map[string][]string)
m.mappings("linestring", mappings)
filters := m.elemFilters()
return &TagMatcher{mappings, m.tables("linestring"), filters}
}
func (m *Mapping) PolygonMatcher() *TagMatcher {
mappings := make(map[string]map[string][]string)
m.mappings("polygon", mappings)
filters := m.elemFilters()
return &TagMatcher{mappings, m.tables("polygon"), filters}
}
type TagFilter struct {
mappings map[string]map[string][]string
extraTags map[string]bool
}
type RelationTagFilter struct {
TagFilter
}
func (f *TagFilter) Filter(tags map[string]string) bool {
foundMapping := false
for k, v := range tags {
values, ok := f.mappings[k]
if ok {
if _, ok := values["__any__"]; ok {
foundMapping = true
continue
} else if _, ok := values[v]; ok {
foundMapping = true
continue
} else if _, ok := f.extraTags[k]; !ok {
delete(tags, k)
}
} else if _, ok := f.extraTags[k]; !ok {
delete(tags, k)
}
}
if foundMapping {
return true
} else {
return false
}
}
func (f *RelationTagFilter) Filter(tags map[string]string) bool {
if t, ok := tags["type"]; ok {
if t != "multipolygon" && t != "boundary" && t != "land_area" {
return false
}
} else {
return false
}
f.TagFilter.Filter(tags)
// always return true here since we found a matching type
return true
}
type elemFilter func(elem element.OSMElem) bool
type TagMatcher struct {
mappings map[string]map[string][]string
tables map[string]*TableFields
filters map[string][]elemFilter
}
type Match struct {
Key string
Value string
Table string
tableFields *TableFields
}
func (m *Match) Row(elem *element.OSMElem) []interface{} {
return m.tableFields.MakeRow(elem, *m)
}
func (tagMatcher *TagMatcher) Match(elem element.OSMElem) []Match {
tables := make(map[string]Match)
for k, v := range elem.Tags {
values, ok := tagMatcher.mappings[k]
if ok {
if tbls, ok := values["__any__"]; ok {
for _, t := range tbls {
tables[t] = Match{k, v, t, tagMatcher.tables[t]}
}
continue
} else if tbls, ok := values[v]; ok {
for _, t := range tbls {
tables[t] = Match{k, v, t, tagMatcher.tables[t]}
}
continue
}
}
}
var matches []Match
for t, match := range tables {
filters, ok := tagMatcher.filters[t]
filteredOut := false
if ok {
for _, filter := range filters {
if !filter(elem) {
filteredOut = true
break
}
}
}
if !filteredOut {
matches = append(matches, match)
}
}
return matches
}
func NewMapping(filename string) (*Mapping, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
decoder := json.NewDecoder(f)
mapping := Mapping{}
err = decoder.Decode(&mapping)
if err != nil {
return nil, err
}
mapping.prepare()
return &mapping, nil
}
func main() {
// data := `
// {
// "tables": {
// "roads": {
// "mapping": {
// "highway": [
// "motorway",
// "motorway_link",
// "trunk",
// "trunk_link"
// ]
// },
// "fields": {
// "tunnel": {"type": "bool", "key": "tunnel"},
// "bridge": {"type": "bool"},
// "oneway": {"type": "direction"},
// "ref": {"type": "string"},
// "z_order": {"type": "wayzorder", "key": "NONE"}
// }
// }
// }
// }
// `
// t := Table{map[string][]string{"highway": {"motorway", "trunk"}}}
// b, err := json.Marshal(t)
// if err != nil {
// log.Fatal(err)
// }
// log.Println(string(b))
flag.Parse()
mapping, err := NewMapping(flag.Arg(0))
if err != nil {
log.Fatal(err)
}
// log.Println(mapping.Mappings("point"))
// log.Println(mapping.ExtraTags("point"))
log.Println(mapping.NodeTagFilter())
log.Println(mapping.WayTagFilter())
log.Println(mapping.RelationTagFilter())
// log.Println(mapping)
// b, err := json.MarshalIndent(mapping, "", " ")
// if err != nil {
// log.Fatal(err)
// }
// log.Println(string(b))
}

View File

@ -6,6 +6,23 @@ import (
"strconv"
)
var AvailableFieldTypes map[string]FieldType
func init() {
AvailableFieldTypes = map[string]FieldType{
"bool": {"bool", "bool", Bool},
"id": {"id", "int64", Id},
"string": {"string", "string", String},
"direction": {"direction", "int8", Direction},
"integer": {"integer", "int32", Integer},
"mapping_key": {"mapping_key", "string", Key},
"mapping_value": {"mapping_value", "string", Value},
"geometry": {"geometry", "geometry", Geometry},
"wayzorder": {"wayzorder", "int32", WayZOrder},
"pseudoarea": {"pseudoarea", "float32", PseudoArea},
}
}
type MakeValue func(string, *element.OSMElem, Match) interface{}
type FieldSpec struct {
@ -39,7 +56,7 @@ func (t *Table) TableFields() *TableFields {
field := FieldSpec{}
field.Name = mappingField.Name
fieldType, ok := FieldTypes[mappingField.Type]
fieldType, ok := AvailableFieldTypes[mappingField.Type]
if !ok {
log.Println("unhandled type:", mappingField.Type)
} else {
@ -56,23 +73,6 @@ type FieldType struct {
Func MakeValue
}
var FieldTypes map[string]FieldType
func init() {
FieldTypes = map[string]FieldType{
"bool": {"bool", "bool", Bool},
"id": {"id", "int64", Id},
"string": {"string", "string", String},
"direction": {"direction", "int8", Direction},
"integer": {"integer", "int32", Integer},
"mapping_key": {"mapping_key", "string", Key},
"mapping_value": {"mapping_value", "string", Value},
"geometry": {"geometry", "geometry", Geometry},
"wayzorder": {"wayzorder", "int32", WayZOrder},
"pseudoarea": {"pseudoarea", "float32", PseudoArea},
}
}
func Bool(val string, elem *element.OSMElem, match Match) interface{} {
if val == "" || val == "0" || val == "false" || val == "no" {
return false

View File

@ -1,287 +1,77 @@
package mapping
type TagMap map[string]map[string]bool
func (m *Mapping) NodeTagFilter() *TagFilter {
mappings := make(map[string]map[string][]string)
m.mappings("point", mappings)
tags := make(map[string]bool)
m.extraTags("point", tags)
return &TagFilter{mappings, tags}
}
var PointTags TagMap
var WayTags TagMap
var RelationTags TagMap
func (m *Mapping) WayTagFilter() *TagFilter {
mappings := make(map[string]map[string][]string)
m.mappings("linestring", mappings)
m.mappings("polygon", mappings)
tags := make(map[string]bool)
m.extraTags("linestring", tags)
m.extraTags("polygon", tags)
return &TagFilter{mappings, tags}
}
// default mapping created from imposm defaultmapping.py
// TODO make configurable
func init() {
PointTags = TagMap{
"aeroway": map[string]bool{
"aerodome": true,
"gate": true,
"helipad": true,
"terminal": true,
},
"amenity": map[string]bool{
"fire_station": true,
"fuel": true,
"hospital": true,
"library": true,
"police": true,
"school": true,
"townhall": true,
"university": true,
},
"highway": map[string]bool{
"bus_stop": true,
"motorway_junction": true,
"turning_circle": true,
},
"name": map[string]bool{
"__any__": true,
},
"place": map[string]bool{
"city": true,
"country": true,
"county": true,
"hamlet": true,
"locality": true,
"region": true,
"state": true,
"suburb": true,
"town": true,
"village": true,
},
"population": map[string]bool{
"__any__": true,
},
"railway": map[string]bool{
"crossing": true,
"halt": true,
"level_crossing": true,
"station": true,
"subway_entrance": true,
"tram_stop": true,
},
"ref": map[string]bool{
"__any__": true,
},
func (m *Mapping) RelationTagFilter() *RelationTagFilter {
mappings := make(map[string]map[string][]string)
m.mappings("linestring", mappings)
m.mappings("polygon", mappings)
tags := make(map[string]bool)
m.extraTags("linestring", tags)
m.extraTags("polygon", tags)
tags["type"] = true // do not filter out type tag
return &RelationTagFilter{TagFilter{mappings, tags}}
}
type TagFilter struct {
mappings map[string]map[string][]string
extraTags map[string]bool
}
type RelationTagFilter struct {
TagFilter
}
func (f *TagFilter) Filter(tags map[string]string) bool {
foundMapping := false
for k, v := range tags {
values, ok := f.mappings[k]
if ok {
if _, ok := values["__any__"]; ok {
foundMapping = true
continue
} else if _, ok := values[v]; ok {
foundMapping = true
continue
} else if _, ok := f.extraTags[k]; !ok {
delete(tags, k)
}
} else if _, ok := f.extraTags[k]; !ok {
delete(tags, k)
}
}
WayTags = map[string]map[string]bool{
"admin_level": map[string]bool{
"__any__": true,
},
"aeroway": map[string]bool{
"aerodrome": true,
"apron": true,
"helipad": true,
"runway": true,
"taxiway": true,
"terminal": true,
},
"amenity": map[string]bool{
"cinema": true,
"college": true,
"fuel": true,
"hospital": true,
"library": true,
"parking": true,
"place_of_worship": true,
"school": true,
"theatre": true,
"university": true,
},
"area": map[string]bool{
"__any__": true,
},
"boundary": map[string]bool{
"administrative": true,
},
"bridge": map[string]bool{
"__any__": true,
},
"building": map[string]bool{
"__any__": true,
},
"highway": map[string]bool{
"bridleway": true,
"cycleway": true,
"footway": true,
"living_street": true,
"motorway": true,
"motorway_link": true,
"path": true,
"pedestrian": true,
"primary": true,
"primary_link": true,
"residential": true,
"road": true,
"secondary": true,
"secondary_link": true,
"service": true,
"steps": true,
"tertiary": true,
"track": true,
"trunk": true,
"trunk_link": true,
"unclassified": true,
},
"landuse": map[string]bool{
"allotments": true,
"basin": true,
"cemetery": true,
"commercial": true,
"farm": true,
"farmland": true,
"farmyard": true,
"forest": true,
"grass": true,
"industrial": true,
"meadow": true,
"park": true,
"quarry": true,
"railway": true,
"recreation_ground": true,
"reservoir": true,
"residential": true,
"retail": true,
"village_green": true,
"wood": true,
},
"leisure": map[string]bool{
"common": true,
"garden": true,
"golf_course": true,
"nature_reserve": true,
"park": true,
"pitch": true,
"playground": true,
"sports_centre": true,
"stadium": true,
},
"name": map[string]bool{
"__any__": true,
},
"natural": map[string]bool{
"land": true,
"scrub": true,
"water": true,
"wood": true,
},
"oneway": map[string]bool{
"__any__": true,
},
"railway": map[string]bool{
"funicular": true,
"light_rail": true,
"monorail": true,
"narrow_gauge": true,
"preserved": true,
"rail": true,
"station": true,
"subway": true,
"tram": true,
},
"ref": map[string]bool{
"__any__": true,
},
"tunnel": map[string]bool{
"__any__": true,
},
"waterway": map[string]bool{
"canal": true,
"drain": true,
"river": true,
"riverbank": true,
"stream": true,
}}
RelationTags = map[string]map[string]bool{
"admin_level": map[string]bool{
"__any__": true,
},
"aeroway": map[string]bool{
"aerodrome": true,
"apron": true,
"helipad": true,
"runway": true,
"taxiway": true,
"terminal": true,
},
"amenity": map[string]bool{
"cinema": true,
"college": true,
"fuel": true,
"hospital": true,
"library": true,
"parking": true,
"place_of_worship": true,
"school": true,
"theatre": true,
"university": true,
},
"area": map[string]bool{
"__any__": true,
},
"boundary": map[string]bool{
"administrative": true,
},
"bridge": map[string]bool{
"__any__": true,
},
"building": map[string]bool{
"__any__": true,
},
"highway": map[string]bool{
"bridleway": true,
"cycleway": true,
"footway": true,
"living_street": true,
"motorway": true,
"motorway_link": true,
"path": true,
"pedestrian": true,
"primary": true,
"primary_link": true,
"residential": true,
"road": true,
"secondary": true,
"secondary_link": true,
"service": true,
"steps": true,
"tertiary": true,
"track": true,
"trunk": true,
"trunk_link": true,
"unclassified": true,
},
"landuse": map[string]bool{
"allotments": true,
"basin": true,
"cemetery": true,
"commercial": true,
"farm": true,
"farmland": true,
"farmyard": true,
"forest": true,
"grass": true,
"industrial": true,
"meadow": true,
"park": true,
"quarry": true,
"railway": true,
"recreation_ground": true,
"reservoir": true,
"residential": true,
"retail": true,
"village_green": true,
"wood": true,
},
"leisure": map[string]bool{
"common": true,
"garden": true,
"golf_course": true,
"nature_reserve": true,
"park": true,
"pitch": true,
"playground": true,
"sports_centre": true,
"stadium": true,
},
if foundMapping {
return true
} else {
return false
}
}
func (f *RelationTagFilter) Filter(tags map[string]string) bool {
if t, ok := tags["type"]; ok {
if t != "multipolygon" && t != "boundary" && t != "land_area" {
return false
}
} else {
return false
}
f.TagFilter.Filter(tags)
// always return true here since we found a matching type
return true
}

81
mapping/matcher.go Normal file
View File

@ -0,0 +1,81 @@
package mapping
import (
"goposm/element"
)
func (m *Mapping) PointMatcher() *TagMatcher {
mappings := make(TagTables)
m.mappings("point", mappings)
filters := m.ElementFilters()
return &TagMatcher{mappings, m.tables("point"), filters}
}
func (m *Mapping) LineStringMatcher() *TagMatcher {
mappings := make(TagTables)
m.mappings("linestring", mappings)
filters := m.ElementFilters()
return &TagMatcher{mappings, m.tables("linestring"), filters}
}
func (m *Mapping) PolygonMatcher() *TagMatcher {
mappings := make(TagTables)
m.mappings("polygon", mappings)
filters := m.ElementFilters()
return &TagMatcher{mappings, m.tables("polygon"), filters}
}
type TagMatcher struct {
mappings TagTables
tables map[string]*TableFields
filters map[string][]ElementFilter
}
type Match struct {
Key string
Value string
Table string
tableFields *TableFields
}
func (m *Match) Row(elem *element.OSMElem) []interface{} {
return m.tableFields.MakeRow(elem, *m)
}
func (tagMatcher *TagMatcher) Match(elem element.OSMElem) []Match {
tables := make(map[string]Match)
for k, v := range elem.Tags {
values, ok := tagMatcher.mappings[k]
if ok {
if tbls, ok := values["__any__"]; ok {
for _, t := range tbls {
tables[t] = Match{k, v, t, tagMatcher.tables[t]}
}
continue
} else if tbls, ok := values[v]; ok {
for _, t := range tbls {
tables[t] = Match{k, v, t, tagMatcher.tables[t]}
}
continue
}
}
}
var matches []Match
for t, match := range tables {
filters, ok := tagMatcher.filters[t]
filteredOut := false
if ok {
for _, filter := range filters {
if !filter(elem) {
filteredOut = true
break
}
}
}
if !filteredOut {
matches = append(matches, match)
}
}
return matches
}