Mercurial > gemma
changeset 3826:4b1184fa0326 sld-colors
Added templating and a failing test.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Wed, 03 Jul 2019 17:47:17 +0200 |
parents | 36129677ff24 |
children | 6028326b88d6 |
files | 3rdpartylibs.sh pkg/geoserver/templates.go pkg/geoserver/templates_test.go |
diffstat | 3 files changed, 1131 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/3rdpartylibs.sh Wed Jun 26 17:42:51 2019 +0200 +++ b/3rdpartylibs.sh Wed Jul 03 17:47:17 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)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/geoserver/templates.go Wed Jul 03 17:47:17 2019 +0200 @@ -0,0 +1,169 @@ +// 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 ( + "image/color" + "math" + "sort" + "strconv" + "strings" +) + +type colorClass struct { + value float64 + color color.RGBA +} + +type colorClasses []colorClass + +func round(c float64) uint8 { + c = math.Round(c) + if c < 0 { + c = 0 + } else if c > 255 { + c = 255 + } + return uint8(c) +} + +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 + + r := (1-s)*float64(cc[i].color.R) + s*float64(cc[i+1].color.R) + g := (1-s)*float64(cc[i].color.G) + s*float64(cc[i+1].color.G) + b := (1-s)*float64(cc[i].color.B) + s*float64(cc[i+1].color.B) + + return color.RGBA{ + R: round(r), + G: round(g), + B: round(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 +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pkg/geoserver/templates_test.go Wed Jul 03 17:47:17 2019 +0200 @@ -0,0 +1,958 @@ +// 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 ( + "fmt" + "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 . -}} + {{- if and .HasLow .HasHigh -}} + <se:Rule> + <se:Name>≤ {{ printf "%.1f" .High }}</se:Name> + <ogc:Filter> + <ogc:And> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>{{.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> + <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> + {{ else if .HasLow -}} + <se:Rule> + <se:Name>> {{ printf "%.1f" .Low }}</se:Name> + <ogc:Filter> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>{{ printf "%g" .Low }}</ogc:Literal> + </ogc:PropertyIsGreaterThan> + </ogc:Filter> + <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> + {{ else if .HasHigh -}} + <se:Rule> + <se:Name>≤ {{ printf "%.1f" .High }}</se:Name> + <ogc:Filter> + <ogc:PropertyIsLessThanOrEqualTo> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>{{ printf "%g" .High }}</ogc:Literal> + </ogc:PropertyIsLessThanOrEqualTo> + </ogc:Filter> + <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 -}} + {{ 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 "%.1f" .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 "%.1f" .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 "%.1f" .High}}</ogc:Literal><ogc:Literal>{{ printf "%.1f" .High}}</ogc:Literal> + {{- else if .HasLow -}} + <ogc:Literal>{{ printf "%.1f" .Low}}</ogc:Literal><ogc:Literal>{{ printf "%.1f" .Low}}</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.0</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">#fb08b6</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">#f81090</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">#f5196a</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">#f22144</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">#f03326</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">#f38b1a</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">#f5b814</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">#f7e40e</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">#e2f30e</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">#b6e414</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 4.0</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">#5ec620</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 5.0</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">#32b726</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">#249a49</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 6.0</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">#207977</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">#1c57a4</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>≤ 7.0</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">#1835d1</se:SvgParameter> + <se:SvgParameter name="stroke-width">0.5</se:SvgParameter> + </se:Stroke> + </se:LineSymbolizer> + </se:Rule> + <se:Rule> + <se:Name>> 7.0</se:Name> + <ogc:Filter> + <ogc:PropertyIsGreaterThan> + <ogc:PropertyName>height</ogc:PropertyName> + <ogc:Literal>7</ogc:Literal> + </ogc:PropertyIsGreaterThan> + </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.0</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.0</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.0</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.0</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.0</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.5</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.0</ogc:Literal> + <ogc:Literal>#ff00dd</ogc:Literal> + <ogc:Literal>1.5</ogc:Literal> + <ogc:Literal>#fb08b6</ogc:Literal> + <ogc:Literal>1.7</ogc:Literal> + <ogc:Literal>#f81090</ogc:Literal> + <ogc:Literal>1.9</ogc:Literal> + <ogc:Literal>#f5196a</ogc:Literal> + <ogc:Literal>2.1</ogc:Literal> + <ogc:Literal>#f22144</ogc:Literal> + <ogc:Literal>2.3</ogc:Literal> + <ogc:Literal>#f03326</ogc:Literal> + <ogc:Literal>2.5</ogc:Literal> + <ogc:Literal>#f25f20</ogc:Literal> + <ogc:Literal>2.7</ogc:Literal> + <ogc:Literal>#f38b1a</ogc:Literal> + <ogc:Literal>2.9</ogc:Literal> + <ogc:Literal>#f5b814</ogc:Literal> + <ogc:Literal>3.1</ogc:Literal> + <ogc:Literal>#f7e40e</ogc:Literal> + <ogc:Literal>3.3</ogc:Literal> + <ogc:Literal>#e2f30e</ogc:Literal> + <ogc:Literal>3.5</ogc:Literal> + <ogc:Literal>#b6e414</ogc:Literal> + <ogc:Literal>4.0</ogc:Literal> + <ogc:Literal>#8ad51a</ogc:Literal> + <ogc:Literal>4.5</ogc:Literal> + <ogc:Literal>#5ec620</ogc:Literal> + <ogc:Literal>5.0</ogc:Literal> + <ogc:Literal>#32b726</ogc:Literal> + <ogc:Literal>5.5</ogc:Literal> + <ogc:Literal>#249a49</ogc:Literal> + <ogc:Literal>6.0</ogc:Literal> + <ogc:Literal>#207977</ogc:Literal> + <ogc:Literal>6.5</ogc:Literal> + <ogc:Literal>#1c57a4</ogc:Literal> + <ogc:Literal>7.0</ogc:Literal> + <ogc:Literal>#1835d1</ogc:Literal> + <ogc:Literal>7.5</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.0</ogc:Literal><ogc:Literal>1.0</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.0</ogc:Literal><ogc:Literal>4.0</ogc:Literal> + <ogc:Literal>4.5</ogc:Literal><ogc:Literal>4.5</ogc:Literal> + <ogc:Literal>5.0</ogc:Literal><ogc:Literal>5.0</ogc:Literal> + <ogc:Literal>5.5</ogc:Literal><ogc:Literal>5.5</ogc:Literal> + <ogc:Literal>6.0</ogc:Literal><ogc:Literal>6.0</ogc:Literal> + <ogc:Literal>6.5</ogc:Literal><ogc:Literal>6.5</ogc:Literal> + <ogc:Literal>7.0</ogc:Literal><ogc:Literal>7.0</ogc:Literal> + <ogc:Literal>7.5</ogc:Literal><ogc:Literal>7.5</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,7.5:#1414ff` + +type classBreaks struct { + High float64 + HasHigh bool + Low float64 + HasLow bool + Color string +} + +func TestTemplate(t *testing.T) { + + ccs, err := parseColorClasses(classBreaksConfig) + if err != nil { + t.Fatalf("parsing color config failed: %v", err) + } + + /* + for i := range ccs { + fmt.Printf("%.1f #%02x%02x%02x\n", + ccs[i].value, + ccs[i].color.R, + ccs[i].color.G, + ccs[i].color.B) + } + */ + + cbs := make([]classBreaks, len(ccs)) + for i := range ccs { + if i > 0 { + cbs[i].Low = ccs[i-1].value + cbs[i].HasLow = true + } + if i < len(ccs)-1 { + cbs[i].High = ccs[i].value + cbs[i].HasHigh = true + } + cbs[i].Color = fmt.Sprintf("#%02x%02x%02x", + ccs[i].color.R, + ccs[i].color.G, + ccs[i].color.B) + } + + 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)) + } +}