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