diff --git a/mapping/config.go b/mapping/config.go index 4b46da5..af2d374 100644 --- a/mapping/config.go +++ b/mapping/config.go @@ -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)) -} diff --git a/mapping/fields.go b/mapping/fields.go index e662b36..d3351d2 100644 --- a/mapping/fields.go +++ b/mapping/fields.go @@ -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 diff --git a/mapping/filter.go b/mapping/filter.go index 1aa9355..213953d 100644 --- a/mapping/filter.go +++ b/mapping/filter.go @@ -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 +} diff --git a/mapping/matcher.go b/mapping/matcher.go new file mode 100644 index 0000000..1cb7780 --- /dev/null +++ b/mapping/matcher.go @@ -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 +}