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
+}