changeset 3825:36129677ff24 sld-colors

fetch data for sld template from data base and interpolate colors (WIP)
author Markus Kottlaender <markus@intevation.de>
date Wed, 26 Jun 2019 17:42:51 +0200
parents a1bb7c894058
children 4b1184fa0326
files pkg/geoserver/boot.go
diffstat 1 files changed, 128 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/pkg/geoserver/boot.go	Wed Jun 26 14:17:18 2019 +0200
+++ b/pkg/geoserver/boot.go	Wed Jun 26 17:42:51 2019 +0200
@@ -14,18 +14,24 @@
 package geoserver
 
 import (
+	"context"
 	"bytes"
 	"encoding/json"
 	"encoding/xml"
 	"fmt"
+	"strconv"
+	"math"
 	"io"
 	"log"
 	"net/http"
 	"net/url"
 	"strings"
+	"text/template"
+	"database/sql"
 
 	"golang.org/x/net/html/charset"
 
+	"gemma.intevation.de/gemma/pkg/auth"
 	"gemma.intevation.de/gemma/pkg/config"
 	"gemma.intevation.de/gemma/pkg/models"
 )
@@ -36,11 +42,12 @@
 	databaseScheme        = "waterway"
 	databaseType          = "postgis"
 	primaryKeyMetadataTbl = "waterway.gt_pk_metadata"
-)
 
-const (
+	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'`
 )
 
 func basicAuth(user, password string) func(req *http.Request) {
@@ -391,6 +398,114 @@
 	return nil
 }
 
+type classBreak struct {
+	Low   string
+	High  string
+	Color string
+}
+
+func interpolateColor(i int, classBreaks [][]string) string {
+	var leftColor, rightColor string
+
+	// 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 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 updateStyle(entry *models.IntEntry, create bool) error {
 
 	log.Printf("info: creating style %s\n", entry.Name)
@@ -400,6 +515,17 @@
 	if err != nil {
 		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 {
+			return err
+		}
+		var buffer bytes.Buffer
+		err = tmpl.Execute(&buffer, getTemplateData(entry.Name))
+		data = buffer.String()
+	}
 
 	var (
 		geoURL   = config.GeoServerURL()