Mercurial > gemma
changeset 3845:5983eec9436c
Merged sld-colors branch back into default branch.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Mon, 08 Jul 2019 16:04:02 +0200 |
parents | cc585e068ca0 (current diff) 40b28a8c0aaa (diff) |
children | f1ce295d9283 |
files | |
diffstat | 7 files changed, 1399 insertions(+), 29 deletions(-) [+] |
line wrap: on
line diff
--- a/3rdpartylibs.sh Mon Jul 08 15:45:42 2019 +0200 +++ b/3rdpartylibs.sh Mon Jul 08 16:04:02 2019 +0200 @@ -44,6 +44,10 @@ go get -u -v gonum.org/v1/gonum/stat # BSD-3-Clause +go get -u -v github.com/sergi/go-diff/diffmatchpatch +# MIT +# Only used in tests. + # Only needed when generating SVG graphics for debugging. # go get -u -v github.com/ajstarks/svgo # Attribution 3.0 United States (CC BY 3.0 US)
--- a/pkg/controllers/system.go Mon Jul 08 15:45:42 2019 +0200 +++ b/pkg/controllers/system.go Mon Jul 08 16:04:02 2019 +0200 @@ -20,10 +20,12 @@ "io/ioutil" "net/http" "strings" + "sync" "github.com/gorilla/mux" "gemma.intevation.de/gemma/pkg/config" + "gemma.intevation.de/gemma/pkg/geoserver" "gemma.intevation.de/gemma/pkg/models" ) @@ -39,6 +41,11 @@ SELECT config_key, config_val FROM sys_admin.system_config` + getConfigSQL = ` +SELECT config_val +FROM sys_admin.system_config +WHERE config_key = $1` + updateSettingSQL = ` INSERT INTO sys_admin.system_config (config_key, config_val) VALUES ($1, $2) @@ -139,6 +146,32 @@ return } +var ( + reconfigureFuncsMu sync.Mutex + reconfigureFuncs = map[string]func(){} +) + +func registerReconfigureFunc(key string, fn func()) { + reconfigureFuncsMu.Lock() + defer reconfigureFuncsMu.Unlock() + reconfigureFuncs[key] = fn +} + +func reconfigureFunc(key string) func() { + reconfigureFuncsMu.Lock() + defer reconfigureFuncsMu.Unlock() + return reconfigureFuncs[key] +} + +func init() { + registerReconfigureFunc("morphology_classbreaks", func() { + geoserver.ReconfigureStyle("sounding_results_contour_lines_geoserver") + }) + registerReconfigureFunc("morphology_classbreaks_compare", func() { + geoserver.ReconfigureStyle("sounding_differences") + }) +} + func setSystemSettings( input interface{}, req *http.Request, @@ -159,8 +192,30 @@ return } defer setStmt.Close() + var getStmt *sql.Stmt + if getStmt, err = tx.PrepareContext(ctx, getConfigSQL); err != nil { + return + } + defer getStmt.Close() + + reconfigure := map[string]func(){} for key, value := range *settings { + var old sql.NullString + err = getStmt.QueryRowContext(ctx, key).Scan(&old) + switch { + case err == sql.ErrNoRows: + old.Valid, err = false, nil + case err != nil: + return + } + + if !old.Valid || old.String != value { + if fn := reconfigureFunc(key); fn != nil { + reconfigure[key] = fn + } + } + if _, err = setStmt.ExecContext(ctx, key, value); err != nil { return } @@ -170,6 +225,10 @@ return } + for _, fn := range reconfigure { + fn() + } + jr = JSONResult{ Code: http.StatusCreated, Result: struct {
--- a/pkg/geoserver/boot.go Mon Jul 08 15:45:42 2019 +0200 +++ b/pkg/geoserver/boot.go Mon Jul 08 16:04:02 2019 +0200 @@ -23,6 +23,7 @@ "net/http" "net/url" "strings" + "sync" "golang.org/x/net/html/charset" @@ -391,6 +392,23 @@ return nil } +var ( + stylePreprocessorsMu sync.Mutex + stylePreprocessors = map[string]func(string) (string, error){} +) + +func RegisterStylePreprocessor(name string, processor func(string) (string, error)) { + stylePreprocessorsMu.Lock() + defer stylePreprocessorsMu.Unlock() + stylePreprocessors[name] = processor +} + +func FindStylePreprocessor(name string) func(string) (string, error) { + stylePreprocessorsMu.Lock() + defer stylePreprocessorsMu.Unlock() + return stylePreprocessors[name] +} + func updateStyle(entry *models.IntEntry, create bool) error { log.Printf("info: creating style %s\n", entry.Name) @@ -401,6 +419,12 @@ return err } + if processor := FindStylePreprocessor(entry.Name); processor != nil { + if data, err = processor(data); err != nil { + return err + } + } + var ( geoURL = config.GeoServerURL() user = config.GeoServerUser()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/geoserver/templates.go Mon Jul 08 16:04:02 2019 +0200 @@ -0,0 +1,279 @@ +// 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) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> + +package geoserver + +import ( + "context" + "database/sql" + "fmt" + "image/color" + "math" + "sort" + "strconv" + "strings" + "text/template" + + "gemma.intevation.de/gemma/pkg/auth" +) + +const ( + selectClassBreaksSQL = ` +SELECT config_val FROM sys_admin.system_config +WHERE config_key = $1` +) + +func init() { + RegisterStylePreprocessor( + "sounding_results_contour_lines_geoserver", + templateContourLinesFunc("morphology_classbreaks")) + RegisterStylePreprocessor( + "sounding_differences", + templateContourLinesFunc("morphology_classbreaks_compare")) +} + +type ( + colorClass struct { + value float64 + color color.RGBA + } + + colorClasses []colorClass + + classBreak struct { + High float64 + HasHigh bool + Low float64 + HasLow bool + color color.RGBA + } +) + +func (cb *classBreak) Color() string { + return fmt.Sprintf("#%02x%02x%02x", + cb.color.R, + cb.color.G, + cb.color.B, + ) +} + +func (cc colorClasses) toClassBreaks() []classBreak { + + cbs := make([]classBreak, len(cc), len(cc)+1) + for i := range cc { + if i > 0 { + cbs[i].Low = cc[i-1].value + cbs[i].HasLow = true + } + cbs[i].High = cc[i].value + cbs[i].HasHigh = true + cbs[i].color = cc[i].color + } + if len(cc) > 0 { + cbs = append(cbs, classBreak{ + color: cc[len(cc)-1].color, + Low: cc[len(cc)-1].value, + HasLow: true, + }) + } + + return cbs +} + +func (cc colorClasses) interpolate(v float64) (color.RGBA, bool) { + if len(cc) == 0 || v < cc[0].value || v > cc[len(cc)-1].value { + return color.RGBA{}, false + } + + if len(cc) == 1 { + return cc[0].color, cc[0].value == v + } + + for i := 0; i < len(cc)-1; i++ { + v1 := cc[i].value + v2 := cc[i+1].value + if v1 <= v && v <= v2 { + // f(v1) = 0 + // f(v2) = 1 + // 0 = m*v1 + c <=> c = -m*v1 + // 1 = m*v2 + c + // (1 - 0) = m*(v2 - v1) + // m = 1/(v2 - v1) for v2 != v1 + + if v1 == v2 { + return color.RGBA{ + R: uint8((uint16(cc[i].color.R) + uint16(cc[i+1].color.R)) / 2), + G: uint8((uint16(cc[i].color.G) + uint16(cc[i+1].color.G)) / 2), + B: uint8((uint16(cc[i].color.B) + uint16(cc[i+1].color.B)) / 2), + A: 0xff, + }, true + } + m := 1 / (v2 - v1) + c := -m * v1 + s := v*m + c + + interpolate := func(a, b uint8) uint8 { + v := math.Round(float64(a) + (float64(b)-float64(a))*s) + if v < 0 { + return 0 + } + if v > 255 { + return 255 + } + return uint8(v) + } + + return color.RGBA{ + R: interpolate(cc[i].color.R, cc[i+1].color.R), + G: interpolate(cc[i].color.G, cc[i+1].color.G), + B: interpolate(cc[i].color.B, cc[i+1].color.B), + A: 0xff, + }, true + } + } + + return color.RGBA{}, false +} + +func parseColorClasses(s string) (colorClasses, error) { + + var err error + + parseFloat := func(s string) float64 { + var v float64 + if err == nil { + v, err = strconv.ParseFloat(s, 64) + } + return v + } + + parseColor := func(s string) color.RGBA { + if err != nil { + return color.RGBA{} + } + s = strings.Map(func(r rune) rune { + if ('a' <= r && r <= 'f') || ('0' <= r && r <= '9') { + return r + } + return -1 + }, strings.ToLower(s)) + + var v int64 + v, err = strconv.ParseInt(s, 16, 64) + return color.RGBA{ + R: uint8(v >> 16), + G: uint8(v >> 8), + B: uint8(v >> 0), + A: 0xff, + } + } + + lines := strings.Split(s, ",") + + // first pass: find defined colors + var defined colorClasses + + for _, line := range lines { + // ignore the lines w/o a color. + if !strings.ContainsRune(line, ':') { + continue + } + parts := strings.SplitN(line, ":", 2) + if len(parts) < 2 { + continue + } + value := parseFloat(parts[0]) + color := parseColor(parts[1]) + + defined = append(defined, colorClass{ + value: value, + color: color, + }) + } + + if err != nil { + return nil, err + } + + sort.Slice(defined, func(i, j int) bool { + return defined[i].value < defined[j].value + }) + + // second pass: interpolate the rest + var final colorClasses + for _, line := range lines { + if idx := strings.IndexRune(line, ':'); idx >= 0 { + line = line[:idx] + } + value := parseFloat(line) + if color, ok := defined.interpolate(value); ok { + final = append(final, colorClass{ + value: value, + color: color, + }) + } + } + + return final, err +} + +func templateContourLinesFunc(configKey string) func(string) (string, error) { + return func(data string) (string, error) { + return templateContourLines(data, configKey) + } +} + +func templateContourLines(data, configKey string) (string, error) { + tmpl, err := template.New("template").Parse(data) + if err != nil { + return "", err + } + + var cb []classBreak + + if cb, err = countourLinesClassBreaks(configKey); err != nil { + return "", err + } + + var buf strings.Builder + if err = tmpl.Execute(&buf, cb); err != nil { + return "", err + } + return buf.String(), nil +} + +func countourLinesClassBreaks(configKey string) ([]classBreak, error) { + + var config string + ctx := context.Background() + if err := auth.RunAs( + ctx, + "sys_admin", + func(conn *sql.Conn) error { + return conn.QueryRowContext( + ctx, + selectClassBreaksSQL, + configKey, + ).Scan(&config) + }, + ); err != nil { + return nil, err + } + + cc, err := parseColorClasses(config) + if err != nil { + return nil, err + } + + return cc.toClassBreaks(), nil +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/geoserver/templates_test.go Mon Jul 08 16:04:02 2019 +0200 @@ -0,0 +1,907 @@ +// 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) 2018 by via donau +// – Österreichische Wasserstraßen-Gesellschaft mbH +// Software engineering by Intevation GmbH +// +// Author(s): +// * Sascha L. Teichmann <sascha.teichmann@intevation.de> + +package geoserver + +import ( + "strings" + "testing" + "text/template" + + "github.com/sergi/go-diff/diffmatchpatch" +) + +const sldTmplTxt = `<?xml version="1.0" encoding="UTF-8"?> +<StyledLayerDescriptor + xmlns="http://www.opengis.net/sld" + xmlns:se="http://www.opengis.net/se" + xmlns:ogc="http://www.opengis.net/ogc" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" + version="1.1.0"> + <NamedLayer> + <se:Name>sounding_results_contour_lines</se:Name> + <UserStyle> + <se:Name>sounding_results_contour_lines</se:Name> + <se:FeatureTypeStyle> + <se:Name>contour_line_colours</se:Name> + <se:Description> + <se:Abstract> + FeatureTypeStyle defining colour classes for height attribute + </se:Abstract> + </se:Description> + {{ range . -}} + <se:Rule> + {{- if not .HasLow }} + <se:Name>≤ {{ printf "%g" .High }}</se:Name> + <ogc:Filter> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>{{ printf "%g" .High }}</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:Filter> + {{- else if not .HasHigh }} + <se:Name>> {{ printf "%g" .Low }}</se:Name> + <ogc:Filter> + <ogc:PropertyIsGreaterThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>{{ printf "%g" .Low }}</ogc:Literal> + </ogc:PropertyIsGreaterThanOrEqualTo> + </ogc:Filter> + {{- else }} + <se:Name>≤ {{ printf "%g" .High }}</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>{{ printf "%g" .Low }}</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>{{ printf "%g" .High }}</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + {{- end }} + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">{{ .Color }}</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + {{ end }} + </se:FeatureTypeStyle> + <se:FeatureTypeStyle> + <se:Name>contour_lines_emph</se:Name> + <se:Description> + <se:Abstract> + FeatureTypeStyle for emphasized contour lines + </se:Abstract> + </se:Description> + <se:Rule> + <se:LegendGraphic> + <se:Graphic> + </se:Graphic> + </se:LegendGraphic> + <ogc:Filter> + <ogc:Or> + {{ range . -}} + {{ if .HasHigh -}} + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>{{ printf "%g" .High }}</ogc:Literal> + </ogc:PropertyIsEqualTo> + {{ end -}} + {{ end }} + </ogc:Or> + </ogc:Filter> + <se:MaxScaleDenominator>5e3</se:MaxScaleDenominator> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke-width">1.5</se:SvgParameter> + <se:SvgParameter name="stroke"> + <ogc:Function name="Recode"> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + {{ range . -}} + {{ if .HasHigh -}} + <ogc:Literal>{{ printf "%g" .High }}</ogc:Literal> + <ogc:Literal>{{ .Color }}</ogc:Literal> + {{ end -}} + {{ end }} + </ogc:Function> + </se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + </se:FeatureTypeStyle> + <se:FeatureTypeStyle> + <se:Name>contour_lines_label</se:Name> + <se:Description> + <se:Abstract> + FeatureTypeStyle for labels at contour lines + </se:Abstract> + </se:Description> + <se:Rule> + <se:MaxScaleDenominator>5e3</se:MaxScaleDenominator> + <se:TextSymbolizer> + <se:Label> + <ogc:Function name="Recode"> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + {{ range . -}} + {{ if .HasHigh -}} + <ogc:Literal> + {{- printf "%g" .High -}} + </ogc:Literal><ogc:Literal> + {{- printf "%g" .High -}} + </ogc:Literal> + {{ end -}} + {{ end }} + </ogc:Function> + </se:Label> + <se:LabelPlacement> + <se:LinePlacement> + <se:PerpendicularOffset>5</se:PerpendicularOffset> + </se:LinePlacement> + </se:LabelPlacement> + <se:Font> + <se:SvgParameter name="font-family">Avenir</se:SvgParameter> + <se:SvgParameter name="font-family">Helvetica</se:SvgParameter> + <se:SvgParameter name="font-family">Arial</se:SvgParameter> + <se:SvgParameter name="font-family">sans-serif</se:SvgParameter> + </se:Font> + <se:Fill> + <se:SvgParameter name="fill">#070707</se:SvgParameter> + </se:Fill> + </se:TextSymbolizer> + </se:Rule> + </se:FeatureTypeStyle> + </UserStyle> + </NamedLayer> +</StyledLayerDescriptor> +` + +const origSLD = `<?xml version="1.0" encoding="UTF-8"?> +<StyledLayerDescriptor + xmlns="http://www.opengis.net/sld" + xmlns:se="http://www.opengis.net/se" + xmlns:ogc="http://www.opengis.net/ogc" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" + version="1.1.0"> + <NamedLayer> + <se:Name>sounding_results_contour_lines</se:Name> + <UserStyle> + <se:Name>sounding_results_contour_lines</se:Name> + <se:FeatureTypeStyle> + <se:Name>contour_line_colours</se:Name> + <se:Description> + <se:Abstract> + FeatureTypeStyle defining colour classes for height attribute + </se:Abstract> + </se:Description> + <se:Rule> + <se:Name>≤ 1</se:Name> + <ogc:Filter> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>1</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#ff00dd</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 1.5</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>1</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>1.5</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#fb209e</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 1.7</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>1.5</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>1.7</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#f92c85</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 1.9</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>1.7</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>1.9</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#f7396c</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 2.1</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>1.9</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>2.1</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#f54652</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 2.3</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>2.1</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>2.3</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#f45239</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 2.5</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>2.3</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>2.5</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#f25f20</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 2.7</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>2.5</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>2.7</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#e46f1f</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 2.9</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>2.7</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>2.9</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#d67e1e</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 3.1</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>2.9</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>3.1</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#c88e1e</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 3.3</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>3.1</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>3.3</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#bb9e1d</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 3.5</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>3.3</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>3.5</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#adae1c</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 4</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>3.5</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>4</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#8ad51a</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 4.5</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>4</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>4.5</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#76b540</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 5</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>4.5</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>5</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#639566</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 5.5</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>5</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>5.5</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#4f758d</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 6</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>5.5</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>6</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#3b54b3</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 6.5</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>6</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>6.5</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#2834d9</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 7</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>6.5</ogc:Literal> + </ogc:PropertyIsGreaterThan> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>7</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:And> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#1414ff</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>> 7</se:Name> + <ogc:Filter> + <ogc:PropertyIsGreaterThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>7</ogc:Literal> + </ogc:PropertyIsGreaterThanOrEqualTo> + </ogc:Filter> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke">#1414ff</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + + </se:FeatureTypeStyle> + <se:FeatureTypeStyle> + <se:Name>contour_lines_emph</se:Name> + <se:Description> + <se:Abstract> + FeatureTypeStyle for emphasized contour lines + </se:Abstract> + </se:Description> + <se:Rule> + <se:LegendGraphic> + <se:Graphic> + </se:Graphic> + </se:LegendGraphic> + <ogc:Filter> + <ogc:Or> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>1</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>1.5</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>1.7</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>1.9</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>2.1</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>2.3</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>2.5</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>2.7</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>2.9</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>3.1</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>3.3</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>3.5</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>4</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>4.5</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>5</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>5.5</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>6</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>6.5</ogc:Literal> + </ogc:PropertyIsEqualTo> + <ogc:PropertyIsEqualTo> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>7</ogc:Literal> + </ogc:PropertyIsEqualTo> + + </ogc:Or> + </ogc:Filter> + <se:MaxScaleDenominator>5e3</se:MaxScaleDenominator> + <se:LineSymbolizer> + <se:Stroke> + <se:SvgParameter name="stroke-width">1.5</se:SvgParameter> + <se:SvgParameter name="stroke"> + <ogc:Function name="Recode"> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>1</ogc:Literal> + <ogc:Literal>#ff00dd</ogc:Literal> + <ogc:Literal>1.5</ogc:Literal> + <ogc:Literal>#fb209e</ogc:Literal> + <ogc:Literal>1.7</ogc:Literal> + <ogc:Literal>#f92c85</ogc:Literal> + <ogc:Literal>1.9</ogc:Literal> + <ogc:Literal>#f7396c</ogc:Literal> + <ogc:Literal>2.1</ogc:Literal> + <ogc:Literal>#f54652</ogc:Literal> + <ogc:Literal>2.3</ogc:Literal> + <ogc:Literal>#f45239</ogc:Literal> + <ogc:Literal>2.5</ogc:Literal> + <ogc:Literal>#f25f20</ogc:Literal> + <ogc:Literal>2.7</ogc:Literal> + <ogc:Literal>#e46f1f</ogc:Literal> + <ogc:Literal>2.9</ogc:Literal> + <ogc:Literal>#d67e1e</ogc:Literal> + <ogc:Literal>3.1</ogc:Literal> + <ogc:Literal>#c88e1e</ogc:Literal> + <ogc:Literal>3.3</ogc:Literal> + <ogc:Literal>#bb9e1d</ogc:Literal> + <ogc:Literal>3.5</ogc:Literal> + <ogc:Literal>#adae1c</ogc:Literal> + <ogc:Literal>4</ogc:Literal> + <ogc:Literal>#8ad51a</ogc:Literal> + <ogc:Literal>4.5</ogc:Literal> + <ogc:Literal>#76b540</ogc:Literal> + <ogc:Literal>5</ogc:Literal> + <ogc:Literal>#639566</ogc:Literal> + <ogc:Literal>5.5</ogc:Literal> + <ogc:Literal>#4f758d</ogc:Literal> + <ogc:Literal>6</ogc:Literal> + <ogc:Literal>#3b54b3</ogc:Literal> + <ogc:Literal>6.5</ogc:Literal> + <ogc:Literal>#2834d9</ogc:Literal> + <ogc:Literal>7</ogc:Literal> + <ogc:Literal>#1414ff</ogc:Literal> + + </ogc:Function> + </se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + </se:FeatureTypeStyle> + <se:FeatureTypeStyle> + <se:Name>contour_lines_label</se:Name> + <se:Description> + <se:Abstract> + FeatureTypeStyle for labels at contour lines + </se:Abstract> + </se:Description> + <se:Rule> + <se:MaxScaleDenominator>5e3</se:MaxScaleDenominator> + <se:TextSymbolizer> + <se:Label> + <ogc:Function name="Recode"> + <ogc:Function name="numberFormat"> + <ogc:Literal>0.0</ogc:Literal> + <ogc:PropertyName>height</ogc:PropertyName> + </ogc:Function> + <ogc:Literal>1</ogc:Literal><ogc:Literal>1</ogc:Literal> + <ogc:Literal>1.5</ogc:Literal><ogc:Literal>1.5</ogc:Literal> + <ogc:Literal>1.7</ogc:Literal><ogc:Literal>1.7</ogc:Literal> + <ogc:Literal>1.9</ogc:Literal><ogc:Literal>1.9</ogc:Literal> + <ogc:Literal>2.1</ogc:Literal><ogc:Literal>2.1</ogc:Literal> + <ogc:Literal>2.3</ogc:Literal><ogc:Literal>2.3</ogc:Literal> + <ogc:Literal>2.5</ogc:Literal><ogc:Literal>2.5</ogc:Literal> + <ogc:Literal>2.7</ogc:Literal><ogc:Literal>2.7</ogc:Literal> + <ogc:Literal>2.9</ogc:Literal><ogc:Literal>2.9</ogc:Literal> + <ogc:Literal>3.1</ogc:Literal><ogc:Literal>3.1</ogc:Literal> + <ogc:Literal>3.3</ogc:Literal><ogc:Literal>3.3</ogc:Literal> + <ogc:Literal>3.5</ogc:Literal><ogc:Literal>3.5</ogc:Literal> + <ogc:Literal>4</ogc:Literal><ogc:Literal>4</ogc:Literal> + <ogc:Literal>4.5</ogc:Literal><ogc:Literal>4.5</ogc:Literal> + <ogc:Literal>5</ogc:Literal><ogc:Literal>5</ogc:Literal> + <ogc:Literal>5.5</ogc:Literal><ogc:Literal>5.5</ogc:Literal> + <ogc:Literal>6</ogc:Literal><ogc:Literal>6</ogc:Literal> + <ogc:Literal>6.5</ogc:Literal><ogc:Literal>6.5</ogc:Literal> + <ogc:Literal>7</ogc:Literal><ogc:Literal>7</ogc:Literal> + + </ogc:Function> + </se:Label> + <se:LabelPlacement> + <se:LinePlacement> + <se:PerpendicularOffset>5</se:PerpendicularOffset> + </se:LinePlacement> + </se:LabelPlacement> + <se:Font> + <se:SvgParameter name="font-family">Avenir</se:SvgParameter> + <se:SvgParameter name="font-family">Helvetica</se:SvgParameter> + <se:SvgParameter name="font-family">Arial</se:SvgParameter> + <se:SvgParameter name="font-family">sans-serif</se:SvgParameter> + </se:Font> + <se:Fill> + <se:SvgParameter name="fill">#070707</se:SvgParameter> + </se:Fill> + </se:TextSymbolizer> + </se:Rule> + </se:FeatureTypeStyle> + </UserStyle> + </NamedLayer> +</StyledLayerDescriptor> +` + +const classBreaksConfig = `1:#ff00dd,1.5,1.7,1.9,2.1,2.3,` + + `2.5:#f25f20,2.7,2.9,3.1,3.3,3.5,` + + `4:#8ad51a,4.5,5,5.5,6,6.5,` + + `7:#1414ff` + +func TestTemplate(t *testing.T) { + + ccs, err := parseColorClasses(classBreaksConfig) + if err != nil { + t.Fatalf("parsing color config failed: %v", err) + } + + cbs := ccs.toClassBreaks() + + tmpl, err := template.New("test").Parse(sldTmplTxt) + if err != nil { + t.Fatalf("parsing template failed: %v", err) + } + + var buf strings.Builder + if err := tmpl.Execute(&buf, cbs); err != nil { + t.Fatalf("templating failed: %v", err) + } + + has := buf.String() + if has != origSLD { + dmp := diffmatchpatch.New() + diffs := dmp.DiffMain(has, origSLD, true) + t.Fatalf("Templating results differ: %s", dmp.DiffPrettyText(diffs)) + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/octree/classbreaks.go Mon Jul 08 16:04:02 2019 +0200 @@ -0,0 +1,126 @@ +// 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 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") + } + + parts := strings.Split(config.String, ",") + classes := make([]float64, 0, len(parts)) + for _, part := range parts { + if idx := strings.IndexRune(part, ':'); idx >= 0 { + part = part[idx+1:] + } + 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 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 +}
--- a/pkg/octree/contours.go Mon Jul 08 15:45:42 2019 +0200 +++ b/pkg/octree/contours.go Mon Jul 08 16:04:02 2019 +0200 @@ -19,35 +19,6 @@ "sync" ) -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 -} - // ContourResult stores an calculated iso line for a given height. // Is used as a future variable in the concurrent iso line calculation. type ContourResult struct {