remove duplicate nodes

prevents inserts of broken ways with only two identical nodes
master
Oliver Tonnhofer 2013-09-11 11:35:11 +02:00
parent c49bf4c5b9
commit a48cd0317d
4 changed files with 162 additions and 0 deletions

View File

@ -4,6 +4,7 @@ import (
"errors"
"imposm3/element"
"imposm3/geom/geos"
"math"
)
type GeomError struct {
@ -37,7 +38,43 @@ func Point(g *geos.Geos, node element.Node) (*geos.Geom, error) {
return geom, nil
}
func nodesEqual(a, b element.Node) bool {
if d := a.Long - b.Long; math.Abs(d) < 1e-9 {
if d := a.Lat - b.Lat; math.Abs(d) < 1e-9 {
return true
}
}
return false
}
func unduplicateNodes(nodes []element.Node) []element.Node {
if len(nodes) < 2 {
return nodes
}
foundDup := false
for i := 1; i < len(nodes); i++ {
if nodesEqual(nodes[i-1], nodes[i]) {
foundDup = true
break
}
}
if !foundDup {
return nodes
}
result := make([]element.Node, 0, len(nodes))
result = append(result, nodes[0])
for i := 1; i < len(nodes); i++ {
if nodesEqual(nodes[i-1], nodes[i]) {
continue
}
result = append(result, nodes[i])
}
return result
}
func LineString(g *geos.Geos, nodes []element.Node) (*geos.Geom, error) {
nodes = unduplicateNodes(nodes)
if len(nodes) < 2 {
return nil, ErrorOneNodeWay
}
@ -60,6 +97,11 @@ func LineString(g *geos.Geos, nodes []element.Node) (*geos.Geom, error) {
}
func Polygon(g *geos.Geos, nodes []element.Node) (*geos.Geom, error) {
nodes = unduplicateNodes(nodes)
if len(nodes) < 4 {
return nil, ErrorNoRing
}
coordSeq, err := g.CreateCoordSeq(uint32(len(nodes)), 2)
if err != nil {
return nil, err

View File

@ -90,3 +90,57 @@ func BenchmarkLineString(b *testing.B) {
LineString(g, nodes)
}
}
func TestUnduplicateNodes(t *testing.T) {
var nodes []element.Node
nodes = []element.Node{
element.Node{Lat: 0, Long: 0},
}
if res := unduplicateNodes(nodes); len(res) != 1 {
t.Fatal(res)
}
nodes = []element.Node{
element.Node{Lat: 47.0, Long: 80.0},
element.Node{Lat: 47.0, Long: 80.0},
}
if res := unduplicateNodes(nodes); len(res) != 1 {
t.Fatal(res)
}
nodes = []element.Node{
element.Node{Lat: 0, Long: -10},
element.Node{Lat: 0, Long: -10},
element.Node{Lat: 0, Long: -10},
element.Node{Lat: 10, Long: 10},
element.Node{Lat: 10, Long: 10},
element.Node{Lat: 10, Long: 10},
}
if res := unduplicateNodes(nodes); len(res) != 2 {
t.Fatal(res)
}
nodes = []element.Node{
element.Node{Lat: 10, Long: 10},
element.Node{Lat: 0, Long: 10},
element.Node{Lat: 10, Long: 10},
element.Node{Lat: 10, Long: 10},
element.Node{Lat: 0, Long: 10},
element.Node{Lat: 0, Long: 10},
}
if res := unduplicateNodes(nodes); len(res) != 4 {
t.Fatal(res)
}
nodes = []element.Node{
element.Node{Lat: 0, Long: 0},
element.Node{Lat: 0, Long: -10},
element.Node{Lat: 10, Long: -10},
element.Node{Lat: 10, Long: 0},
element.Node{Lat: 0, Long: 0},
}
if res := unduplicateNodes(nodes); len(res) != 5 {
t.Fatal(res)
}
}

View File

@ -230,6 +230,23 @@ def test_relation_way_inserted():
assert park['name'] == 'rel 8001'
assert query_row(db_conf, 'osm_roads', 8009)["type"] == 'residential'
def test_single_node_ways_not_inserted():
"""Ways with single/duplicate nodes are not inserted."""
assert not query_row(db_conf, 'osm_roads', 30001)
assert not query_row(db_conf, 'osm_roads', 30002)
assert not query_row(db_conf, 'osm_roads', 30003)
def test_polygon_with_duplicate_nodes_is_valid():
"""Polygon with duplicate nodes is valid."""
geom = query_row(db_conf, 'osm_landusages', 30005)['geometry']
assert geom.is_valid
assert len(geom.exterior.coords) == 4
def test_incomplete_polygons():
"""Non-closed/incomplete polygons are not inserted."""
assert not query_row(db_conf, 'osm_landusages', 30004)
assert not query_row(db_conf, 'osm_landusages', 30006)
#######################################################################
def test_update():

View File

@ -302,6 +302,55 @@
<tag k="type" v="multipolygon"/>
</relation>
<!-- 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"/>
<node id="30003" version="1" timestamp="2011-11-11T00:11:11Z" lat="47" lon="81"/>
<node id="30004" version="1" timestamp="2011-11-11T00:11:11Z" lat="48" lon="80.5"/>
<!-- single node way -->
<way id="30001" version="1" timestamp="2011-11-11T00:11:11Z">
<nd ref="30001"/>
<tag k="highway" v="residential"/>
</way>
<!-- duplicate node way -->
<way id="30002" version="1" timestamp="2011-11-11T00:11:11Z">
<nd ref="30001"/>
<nd ref="30001"/>
<tag k="highway" v="residential"/>
</way>
<!-- same coordinates way -->
<way id="30003" version="1" timestamp="2011-11-11T00:11:11Z">
<nd ref="30001"/>
<nd ref="30002"/>
<tag k="highway" v="residential"/>
</way>
<!-- polygon not closed -->
<way id="30004" version="1" timestamp="2011-11-11T00:11:11Z">
<nd ref="30002"/>
<nd ref="30003"/>
<nd ref="30004"/>
<tag k="landuse" v="park"/>
</way>
<!-- polygon closed with duplicated -->
<way id="30005" version="1" timestamp="2011-11-11T00:11:11Z">
<nd ref="30002"/>
<nd ref="30002"/>
<nd ref="30003"/>
<nd ref="30004"/>
<nd ref="30004"/>
<nd ref="30002"/>
<tag k="landuse" v="park"/>
</way>
<!-- polygon non-closed with duplicate nodes -->
<way id="30006" version="1" timestamp="2011-11-11T00:11:11Z">
<nd ref="30001"/>
<nd ref="30002"/>
<nd ref="30003"/>
<nd ref="30001"/>
<tag k="landuse" v="park"/>
</way>
<!-- test multipolygon way was _not_ inserted -->
<node id="9001" version="1" timestamp="2011-11-11T00:11:11Z" lat="47" lon="80"/>
<node id="9002" version="1" timestamp="2011-11-11T00:11:11Z" lat="47" lon="82"/>