improve area=yes/no handling

master
Oliver Tonnhofer 2016-11-10 11:00:34 +01:00
parent 81e363ef1a
commit b6b3d262d4
9 changed files with 211 additions and 20 deletions

View File

@ -316,3 +316,27 @@ To load all tags except ``created_by``, ``source``, and ``tiger:county``, ``tige
load_all: true, load_all: true,
exclude: [created_by, source, "tiger:*"] exclude: [created_by, source, "tiger:*"]
.. _Areas:
Areas
-----
A closed way is way where the first and last nodes are identical. These closed ways are used to represent elements like building, forest or park polygons, but they can also represent linear (non-polygon) features, like a roundabout or a race track.
OpenStreetMap uses the `area <http://wiki.openstreetmap.org/wiki/Key:area>`_ tag to specify if a closed way is an area (polygon) or a linear feature (linestring). For example ``highway=pedestrian, area=yes`` is a polygon feature.
By default, Imposm inserts all closed ways into polygon tables as long as ``area`` is not ``no`` and linestring tables will contain all closed ways as long as the ``area`` is not ``yes``.
However, the ``area`` tag is missing from most OSM elements, as buildings, landuse, etc. should be interpreted as ``area=yes`` by default and highways for example are ``area=no`` by default.
You can configure these default interpretations with the ``areas`` option.
.. code-block:: yaml
areas:
area_tags: [buildings, landuse, leisure, natural, aeroway]
linear_tags: [highway, barrier]
With this ``areas`` configuration, ``highway`` elements are only inserted into polygon tables if there is an ``area=yes`` tag. ``aeroway`` elements are only inserted into linestring tables if there is an ``area=no`` tag.

View File

@ -1,3 +1,6 @@
areas:
area_tags: [buildings, landuse, leisure, natural, aeroway]
linear_tags: [highway, barrier]
generalized_tables: generalized_tables:
landusages_gen0: landusages_gen0:
source: landusages_gen1 source: landusages_gen1

View File

@ -49,11 +49,17 @@ type Mapping struct {
Tables Tables `yaml:"tables"` Tables Tables `yaml:"tables"`
GeneralizedTables GeneralizedTables `yaml:"generalized_tables"` GeneralizedTables GeneralizedTables `yaml:"generalized_tables"`
Tags Tags `yaml:"tags"` Tags Tags `yaml:"tags"`
Areas Areas `yaml:"areas"`
// SingleIdSpace mangles the overlapping node/way/relation IDs // SingleIdSpace mangles the overlapping node/way/relation IDs
// to be unique (nodes positive, ways negative, relations negative -1e17) // to be unique (nodes positive, ways negative, relations negative -1e17)
SingleIdSpace bool `yaml:"use_single_id_space"` SingleIdSpace bool `yaml:"use_single_id_space"`
} }
type Areas struct {
AreaTags []Key `yaml:"area_tags"`
LinearTags []Key `yaml:"linear_tags"`
}
type Tags struct { type Tags struct {
LoadAll bool `yaml:"load_all"` LoadAll bool `yaml:"load_all"`
Exclude []Key `yaml:"exclude"` Exclude []Key `yaml:"exclude"`
@ -106,7 +112,7 @@ type TypeMappings struct {
Polygons KeyValues `yaml:"polygons"` Polygons KeyValues `yaml:"polygons"`
} }
type ElementFilter func(tags *element.Tags) bool type ElementFilter func(tags element.Tags, key Key, closed bool) bool
type TagTables map[Key]map[Value][]OrderedDestTable type TagTables map[Key]map[Value][]OrderedDestTable
@ -261,18 +267,67 @@ func (m *Mapping) extraTags(tableType TableType, tags map[Key]bool) {
} }
} }
} }
// always include area tag for closed-way handling
tags["area"] = true
} }
func (m *Mapping) ElementFilters() map[string][]ElementFilter { func (m *Mapping) ElementFilters() map[string][]ElementFilter {
result := make(map[string][]ElementFilter) result := make(map[string][]ElementFilter)
var areaTags map[Key]struct{}
var linearTags map[Key]struct{}
if m.Areas.AreaTags != nil {
areaTags = make(map[Key]struct{})
for _, tag := range m.Areas.AreaTags {
areaTags[tag] = struct{}{}
}
}
if m.Areas.LinearTags != nil {
linearTags = make(map[Key]struct{})
for _, tag := range m.Areas.LinearTags {
linearTags[tag] = struct{}{}
}
}
for name, t := range m.Tables { for name, t := range m.Tables {
if t.Type == LineStringTable && areaTags != nil {
f := func(tags element.Tags, key Key, closed bool) bool {
if closed {
if tags["area"] == "yes" {
return false
}
if tags["area"] != "no" {
if _, ok := areaTags[key]; ok {
return false
}
}
}
return true
}
result[name] = append(result[name], f)
}
if t.Type == PolygonTable && linearTags != nil {
f := func(tags element.Tags, key Key, closed bool) bool {
if closed && tags["area"] == "no" {
return false
}
if tags["area"] != "yes" {
if _, ok := linearTags[key]; ok {
return false
}
}
return true
}
result[name] = append(result[name], f)
}
if t.Filters == nil { if t.Filters == nil {
continue continue
} }
if t.Filters.ExcludeTags != nil { if t.Filters.ExcludeTags != nil {
for _, filterKeyVal := range *t.Filters.ExcludeTags { for _, filterKeyVal := range *t.Filters.ExcludeTags {
f := func(tags *element.Tags) bool { f := func(tags element.Tags, key Key, closed bool) bool {
if v, ok := (*tags)[filterKeyVal[0]]; ok { if v, ok := tags[filterKeyVal[0]]; ok {
if filterKeyVal[1] == "__any__" || v == filterKeyVal[1] { if filterKeyVal[1] == "__any__" || v == filterKeyVal[1] {
return false return false
} }

View File

@ -278,6 +278,11 @@ func TestPointMatcher(t *testing.T) {
func TestLineStringMatcher(t *testing.T) { func TestLineStringMatcher(t *testing.T) {
elem := element.Way{} 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() ls := mapping.LineStringMatcher()
elem.Tags = element.Tags{"unknown": "baz"} elem.Tags = element.Tags{"unknown": "baz"}
@ -293,6 +298,18 @@ func TestLineStringMatcher(t *testing.T) {
elem.Tags = element.Tags{"highway": "pedestrian", "area": "yes"} elem.Tags = element.Tags{"highway": "pedestrian", "area": "yes"}
matchesEqual(t, []Match{}, ls.MatchWay(&elem)) 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"} elem.Tags = element.Tags{"highway": "secondary", "railway": "tram"}
matchesEqual(t, matchesEqual(t,
[]Match{ []Match{
@ -320,6 +337,12 @@ func TestPolygonMatcher(t *testing.T) {
elem.Tags = element.Tags{"building": "residential"} elem.Tags = element.Tags{"building": "residential"}
matchesEqual(t, []Match{{"building", "residential", DestTable{Name: "buildings"}, nil}}, polys.MatchRelation(&elem)) 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"} elem.Tags = element.Tags{"building": "shop"}
matchesEqual(t, []Match{ matchesEqual(t, []Match{
{"building", "shop", DestTable{Name: "buildings"}, nil}, {"building", "shop", DestTable{Name: "buildings"}, nil},
@ -339,7 +362,10 @@ func TestPolygonMatcher(t *testing.T) {
{"landuse", "farm", DestTable{Name: "landusages"}, nil}}, {"landuse", "farm", DestTable{Name: "landusages"}, nil}},
polys.MatchRelation(&elem)) polys.MatchRelation(&elem))
elem.Tags = element.Tags{"highway": "footway"} elem.Tags = element.Tags{"highway": "footway"} // linear by default
matchesEqual(t, []Match{}, polys.MatchRelation(&elem))
elem.Tags = element.Tags{"highway": "footway", "area": "yes"}
matchesEqual(t, []Match{{"highway", "footway", DestTable{Name: "landusages"}, nil}}, polys.MatchRelation(&elem)) matchesEqual(t, []Match{{"highway", "footway", DestTable{Name: "landusages"}, nil}}, polys.MatchRelation(&elem))
elem.Tags = element.Tags{"boundary": "administrative", "admin_level": "8"} elem.Tags = element.Tags{"boundary": "administrative", "admin_level": "8"}

View File

@ -105,7 +105,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) return tm.match(node.Tags, false)
} }
func (tm *tagMatcher) MatchWay(way *element.Way) []Match { func (tm *tagMatcher) MatchWay(way *element.Way) []Match {
@ -114,21 +114,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) return tm.match(way.Tags, true)
} }
} 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) return tm.match(way.Tags, 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) return tm.match(rel.Tags, true)
} }
type orderedMatch struct { type orderedMatch struct {
@ -136,13 +137,18 @@ type orderedMatch struct {
order int order int
} }
func (tm *tagMatcher) match(tags *element.Tags) []Match { func (tm *tagMatcher) match(tags element.Tags, closed 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) {
for _, t := range tbls { for _, t := range tbls {
this := orderedMatch{ this := orderedMatch{
Match: Match{k, v, t.DestTable, tm.tables[t.Name]}, Match: Match{
Key: k,
Value: v,
Table: t.DestTable,
tableFields: tm.tables[t.Name],
},
order: t.order, order: t.order,
} }
if other, ok := tables[t.DestTable]; ok { if other, ok := tables[t.DestTable]; ok {
@ -158,7 +164,7 @@ func (tm *tagMatcher) match(tags *element.Tags) []Match {
addTables("__any__", "__any__", values["__any__"]) addTables("__any__", "__any__", values["__any__"])
} }
for k, v := range *tags { for k, v := range tags {
values, ok := tm.mappings[Key(k)] values, ok := tm.mappings[Key(k)]
if ok { if ok {
if tbls, ok := values["__any__"]; ok { if tbls, ok := values["__any__"]; ok {
@ -175,7 +181,7 @@ func (tm *tagMatcher) match(tags *element.Tags) []Match {
filteredOut := false filteredOut := false
if ok { if ok {
for _, filter := range filters { for _, filter := range filters {
if !filter(tags) { if !filter(tags, Key(match.Key), closed) {
filteredOut = true filteredOut = true
break break
} }

View File

@ -1,3 +1,6 @@
areas:
area_tags: [buildings, landuse, leisure, natural, aeroway]
linear_tags: [highway, barrier]
generalized_tables: generalized_tables:
landusages_gen0: landusages_gen0:
source: landusages_gen1 source: landusages_gen1

View File

@ -1155,4 +1155,49 @@
<tag k="landuse" v="park"/> <tag k="landuse" v="park"/>
</way> </way>
<!-- test area mapping -->
<node id="301101" version="1" timestamp="2011-11-11T00:11:11Z" lat="-32" lon="10"/>
<node id="301102" version="1" timestamp="2011-11-11T00:11:11Z" lat="-32" lon="11"/>
<node id="301103" version="1" timestamp="2011-11-11T00:11:11Z" lat="-34" lon="11"/>
<node id="301104" version="1" timestamp="2011-11-11T00:11:11Z" lat="-34" lon="10"/>
<way id="301151" version="1" timestamp="2011-11-11T00:11:11Z">
<nd ref="301101"/>
<nd ref="301102"/>
<nd ref="301103"/>
<nd ref="301104"/>
<nd ref="301101"/>
<tag k="highway" v="pedestrian"/>
<tag k="name" v="name"/>
</way>
<way id="301152" version="1" timestamp="2011-11-11T00:11:11Z">
<nd ref="301101"/>
<nd ref="301102"/>
<nd ref="301103"/>
<nd ref="301104"/>
<nd ref="301101"/>
<tag k="highway" v="pedestrian"/>
<tag k="name" v="name"/>
<tag k="area" v="yes"/>
</way>
<way id="301153" version="1" timestamp="2011-11-11T00:11:11Z">
<nd ref="301101"/>
<nd ref="301102"/>
<nd ref="301103"/>
<nd ref="301104"/>
<nd ref="301101"/>
<tag k="leisure" v="track"/>
</way>
<way id="301154" version="1" timestamp="2011-11-11T00:11:11Z">
<nd ref="301101"/>
<nd ref="301102"/>
<nd ref="301103"/>
<nd ref="301104"/>
<nd ref="301101"/>
<tag k="leisure" v="track"/>
<tag k="area" v="no"/>
</way>
</osm> </osm>

View File

@ -1,4 +1,8 @@
{ {
"areas": {
"area_tags": ["leisure"],
"linear_tags": ["highway"],
},
"generalized_tables": { "generalized_tables": {
"waterareas_gen1": { "waterareas_gen1": {
"source": "waterareas", "source": "waterareas",
@ -125,7 +129,8 @@
"pitch", "pitch",
"stadium", "stadium",
"common", "common",
"nature_reserve" "nature_reserve",
"track"
], ],
"tourism": [ "tourism": [
"zoo" "zoo"
@ -202,6 +207,7 @@
} }
], ],
"type": "polygon", "type": "polygon",
"filters": {"areas": {}},
"mapping": { "mapping": {
"building": [ "building": [
"__any__" "__any__"
@ -711,9 +717,7 @@
], ],
"type": "linestring", "type": "linestring",
"filters": { "filters": {
"exclude_tags": [ "areas": {},
["area", "yes"]
]
}, },
"mappings": { "mappings": {
"railway": { "railway": {
@ -733,10 +737,6 @@
}, },
"roads": { "roads": {
"mapping": { "mapping": {
"man_made": [
"pier",
"groyne"
],
"highway": [ "highway": [
"motorway", "motorway",
"motorway_link", "motorway_link",
@ -761,6 +761,13 @@
"unclassified", "unclassified",
"residential", "residential",
"raceway" "raceway"
],
"man_made": [
"pier",
"groyne"
],
"leisure": [
"track"
] ]
} }
} }

View File

@ -364,6 +364,28 @@ func TestComplete_EnumerateKey(t *testing.T) {
}) })
} }
func TestComplete_AreaMapping(t *testing.T) {
// Mapping type dependent area-defaults.
assertRecords(t, []checkElem{
// highway=pedestrian
{"osm_roads", 301151, "pedestrian", nil},
{"osm_landusages", 301151, Missing, nil},
// // highway=pedestrian, area=yes
{"osm_roads", 301152, Missing, nil},
{"osm_landusages", 301152, "pedestrian", nil},
// // leisure=track
{"osm_roads", 301153, Missing, nil},
{"osm_landusages", 301153, "track", nil},
// // leisure=track, area=no
{"osm_roads", 301154, "track", nil},
{"osm_landusages", 301154, Missing, nil},
})
}
func TestComplete_Update(t *testing.T) { func TestComplete_Update(t *testing.T) {
ts.updateOsm(t, "./build/complete_db.osc.gz") ts.updateOsm(t, "./build/complete_db.osc.gz")
} }