close open ways when end-nodes are close to each other but not identical
parent
e440ca7489
commit
0a311d62d7
|
@ -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 (
|
||||
|
|
16
geom/ring.go
16
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) {
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue