add relation_type

master
Oliver Tonnhofer 2017-05-10 14:23:32 +02:00
parent b4cf7b91f3
commit 7157b5252a
10 changed files with 343 additions and 196 deletions

View File

@ -20,14 +20,15 @@ type Field struct {
} }
type Table struct { type Table struct {
Name string Name string
Type TableType `yaml:"type"` Type TableType `yaml:"type"`
Mapping KeyValues `yaml:"mapping"` Mapping KeyValues `yaml:"mapping"`
Mappings map[string]SubMapping `yaml:"mappings"` Mappings map[string]SubMapping `yaml:"mappings"`
TypeMappings TypeMappings `yaml:"type_mappings"` TypeMappings TypeMappings `yaml:"type_mappings"`
Fields []*Field `yaml:"columns"` // TODO rename Fields internaly to Columns Fields []*Field `yaml:"columns"` // TODO rename Fields internaly to Columns
OldFields []*Field `yaml:"fields"` OldFields []*Field `yaml:"fields"`
Filters *Filters `yaml:"filters"` Filters *Filters `yaml:"filters"`
RelationTypes []string `yaml:"relation_types"`
} }
type GeneralizedTable struct { type GeneralizedTable struct {
@ -179,19 +180,6 @@ func NewMapping(filename string) (*Mapping, error) {
return &mapping, nil 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 { func (m *Mapping) prepare() error {
for name, t := range m.Tables { for name, t := range m.Tables {
t.Name = name t.Name = name
@ -247,7 +235,7 @@ func (m *Mapping) mappings(tableType TableType, mappings TagTables) {
func (m *Mapping) tables(tableType TableType) map[string]*TableFields { func (m *Mapping) tables(tableType TableType) map[string]*TableFields {
result := make(map[string]*TableFields) result := make(map[string]*TableFields)
for name, t := range m.Tables { for name, t := range m.Tables {
if t.Type == tableType || t.Type == "geometry" { if t.Type == tableType || t.Type == GeometryTable {
result[name] = t.TableFields() 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) { func (m *Mapping) extraTags(tableType TableType, tags map[Key]bool) {
for _, t := range m.Tables { for _, t := range m.Tables {
if t.Type != tableType && t.Type != "geometry" { if t.Type != tableType && t.Type != GeometryTable {
continue 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 { if t.Filters != nil && t.Filters.ExcludeTags != nil {
for _, keyVal := range *t.Filters.ExcludeTags { for _, keyVal := range *t.Filters.ExcludeTags {
tags[Key(keyVal[0])] = true 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 { for _, k := range m.Tags.Include {
tags[k] = true 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) { func (m *Mapping) addFilters(filters tableFilters) {
for name, t := range m.Tables { for name, t := range m.Tables {
if t.Filters == nil { if t.Filters == nil {

View File

@ -12,9 +12,10 @@ func (m *Mapping) NodeTagFilter() TagFilterer {
return newExcludeFilter(m.Tags.Exclude) return newExcludeFilter(m.Tags.Exclude)
} }
mappings := make(map[Key]map[Value][]OrderedDestTable) mappings := make(map[Key]map[Value][]OrderedDestTable)
m.mappings("point", mappings) m.mappings(PointTable, mappings)
tags := make(map[Key]bool) tags := make(map[Key]bool)
m.extraTags("point", tags) m.extraTags(PointTable, tags)
m.extraTags(RelationMemberTable, tags)
return &TagFilter{mappings, tags} return &TagFilter{mappings, tags}
} }
@ -23,11 +24,12 @@ func (m *Mapping) WayTagFilter() TagFilterer {
return newExcludeFilter(m.Tags.Exclude) return newExcludeFilter(m.Tags.Exclude)
} }
mappings := make(map[Key]map[Value][]OrderedDestTable) mappings := make(map[Key]map[Value][]OrderedDestTable)
m.mappings("linestring", mappings) m.mappings(LineStringTable, mappings)
m.mappings("polygon", mappings) m.mappings(PolygonTable, mappings)
tags := make(map[Key]bool) tags := make(map[Key]bool)
m.extraTags("linestring", tags) m.extraTags(LineStringTable, tags)
m.extraTags("polygon", tags) m.extraTags(PolygonTable, tags)
m.extraTags(RelationMemberTable, tags)
return &TagFilter{mappings, tags} return &TagFilter{mappings, tags}
} }
@ -42,11 +44,15 @@ func (m *Mapping) RelationTagFilter() TagFilterer {
"boundary": []OrderedDestTable{}, "boundary": []OrderedDestTable{},
"land_area": []OrderedDestTable{}, "land_area": []OrderedDestTable{},
} }
m.mappings("linestring", mappings) m.mappings(LineStringTable, mappings)
m.mappings("polygon", mappings) m.mappings(PolygonTable, mappings)
m.mappings(RelationTable, mappings)
m.mappings(RelationMemberTable, mappings)
tags := make(map[Key]bool) tags := make(map[Key]bool)
m.extraTags("linestring", tags) m.extraTags(LineStringTable, tags)
m.extraTags("polygon", tags) m.extraTags(PolygonTable, tags)
m.extraTags(RelationTable, tags)
m.extraTags(RelationMemberTable, tags)
return &TagFilter{mappings, tags} return &TagFilter{mappings, tags}
} }

View File

@ -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 { func stringMapEqual(expected, actual map[string]string) bool {
if len(expected) != len(actual) { if len(expected) != len(actual) {
return false return false
@ -50,12 +34,12 @@ func stringMapEqual(expected, actual map[string]string) bool {
return true return true
} }
func matchesEqual(t *testing.T, expected []Match, actual []Match) { func matchesEqual(expected []Match, actual []Match) bool {
expectedMatches := make(map[DestTable]Match) expectedMatches := make(map[DestTable]Match)
actualMatches := make(map[DestTable]Match) actualMatches := make(map[DestTable]Match)
if len(expected) != len(actual) { if len(expected) != len(actual) {
t.Fatalf("different length in %v and %v\n", expected, actual) return false
} }
for _, match := range expected { for _, match := range expected {
@ -70,12 +54,13 @@ func matchesEqual(t *testing.T, expected []Match, actual []Match) {
if expectedMatch.Table != actualMatch.Table || if expectedMatch.Table != actualMatch.Table ||
expectedMatch.Key != actualMatch.Key || expectedMatch.Key != actualMatch.Key ||
expectedMatch.Value != actualMatch.Value { expectedMatch.Value != actualMatch.Value {
t.Fatalf("match differ %v != %v", expectedMatch, actualMatch) return false
} }
} else { } else {
t.Fatalf("%s not in %v", name, actualMatches) return false
} }
} }
return true
} }
func TestTagFilterNodes(t *testing.T) { func TestTagFilterNodes(t *testing.T) {
@ -154,151 +139,209 @@ func TestTagFilterRelations(t *testing.T) {
} }
func TestPointMatcher(t *testing.T) { func TestPointMatcher(t *testing.T) {
elem := element.Node{} tests := []struct {
points := mapping.PointMatcher() tags element.Tags
matches []Match
elem.Tags = element.Tags{"unknown": "baz"} }{
matchesEqual(t, []Match{}, points.MatchNode(&elem)) {element.Tags{"unknown": "baz"}, []Match{}},
{element.Tags{"place": "unknown"}, []Match{}},
elem.Tags = element.Tags{"place": "unknown"} {element.Tags{"place": "city"}, []Match{{"place", "city", DestTable{Name: "places"}, nil}}},
matchesEqual(t, []Match{}, points.MatchNode(&elem)) {element.Tags{"place": "city", "highway": "unknown"}, []Match{{"place", "city", DestTable{Name: "places"}, nil}}},
{element.Tags{"place": "city", "highway": "bus_stop"}, []Match{
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{
{"place", "city", DestTable{Name: "places"}, nil}, {"place", "city", DestTable{Name: "places"}, nil},
{"highway", "bus_stop", DestTable{Name: "transport_points"}, 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) { 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{} elem := element.Way{}
// fake closed way for area matching // fake closed way for area matching
elem.Refs = []int64{1, 2, 3, 4, 1} elem.Refs = []int64{1, 2, 3, 4, 1}
if !elem.IsClosed() { if !elem.IsClosed() {
t.Fatal("way not closed") t.Fatal("way not closed")
} }
ls := mapping.LineStringMatcher() m := mapping.LineStringMatcher()
for i, test := range tests {
elem.Tags = element.Tags{"unknown": "baz"} elem.Tags = test.tags
matchesEqual(t, []Match{}, ls.MatchWay(&elem)) actual := m.MatchWay(&elem)
if !matchesEqual(actual, test.matches) {
elem.Tags = element.Tags{"highway": "unknown"} t.Errorf("unexpected result for case %d: %v != %v", i+1, actual, test.matches)
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))
} }
func TestPolygonMatcher(t *testing.T) { func TestPolygonMatcher_MatchWay(t *testing.T) {
elem := element.Relation{} tests := []struct {
polys := mapping.PolygonMatcher() 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"} {element.Tags{"building": "shop"}, []Match{
matchesEqual(t, []Match{}, polys.MatchRelation(&elem)) {"building", "shop", DestTable{Name: "buildings"}, nil},
{"building", "shop", DestTable{Name: "amenity_areas"}, nil},
}},
elem.Tags = element.Tags{"landuse": "unknowns"} {element.Tags{"aeroway": "apron", "landuse": "farm"}, []Match{
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{
{"aeroway", "apron", DestTable{Name: "transport_areas"}, nil}, {"aeroway", "apron", DestTable{Name: "transport_areas"}, nil},
{"landuse", "farm", DestTable{Name: "landusages"}, nil}}, {"landuse", "farm", DestTable{Name: "landusages"}, nil},
polys.MatchRelation(&elem)) }},
elem.Tags = element.Tags{"highway": "footway"} // linear by default {element.Tags{"landuse": "farm", "highway": "secondary"}, []Match{
matchesEqual(t, []Match{}, polys.MatchRelation(&elem)) {"landuse", "farm", DestTable{Name: "landusages"}, nil},
}},
elem.Tags = element.Tags{"highway": "footway", "area": "yes"} {element.Tags{"highway": "footway"}, []Match{}},
matchesEqual(t, []Match{{"highway", "footway", DestTable{Name: "landusages"}, nil}}, polys.MatchRelation(&elem)) {element.Tags{"highway": "footway", "area": "yes"}, []Match{
{"highway", "footway", DestTable{Name: "landusages"}, nil},
}},
elem.Tags = element.Tags{"boundary": "administrative", "admin_level": "8"} {element.Tags{"boundary": "administrative", "admin_level": "8"}, []Match{{"boundary", "administrative", DestTable{Name: "admin"}, nil}}},
matchesEqual(t, []Match{{"boundary", "administrative", DestTable{Name: "admin"}, nil}}, polys.MatchRelation(&elem))
/*
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{} elem := element.Relation{}
polys := mapping.PolygonMatcher() m := mapping.PolygonMatcher()
for i, test := range tests {
/* elem.Tags = test.tags
landusages mapping has the following order, actual := m.MatchRelation(&elem)
check that XxxMatcher always uses the first if !matchesEqual(actual, test.matches) {
t.Errorf("unexpected result for case %d: %v != %v", i+1, actual, test.matches)
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))
} }
func TestExcludeFilter(t *testing.T) { func TestExcludeFilter(t *testing.T) {

View File

@ -39,10 +39,13 @@ func (m *Mapping) PolygonMatcher() RelWayMatcher {
filters := make(tableFilters) filters := make(tableFilters)
m.addFilters(filters) m.addFilters(filters)
m.addTypedFilters(PolygonTable, filters) m.addTypedFilters(PolygonTable, filters)
relFilters := make(tableFilters)
m.addRelationFilters(PolygonTable, relFilters)
return &tagMatcher{ return &tagMatcher{
mappings: mappings, mappings: mappings,
tables: m.tables(PolygonTable), tables: m.tables(PolygonTable),
filters: filters, filters: filters,
relFilters: relFilters,
matchAreas: true, matchAreas: true,
} }
} }
@ -54,10 +57,13 @@ func (m *Mapping) RelationMatcher() RelationMatcher {
m.addFilters(filters) m.addFilters(filters)
m.addTypedFilters(PolygonTable, filters) m.addTypedFilters(PolygonTable, filters)
m.addTypedFilters(RelationTable, filters) m.addTypedFilters(RelationTable, filters)
relFilters := make(tableFilters)
m.addRelationFilters(RelationTable, relFilters)
return &tagMatcher{ return &tagMatcher{
mappings: mappings, mappings: mappings,
tables: m.tables(RelationTable), tables: m.tables(RelationTable),
filters: filters, filters: filters,
relFilters: relFilters,
matchAreas: true, matchAreas: true,
} }
} }
@ -68,10 +74,13 @@ func (m *Mapping) RelationMemberMatcher() RelationMatcher {
filters := make(tableFilters) filters := make(tableFilters)
m.addFilters(filters) m.addFilters(filters)
m.addTypedFilters(RelationMemberTable, filters) m.addTypedFilters(RelationMemberTable, filters)
relFilters := make(tableFilters)
m.addRelationFilters(RelationMemberTable, relFilters)
return &tagMatcher{ return &tagMatcher{
mappings: mappings, mappings: mappings,
tables: m.tables(RelationMemberTable), tables: m.tables(RelationMemberTable),
filters: filters, filters: filters,
relFilters: relFilters,
matchAreas: true, matchAreas: true,
} }
} }
@ -104,6 +113,7 @@ type tagMatcher struct {
mappings TagTables mappings TagTables
tables map[string]*TableFields tables map[string]*TableFields
filters map[string][]ElementFilter filters map[string][]ElementFilter
relFilters map[string][]ElementFilter
matchAreas bool 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 { 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 { 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" { if way.Tags["area"] == "no" {
return nil return nil
} }
return tm.match(way.Tags, true) return tm.match(way.Tags, true, false)
} }
} else { // match way as linestring } else { // match way as linestring
if way.IsClosed() { if way.IsClosed() {
if way.Tags["area"] == "yes" { if way.Tags["area"] == "yes" {
return nil 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 return nil
} }
func (tm *tagMatcher) MatchRelation(rel *element.Relation) []Match { func (tm *tagMatcher) MatchRelation(rel *element.Relation) []Match {
return tm.match(rel.Tags, true) return tm.match(rel.Tags, true, true)
} }
type orderedMatch struct { type orderedMatch struct {
@ -148,7 +158,7 @@ type orderedMatch struct {
order int 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) tables := make(map[DestTable]orderedMatch)
addTables := func(k, v string, tbls []OrderedDestTable) { 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 { if !filteredOut {
matches = append(matches, match.Match) matches = append(matches, match.Match)
} }

View File

@ -43,7 +43,7 @@ func TestSelectRelationPolygonsSimple(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
r := element.Relation{} r := element.Relation{}
r.Tags = element.Tags{"landuse": "park"} r.Tags = element.Tags{"landuse": "park", "type": "multipolygon"}
r.Members = []element.Member{ r.Members = []element.Member{
makeMember(0, element.Tags{"landuse": "forest"}), makeMember(0, element.Tags{"landuse": "forest"}),
makeMember(1, element.Tags{"landuse": "park"}), makeMember(1, element.Tags{"landuse": "park"}),
@ -68,7 +68,7 @@ func TestSelectRelationPolygonsUnrelatedTags(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
r := element.Relation{} r := element.Relation{}
r.Tags = element.Tags{"landuse": "park"} r.Tags = element.Tags{"landuse": "park", "type": "multipolygon"}
r.Members = []element.Member{ r.Members = []element.Member{
makeMember(0, element.Tags{"landuse": "park", "layer": "2", "name": "foo"}), makeMember(0, element.Tags{"landuse": "park", "layer": "2", "name": "foo"}),
makeMember(1, element.Tags{"landuse": "forest"}), makeMember(1, element.Tags{"landuse": "forest"}),
@ -91,7 +91,7 @@ func TestSelectRelationPolygonsMultiple(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
r := element.Relation{} r := element.Relation{}
r.Tags = element.Tags{"landuse": "park"} r.Tags = element.Tags{"landuse": "park", "type": "multipolygon"}
r.Members = []element.Member{ r.Members = []element.Member{
makeMember(0, element.Tags{"landuse": "park"}), makeMember(0, element.Tags{"landuse": "park"}),
makeMember(1, element.Tags{"natural": "forest"}), makeMember(1, element.Tags{"natural": "forest"}),
@ -117,7 +117,7 @@ func TestSelectRelationPolygonsMultipleTags(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
r := element.Relation{} 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{ r.Members = []element.Member{
makeMember(0, element.Tags{"natural": "scrub"}), makeMember(0, element.Tags{"natural": "scrub"}),
makeMember(1, element.Tags{"landuse": "forest"}), makeMember(1, element.Tags{"landuse": "forest"}),
@ -139,7 +139,7 @@ func TestSelectRelationPolygonsMultipleTagsOnWay(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
r := element.Relation{} r := element.Relation{}
r.Tags = element.Tags{"waterway": "riverbank"} r.Tags = element.Tags{"waterway": "riverbank", "type": "multipolygon"}
r.Members = []element.Member{ r.Members = []element.Member{
makeMemberRole(0, element.Tags{"waterway": "riverbank", "natural": "water"}, "outer"), makeMemberRole(0, element.Tags{"waterway": "riverbank", "natural": "water"}, "outer"),
makeMemberRole(1, element.Tags{"natural": "water"}, "inner"), makeMemberRole(1, element.Tags{"natural": "water"}, "inner"),

View File

@ -94,6 +94,7 @@
<nd ref="1005"/> <nd ref="1005"/>
<nd ref="1001"/> <nd ref="1001"/>
<tag k="name" v="way 1001"/> <tag k="name" v="way 1001"/>
<tag k="landuse" v="wood"/>
</way> </way>
<way id="1002" version="1" timestamp="2011-11-11T00:11:11Z"> <way id="1002" version="1" timestamp="2011-11-11T00:11:11Z">
<nd ref="1006"/> <nd ref="1006"/>
@ -269,6 +270,16 @@
<tag k="landuse" v="wood"/> <tag k="landuse" v="wood"/>
<tag k="type" v="multipolygon"/> <tag k="type" v="multipolygon"/>
</relation> </relation>
<relation id="1011" version="1" timestamp="2011-11-11T00:11:11Z">
<member type="way" ref="1001" role="outer"/>
<member type="way" ref="1002" role="inner"/>
<tag k="type" v="multipolygon"/>
</relation>
<relation id="1021" version="1" timestamp="2011-11-11T00:11:11Z">
<member type="way" ref="1001" role="outer"/>
<member type="way" ref="1002" role="inner"/>
<tag k="landuse" v="wood"/>
</relation>
<relation id="2001" version="1" timestamp="2011-11-11T00:11:11Z"> <relation id="2001" version="1" timestamp="2011-11-11T00:11:11Z">
<member type="way" ref="2001" role="outer"/> <member type="way" ref="2001" role="outer"/>
<member type="way" ref="2002" role="inner"/> <member type="way" ref="2002" role="inner"/>

View File

@ -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) { func TestComplete_LandusageToWaterarea1(t *testing.T) {
// Parks inserted into landusages // Parks inserted into landusages
cache := ts.cache(t) cache := ts.cache(t)

View File

@ -153,5 +153,15 @@
<tag k="type" v="route"/> <tag k="type" v="route"/>
</relation> </relation>
<!-- non-route type is not imported -->
<node id="130101" version="1" timestamp="2015-12-31T23:59:99Z" lat="53.0" lon="8.200">
<tag k="name" v="Stop"/>
</node>
<relation id="130901" version="23" timestamp="2015-06-02T04:13:19Z">
<member type="node" ref="130101" role="stop"/>
<tag k="route" v="bus"/>
<tag k="type" v="bus_route"/> <!-- invalid type -->
</relation>
</osm> </osm>

View File

@ -1,9 +1,3 @@
tags:
load_all: true
exclude:
- created_by
- source
tables: tables:
master_routes: master_routes:
type: relation_member type: relation_member
@ -27,6 +21,7 @@ tables:
- key: name - key: name
name: name name: name
type: string type: string
relation_types: [route_master]
mapping: mapping:
route_master: [bus] route_master: [bus]
route_members: route_members:
@ -54,6 +49,7 @@ tables:
key: name key: name
type: string type: string
from_member: true from_member: true
relation_types: [route]
mapping: mapping:
route: [bus, tram, rail] route: [bus, tram, rail]
routes: routes:
@ -66,5 +62,7 @@ tables:
type: string type: string
- name: tags - name: tags
type: hstore_tags type: hstore_tags
relation_types: [route, route_master]
mapping: mapping:
route_master: [bus, tram, rail]
route: [bus, tram, rail] route: [bus, tram, rail]

View File

@ -58,6 +58,23 @@ func TestRouteRelation_RelationData(t *testing.T) {
if r.tags["name"] != "Bus 301: A => B" { if r.tags["name"] != "Bus 301: A => B" {
t.Error(r) 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) { 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 // check that member is updated after node was modified
rows := ts.queryDynamic(t, "osm_route_members", "osm_id = -110901 AND member = 110101") rows := ts.queryDynamic(t, "osm_route_members", "osm_id = -110901 AND member = 110101")
if len(rows) != 1 { if len(rows) != 1 {