Mercurial > gemma
changeset 3848:1f8a89954bc3
Merged
author | Sascha Wilde <wilde@intevation.de> |
---|---|
date | Mon, 08 Jul 2019 17:00:54 +0200 |
parents | d7b9d5c0ebad (current diff) f1ce295d9283 (diff) |
children | 4a584a99bec9 |
files | |
diffstat | 12 files changed, 1428 insertions(+), 49 deletions(-) [+] |
line wrap: on
line diff
--- a/3rdpartylibs.sh Mon Jul 08 10:54:51 2019 +0200 +++ b/3rdpartylibs.sh Mon Jul 08 17:00:54 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/client/src/components/Pdftool.vue Mon Jul 08 10:54:51 2019 +0200 +++ b/client/src/components/Pdftool.vue Mon Jul 08 17:00:54 2019 +0200 @@ -108,12 +108,14 @@ import { displayError } from "@/lib/errors"; import { pdfgen, templateLoader } from "@/lib/mixins"; -var paperSizes = { +const paperSizes = { // in millimeter, landscape [width, height] a3: [420, 297], a4: [297, 210] }; +const DEFAULT_TEMPLATE = "Default"; + export default { mixins: [pdfgen, templateLoader], name: "pdftool", @@ -128,7 +130,7 @@ }, templates: [ { - name: "Default", + name: DEFAULT_TEMPLATE, properties: { format: "landscape", paperSize: "a4", @@ -150,7 +152,7 @@ }, { type: "northarrow", - position: "topright", + position: "topleft", offset: { x: 6, y: 4 }, size: 2 } @@ -210,20 +212,17 @@ // When a template is chosen from the dropdown, its propoerties are // applied to the rest of the form. applyTemplateToForm() { - if (this.form.template) { + if (this.form.template && this.form.template.name !== DEFAULT_TEMPLATE) { this.loadTemplates( `/templates/${this.form.template.type}/${this.form.template.name}` ) .then(response => { this.prepareImages(response.data.template_data.elements).then( values => { - this.templateData = response.data.template_data; values.forEach(v => { response.data.template_data.elements[v.index].url = v.url; }); - this.form.format = this.templateData.properties.format; - this.form.paperSize = this.templateData.properties.paperSize; - this.form.resolution = this.templateData.properties.resolution; + this.setTemplate(response.data.template_data); } ); }) @@ -234,8 +233,16 @@ message: `${status}: ${data.message || data}` }); }); + } else { + this.setTemplate(this.templates[0]); } }, + setTemplate(template) { + this.templateData = template; + this.form.format = this.templateData.properties.format; + this.form.paperSize = this.templateData.properties.paperSize; + this.form.resolution = this.templateData.properties.resolution; + }, download() { // disable button while working on it this.readyToGenerate = false; @@ -803,8 +810,8 @@ }) .then(response => { if (response.data.length) { - this.templates = response.data; - this.form.template = this.templates[0]; + this.templates = [...this.templates, ...response.data]; + this.form.template = this.templates[1]; this.applyTemplateToForm(); } else { this.form.template = this.templates[0];
--- a/client/src/components/gauge/HydrologicalConditions.vue Mon Jul 08 10:54:51 2019 +0200 +++ b/client/src/components/gauge/HydrologicalConditions.vue Mon Jul 08 17:00:54 2019 +0200 @@ -506,7 +506,7 @@ this.svg .selectAll(".tick line") .attr("stroke-dasharray", 5) - .attr("stroke", " #ccc"); + .attr("stroke", "#ccc"); this.svg.selectAll(".tick text").attr("fill", "black"); this.svg.selectAll(".domain").attr("stroke", "black");
--- a/client/src/components/importconfiguration/ScheduledImports.vue Mon Jul 08 10:54:51 2019 +0200 +++ b/client/src/components/importconfiguration/ScheduledImports.vue Mon Jul 08 17:00:54 2019 +0200 @@ -763,7 +763,9 @@ this.retry = this.currentSchedule.trys === null || this.currentSchedule.trys === undefined || - this.currentSchedule === 0 ? false : true; + this.currentSchedule === 0 + ? false + : true; }, isWeekly(cron) { return /0 \d{1,2} \d{1,2} \* \* \d{1}/.test(cron);
--- a/client/src/components/map/styles.js Mon Jul 08 10:54:51 2019 +0200 +++ b/client/src/components/map/styles.js Mon Jul 08 17:00:54 2019 +0200 @@ -257,11 +257,11 @@ (map.getLayer("GAUGES").getVisible() && feature.getId().indexOf("gauges") > -1) ) { - let frame = `<polyline points='16,0 32,32 0,32 16,0' stroke='grey' stroke-width='1' fill='white'/>`; - let waterlevel = `<polyline points="16,0 24,16 16,32 8,16 16,0" stroke='grey' stroke-width='1' fill='${colorWaterlevel}'/>`; - let accuracy = `<polyline points="24,16 32,32 16,32 24,16" stroke='grey' stroke-width='1' fill='${colorAccuracy}'/>`; - let comparison = `<polyline points="8,16 16,32 0,32 8,16" stroke='grey' stroke-width='1' fill='${colorComparison}'/>`; - let svg = `data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32'><g>${frame}${waterlevel}${comparison}${accuracy}</g></svg>`; + let frame = `<polyline points='16,0 32,28 0,28 16,0' stroke='grey' stroke-width='1' fill='white'/>`; + let waterlevel = `<polyline points="16,0 24,14 16,28 8,14 16,0" stroke='grey' stroke-width='1' fill='${colorWaterlevel}'/>`; + let accuracy = `<polyline points="24,14 32,28 16,28 24,14" stroke='grey' stroke-width='1' fill='${colorAccuracy}'/>`; + let comparison = `<polyline points="8,14 16,28 0,28 8,14" stroke='grey' stroke-width='1' fill='${colorComparison}'/>`; + let svg = `data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='28'><g>${frame}${waterlevel}${comparison}${accuracy}</g></svg>`; s.push( new Style({ geometry: geom, @@ -278,8 +278,8 @@ feature.getId().indexOf("bottlenecks") > -1 ) { let colorUniformTriangle = classifications.surveyCurrency(feature); - let frame = `<polyline points='16,0 32,32 0,32 16,0' stroke='grey' stroke-width='1' fill='${colorUniformTriangle}'/>`; - let svg = `data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32'><g>${frame}</g></svg>`; + let frame = `<polyline points='16,0 32,28 0,28 16,0' stroke='grey' stroke-width='1' fill='${colorUniformTriangle}'/>`; + let svg = `data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='32' height='28'><g>${frame}</g></svg>`; s.push( new Style({ geometry: geom,
--- a/pkg/controllers/system.go Mon Jul 08 10:54:51 2019 +0200 +++ b/pkg/controllers/system.go Mon Jul 08 17:00:54 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 10:54:51 2019 +0200 +++ b/pkg/geoserver/boot.go Mon Jul 08 17:00:54 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 17:00:54 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 17:00:54 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 17:00:54 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 10:54:51 2019 +0200 +++ b/pkg/octree/contours.go Mon Jul 08 17:00:54 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 {
--- a/schema/default_sysconfig.sql Mon Jul 08 10:54:51 2019 +0200 +++ b/schema/default_sysconfig.sql Mon Jul 08 17:00:54 2019 +0200 @@ -35,7 +35,7 @@ INSERT INTO sys_admin.system_config VALUES ('gm_forecast_offset_72h', 15); INSERT INTO sys_admin.system_config VALUES ('gm_forecast_vs_reality_nsc_24h', -12.5); INSERT INTO sys_admin.system_config VALUES ('gm_forecast_vs_reality_nsc_72h', -12.5); -INSERT INTO sys_admin.system_config VALUES ('morphology_classbreaks', '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'); +INSERT INTO sys_admin.system_config VALUES ('morphology_classbreaks', '1:#ff00dd,1.5,1.7,1.9,2.1,2.3,2.5:#f25f20,2.7,2.9,3.1:#f7e40e,3.3,3.5,4:#8ad51a,4.5,5,5.5,6,6.5,7:#1414ff'); INSERT INTO sys_admin.system_config VALUES ('morphology_classbreaks_compare', '-2:#06b100,-1.9,-1.8,-1.7,-1.6,-1.5,-1.4,-1.3,-1.2,-1.1,-1:#1cc68e,-0.9,-0.8,-0.7,-0.6,-0.5,-0.4,-0.3,-0.2,-0.1,0:#c2c2c2,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1:#fff01a,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2:#f80012'); COMMIT;