do not import outer way of relation into same table as relation
improved inserted-ways detection by marking outer ways as inserted if they match the same table as the relation.
parent
530bdda699
commit
6394f92a7a
|
@ -141,14 +141,17 @@ func BuildRelGeometry(rel *element.Relation, rings []*Ring, srid int) (*geos.Geo
|
|||
// add ring as hole or shell
|
||||
if ringIsHole(rings, j) {
|
||||
rings[i].holes[rings[j]] = true
|
||||
rings[i].outer = false
|
||||
} else {
|
||||
shells[rings[j]] = true
|
||||
rings[i].outer = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if rings[i].containedBy == -1 {
|
||||
// add as shell if it is not a hole
|
||||
shells[rings[i]] = true
|
||||
rings[i].outer = true
|
||||
}
|
||||
g.PreparedDestroy(testGeom)
|
||||
}
|
||||
|
@ -157,7 +160,6 @@ func BuildRelGeometry(rel *element.Relation, rings []*Ring, srid int) (*geos.Geo
|
|||
for shell, _ := range shells {
|
||||
var interiors []*geos.Geom
|
||||
for hole, _ := range shell.holes {
|
||||
hole.MarkInserted(rel.Tags)
|
||||
ring := g.Clone(g.ExteriorRing(hole.geom))
|
||||
g.Destroy(hole.geom)
|
||||
if ring == nil {
|
||||
|
@ -165,7 +167,6 @@ func BuildRelGeometry(rel *element.Relation, rings []*Ring, srid int) (*geos.Geo
|
|||
}
|
||||
interiors = append(interiors, ring)
|
||||
}
|
||||
shell.MarkInserted(rel.Tags)
|
||||
exterior := g.Clone(g.ExteriorRing(shell.geom))
|
||||
g.Destroy(shell.geom)
|
||||
if exterior == nil {
|
||||
|
@ -198,10 +199,20 @@ func BuildRelGeometry(rel *element.Relation, rings []*Ring, srid int) (*geos.Geo
|
|||
|
||||
g.DestroyLater(result)
|
||||
|
||||
insertedWays := make(map[int64]bool)
|
||||
for _, r := range rings {
|
||||
for id, _ := range r.inserted {
|
||||
insertedWays[id] = true
|
||||
outer := make(map[int64]struct{})
|
||||
for i := range rings {
|
||||
if rings[i].outer {
|
||||
for _, w := range rings[i].ways {
|
||||
outer[w.Id] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range rel.Members {
|
||||
mid := rel.Members[i].Id
|
||||
if _, ok := outer[mid]; ok {
|
||||
rel.Members[i].Role = "outer"
|
||||
} else {
|
||||
rel.Members[i].Role = "inner"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
35
geom/ring.go
35
geom/ring.go
|
@ -13,6 +13,7 @@ type Ring struct {
|
|||
holes map[*Ring]bool
|
||||
containedBy int
|
||||
area float64
|
||||
outer bool
|
||||
inserted map[int64]bool
|
||||
}
|
||||
|
||||
|
@ -24,40 +25,6 @@ func (r *Ring) TryClose(maxRingGap float64) bool {
|
|||
return element.TryCloseWay(r.refs, r.nodes, maxRingGap)
|
||||
}
|
||||
|
||||
func (r *Ring) MarkInserted(tags element.Tags) {
|
||||
if r.inserted == nil {
|
||||
r.inserted = make(map[int64]bool)
|
||||
}
|
||||
for _, w := range r.ways {
|
||||
if tagsSameOrEmpty(tags, w.Tags) {
|
||||
r.inserted[w.Id] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func tagsSameOrEmpty(a, b element.Tags) bool {
|
||||
if len(b) == 0 {
|
||||
return true
|
||||
}
|
||||
for k, v := range a {
|
||||
if k == "name" {
|
||||
continue
|
||||
}
|
||||
if cmp, ok := b[k]; !ok || v != cmp {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for k, v := range b {
|
||||
if k == "name" {
|
||||
continue
|
||||
}
|
||||
if cmp, ok := a[k]; !ok || v != cmp {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func NewRing(way *element.Way) *Ring {
|
||||
ring := Ring{}
|
||||
ring.ways = []*element.Way{way}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package geom
|
||||
|
||||
import (
|
||||
"github.com/omniscale/imposm3/element"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/omniscale/imposm3/element"
|
||||
)
|
||||
|
||||
func TestRingMerge(t *testing.T) {
|
||||
|
@ -210,42 +211,3 @@ func TestRingMergePermutations(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTagsSameOrEmpty(t *testing.T) {
|
||||
var a, b element.Tags
|
||||
|
||||
a = element.Tags{"natural": "water"}
|
||||
b = element.Tags{"natural": "water"}
|
||||
if tagsSameOrEmpty(a, b) != true {
|
||||
t.Fatal(a, b)
|
||||
}
|
||||
|
||||
a = element.Tags{"natural": "water"}
|
||||
b = element.Tags{"natural": "water", "name": "bar"}
|
||||
if tagsSameOrEmpty(a, b) != true {
|
||||
t.Fatal(a, b)
|
||||
}
|
||||
if tagsSameOrEmpty(b, a) != true {
|
||||
t.Fatal(a, b)
|
||||
}
|
||||
|
||||
a = element.Tags{"natural": "water"}
|
||||
b = element.Tags{}
|
||||
if tagsSameOrEmpty(a, b) != true {
|
||||
t.Fatal(a, b)
|
||||
}
|
||||
|
||||
if tagsSameOrEmpty(b, a) != false {
|
||||
t.Fatal(a, b)
|
||||
}
|
||||
|
||||
a = element.Tags{"natural": "water"}
|
||||
b = element.Tags{"landusage": "forest", "natural": "water"}
|
||||
if tagsSameOrEmpty(a, b) != false {
|
||||
t.Fatal(a, b)
|
||||
}
|
||||
if tagsSameOrEmpty(b, a) != false {
|
||||
t.Fatal(a, b)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package mapping
|
||||
|
||||
import (
|
||||
"github.com/omniscale/imposm3/element"
|
||||
)
|
||||
import "github.com/omniscale/imposm3/element"
|
||||
|
||||
func (m *Mapping) PointMatcher() NodeMatcher {
|
||||
mappings := make(TagTables)
|
||||
|
@ -124,7 +122,9 @@ func (tm *tagMatcher) match(tags *element.Tags) []Match {
|
|||
}
|
||||
|
||||
// SelectRelationPolygons returns a slice of all members that are already
|
||||
// imported with a relation with tags.
|
||||
// imported as part of the relation.
|
||||
// Outer members are "imported" if they share the same destination table. Inner members
|
||||
// are "imported" when they also share the same key/value.
|
||||
func SelectRelationPolygons(polygonTagMatcher RelWayMatcher, rel *element.Relation) []element.Member {
|
||||
relMatches := polygonTagMatcher.MatchRelation(rel)
|
||||
result := []element.Member{}
|
||||
|
@ -133,13 +133,16 @@ func SelectRelationPolygons(polygonTagMatcher RelWayMatcher, rel *element.Relati
|
|||
continue
|
||||
}
|
||||
memberMatches := polygonTagMatcher.MatchWay(m.Way)
|
||||
if matchEquals(relMatches, memberMatches) {
|
||||
if m.Role == "outer" && dstEquals(relMatches, memberMatches) {
|
||||
result = append(result, m)
|
||||
} else if matchEquals(relMatches, memberMatches) {
|
||||
result = append(result, m)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// matchEquals returns true if both matches share key/value and table
|
||||
func matchEquals(matchesA, matchesB []Match) bool {
|
||||
for _, matchA := range matchesA {
|
||||
for _, matchB := range matchesB {
|
||||
|
@ -152,3 +155,15 @@ func matchEquals(matchesA, matchesB []Match) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// dstEquals returns true if both matches share a single destination table
|
||||
func dstEquals(matchesA, matchesB []Match) bool {
|
||||
for _, matchA := range matchesA {
|
||||
for _, matchB := range matchesB {
|
||||
if matchA.Table == matchB.Table {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -26,8 +26,15 @@ func makeMember(id int64, tags element.Tags) element.Member {
|
|||
element.OSMElem{id, tags, nil},
|
||||
[]int64{0, 1, 2, 0}, // fake closed way, req. for SelectRelationPolygons
|
||||
nil}
|
||||
return element.Member{Id: id, Type: element.WAY, Role: "outer", Way: way}
|
||||
return element.Member{Id: id, Type: element.WAY, Role: "", Way: way}
|
||||
}
|
||||
|
||||
func makeMemberRole(id int64, tags element.Tags, role string) element.Member {
|
||||
way := &element.Way{
|
||||
element.OSMElem{id, tags, nil},
|
||||
[]int64{0, 1, 2, 0}, // fake closed way, req. for SelectRelationPolygons
|
||||
nil}
|
||||
return element.Member{Id: id, Type: element.WAY, Role: role, Way: way}
|
||||
}
|
||||
|
||||
func TestSelectRelationPolygonsSimple(t *testing.T) {
|
||||
|
@ -125,3 +132,28 @@ func TestSelectRelationPolygonsMultipleTags(t *testing.T) {
|
|||
t.Fatal(filtered)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelectRelationPolygonsMultipleTagsOnWay(t *testing.T) {
|
||||
mapping, err := NewMapping("test_mapping.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
r := element.Relation{}
|
||||
r.Tags = element.Tags{"waterway": "riverbank"}
|
||||
r.Members = []element.Member{
|
||||
makeMemberRole(0, element.Tags{"waterway": "riverbank", "natural": "water"}, "outer"),
|
||||
makeMemberRole(1, element.Tags{"natural": "water"}, "inner"),
|
||||
makeMemberRole(2, element.Tags{"place": "islet"}, "inner"),
|
||||
}
|
||||
filtered := SelectRelationPolygons(
|
||||
mapping.PolygonMatcher(),
|
||||
&r,
|
||||
)
|
||||
|
||||
if len(filtered) != 1 {
|
||||
t.Fatal(filtered)
|
||||
}
|
||||
if filtered[0].Id != 0 {
|
||||
t.Fatal(filtered)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,6 +158,7 @@
|
|||
],
|
||||
"natural": [
|
||||
"wood",
|
||||
"water",
|
||||
"land",
|
||||
"scrub",
|
||||
"wetland",
|
||||
|
@ -201,6 +202,9 @@
|
|||
"highway": [
|
||||
"pedestrian",
|
||||
"footway"
|
||||
],
|
||||
"waterway": [
|
||||
"riverbank"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -857,54 +861,6 @@
|
|||
"__any__"
|
||||
]
|
||||
}
|
||||
},
|
||||
"waterareas": {
|
||||
"fields": [
|
||||
{
|
||||
"type": "id",
|
||||
"name": "osm_id",
|
||||
"key": null
|
||||
},
|
||||
{
|
||||
"type": "geometry",
|
||||
"name": "geometry",
|
||||
"key": null
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "name",
|
||||
"key": "name"
|
||||
},
|
||||
{
|
||||
"type": "mapping_value",
|
||||
"name": "type",
|
||||
"key": null
|
||||
},
|
||||
{
|
||||
"type": "pseudoarea",
|
||||
"name": "area",
|
||||
"key": null
|
||||
}
|
||||
],
|
||||
"type": "polygon",
|
||||
"mapping": {
|
||||
"waterway": [
|
||||
"riverbank"
|
||||
],
|
||||
"landuse": [
|
||||
"basin",
|
||||
"reservoir"
|
||||
],
|
||||
"natural": [
|
||||
"water"
|
||||
],
|
||||
"amenity": [
|
||||
"swimming_pool"
|
||||
],
|
||||
"leisure": [
|
||||
"swimming_pool"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -785,6 +785,79 @@
|
|||
<tag k="railway" v="tram"/>
|
||||
</way>
|
||||
|
||||
<!-- test outer way not inserted (different tag but same table) -->
|
||||
<node id="19001" version="1" timestamp="2011-11-11T00:11:11Z" lat="21" lon="89"/>
|
||||
<node id="19002" version="1" timestamp="2011-11-11T00:11:11Z" lat="21" lon="90"/>
|
||||
<node id="19003" version="1" timestamp="2011-11-11T00:11:11Z" lat="24" lon="90"/>
|
||||
<node id="19004" version="1" timestamp="2011-11-11T00:11:11Z" lat="24" lon="89"/>
|
||||
<node id="19011" version="1" timestamp="2011-11-11T00:11:11Z" lat="21.8" lon="89.8"/>
|
||||
<node id="19012" version="1" timestamp="2011-11-11T00:11:11Z" lat="21.8" lon="89.9"/>
|
||||
<node id="19013" version="1" timestamp="2011-11-11T00:11:11Z" lat="23.9" lon="89.9"/>
|
||||
<node id="19014" version="1" timestamp="2011-11-11T00:11:11Z" lat="23.9" lon="89.8"/>
|
||||
|
||||
<way id="19001" version="1" timestamp="2011-11-11T00:11:11Z">
|
||||
<nd ref="19001"/>
|
||||
<nd ref="19002"/>
|
||||
<nd ref="19003"/>
|
||||
<nd ref="19004"/>
|
||||
<nd ref="19001"/>
|
||||
<tag k="name" v="farm"/>
|
||||
<tag k="landuse" v="farm"/>
|
||||
</way>
|
||||
<way id="19002" version="1" timestamp="2011-11-11T00:11:11Z">
|
||||
<nd ref="19011"/>
|
||||
<nd ref="19012"/>
|
||||
<nd ref="19013"/>
|
||||
<nd ref="19014"/>
|
||||
<nd ref="19011"/>
|
||||
<tag k="name" v="farmyard"/>
|
||||
<tag k="landuse" v="farmyard"/>
|
||||
</way>
|
||||
|
||||
<relation id="19001" version="1" timestamp="2011-11-11T00:11:11Z">
|
||||
<member type="way" ref="19001" role="outer"/>
|
||||
<member type="way" ref="19002" role="inner"/>
|
||||
<tag k="type" v="multipolygon"/>
|
||||
<tag k="landuse" v="farmland"/>
|
||||
</relation>
|
||||
|
||||
<!-- test outer way inserted (different tag and table) -->
|
||||
<node id="19101" version="1" timestamp="2011-11-11T00:11:11Z" lat="31" lon="89"/>
|
||||
<node id="19102" version="1" timestamp="2011-11-11T00:11:11Z" lat="31" lon="90"/>
|
||||
<node id="19103" version="1" timestamp="2011-11-11T00:11:11Z" lat="34" lon="90"/>
|
||||
<node id="19104" version="1" timestamp="2011-11-11T00:11:11Z" lat="34" lon="89"/>
|
||||
<node id="19111" version="1" timestamp="2011-11-11T00:11:11Z" lat="31.8" lon="89.8"/>
|
||||
<node id="19112" version="1" timestamp="2011-11-11T00:11:11Z" lat="31.8" lon="89.9"/>
|
||||
<node id="19113" version="1" timestamp="2011-11-11T00:11:11Z" lat="33.9" lon="89.9"/>
|
||||
<node id="19114" version="1" timestamp="2011-11-11T00:11:11Z" lat="33.9" lon="89.8"/>
|
||||
|
||||
<way id="19101" version="1" timestamp="2011-11-11T00:11:11Z">
|
||||
<nd ref="19101"/>
|
||||
<nd ref="19102"/>
|
||||
<nd ref="19103"/>
|
||||
<nd ref="19104"/>
|
||||
<nd ref="19101"/>
|
||||
<tag k="name" v="farm"/>
|
||||
<tag k="landuse" v="farm"/>
|
||||
</way>
|
||||
<way id="19102" version="1" timestamp="2011-11-11T00:11:11Z">
|
||||
<nd ref="19111"/>
|
||||
<nd ref="19112"/>
|
||||
<nd ref="19113"/>
|
||||
<nd ref="19114"/>
|
||||
<nd ref="19111"/>
|
||||
<tag k="name" v="farmyard"/>
|
||||
<tag k="landuse" v="farmyard"/>
|
||||
</way>
|
||||
|
||||
<relation id="19101" version="1" timestamp="2011-11-11T00:11:11Z">
|
||||
<member type="way" ref="19101" role="outer"/>
|
||||
<member type="way" ref="19102" role="outer"/>
|
||||
<tag k="type" v="multipolygon"/>
|
||||
<tag k="boundary" v="administrative"/>
|
||||
</relation>
|
||||
|
||||
|
||||
<!-- test that node (n:20001) does not reference way (w:20001) after it was deleted -->
|
||||
<node id="20001" version="1" timestamp="2011-11-11T00:11:11Z" lat="30" lon="10">
|
||||
<tag k="name" v="way 14001"/>
|
||||
|
|
|
@ -124,6 +124,27 @@ def test_node_way_inserted_twice():
|
|||
assert rows[0]['type'] == 'residential'
|
||||
assert rows[1]['type'] == 'tram'
|
||||
|
||||
def test_outer_way_not_inserted():
|
||||
"""Outer way with different tag is not inserted twice into same table"""
|
||||
farm = t.query_row(t.db_conf, 'osm_landusages', -19001)
|
||||
assert farm['type'] == 'farmland'
|
||||
assert not t.query_row(t.db_conf, 'osm_landusages', 19001)
|
||||
|
||||
farmyard = t.query_row(t.db_conf, 'osm_landusages', 19002)
|
||||
assert farmyard['type'] == 'farmyard'
|
||||
|
||||
def test_outer_way_inserted():
|
||||
"""Outer way with different tag is inserted twice into different table"""
|
||||
farm = t.query_row(t.db_conf, 'osm_landusages', 19101)
|
||||
assert farm['type'] == 'farm'
|
||||
assert not t.query_row(t.db_conf, 'osm_landusages', -19101)
|
||||
|
||||
farmyard = t.query_row(t.db_conf, 'osm_landusages', 19102)
|
||||
assert farmyard['type'] == 'farmyard'
|
||||
|
||||
admin = t.query_row(t.db_conf, 'osm_admin', -19101)
|
||||
assert admin['type'] == 'administrative'
|
||||
|
||||
def test_node_way_ref_after_delete_1():
|
||||
"""Nodes refereces way"""
|
||||
data = t.cache_query(nodes=[20001, 20002], deps=True)
|
||||
|
@ -159,6 +180,7 @@ def test_relation_ways_inserted():
|
|||
park = t.query_row(t.db_conf, 'osm_landusages', -9201)
|
||||
assert park['type'] == 'park'
|
||||
assert park['name'] == '9209'
|
||||
assert not t.query_row(t.db_conf, 'osm_landusages', 9201)
|
||||
|
||||
# outer ways of multipolygon stand for their own
|
||||
road = t.query_row(t.db_conf, 'osm_roads', 9209)
|
||||
|
|
Loading…
Reference in New Issue