remove duplicate nodes
prevents inserts of broken ways with only two identical nodesmaster
parent
c49bf4c5b9
commit
a48cd0317d
42
geom/geom.go
42
geom/geom.go
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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"/>
|
||||
|
|
Loading…
Reference in New Issue