Mercurial > gemma
diff pkg/mesh/classbreaks.go @ 4827:f4abfd0ee8ad remove-octree-debris
Renamed octree package to mesh as there is no octree any more.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Tue, 05 Nov 2019 14:30:22 +0100 |
parents | pkg/octree/classbreaks.go@c93c8a837af8 |
children | 4847ac70103a |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/mesh/classbreaks.go Tue Nov 05 14:30:22 2019 +0100 @@ -0,0 +1,150 @@ +// 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) 2019 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> + +package mesh + +import ( + "context" + "database/sql" + "errors" + "math" + "sort" + "strconv" + "strings" +) + +const ( + selectClassBreaksSQL = ` +SELECT config_val FROM sys_admin.system_config +WHERE config_key = $1` +) + +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 +} + +func ParseClassBreaks(config string) ([]float64, error) { + + parts := strings.Split(config, ",") + classes := make([]float64, 0, len(parts)) + for _, part := range parts { + if idx := strings.IndexRune(part, ':'); idx >= 0 { + part = part[:idx] + } + if part = strings.TrimSpace(part); part == "" { + continue + } + v, err := strconv.ParseFloat(part, 64) + if err != nil { + return nil, err + } + classes = append(classes, v) + } + + sort.Float64s(classes) + return classes, nil +} + +func LoadClassBreaks(ctx context.Context, tx *sql.Tx, key string) ([]float64, error) { + + var config sql.NullString + + err := tx.QueryRowContext(ctx, selectClassBreaksSQL, key).Scan(&config) + + switch { + case err == sql.ErrNoRows: + return nil, nil + case err != nil: + return nil, err + case !config.Valid: + return nil, errors.New("Invalid config string") + } + + return ParseClassBreaks(config.String) +} + +func round(v float64) float64 { + return math.Round(v*10000) / 10000 +} + +func ExtrapolateClassBreaks(cbs []float64, min, max float64) []float64 { + if min > max { + min, max = max, min + } + + n := make([]float64, len(cbs)) + copy(n, cbs) + sort.Float64s(n) + + for len(n) > 0 && n[0] < min { + n = n[1:] + } + + if len(n) == 0 { + return n + } + + for len(n) > 0 && n[len(n)-1] > max { + n = n[:len(n)-1] + } + + if len(n) == 0 { + return n + } + + for min < n[0] { + diff := n[1] - n[0] + if diff == 0 { + break + } + m := make([]float64, len(n)+1) + m[0] = round(n[0] - diff) + copy(m[1:], n) + n = m + } + + for max > n[len(n)-1] { + diff := n[len(n)-1] - n[len(n)-2] + if diff == 0 { + break + } + n = append(n, round(n[len(n)-1]+diff)) + } + + return n +}