view pkg/octree/contours.go @ 976:c397fdd8c327

Generate the contour lines of the sounding result during the import, too.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 18 Oct 2018 17:05:54 +0200
parents
children 4a2ca0e20006
line wrap: on
line source

package octree

import (
	"database/sql"
	"runtime"
	"sync"
)

const (
	contourStepWidth = 0.5
	contourTolerance = 0.1
)

type contourResult struct {
	height float64
	lines  MultiLineStringZ
}

const (
	insertContourSQL = `
INSERT INTO waterway.sounding_results_contour_lines (
  sounding_result_id,
  height,
  lines
)
SELECT
  $1,
  $2,
  ST_Transform(
    ST_Multi(
      ST_CollectionExtract(
        ST_Intersection(
          ST_Transform(sr.area::geometry, $3::integer),
          ST_SimplifyPreserveTopology(
            ST_GeomFromWKB($4, $3::integer),
            $5
          )
        ),
        2
      )
    ),
    4326
  )
FROM waterway.sounding_results sr
WHERE id = $1
`
)

func generateContours(tree *Tree, tx *sql.Tx, id int64) error {
	stmt, err := tx.Prepare(insertContourSQL)
	if err != nil {
		return err
	}
	defer stmt.Close()

	doContours(tree, contourStepWidth, func(res *contourResult) {
		if err == nil {
			_, err = stmt.Exec(
				id, res.height, tree.EPSG,
				res.lines.AsWKB2D(),
				contourTolerance)
		}
	})

	return err
}

func doContours(tree *Tree, step float64, store func(*contourResult)) {

	results := make(chan *contourResult)
	done := make(chan struct{})
	jobs := make(chan float64)

	go func() {
		for {
			select {
			case r := <-results:
				store(r)
			case <-done:
				return
			}
		}
	}()

	var wg sync.WaitGroup
	for i, n := 0, runtime.NumCPU(); i < n; i++ {
		wg.Add(1)
		go processLevels(tree, jobs, results, &wg)
	}
	for h := tree.Min.Z; h <= tree.Max.Z; h += step {
		jobs <- h
	}
	close(jobs)
	wg.Wait()
	done <- struct{}{}
}

func processLevels(
	tree *Tree,
	jobs <-chan float64,
	results chan<- *contourResult,
	wg *sync.WaitGroup,
) {
	defer wg.Done()
	for h := range jobs {
		var lines MultiLineStringZ
		tree.Horizontal(h, func(t *Triangle) {
			line := t.IntersectHorizontal(h)
			if len(line) > 1 {
				lines = append(lines, line)
			}
		})
		lines = lines.Merge()
		results <- &contourResult{h, lines}
	}
}