Mercurial > gemma
diff pkg/octree/areas.go @ 4768:a2f16bbcc846 direct-diff
Morph differences: Directly raster A and subtract B as a raster.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Mon, 21 Oct 2019 02:01:56 +0200 |
parents | 5c80a33edd44 |
children |
line wrap: on
line diff
--- a/pkg/octree/areas.go Sat Oct 19 23:07:59 2019 +0200 +++ b/pkg/octree/areas.go Mon Oct 21 02:01:56 2019 +0200 @@ -14,16 +14,10 @@ package octree import ( - "log" - "math" "runtime" "sync" - "time" - - "github.com/fogleman/contourmap" "gemma.intevation.de/gemma/pkg/common" - "gemma.intevation.de/gemma/pkg/wkb" ) func GenerateRandomVertices( @@ -92,300 +86,3 @@ close(out) <-done } - -func TraceAreas( - heights []float64, - cellSize float64, - min, max Vertex, - eval func(float64, float64) (float64, bool), -) []wkb.MultiPolygonGeom { - - width := max.X - min.X - height := max.Y - min.Y - - log.Printf("info: Width/Height: %.2f / %.2f\n", width, height) - - xcells := int(math.Ceil(width / cellSize)) - ycells := int(math.Ceil(height / cellSize)) - - log.Printf("info: Raster size: (%d, %d)\n", xcells, ycells) - - start := time.Now() - - // Add border for closing - raster := make([]float64, (xcells+2)*(ycells+2)) - - // prefill for no data - const nodata = -math.MaxFloat64 - for i := range raster { - raster[i] = nodata - } - - // rasterize the height model - - var wg sync.WaitGroup - - rows := make(chan int) - - rasterRow := func() { - defer wg.Done() - quat := 0.25 * cellSize - for i := range rows { - pos := (i+1)*(xcells+2) + 1 - row := raster[pos : pos+xcells] - py := min.Y + float64(i)*cellSize + cellSize/2 - px := min.X + cellSize/2 - y1 := py - quat - y2 := py + quat - for j := range row { - var n int - var sum float64 - - if v, ok := eval(px-quat, y1); ok { - sum = v - n = 1 - } - if v, ok := eval(px-quat, y2); ok { - sum += v - n++ - } - if v, ok := eval(px+quat, y1); ok { - sum += v - n++ - } - if v, ok := eval(px+quat, y2); ok { - sum += v - n++ - } - - if n > 0 { - row[j] = sum / float64(n) - } - px += cellSize - } - } - } - - for n := runtime.NumCPU(); n >= 1; n-- { - wg.Add(1) - go rasterRow() - } - - for i := 0; i < ycells; i++ { - rows <- i - } - close(rows) - - wg.Wait() - log.Printf("info: Rastering took %v\n", time.Since(start)) - - start = time.Now() - - tracer := contourmap.FromFloat64s(xcells+2, ycells+2, raster) - - areas := make([]wkb.MultiPolygonGeom, len(heights)) - - // TODO: Check if this correct! - reprojX := common.Linear(0.5, min.X, 1.5, min.X+cellSize) - reprojY := common.Linear(0.5, min.Y, 1.5, min.Y+cellSize) - - cnts := make(chan int) - - doContours := func() { - defer wg.Done() - for hIdx := range cnts { - c := tracer.Contours(heights[hIdx]) - - if len(c) == 0 { - continue - } - - areas[hIdx] = buildMultipolygon(c, reprojX, reprojY) - } - } - - for n := runtime.NumCPU(); n >= 1; n-- { - wg.Add(1) - go doContours() - } - - for i := range heights { - cnts <- i - } - close(cnts) - - wg.Wait() - log.Printf("info: Tracing areas took %v\n", time.Since(start)) - - return areas -} - -type contour []contourmap.Point - -type bboxNode struct { - box Box2D - cnt contour - children []*bboxNode -} - -func (cnt contour) contains(o contour) bool { - return contains(cnt, o[0].X, o[0].Y) || - contains(cnt, o[len(o)/2].X, o[len(o)/2].Y) -} - -func (cnt contour) length() int { - return len(cnt) -} - -func (cnt contour) point(i int) (float64, float64) { - return cnt[i].X, cnt[i].Y -} - -func (cnt contour) bbox() Box2D { - minX, minY := math.MaxFloat64, math.MaxFloat64 - maxX, maxY := -math.MaxFloat64, -math.MaxFloat64 - - for _, p := range cnt { - if p.X < minX { - minX = p.X - } - if p.X > maxX { - maxX = p.X - } - if p.Y < minY { - minY = p.Y - } - if p.Y > maxY { - maxY = p.Y - } - } - return Box2D{X1: minX, X2: maxX, Y1: minY, Y2: maxY} -} - -func (bn *bboxNode) insert(cnt contour, box Box2D) { - // check if children are inside new - var nr *bboxNode - - for i, r := range bn.children { - if r.box.Inside(box) && cnt.contains(r.cnt) { - if nr == nil { - nr = &bboxNode{box: box, cnt: cnt} - } - nr.children = append(nr.children, r) - bn.children[i] = nil - } - } - - // we have a new child - if nr != nil { - // compact the list - for i := len(bn.children) - 1; i >= 0; i-- { - if bn.children[i] == nil { - if i < len(bn.children)-1 { - copy(bn.children[i:], bn.children[i+1:]) - } - bn.children[len(bn.children)-1] = nil - bn.children = bn.children[:len(bn.children)-1] - } - } - bn.children = append(bn.children, nr) - return - } - - // check if new is inside an old - for _, r := range bn.children { - if box.Inside(r.box) && r.cnt.contains(cnt) { - r.insert(cnt, box) - return - } - } - - // its a new child node. - nr = &bboxNode{box: box, cnt: cnt} - bn.children = append(bn.children, nr) -} - -func (bn *bboxNode) insertRoot(cnt contour) { - bn.insert(cnt, cnt.bbox()) -} - -type bboxOutFunc func(contour, []contour) - -func (bn *bboxNode) generate(out bboxOutFunc) { - - var grands []*bboxNode - - holes := make([]contour, len(bn.children)) - - for i, ch := range bn.children { - holes[i] = ch.cnt - grands = append(grands, ch.children...) - } - out(bn.cnt, holes) - - // the grand children are new polygons. - for _, grand := range grands { - grand.generate(out) - } -} - -func (bn *bboxNode) generateRoot(out bboxOutFunc) { - for _, r := range bn.children { - r.generate(out) - } -} - -func buildMultipolygon( - cnts []contourmap.Contour, - reprojX, reprojY func(float64) float64, -) wkb.MultiPolygonGeom { - - var forest bboxNode - - for _, cnt := range cnts { - forest.insertRoot(contour(cnt)) - } - - //log.Printf("cnts: %d roots: %d\n", len(cnts), len(bf.roots)) - - var mp wkb.MultiPolygonGeom - - out := func(sh contour, hls []contour) { - - polygon := make(wkb.PolygonGeom, 1+len(hls)) - - // Handle shell - shell := make(wkb.LinearRingGeom, len(sh)) - for j, pt := range sh { - shell[j] = wkb.PointGeom{ - X: reprojX(pt.X), - Y: reprojY(pt.Y), - } - } - if shell.CCW() { - shell.Reverse() - } - polygon[0] = shell - - // handle holes - for i, hl := range hls { - hole := make(wkb.LinearRingGeom, len(hl)) - for j, pt := range hl { - hole[j] = wkb.PointGeom{ - X: reprojX(pt.X), - Y: reprojY(pt.Y), - } - } - if !hole.CCW() { - hole.Reverse() - } - polygon[1+i] = hole - } - - mp = append(mp, polygon) - } - - forest.generateRoot(out) - - return mp -}