From 0a311d62d7f70859b631287a6790d3e96816bbc8 Mon Sep 17 00:00:00 2001 From: Oliver Tonnhofer Date: Wed, 22 Oct 2014 13:36:06 +0200 Subject: [PATCH] close open ways when end-nodes are close to each other but not identical --- element/element.go | 21 +++++++++++++++++++++ geom/ring.go | 16 +--------------- test/complete_db.osm | 11 ++++++++++- test/complete_db_test.py | 7 +++++-- writer/relations.go | 10 +++++----- writer/ways.go | 8 +++++++- 6 files changed, 49 insertions(+), 24 deletions(-) diff --git a/element/element.go b/element/element.go index fcc6ce2..f3efa81 100644 --- a/element/element.go +++ b/element/element.go @@ -2,6 +2,7 @@ package element import ( "fmt" + "math" "sort" "github.com/omniscale/imposm3/geom/geos" @@ -40,6 +41,26 @@ func (w *Way) IsClosed() bool { return len(w.Refs) >= 4 && w.Refs[0] == w.Refs[len(w.Refs)-1] } +func (w *Way) TryClose(maxGap float64) bool { + return TryCloseWay(w.Refs, w.Nodes, maxGap) +} + +// TryCloseWay closes the way if both end nodes are nearly identical. +// Returns true if it succeeds. +func TryCloseWay(refs []int64, nodes []Node, maxGap float64) bool { + if len(refs) < 4 { + return false + } + start, end := nodes[0], nodes[len(nodes)-1] + dist := math.Hypot(start.Lat-end.Lat, start.Long-end.Long) + if dist < maxGap { + refs[len(refs)-1] = refs[0] + nodes[len(nodes)-1] = nodes[0] + return true + } + return false +} + type MemberType int const ( diff --git a/geom/ring.go b/geom/ring.go index 0889aa6..2de5048 100644 --- a/geom/ring.go +++ b/geom/ring.go @@ -1,8 +1,6 @@ package geom import ( - "math" - "github.com/omniscale/imposm3/element" "github.com/omniscale/imposm3/geom/geos" ) @@ -22,20 +20,8 @@ func (r *Ring) IsClosed() bool { return len(r.refs) >= 4 && r.refs[0] == r.refs[len(r.refs)-1] } -// TryClose closes the ring if both end nodes are nearly identical. -// Returns true if it succeeds. func (r *Ring) TryClose(maxRingGap float64) bool { - if len(r.refs) < 4 { - return false - } - start, end := r.nodes[0], r.nodes[len(r.nodes)-1] - dist := math.Hypot(start.Lat-end.Lat, start.Long-end.Long) - if dist < maxRingGap { - r.refs[len(r.refs)-1] = r.refs[0] - r.nodes[len(r.nodes)-1] = r.nodes[0] - return true - } - return false + return element.TryCloseWay(r.refs, r.nodes, maxRingGap) } func (r *Ring) MarkInserted(tags element.Tags) { diff --git a/test/complete_db.osm b/test/complete_db.osm index b6cd142..4ebfd11 100644 --- a/test/complete_db.osm +++ b/test/complete_db.osm @@ -353,7 +353,7 @@ - + @@ -377,6 +377,15 @@ + + + + + + + + + diff --git a/test/complete_db_test.py b/test/complete_db_test.py index 9db33c0..be60f9e 100644 --- a/test/complete_db_test.py +++ b/test/complete_db_test.py @@ -254,11 +254,14 @@ def test_generalized_linestring_is_valid(): assert road['geometry'].is_valid, road['geometry'].wkt assert road['geometry'].length > 1000000 -def test_relation_with_gap(): - """Multipolygon with gap (overlapping but different endpoints) gets closed""" +def test_ring_with_gap(): + """Multipolygon and way with gap (overlapping but different endpoints) gets closed""" park = t.query_row(t.db_conf, 'osm_landusages', -7301) assert park['geometry'].is_valid, park + park = t.query_row(t.db_conf, 'osm_landusages', 7311) + assert park['geometry'].is_valid, park + def test_updated_nodes1(): """Zig-Zag line is inserted.""" road = t.query_row(t.db_conf, 'osm_roads', 60000) diff --git a/writer/relations.go b/writer/relations.go index 9262b18..1c5b5fb 100644 --- a/writer/relations.go +++ b/writer/relations.go @@ -19,7 +19,7 @@ type RelationWriter struct { singleIdSpace bool rel chan *element.Relation polygonMatcher mapping.RelWayMatcher - maxRingGap float64 + maxGap float64 } func NewRelationWriter( @@ -32,9 +32,9 @@ func NewRelationWriter( matcher mapping.RelWayMatcher, srid int, ) *OsmElemWriter { - maxRingGap := 1e-1 // 0.1m + maxGap := 1e-1 // 0.1m if srid == 4326 { - maxRingGap = 1e-6 // ~0.1m + maxGap = 1e-6 // ~0.1m } rw := RelationWriter{ OsmElemWriter: OsmElemWriter{ @@ -48,7 +48,7 @@ func NewRelationWriter( singleIdSpace: singleIdSpace, polygonMatcher: matcher, rel: rel, - maxRingGap: maxRingGap, + maxGap: maxGap, } rw.OsmElemWriter.writer = &rw return &rw.OsmElemWriter @@ -96,7 +96,7 @@ NextRel: // prepare relation first (build rings and compute actual // relation tags) - prepedRel, err := geom.PrepareRelation(r, rw.srid, rw.maxRingGap) + prepedRel, err := geom.PrepareRelation(r, rw.srid, rw.maxGap) if err != nil { if errl, ok := err.(ErrorLevel); !ok || errl.Level() > 0 { log.Warn(err) diff --git a/writer/ways.go b/writer/ways.go index 099c577..5644d68 100644 --- a/writer/ways.go +++ b/writer/ways.go @@ -19,6 +19,7 @@ type WayWriter struct { ways chan *element.Way lineMatcher mapping.WayMatcher polygonMatcher mapping.WayMatcher + maxGap float64 } func NewWayWriter( @@ -32,6 +33,10 @@ func NewWayWriter( lineMatcher mapping.WayMatcher, srid int, ) *OsmElemWriter { + maxGap := 1e-1 // 0.1m + if srid == 4326 { + maxGap = 1e-6 // ~0.1m + } ww := WayWriter{ OsmElemWriter: OsmElemWriter{ osmCache: osmCache, @@ -45,6 +50,7 @@ func NewWayWriter( lineMatcher: lineMatcher, polygonMatcher: polygonMatcher, ways: ways, + maxGap: maxGap, } ww.OsmElemWriter.writer = &ww return &ww.OsmElemWriter @@ -91,7 +97,7 @@ func (ww *WayWriter) loop() { } inserted = true } - if w.IsClosed() && !insertedAsRelation { + if !insertedAsRelation && (w.IsClosed() || w.TryClose(ww.maxGap)) { // only add polygons that were not inserted as a MultiPolygon relation if matches := ww.polygonMatcher.MatchWay(w); len(matches) > 0 { err := ww.buildAndInsert(geos, w, matches, true)