Mercurial > gemma
view pkg/octree/areas.go @ 4723:baabc2b2f094
Avoid creating user profiles without matching role
The INSTEAD OF triggers on users.list_users did that already, but
profile data coming e.g. via restoring a dump had been added also
if there was no matching database role in the cluster.
This also unifies the errors occuring on creation of users with existing
role names that differed between roles with and without profile before.
Note this is no referential integrity. A dropped role still leaves an
orphaned profile behind.
author | Tom Gottfried <tom@intevation.de> |
---|---|
date | Thu, 17 Oct 2019 18:56:59 +0200 |
parents | 0ddb308fed37 |
children | c91e759007da |
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> 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( n int, min, max Vertex, eval func(float64, float64) (float64, bool), callback func([]Vertex), ) { var wg sync.WaitGroup jobs := make(chan int) out := make(chan []Vertex) done := make(chan struct{}) cpus := runtime.NumCPU() free := make(chan []Vertex, cpus) for i := 0; i < cpus; i++ { wg.Add(1) go func() { defer wg.Done() xRange := common.Random(min.X, max.X) yRange := common.Random(min.Y, max.Y) for size := range jobs { var vertices []Vertex select { case vertices = <-free: default: vertices = make([]Vertex, 0, 1000) } for len(vertices) < size { x, y := xRange(), yRange() if z, ok := eval(x, y); ok { vertices = append(vertices, Vertex{X: x, Y: y, Z: z}) } } out <- vertices } }() } go func() { defer close(done) for vertices := range out { callback(vertices) select { case free <- vertices[:0]: default: } } }() for remain := n; remain > 0; { if remain > 1000 { jobs <- 1000 remain -= 1000 } else { jobs <- remain remain = 0 } } close(jobs) wg.Wait() 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 } // We need to bring it back to the // none raster coordinate system. a := make(wkb.MultiPolygonGeom, len(c)) for i, pl := range c { shell := make(wkb.LinearRingGeom, len(pl)) for j, pt := range pl { shell[j] = wkb.PointGeom{ X: reprojX(pt.X), Y: reprojY(pt.Y), } } /* if !shell.CCW() { log.Println("not ccw") shell.Reverse() } */ a[i] = wkb.PolygonGeom{shell} } areas[hIdx] = a } } 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 }