Mercurial > gemma
view pkg/octree/contours.go @ 3678:8f58851927c0
client: make layer factory only return new layer config for individual maps
instead of each time it is invoked. The purpose of the factory was to support multiple maps with individual layers.
But returning a new config each time it is invoked leads to bugs that rely on the layer's state. Now this factory
reuses the same objects it created before, per map.
author | Markus Kottlaender <markus@intevation.de> |
---|---|
date | Mon, 17 Jun 2019 17:31:35 +0200 |
parents | 647a58ee9ae9 |
children | 631f5eaf29de |
line wrap: on
line source
// This is Free Software under GNU Affero General Public License v >= 3.0 // without warranty, see README.md and license for details. // // SPDX-License-Identifier: AGPL-3.0-or-later // License-Filename: LICENSES/AGPL-3.0.txt // // Copyright (C) 2018 by via donau // – Österreichische Wasserstraßen-Gesellschaft mbH // Software engineering by Intevation GmbH // // Author(s): // * Sascha L. Teichmann <sascha.teichmann@intevation.de> // * Tom Gottfried <tom.gottfried@intevation.de> package octree import ( "runtime" "sync" ) func SampleDiffHeights(min, max, step float64) []float64 { var heights []float64 switch { case min >= 0: // All values positive. for v := 0.0; v <= max; v += step { if v >= min { heights = append(heights, v) } } case max <= 0: // All values negative. for v := 0.0; v >= min; v -= step { if v <= max { heights = append(heights, v) } } default: // Positive and negative. for v := step; v <= max; v += step { heights = append(heights, v) } for i, j := 0, len(heights)-1; i < j; i, j = i+1, j-1 { heights[i], heights[j] = heights[j], heights[i] } for v := 0.0; v >= min; v -= step { heights = append(heights, v) } } return heights } // ContourResult stores an calculated iso line for a given height. // Is used as a future variable in the concurrent iso line calculation. type ContourResult struct { Height float64 Lines MultiLineStringZ done bool mu sync.Mutex cond *sync.Cond } // NewContourResult prepares a future variable to later hold // the result of the iso line calculation. func NewContourResult(height float64) *ContourResult { cr := ContourResult{Height: height} cr.cond = sync.NewCond(&cr.mu) return &cr } func (cr *ContourResult) wait() { cr.cond.L.Lock() for !cr.done { cr.cond.Wait() } cr.cond.L.Unlock() } func (cr *ContourResult) get() float64 { cr.cond.L.Lock() defer cr.cond.L.Unlock() return cr.Height } func (cr *ContourResult) set(lines MultiLineStringZ) { cr.cond.L.Lock() defer cr.cond.L.Unlock() cr.Lines = lines cr.done = true cr.cond.Signal() } // DoContours calculates the iso line for the given heights. // This is done concurrently. // It is guaranteed that the results are given to the store // function in order of the original heights values. func DoContours(tree *Tree, heights []float64, store func(*ContourResult)) { if len(heights) == 0 { return } contours := make([]*ContourResult, len(heights)) for i, h := range heights { contours[i] = NewContourResult(h) } jobs := make(chan *ContourResult) var wg sync.WaitGroup for i, n := 0, runtime.NumCPU(); i < n; i++ { wg.Add(1) go processLevels(tree, jobs, &wg) } done := make(chan struct{}) go func() { defer close(done) for _, cr := range contours { cr.wait() store(cr) } }() for _, cr := range contours { jobs <- cr } close(jobs) wg.Wait() <-done } func processLevels( tree *Tree, jobs <-chan *ContourResult, wg *sync.WaitGroup, ) { defer wg.Done() for cr := range jobs { var lines MultiLineStringZ h := cr.get() tree.Horizontal(h, func(t *Triangle) { line := t.IntersectHorizontal(h) if len(line) > 1 { lines = append(lines, line) } }) cr.set(lines.Merge()) } }