close open ways when end-nodes are close to each other but not identical

Oliver Tonnhofer 2014-10-22 13:36:06 +02:00
parent e440ca7489
commit 0a311d62d7
6 changed files with 49 additions and 24 deletions

View File

@ -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 (

View File

@ -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) {

View File

@ -353,7 +353,7 @@
<tag k="name" v="self-intersecting"/>
</way>
<!-- relation with "gap" (ways overlap, but are only sharing one endpoint) -->
<!-- relation/way with "gap" (ways overlap, but are only sharing one endpoint) -->
<node id="7301" version="1" timestamp="2011-11-11T00:11:11Z" lat="60" lon="60"/>
<node id="7302" version="1" timestamp="2011-11-11T00:11:11Z" lat="60" lon="62"/>
<node id="7303" version="1" timestamp="2011-11-11T00:11:11Z" lat="62" lon="62"/>
@ -377,6 +377,15 @@
<tag k="type" v="multipolygon"/>
</relation>
<way id="7311" version="1" timestamp="2011-11-11T00:11:11Z">
<nd ref="7313"/> <!-- same coord as 7303 -->
<nd ref="7314"/>
<nd ref="7301"/>
<nd ref="7302"/>
<nd ref="7303"/>
<tag k="landuse" v="park"/>
</way>
<!-- test that single node ways or incomplete polygons are _not_ inserted -->
<node id="30001" version="1" timestamp="2011-11-11T00:11:11Z" lat="47" lon="80"/>
<node id="30002" version="1" timestamp="2011-11-11T00:11:11Z" lat="47" lon="80"/>

View File

@ -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)

View File

@ -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)

View File

@ -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)