changeset 3828:885eb90255ff sld-colors

Cleanup templating of styles [WIP].
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Wed, 03 Jul 2019 18:26:41 +0200
parents 6028326b88d6
children c1be6beafb54
files pkg/geoserver/boot.go pkg/geoserver/templates.go pkg/geoserver/templates_test.go
diffstat 3 files changed, 111 insertions(+), 149 deletions(-) [+]
line wrap: on
line diff
--- a/pkg/geoserver/boot.go	Wed Jul 03 17:48:47 2019 +0200
+++ b/pkg/geoserver/boot.go	Wed Jul 03 18:26:41 2019 +0200
@@ -15,23 +15,18 @@
 
 import (
 	"bytes"
-	"context"
-	"database/sql"
 	"encoding/json"
 	"encoding/xml"
 	"fmt"
 	"io"
 	"log"
-	"math"
 	"net/http"
 	"net/url"
-	"strconv"
 	"strings"
-	"text/template"
+	"sync"
 
 	"golang.org/x/net/html/charset"
 
-	"gemma.intevation.de/gemma/pkg/auth"
 	"gemma.intevation.de/gemma/pkg/config"
 	"gemma.intevation.de/gemma/pkg/models"
 )
@@ -42,12 +37,11 @@
 	databaseScheme        = "waterway"
 	databaseType          = "postgis"
 	primaryKeyMetadataTbl = "waterway.gt_pk_metadata"
-
-	bootRole = "sys_admin"
+)
 
-	startupSQL           = `SELECT public.setrole('${user,'||encode('waterway_user', 'hex')||'}')`
-	closeupSQL           = `RESET ROLE`
-	selectClassBreaksSQL = `SELECT config_val FROM sys_admin.system_config WHERE config_key = 'morphology_classbreaks'`
+const (
+	startupSQL = `SELECT public.setrole('${user,'||encode('waterway_user', 'hex')||'}')`
+	closeupSQL = `RESET ROLE`
 )
 
 func basicAuth(user, password string) func(req *http.Request) {
@@ -398,112 +392,21 @@
 	return nil
 }
 
-type classBreak struct {
-	Low   string
-	High  string
-	Color string
-}
-
-func interpolateColor(i int, classBreaks [][]string) string {
-	var leftColor, rightColor string
+var (
+	stylePreprocessorsMu sync.Mutex
+	stylePreprocessors   = map[string]func(string) (string, error){}
+)
 
-	// look for next color to the left
-	lookLeft := i
-	for lookLeft >= 0 && leftColor == "" {
-		lookLeft -= 1
-		if len(classBreaks[lookLeft]) == 2 {
-			leftColor = classBreaks[lookLeft][1]
-		}
-	}
-	// look for next color to the right
-	lookRight := i
-	for lookRight < len(classBreaks) && rightColor == "" {
-		lookRight += 1
-		if len(classBreaks[lookRight]) == 2 {
-			rightColor = classBreaks[lookRight][1]
-		}
-	}
-
-	// calculate interpolation factor
-	middle, left, right := float32(i), float32(lookLeft), float32(lookRight)
-	interpolationFactor := (middle - left) / (right - left)
-
-	// convert colors to rgb
-	leftHex := []string{leftColor[1:3], leftColor[3:5], leftColor[5:7]}
-	leftR, _ := strconv.ParseFloat(leftHex[0], 32)
-	leftG, _ := strconv.ParseFloat(leftHex[1], 32)
-	leftB, _ := strconv.ParseFloat(leftHex[2], 32)
-	rightHex := []string{rightColor[1:3], rightColor[3:5], rightColor[5:7]}
-	rightR, _ := strconv.ParseFloat(rightHex[0], 32)
-	rightG, _ := strconv.ParseFloat(rightHex[1], 32)
-	rightB, _ := strconv.ParseFloat(rightHex[2], 32)
-
-	interpolatedR := leftR + float64(interpolationFactor)*(rightR-leftR)
-	interpolatedG := leftG + float64(interpolationFactor)*(rightG-leftG)
-	interpolatedB := leftB + float64(interpolationFactor)*(rightB-leftB)
-
-	// hexR := fmt.Sprintf("%x", math.Round(interpolatedR))
-	// hexG := fmt.Sprintf("%x", math.Round(interpolatedG))
-	// hexB := fmt.Sprintf("%x", math.Round(interpolatedB))
-	log.Printf("#%x%x%x", int(math.Round(interpolatedR)), int(math.Round(interpolatedG)), int(math.Round(interpolatedB)))
-
-	return "asd"
+func RegisterStylePreprocessor(name string, processor func(string) (string, error)) {
+	stylePreprocessorsMu.Lock()
+	defer stylePreprocessorsMu.Unlock()
+	stylePreprocessors[name] = processor
 }
 
-func getTemplateData(name string) interface{} {
-	if name == "sounding_results_contour_lines_geoserver" {
-		// get class breaks string from database
-		var classBreaksString string
-		ctx := context.Background()
-		err := auth.RunAs(
-			ctx, bootRole,
-			func(conn *sql.Conn) error {
-				return conn.QueryRowContext(ctx, selectClassBreaksSQL).Scan(&classBreaksString)
-			})
-		if err != nil {
-			log.Printf("error: %v\n", err)
-		}
-
-		// convert string to array of arrays [[num, color], [num], [num], [num, color] ...]
-		classBreaksTmp := strings.Split(classBreaksString, ",")
-		classBreaks := [][]string{}
-		for _, cb := range classBreaksTmp {
-			classBreaks = append(classBreaks, strings.Split(cb, ":"))
-		}
-
-		// interpolate colors
-		for i, cb := range classBreaks {
-			if len(cb) == 1 {
-				classBreaks[i] = append(cb, interpolateColor(i, classBreaks))
-			}
-		}
-
-		classBreaksForTmpl := []classBreak{}
-		for i := range classBreaks {
-			// set low/high
-			var low, high string
-			if i == 0 {
-				high = classBreaks[i][0]
-			} else {
-				low = classBreaks[i-1][0]
-				high = classBreaks[i][0]
-			}
-			classBreaksForTmpl = append(classBreaksForTmpl, classBreak{
-				Low:   low,
-				High:  high,
-				Color: classBreaks[i][1],
-			})
-		}
-		// add last class break (> last value)
-		classBreaksForTmpl = append(classBreaksForTmpl, classBreak{
-			Low:   classBreaks[len(classBreaks)-1][0],
-			High:  "",
-			Color: classBreaks[len(classBreaks)-1][1],
-		})
-		log.Printf("%v", classBreaksForTmpl)
-		return classBreaksForTmpl
-	}
-	return nil
+func FindStylePreprocessor(name string) func(string) (string, error) {
+	stylePreprocessorsMu.Lock()
+	defer stylePreprocessorsMu.Unlock()
+	return stylePreprocessors[name]
 }
 
 func updateStyle(entry *models.IntEntry, create bool) error {
@@ -516,15 +419,10 @@
 		return err
 	}
 
-	// Parse data as template
-	if entry.Name == "sounding_results_contour_lines_geoserver" {
-		tmpl, err := template.New(entry.Name).Parse(data)
-		if err != nil {
+	if processor := FindStylePreprocessor(entry.Name); processor != nil {
+		if data, err = processor(data); err != nil {
 			return err
 		}
-		var buffer bytes.Buffer
-		err = tmpl.Execute(&buffer, getTemplateData(entry.Name))
-		data = buffer.String()
 	}
 
 	var (
--- a/pkg/geoserver/templates.go	Wed Jul 03 17:48:47 2019 +0200
+++ b/pkg/geoserver/templates.go	Wed Jul 03 18:26:41 2019 +0200
@@ -14,13 +14,31 @@
 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 = 'morphology_classbreaks'`
+)
+
+func init() {
+	RegisterStylePreprocessor(
+		"sounding_results_contour_lines_geoserver",
+		templateSRContourLines)
+}
+
 type colorClass struct {
 	value float64
 	color color.RGBA
@@ -167,3 +185,72 @@
 
 	return final, err
 }
+
+func templateSRContourLines(data string) (string, error) {
+	tmpl, err := template.New("template").Parse(data)
+	if err != nil {
+		return "", err
+	}
+
+	var cb []classBreak
+
+	if cb, err = getSRContoursClassBreaks(); err != nil {
+		return "", err
+	}
+
+	var buf strings.Builder
+	if err = tmpl.Execute(&buf, cb); err != nil {
+		return "", err
+	}
+	return buf.String(), nil
+}
+
+type classBreak struct {
+	High    float64
+	HasHigh bool
+	Low     float64
+	HasLow  bool
+	Color   string
+}
+
+func (cc colorClasses) toClassBreaks() []classBreak {
+
+	cbs := make([]classBreak, len(cc))
+	for i := range cc {
+		if i > 0 {
+			cbs[i].Low = cc[i-1].value
+			cbs[i].HasLow = true
+		}
+		if i < len(cc)-1 {
+			cbs[i].High = cc[i].value
+			cbs[i].HasHigh = true
+		}
+		cbs[i].Color = fmt.Sprintf("#%02x%02x%02x",
+			cc[i].color.R,
+			cc[i].color.G,
+			cc[i].color.B)
+	}
+	return cbs
+}
+
+func getSRContoursClassBreaks() ([]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).Scan(&config)
+		},
+	); err != nil {
+		return nil, err
+	}
+
+	cc, err := parseColorClasses(config)
+	if err != nil {
+		return nil, err
+	}
+
+	return cc.toClassBreaks(), nil
+}
--- a/pkg/geoserver/templates_test.go	Wed Jul 03 17:48:47 2019 +0200
+++ b/pkg/geoserver/templates_test.go	Wed Jul 03 18:26:41 2019 +0200
@@ -14,7 +14,6 @@
 package geoserver
 
 import (
-	"fmt"
 	"strings"
 	"testing"
 	"text/template"
@@ -166,10 +165,10 @@
                 </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 }}
+                <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>
@@ -898,14 +897,6 @@
 
 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)
@@ -923,21 +914,7 @@
 		}
 	*/
 
-	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)
-	}
+	cbs := ccs.toClassBreaks()
 
 	tmpl, err := template.New("test").Parse(sldTmplTxt)
 	if err != nil {