Mercurial > gemma
view pkg/octree/classbreaks.go @ 4500:e0e9cd86ec04
import_overview: add country column to csv export
author | Thomas Junk <thomas.junk@intevation.de> |
---|---|
date | Mon, 30 Sep 2019 15:58:32 +0200 |
parents | 49564382ffff |
children | 7a9388943840 |
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) 2019 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 ( "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 ExtrapolateClassBreaks(cbs []float64, min, max float64) []float64 { if min > max { min, max = max, min } if len(cbs) < 2 { return cbs } if min >= cbs[0] && max <= cbs[len(cbs)-1] { return cbs } n := make([]float64, len(cbs)) copy(n, cbs) sort.Float64s(n) for min < n[0] { diff := n[1] - n[0] if diff == 0 { break } m := make([]float64, len(n)+1) m[0] = 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, n[len(n)-1]+diff) } return n } func InBetweenClassBreaks(cbs []float64, min float64, steps int) []float64 { if len(cbs) < 2 || steps < 2 { return cbs } out := make([]float64, 1, len(cbs)*steps) out[0] = cbs[0] _1steps := 1 / float64(steps) for i := 1; i < len(cbs); i++ { last, curr := cbs[i-1], cbs[i] // Gap already too small -> proceed with next gap. diff := curr - last if math.Abs(diff) < min { out = append(out, curr) continue } delta := diff * _1steps for p := last + delta; p < curr; p += delta { out = append(out, p) } out = append(out, curr) } return out }