Mercurial > gemma
changeset 928:0a0013fbda4a geo-style
octree2contour: Merge multi linestrings in user land before sending them to database.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Mon, 08 Oct 2018 13:09:02 +0200 |
parents | 9e210b00ace9 |
children | 688e1530f66a |
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 10:54:50 2018 +0200 +++ b/cmd/octree2contour/main.go Mon Oct 08 13:09:02 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 10:54:50 2018 +0200 +++ b/pkg/octree/vertex.go Mon Oct 08 13:09:02 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)