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,
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:
landusages_gen0:
source: landusages_gen1

View File

@ -49,11 +49,17 @@ type Mapping struct {
Tables Tables `yaml:"tables"`
GeneralizedTables GeneralizedTables `yaml:"generalized_tables"`
Tags Tags `yaml:"tags"`
Areas Areas `yaml:"areas"`
// SingleIdSpace mangles the overlapping node/way/relation IDs
// to be unique (nodes positive, ways negative, relations negative -1e17)
SingleIdSpace bool `yaml:"use_single_id_space"`
}
type Areas struct {
AreaTags []Key `yaml:"area_tags"`
LinearTags []Key `yaml:"linear_tags"`
}
type Tags struct {
LoadAll bool `yaml:"load_all"`
Exclude []Key `yaml:"exclude"`
@ -106,7 +112,7 @@ type TypeMappings struct {
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
@ -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 {
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 {
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 {
continue
}
if t.Filters.ExcludeTags != nil {
for _, filterKeyVal := range *t.Filters.ExcludeTags {
f := func(tags *element.Tags) bool {
if v, ok := (*tags)[filterKeyVal[0]]; ok {
f := func(tags element.Tags, key Key, closed bool) bool {
if v, ok := tags[filterKeyVal[0]]; ok {
if filterKeyVal[1] == "__any__" || v == filterKeyVal[1] {
return false
}

View File

@ -278,6 +278,11 @@ func TestPointMatcher(t *testing.T) {
func TestLineStringMatcher(t *testing.T) {
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"}
@ -293,6 +298,18 @@ func TestLineStringMatcher(t *testing.T) {
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{
@ -320,6 +337,12 @@ func TestPolygonMatcher(t *testing.T) {
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},
@ -339,7 +362,10 @@ func TestPolygonMatcher(t *testing.T) {
{"landuse", "farm", DestTable{Name: "landusages"}, nil}},
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))
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 {
return tm.match(&node.Tags)
return tm.match(node.Tags, false)
}
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" {
return nil
}
return tm.match(&way.Tags)
return tm.match(way.Tags, true)
}
} 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)
return tm.match(way.Tags, false)
}
return nil
}
func (tm *tagMatcher) MatchRelation(rel *element.Relation) []Match {
return tm.match(&rel.Tags)
return tm.match(rel.Tags, true)
}
type orderedMatch struct {
@ -136,13 +137,18 @@ type orderedMatch struct {
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)
addTables := func(k, v string, tbls []OrderedDestTable) {
for _, t := range tbls {
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,
}
if other, ok := tables[t.DestTable]; ok {
@ -158,7 +164,7 @@ func (tm *tagMatcher) match(tags *element.Tags) []Match {
addTables("__any__", "__any__", values["__any__"])
}
for k, v := range *tags {
for k, v := range tags {
values, ok := tm.mappings[Key(k)]
if ok {
if tbls, ok := values["__any__"]; ok {
@ -175,7 +181,7 @@ func (tm *tagMatcher) match(tags *element.Tags) []Match {
filteredOut := false
if ok {
for _, filter := range filters {
if !filter(tags) {
if !filter(tags, Key(match.Key), closed) {
filteredOut = true
break
}

View File

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

View File

@ -1155,4 +1155,49 @@
<tag k="landuse" v="park"/>
</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>

View File

@ -1,4 +1,8 @@
{
"areas": {
"area_tags": ["leisure"],
"linear_tags": ["highway"],
},
"generalized_tables": {
"waterareas_gen1": {
"source": "waterareas",
@ -125,7 +129,8 @@
"pitch",
"stadium",
"common",
"nature_reserve"
"nature_reserve",
"track"
],
"tourism": [
"zoo"
@ -202,6 +207,7 @@
}
],
"type": "polygon",
"filters": {"areas": {}},
"mapping": {
"building": [
"__any__"
@ -711,9 +717,7 @@
],
"type": "linestring",
"filters": {
"exclude_tags": [
["area", "yes"]
]
"areas": {},
},
"mappings": {
"railway": {
@ -733,10 +737,6 @@
},
"roads": {
"mapping": {
"man_made": [
"pier",
"groyne"
],
"highway": [
"motorway",
"motorway_link",
@ -761,6 +761,13 @@
"unclassified",
"residential",
"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) {
ts.updateOsm(t, "./build/complete_db.osc.gz")
}