diff --git a/mapping/config.go b/mapping/config.go
index 932a11f..ffb5783 100644
--- a/mapping/config.go
+++ b/mapping/config.go
@@ -20,14 +20,15 @@ type Field struct {
}
type Table struct {
- Name string
- Type TableType `yaml:"type"`
- Mapping KeyValues `yaml:"mapping"`
- Mappings map[string]SubMapping `yaml:"mappings"`
- TypeMappings TypeMappings `yaml:"type_mappings"`
- Fields []*Field `yaml:"columns"` // TODO rename Fields internaly to Columns
- OldFields []*Field `yaml:"fields"`
- Filters *Filters `yaml:"filters"`
+ Name string
+ Type TableType `yaml:"type"`
+ Mapping KeyValues `yaml:"mapping"`
+ Mappings map[string]SubMapping `yaml:"mappings"`
+ TypeMappings TypeMappings `yaml:"type_mappings"`
+ Fields []*Field `yaml:"columns"` // TODO rename Fields internaly to Columns
+ OldFields []*Field `yaml:"fields"`
+ Filters *Filters `yaml:"filters"`
+ RelationTypes []string `yaml:"relation_types"`
}
type GeneralizedTable struct {
@@ -179,19 +180,6 @@ func NewMapping(filename string) (*Mapping, error) {
return &mapping, nil
}
-func (t *Table) ExtraTags() map[Key]bool {
- tags := make(map[Key]bool)
- for _, field := range t.Fields {
- if field.Key != "" {
- tags[field.Key] = true
- }
- for _, k := range field.Keys {
- tags[k] = true
- }
- }
- return tags
-}
-
func (m *Mapping) prepare() error {
for name, t := range m.Tables {
t.Name = name
@@ -247,7 +235,7 @@ func (m *Mapping) mappings(tableType TableType, mappings TagTables) {
func (m *Mapping) tables(tableType TableType) map[string]*TableFields {
result := make(map[string]*TableFields)
for name, t := range m.Tables {
- if t.Type == tableType || t.Type == "geometry" {
+ if t.Type == tableType || t.Type == GeometryTable {
result[name] = t.TableFields()
}
}
@@ -256,17 +244,30 @@ func (m *Mapping) tables(tableType TableType) map[string]*TableFields {
func (m *Mapping) extraTags(tableType TableType, tags map[Key]bool) {
for _, t := range m.Tables {
- if t.Type != tableType && t.Type != "geometry" {
+ if t.Type != tableType && t.Type != GeometryTable {
continue
}
- for key, _ := range t.ExtraTags() {
- tags[key] = true
+
+ for _, field := range t.Fields {
+ if field.Key != "" {
+ tags[field.Key] = true
+ }
+ for _, k := range field.Keys {
+ tags[k] = true
+ }
}
+
if t.Filters != nil && t.Filters.ExcludeTags != nil {
for _, keyVal := range *t.Filters.ExcludeTags {
tags[Key(keyVal[0])] = true
}
}
+
+ if tableType == PolygonTable || tableType == RelationTable || tableType == RelationMemberTable {
+ if t.RelationTypes != nil {
+ tags["type"] = true
+ }
+ }
}
for _, k := range m.Tags.Include {
tags[k] = true
@@ -331,6 +332,37 @@ func (m *Mapping) addTypedFilters(tableType TableType, filters tableFilters) {
}
}
+func (m *Mapping) addRelationFilters(tableType TableType, filters tableFilters) {
+ for name, t := range m.Tables {
+ if t.RelationTypes != nil {
+ f := func(tags element.Tags, key Key, closed bool) bool {
+ if v, ok := tags["type"]; ok {
+ for _, rtype := range t.RelationTypes {
+ if v == rtype {
+ return true
+ }
+ }
+ }
+ return false
+ }
+ filters[name] = append(filters[name], f)
+ } else {
+ if t.Type == PolygonTable {
+ // standard mulipolygon handling (boundary and land_area are for backwards compatibility)
+ f := func(tags element.Tags, key Key, closed bool) bool {
+ if v, ok := tags["type"]; ok {
+ if v == "multipolygon" || v == "boundary" || v == "land_area" {
+ return true
+ }
+ }
+ return false
+ }
+ filters[name] = append(filters[name], f)
+ }
+ }
+ }
+}
+
func (m *Mapping) addFilters(filters tableFilters) {
for name, t := range m.Tables {
if t.Filters == nil {
diff --git a/mapping/filter.go b/mapping/filter.go
index 0142869..9cc52e6 100644
--- a/mapping/filter.go
+++ b/mapping/filter.go
@@ -12,9 +12,10 @@ func (m *Mapping) NodeTagFilter() TagFilterer {
return newExcludeFilter(m.Tags.Exclude)
}
mappings := make(map[Key]map[Value][]OrderedDestTable)
- m.mappings("point", mappings)
+ m.mappings(PointTable, mappings)
tags := make(map[Key]bool)
- m.extraTags("point", tags)
+ m.extraTags(PointTable, tags)
+ m.extraTags(RelationMemberTable, tags)
return &TagFilter{mappings, tags}
}
@@ -23,11 +24,12 @@ func (m *Mapping) WayTagFilter() TagFilterer {
return newExcludeFilter(m.Tags.Exclude)
}
mappings := make(map[Key]map[Value][]OrderedDestTable)
- m.mappings("linestring", mappings)
- m.mappings("polygon", mappings)
+ m.mappings(LineStringTable, mappings)
+ m.mappings(PolygonTable, mappings)
tags := make(map[Key]bool)
- m.extraTags("linestring", tags)
- m.extraTags("polygon", tags)
+ m.extraTags(LineStringTable, tags)
+ m.extraTags(PolygonTable, tags)
+ m.extraTags(RelationMemberTable, tags)
return &TagFilter{mappings, tags}
}
@@ -42,11 +44,15 @@ func (m *Mapping) RelationTagFilter() TagFilterer {
"boundary": []OrderedDestTable{},
"land_area": []OrderedDestTable{},
}
- m.mappings("linestring", mappings)
- m.mappings("polygon", mappings)
+ m.mappings(LineStringTable, mappings)
+ m.mappings(PolygonTable, mappings)
+ m.mappings(RelationTable, mappings)
+ m.mappings(RelationMemberTable, mappings)
tags := make(map[Key]bool)
- m.extraTags("linestring", tags)
- m.extraTags("polygon", tags)
+ m.extraTags(LineStringTable, tags)
+ m.extraTags(PolygonTable, tags)
+ m.extraTags(RelationTable, tags)
+ m.extraTags(RelationMemberTable, tags)
return &TagFilter{mappings, tags}
}
diff --git a/mapping/filter_test.go b/mapping/filter_test.go
index 6869d65..7e55175 100644
--- a/mapping/filter_test.go
+++ b/mapping/filter_test.go
@@ -17,22 +17,6 @@ func init() {
}
}
-func stringMapEquals(t *testing.T, expected, actual map[string]string) {
- if len(expected) != len(actual) {
- t.Errorf("different length in %v and %v\n", expected, actual)
- }
-
- for k, v := range expected {
- if actualV, ok := actual[k]; ok {
- if actualV != v {
- t.Errorf("%s != %s in %v and %v\n", v, actualV, expected, actual)
- }
- } else {
- t.Errorf("%s not in %v\n", k, actual)
- }
- }
-}
-
func stringMapEqual(expected, actual map[string]string) bool {
if len(expected) != len(actual) {
return false
@@ -50,12 +34,12 @@ func stringMapEqual(expected, actual map[string]string) bool {
return true
}
-func matchesEqual(t *testing.T, expected []Match, actual []Match) {
+func matchesEqual(expected []Match, actual []Match) bool {
expectedMatches := make(map[DestTable]Match)
actualMatches := make(map[DestTable]Match)
if len(expected) != len(actual) {
- t.Fatalf("different length in %v and %v\n", expected, actual)
+ return false
}
for _, match := range expected {
@@ -70,12 +54,13 @@ func matchesEqual(t *testing.T, expected []Match, actual []Match) {
if expectedMatch.Table != actualMatch.Table ||
expectedMatch.Key != actualMatch.Key ||
expectedMatch.Value != actualMatch.Value {
- t.Fatalf("match differ %v != %v", expectedMatch, actualMatch)
+ return false
}
} else {
- t.Fatalf("%s not in %v", name, actualMatches)
+ return false
}
}
+ return true
}
func TestTagFilterNodes(t *testing.T) {
@@ -154,151 +139,209 @@ func TestTagFilterRelations(t *testing.T) {
}
func TestPointMatcher(t *testing.T) {
- elem := element.Node{}
- points := mapping.PointMatcher()
-
- elem.Tags = element.Tags{"unknown": "baz"}
- matchesEqual(t, []Match{}, points.MatchNode(&elem))
-
- elem.Tags = element.Tags{"place": "unknown"}
- matchesEqual(t, []Match{}, points.MatchNode(&elem))
-
- elem.Tags = element.Tags{"place": "city"}
- matchesEqual(t, []Match{{"place", "city", DestTable{Name: "places"}, nil}}, points.MatchNode(&elem))
-
- elem.Tags = element.Tags{"place": "city", "highway": "unknown"}
- matchesEqual(t, []Match{{"place", "city", DestTable{Name: "places"}, nil}}, points.MatchNode(&elem))
-
- elem.Tags = element.Tags{"place": "city", "highway": "bus_stop"}
- matchesEqual(t,
- []Match{
+ tests := []struct {
+ tags element.Tags
+ matches []Match
+ }{
+ {element.Tags{"unknown": "baz"}, []Match{}},
+ {element.Tags{"place": "unknown"}, []Match{}},
+ {element.Tags{"place": "city"}, []Match{{"place", "city", DestTable{Name: "places"}, nil}}},
+ {element.Tags{"place": "city", "highway": "unknown"}, []Match{{"place", "city", DestTable{Name: "places"}, nil}}},
+ {element.Tags{"place": "city", "highway": "bus_stop"}, []Match{
{"place", "city", DestTable{Name: "places"}, nil},
{"highway", "bus_stop", DestTable{Name: "transport_points"}, nil}},
- points.MatchNode(&elem))
+ },
+ }
+
+ elem := element.Node{}
+ m := mapping.PointMatcher()
+ for i, test := range tests {
+ elem.Tags = test.tags
+ actual := m.MatchNode(&elem)
+ if !matchesEqual(actual, test.matches) {
+ t.Errorf("unexpected result for case %d: %v != %v", i+1, actual, test.matches)
+ }
+ }
}
func TestLineStringMatcher(t *testing.T) {
+ tests := []struct {
+ tags element.Tags
+ matches []Match
+ }{
+ {element.Tags{"unknown": "baz"}, []Match{}},
+ {element.Tags{"highway": "unknown"}, []Match{}},
+ {element.Tags{"highway": "pedestrian"},
+ []Match{{"highway", "pedestrian", DestTable{Name: "roads", SubMapping: "roads"}, nil}}},
+
+ // exclude_tags area=yes
+ {element.Tags{"highway": "pedestrian", "area": "yes"}, []Match{}},
+
+ {element.Tags{"barrier": "hedge"},
+ []Match{{"barrier", "hedge", DestTable{Name: "barrierways"}, nil}}},
+ {element.Tags{"barrier": "hedge", "area": "yes"}, []Match{}},
+
+ {element.Tags{"aeroway": "runway"}, []Match{}},
+ {element.Tags{"aeroway": "runway", "area": "no"},
+ []Match{{"aeroway", "runway", DestTable{Name: "aeroways"}, nil}}},
+
+ {element.Tags{"highway": "secondary", "railway": "tram"},
+ []Match{
+ {"highway", "secondary", DestTable{Name: "roads", SubMapping: "roads"}, nil},
+ {"railway", "tram", DestTable{Name: "roads", SubMapping: "railway"}, nil}},
+ },
+ {element.Tags{"highway": "footway", "landuse": "park", "barrier": "hedge"},
+ // landusages not a linestring table
+ []Match{
+ {"highway", "footway", DestTable{Name: "roads", SubMapping: "roads"}, nil},
+ {"barrier", "hedge", DestTable{Name: "barrierways"}, nil}},
+ },
+ }
+
elem := element.Way{}
// fake closed way for area matching
elem.Refs = []int64{1, 2, 3, 4, 1}
if !elem.IsClosed() {
t.Fatal("way not closed")
}
- ls := mapping.LineStringMatcher()
-
- elem.Tags = element.Tags{"unknown": "baz"}
- matchesEqual(t, []Match{}, ls.MatchWay(&elem))
-
- elem.Tags = element.Tags{"highway": "unknown"}
- matchesEqual(t, []Match{}, ls.MatchWay(&elem))
-
- elem.Tags = element.Tags{"highway": "pedestrian"}
- matchesEqual(t, []Match{{"highway", "pedestrian", DestTable{Name: "roads", SubMapping: "roads"}, nil}}, ls.MatchWay(&elem))
-
- // exclude_tags area=yes
- elem.Tags = element.Tags{"highway": "pedestrian", "area": "yes"}
- matchesEqual(t, []Match{}, ls.MatchWay(&elem))
-
- elem.Tags = element.Tags{"barrier": "hedge"}
- matchesEqual(t, []Match{{"barrier", "hedge", DestTable{Name: "barrierways"}, nil}}, ls.MatchWay(&elem))
-
- elem.Tags = element.Tags{"barrier": "hedge", "area": "yes"}
- matchesEqual(t, []Match{}, ls.MatchWay(&elem))
-
- elem.Tags = element.Tags{"aeroway": "runway", "area": "no"}
- matchesEqual(t, []Match{{"aeroway", "runway", DestTable{Name: "aeroways"}, nil}}, ls.MatchWay(&elem))
-
- elem.Tags = element.Tags{"aeroway": "runway"}
- matchesEqual(t, []Match{}, ls.MatchWay(&elem))
-
- elem.Tags = element.Tags{"highway": "secondary", "railway": "tram"}
- matchesEqual(t,
- []Match{
- {"highway", "secondary", DestTable{Name: "roads", SubMapping: "roads"}, nil},
- {"railway", "tram", DestTable{Name: "roads", SubMapping: "railway"}, nil}},
- ls.MatchWay(&elem))
-
- elem.Tags = element.Tags{"highway": "footway", "landuse": "park"}
- // landusages not a linestring table
- matchesEqual(t, []Match{{"highway", "footway", DestTable{Name: "roads", SubMapping: "roads"}, nil}}, ls.MatchWay(&elem))
+ m := mapping.LineStringMatcher()
+ for i, test := range tests {
+ elem.Tags = test.tags
+ actual := m.MatchWay(&elem)
+ if !matchesEqual(actual, test.matches) {
+ t.Errorf("unexpected result for case %d: %v != %v", i+1, actual, test.matches)
+ }
+ }
}
-func TestPolygonMatcher(t *testing.T) {
- elem := element.Relation{}
- polys := mapping.PolygonMatcher()
+func TestPolygonMatcher_MatchWay(t *testing.T) {
+ tests := []struct {
+ tags element.Tags
+ matches []Match
+ }{
+ {element.Tags{}, []Match{}},
+ {element.Tags{"unknown": "baz"}, []Match{}},
+ {element.Tags{"landuse": "unknown"}, []Match{}},
+ {element.Tags{"landuse": "unknown", "type": "multipolygon"}, []Match{}},
+ {element.Tags{"building": "yes"}, []Match{{"building", "yes", DestTable{Name: "buildings"}, nil}}},
+ {element.Tags{"building": "residential"}, []Match{{"building", "residential", DestTable{Name: "buildings"}, nil}}},
+ // line type requires area=yes
+ {element.Tags{"barrier": "hedge"}, []Match{}},
+ {element.Tags{"barrier": "hedge", "area": "yes"}, []Match{{"barrier", "hedge", DestTable{Name: "landusages"}, nil}}},
- elem.Tags = element.Tags{"unknown": "baz"}
- matchesEqual(t, []Match{}, polys.MatchRelation(&elem))
+ {element.Tags{"building": "shop"}, []Match{
+ {"building", "shop", DestTable{Name: "buildings"}, nil},
+ {"building", "shop", DestTable{Name: "amenity_areas"}, nil},
+ }},
- elem.Tags = element.Tags{"landuse": "unknowns"}
- matchesEqual(t, []Match{}, polys.MatchRelation(&elem))
-
- elem.Tags = element.Tags{"building": "yes"}
- matchesEqual(t, []Match{{"building", "yes", DestTable{Name: "buildings"}, nil}}, polys.MatchRelation(&elem))
- elem.Tags = element.Tags{"building": "residential"}
- matchesEqual(t, []Match{{"building", "residential", DestTable{Name: "buildings"}, nil}}, polys.MatchRelation(&elem))
-
- elem.Tags = element.Tags{"barrier": "hedge"}
- matchesEqual(t, []Match{}, polys.MatchRelation(&elem))
-
- elem.Tags = element.Tags{"barrier": "hedge", "area": "yes"}
- matchesEqual(t, []Match{{"barrier", "hedge", DestTable{Name: "landusages"}, nil}}, polys.MatchRelation(&elem))
-
- elem.Tags = element.Tags{"building": "shop"}
- matchesEqual(t, []Match{
- {"building", "shop", DestTable{Name: "buildings"}, nil},
- {"building", "shop", DestTable{Name: "amenity_areas"}, nil}},
- polys.MatchRelation(&elem))
-
- elem.Tags = element.Tags{"landuse": "farm"}
- matchesEqual(t, []Match{{"landuse", "farm", DestTable{Name: "landusages"}, nil}}, polys.MatchRelation(&elem))
-
- elem.Tags = element.Tags{"landuse": "farm", "highway": "secondary"}
- matchesEqual(t, []Match{{"landuse", "farm", DestTable{Name: "landusages"}, nil}}, polys.MatchRelation(&elem))
-
- elem.Tags = element.Tags{"landuse": "farm", "aeroway": "apron"}
- matchesEqual(t,
- []Match{
+ {element.Tags{"aeroway": "apron", "landuse": "farm"}, []Match{
{"aeroway", "apron", DestTable{Name: "transport_areas"}, nil},
- {"landuse", "farm", DestTable{Name: "landusages"}, nil}},
- polys.MatchRelation(&elem))
+ {"landuse", "farm", DestTable{Name: "landusages"}, nil},
+ }},
- elem.Tags = element.Tags{"highway": "footway"} // linear by default
- matchesEqual(t, []Match{}, polys.MatchRelation(&elem))
+ {element.Tags{"landuse": "farm", "highway": "secondary"}, []Match{
+ {"landuse", "farm", DestTable{Name: "landusages"}, nil},
+ }},
- elem.Tags = element.Tags{"highway": "footway", "area": "yes"}
- matchesEqual(t, []Match{{"highway", "footway", DestTable{Name: "landusages"}, nil}}, polys.MatchRelation(&elem))
+ {element.Tags{"highway": "footway"}, []Match{}},
+ {element.Tags{"highway": "footway", "area": "yes"}, []Match{
+ {"highway", "footway", DestTable{Name: "landusages"}, nil},
+ }},
- elem.Tags = element.Tags{"boundary": "administrative", "admin_level": "8"}
- matchesEqual(t, []Match{{"boundary", "administrative", DestTable{Name: "admin"}, nil}}, polys.MatchRelation(&elem))
+ {element.Tags{"boundary": "administrative", "admin_level": "8"}, []Match{{"boundary", "administrative", DestTable{Name: "admin"}, nil}}},
+
+ /*
+ landusages mapping has the following order,
+ check that XxxMatcher always uses the first
+
+ amenity:
+ - university
+ landuse:
+ - forest
+ leisure:
+ - park
+ landuse:
+ - park
+ */
+
+ {element.Tags{"landuse": "forest", "leisure": "park"}, []Match{{"landuse", "forest", DestTable{Name: "landusages"}, nil}}},
+ {element.Tags{"landuse": "park", "leisure": "park"}, []Match{{"leisure", "park", DestTable{Name: "landusages"}, nil}}},
+ {element.Tags{"landuse": "park", "leisure": "park", "amenity": "university"}, []Match{{"amenity", "university", DestTable{Name: "landusages"}, nil}}},
+ }
+
+ elem := element.Way{}
+ // fake closed way for area matching
+ elem.Refs = []int64{1, 2, 3, 4, 1}
+ if !elem.IsClosed() {
+ t.Fatal("way not closed")
+ }
+ m := mapping.PolygonMatcher()
+ for i, test := range tests {
+ elem.Tags = test.tags
+ actual := m.MatchWay(&elem)
+ if !matchesEqual(actual, test.matches) {
+ t.Errorf("unexpected result for case %d: %v != %v", i+1, actual, test.matches)
+ }
+ }
+
+ elem.Refs = nil
+ elem.Tags = element.Tags{"building": "yes"}
+ actual := m.MatchWay(&elem)
+ if !matchesEqual([]Match{}, actual) {
+ t.Error("open way matched as polygon")
+ }
}
-func TestMatcherMappingOrder(t *testing.T) {
+func TestPolygonMatcher_MatchRelation(t *testing.T) {
+ // check that only relations with type=multipolygon/boundary are matched as polygon
+
+ tests := []struct {
+ tags element.Tags
+ matches []Match
+ }{
+ {element.Tags{}, []Match{}},
+ {element.Tags{"unknown": "baz"}, []Match{}},
+ {element.Tags{"landuse": "unknown"}, []Match{}},
+ {element.Tags{"landuse": "unknown", "type": "multipolygon"}, []Match{}},
+ {element.Tags{"building": "yes"}, []Match{}},
+ {element.Tags{"building": "yes", "type": "multipolygon"}, []Match{{"building", "yes", DestTable{Name: "buildings"}, nil}}},
+ {element.Tags{"building": "residential", "type": "multipolygon"}, []Match{{"building", "residential", DestTable{Name: "buildings"}, nil}}},
+ // line type requires area=yes
+ {element.Tags{"barrier": "hedge", "type": "multipolygon"}, []Match{}},
+ {element.Tags{"barrier": "hedge", "area": "yes", "type": "multipolygon"}, []Match{{"barrier", "hedge", DestTable{Name: "landusages"}, nil}}},
+
+ {element.Tags{"building": "shop", "type": "multipolygon"}, []Match{
+ {"building", "shop", DestTable{Name: "buildings"}, nil},
+ {"building", "shop", DestTable{Name: "amenity_areas"}, nil},
+ }},
+
+ {element.Tags{"aeroway": "apron", "landuse": "farm", "type": "multipolygon"}, []Match{
+ {"aeroway", "apron", DestTable{Name: "transport_areas"}, nil},
+ {"landuse", "farm", DestTable{Name: "landusages"}, nil},
+ }},
+
+ {element.Tags{"landuse": "farm", "highway": "secondary", "type": "multipolygon"}, []Match{
+ {"landuse", "farm", DestTable{Name: "landusages"}, nil},
+ }},
+
+ {element.Tags{"highway": "footway", "type": "multipolygon"}, []Match{}},
+ {element.Tags{"highway": "footway", "area": "yes", "type": "multipolygon"}, []Match{
+ {"highway", "footway", DestTable{Name: "landusages"}, nil},
+ }},
+
+ {element.Tags{"boundary": "administrative", "admin_level": "8"}, []Match{}},
+ {element.Tags{"boundary": "administrative", "admin_level": "8", "type": "boundary"}, []Match{{"boundary", "administrative", DestTable{Name: "admin"}, nil}}},
+ }
+
elem := element.Relation{}
- polys := mapping.PolygonMatcher()
-
- /*
- landusages mapping has the following order,
- check that XxxMatcher always uses the first
-
- amenity:
- - university
- landuse:
- - forest
- leisure:
- - park
- landuse:
- - park
- */
-
- elem.Tags = element.Tags{"landuse": "forest", "leisure": "park"}
- matchesEqual(t, []Match{{"landuse", "forest", DestTable{Name: "landusages"}, nil}}, polys.MatchRelation(&elem))
-
- elem.Tags = element.Tags{"landuse": "park", "leisure": "park"}
- matchesEqual(t, []Match{{"leisure", "park", DestTable{Name: "landusages"}, nil}}, polys.MatchRelation(&elem))
-
- elem.Tags = element.Tags{"landuse": "park", "leisure": "park", "amenity": "university"}
- matchesEqual(t, []Match{{"amenity", "university", DestTable{Name: "landusages"}, nil}}, polys.MatchRelation(&elem))
+ m := mapping.PolygonMatcher()
+ for i, test := range tests {
+ elem.Tags = test.tags
+ actual := m.MatchRelation(&elem)
+ if !matchesEqual(actual, test.matches) {
+ t.Errorf("unexpected result for case %d: %v != %v", i+1, actual, test.matches)
+ }
+ }
}
func TestExcludeFilter(t *testing.T) {
diff --git a/mapping/matcher.go b/mapping/matcher.go
index da69520..39014ca 100644
--- a/mapping/matcher.go
+++ b/mapping/matcher.go
@@ -39,10 +39,13 @@ func (m *Mapping) PolygonMatcher() RelWayMatcher {
filters := make(tableFilters)
m.addFilters(filters)
m.addTypedFilters(PolygonTable, filters)
+ relFilters := make(tableFilters)
+ m.addRelationFilters(PolygonTable, relFilters)
return &tagMatcher{
mappings: mappings,
tables: m.tables(PolygonTable),
filters: filters,
+ relFilters: relFilters,
matchAreas: true,
}
}
@@ -54,10 +57,13 @@ func (m *Mapping) RelationMatcher() RelationMatcher {
m.addFilters(filters)
m.addTypedFilters(PolygonTable, filters)
m.addTypedFilters(RelationTable, filters)
+ relFilters := make(tableFilters)
+ m.addRelationFilters(RelationTable, relFilters)
return &tagMatcher{
mappings: mappings,
tables: m.tables(RelationTable),
filters: filters,
+ relFilters: relFilters,
matchAreas: true,
}
}
@@ -68,10 +74,13 @@ func (m *Mapping) RelationMemberMatcher() RelationMatcher {
filters := make(tableFilters)
m.addFilters(filters)
m.addTypedFilters(RelationMemberTable, filters)
+ relFilters := make(tableFilters)
+ m.addRelationFilters(RelationMemberTable, relFilters)
return &tagMatcher{
mappings: mappings,
tables: m.tables(RelationMemberTable),
filters: filters,
+ relFilters: relFilters,
matchAreas: true,
}
}
@@ -104,6 +113,7 @@ type tagMatcher struct {
mappings TagTables
tables map[string]*TableFields
filters map[string][]ElementFilter
+ relFilters map[string][]ElementFilter
matchAreas bool
}
@@ -116,7 +126,7 @@ func (m *Match) MemberRow(rel *element.Relation, member *element.Member, geom *g
}
func (tm *tagMatcher) MatchNode(node *element.Node) []Match {
- return tm.match(node.Tags, false)
+ return tm.match(node.Tags, false, false)
}
func (tm *tagMatcher) MatchWay(way *element.Way) []Match {
@@ -125,22 +135,22 @@ func (tm *tagMatcher) MatchWay(way *element.Way) []Match {
if way.Tags["area"] == "no" {
return nil
}
- return tm.match(way.Tags, true)
+ return tm.match(way.Tags, true, false)
}
} else { // match way as linestring
if way.IsClosed() {
if way.Tags["area"] == "yes" {
return nil
}
- return tm.match(way.Tags, true)
+ return tm.match(way.Tags, true, false)
}
- return tm.match(way.Tags, false)
+ return tm.match(way.Tags, false, false)
}
return nil
}
func (tm *tagMatcher) MatchRelation(rel *element.Relation) []Match {
- return tm.match(rel.Tags, true)
+ return tm.match(rel.Tags, true, true)
}
type orderedMatch struct {
@@ -148,7 +158,7 @@ type orderedMatch struct {
order int
}
-func (tm *tagMatcher) match(tags element.Tags, closed bool) []Match {
+func (tm *tagMatcher) match(tags element.Tags, closed bool, relation bool) []Match {
tables := make(map[DestTable]orderedMatch)
addTables := func(k, v string, tbls []OrderedDestTable) {
@@ -198,6 +208,18 @@ func (tm *tagMatcher) match(tags element.Tags, closed bool) []Match {
}
}
}
+ if relation && !filteredOut {
+ filters, ok := tm.relFilters[t.Name]
+ if ok {
+ for _, filter := range filters {
+ if !filter(tags, Key(match.Key), closed) {
+ filteredOut = true
+ break
+ }
+ }
+ }
+ }
+
if !filteredOut {
matches = append(matches, match.Match)
}
diff --git a/mapping/matcher_test.go b/mapping/matcher_test.go
index 8e5efa2..6435719 100644
--- a/mapping/matcher_test.go
+++ b/mapping/matcher_test.go
@@ -43,7 +43,7 @@ func TestSelectRelationPolygonsSimple(t *testing.T) {
t.Fatal(err)
}
r := element.Relation{}
- r.Tags = element.Tags{"landuse": "park"}
+ r.Tags = element.Tags{"landuse": "park", "type": "multipolygon"}
r.Members = []element.Member{
makeMember(0, element.Tags{"landuse": "forest"}),
makeMember(1, element.Tags{"landuse": "park"}),
@@ -68,7 +68,7 @@ func TestSelectRelationPolygonsUnrelatedTags(t *testing.T) {
t.Fatal(err)
}
r := element.Relation{}
- r.Tags = element.Tags{"landuse": "park"}
+ r.Tags = element.Tags{"landuse": "park", "type": "multipolygon"}
r.Members = []element.Member{
makeMember(0, element.Tags{"landuse": "park", "layer": "2", "name": "foo"}),
makeMember(1, element.Tags{"landuse": "forest"}),
@@ -91,7 +91,7 @@ func TestSelectRelationPolygonsMultiple(t *testing.T) {
t.Fatal(err)
}
r := element.Relation{}
- r.Tags = element.Tags{"landuse": "park"}
+ r.Tags = element.Tags{"landuse": "park", "type": "multipolygon"}
r.Members = []element.Member{
makeMember(0, element.Tags{"landuse": "park"}),
makeMember(1, element.Tags{"natural": "forest"}),
@@ -117,7 +117,7 @@ func TestSelectRelationPolygonsMultipleTags(t *testing.T) {
t.Fatal(err)
}
r := element.Relation{}
- r.Tags = element.Tags{"landuse": "forest", "natural": "scrub"}
+ r.Tags = element.Tags{"landuse": "forest", "natural": "scrub", "type": "multipolygon"}
r.Members = []element.Member{
makeMember(0, element.Tags{"natural": "scrub"}),
makeMember(1, element.Tags{"landuse": "forest"}),
@@ -139,7 +139,7 @@ func TestSelectRelationPolygonsMultipleTagsOnWay(t *testing.T) {
t.Fatal(err)
}
r := element.Relation{}
- r.Tags = element.Tags{"waterway": "riverbank"}
+ r.Tags = element.Tags{"waterway": "riverbank", "type": "multipolygon"}
r.Members = []element.Member{
makeMemberRole(0, element.Tags{"waterway": "riverbank", "natural": "water"}, "outer"),
makeMemberRole(1, element.Tags{"natural": "water"}, "inner"),
diff --git a/test/complete_db.osm b/test/complete_db.osm
index e417149..75f3f1e 100644
--- a/test/complete_db.osm
+++ b/test/complete_db.osm
@@ -94,6 +94,7 @@
+
@@ -269,6 +270,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/test/completedb_test.go b/test/completedb_test.go
index 22720e7..4bf0773 100644
--- a/test/completedb_test.go
+++ b/test/completedb_test.go
@@ -61,6 +61,14 @@ func TestComplete_Deploy(t *testing.T) {
}
}
+func TestComplete_OnlyNewStyleMultipolgon(t *testing.T) {
+ assertRecords(t, []checkElem{
+ {"osm_landusages", -1001, "wood", nil},
+ {"osm_landusages", -1011, Missing, nil},
+ {"osm_landusages", -1021, Missing, nil},
+ })
+}
+
func TestComplete_LandusageToWaterarea1(t *testing.T) {
// Parks inserted into landusages
cache := ts.cache(t)
diff --git a/test/route_relation.osm b/test/route_relation.osm
index 9050239..0a0366a 100644
--- a/test/route_relation.osm
+++ b/test/route_relation.osm
@@ -153,5 +153,15 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/test/route_relation_mapping.yml b/test/route_relation_mapping.yml
index aa17e6d..f3955e9 100644
--- a/test/route_relation_mapping.yml
+++ b/test/route_relation_mapping.yml
@@ -1,9 +1,3 @@
-tags:
- load_all: true
- exclude:
- - created_by
- - source
-
tables:
master_routes:
type: relation_member
@@ -27,6 +21,7 @@ tables:
- key: name
name: name
type: string
+ relation_types: [route_master]
mapping:
route_master: [bus]
route_members:
@@ -54,6 +49,7 @@ tables:
key: name
type: string
from_member: true
+ relation_types: [route]
mapping:
route: [bus, tram, rail]
routes:
@@ -66,5 +62,7 @@ tables:
type: string
- name: tags
type: hstore_tags
+ relation_types: [route, route_master]
mapping:
+ route_master: [bus, tram, rail]
route: [bus, tram, rail]
diff --git a/test/route_relation_test.go b/test/route_relation_test.go
index a3aec6d..88522c8 100644
--- a/test/route_relation_test.go
+++ b/test/route_relation_test.go
@@ -58,6 +58,23 @@ func TestRouteRelation_RelationData(t *testing.T) {
if r.tags["name"] != "Bus 301: A => B" {
t.Error(r)
}
+
+ // check tags of master relation
+ r = ts.queryTags(t, "osm_routes", -100911)
+ if r.tags["name"] != "Bus 301" {
+ t.Error(r)
+ }
+}
+
+func TestRouteRelation_MemberUpdatedByNode1(t *testing.T) {
+ // check that member is updated after node was modified
+ rows := ts.queryDynamic(t, "osm_route_members", "osm_id = -110901 AND member = 110101")
+ if len(rows) != 1 {
+ t.Fatal(rows)
+ }
+ if rows[0]["name"] != "Stop" {
+ t.Error(rows[0])
+ }
}
func TestRouteRelation_MemberGeomUpdated1(t *testing.T) {
@@ -131,7 +148,7 @@ func TestRouteRelation_MemberGeomUpdated2(t *testing.T) {
}
-func TestRouteRelation_MemberUpdatedByNode(t *testing.T) {
+func TestRouteRelation_MemberUpdatedByNode2(t *testing.T) {
// check that member is updated after node was modified
rows := ts.queryDynamic(t, "osm_route_members", "osm_id = -110901 AND member = 110101")
if len(rows) != 1 {