changeset 932:ae1531e00344

Merged line merging from geo-style branch into default (where it belongs).
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Mon, 08 Oct 2018 14:52:37 +0200
parents 71445b091415
children 7899867c7bf5 e6220a19f284
files cmd/octree2contour/main.go pkg/octree/vertex.go
diffstat 2 files changed, 119 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/cmd/octree2contour/main.go	Mon Oct 08 14:41:56 2018 +0200
+++ b/cmd/octree2contour/main.go	Mon Oct 08 14:52:37 2018 +0200
@@ -35,6 +35,7 @@
 				lines = append(lines, line)
 			}
 		})
+		lines = lines.Merge()
 		results <- result{h, lines}
 	}
 }
@@ -49,6 +50,7 @@
 				lines = append(lines, line)
 			}
 		})
+		lines = lines.Merge()
 		return []result{{*step, lines}}
 	}
 
--- a/pkg/octree/vertex.go	Mon Oct 08 14:41:56 2018 +0200
+++ b/pkg/octree/vertex.go	Mon Oct 08 14:52:37 2018 +0200
@@ -349,6 +349,123 @@
 	return buf.Bytes()
 }
 
+// Join joins two lines leaving the first of the secoung out.
+func (ls LineStringZ) Join(other LineStringZ) LineStringZ {
+	nline := make(LineStringZ, len(ls)+len(other)-1)
+	copy(nline, ls)
+	copy(nline[len(ls):], other[1:])
+	return nline
+}
+
+func (mls MultiLineStringZ) Merge() MultiLineStringZ {
+
+	var out MultiLineStringZ
+
+	min := Vertex{math.MaxFloat64, math.MaxFloat64, math.MaxFloat64}
+
+	for _, line := range mls {
+		for _, v := range line {
+			min.Minimize(v)
+		}
+	}
+
+	type point struct {
+		x int64
+		y int64
+	}
+
+	const precision = 1e7
+
+	quant := func(v Vertex) point {
+		return point{
+			x: int64(math.Round((v.X - min.X) * precision)),
+			y: int64(math.Round((v.Y - min.Y) * precision)),
+		}
+	}
+
+	heads := make(map[point]*[]LineStringZ)
+
+	for _, line := range mls {
+		if len(line) < 2 {
+			out = append(out, line)
+			continue
+		}
+		head := quant(line[0])
+		tail := quant(line[len(line)-1])
+		if head == tail { // its already a ring
+			out = append(out, line)
+			continue
+		}
+
+		if hs := heads[tail]; hs != nil {
+			l := len(*hs)
+			last := (*hs)[l-1]
+			if l == 1 {
+				delete(heads, tail)
+			} else {
+				(*hs)[l-1] = nil
+				*hs = (*hs)[:l-1]
+			}
+			line = line.Join(last)
+
+			if head == quant(line[len(line)-1]) { // its a ring
+				out = append(out, line)
+				continue
+			}
+		}
+
+		if hs := heads[head]; hs != nil {
+			*hs = append(*hs, line)
+		} else {
+			heads[head] = &[]LineStringZ{line}
+		}
+	}
+
+again:
+	for head, lines := range heads {
+		for i, line := range *lines {
+			tail := quant(line[len(line)-1])
+			if hs := heads[tail]; hs != nil {
+				l := len(*hs)
+				last := (*hs)[l-1]
+				if l == 1 {
+					delete(heads, tail)
+				} else {
+					(*hs)[l-1] = nil
+					*hs = (*hs)[:l-1]
+				}
+				line = line.Join(last)
+
+				if head == quant(line[len(line)-1]) { // its a ring
+					out = append(out, line)
+					// remove from current lines
+					copy((*lines)[i:], (*lines)[i+1:])
+					(*lines)[len(*lines)-1] = nil
+					*lines = (*lines)[:len(*lines)-1]
+				} else {
+					// overwrite in current lines
+					(*lines)[i] = line
+				}
+				goto again
+			}
+		}
+	}
+
+	rings := len(out)
+
+	// The rest are open line strings.
+	for _, lines := range heads {
+		for _, line := range *lines {
+			out = append(out, line)
+		}
+	}
+
+	log.Printf("segments before/after merge: %d/%d (%d rings)\n",
+		len(mls), len(out), rings)
+
+	return out
+}
+
 func (a Box2D) Intersects(b Box2D) bool {
 	return !(a.X2 < a.X1 || a.X2 < b.X1 ||
 		a.Y2 < a.Y1 || a.Y2 < b.Y1)