changeset 643:756f3fc62da6

Cross sections: Using the database in the web service.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 13 Sep 2018 12:09:23 +0200
parents e95df6657023
children c1a31858ad54
files pkg/controllers/cross.go pkg/models/common.go pkg/models/cross.go pkg/models/user.go
diffstat 4 files changed, 173 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/pkg/controllers/cross.go	Thu Sep 13 10:31:22 2018 +0200
+++ b/pkg/controllers/cross.go	Thu Sep 13 12:09:23 2018 +0200
@@ -7,16 +7,62 @@
 	"gemma.intevation.de/gemma/pkg/models"
 )
 
+// TODO: This is hardcoded on SLT's model of a concrete sounding result.
+const crossSQL = `
+SELECT ST_AsBinary((ST_Dump(ST_Multi(ST_3DIntersection(
+  ST_Translate(
+    ST_Extrude(
+      ST_FromWKB($1, 4326),
+    0, 0, 100),
+   0, 0, -150),
+   geom)))).geom)
+FROM b442017
+WHERE ST_Intersects(geom, ST_FromWKB($1, 4326))
+`
+
 func crossSection(
 	input interface{},
 	req *http.Request,
 	db *sql.Conn,
 ) (jr JSONResult, err error) {
 
-	// TODO: Implement me!
 	csi := input.(*models.CrossSectionInput)
 
-	_ = csi
+	// TODO: Use these properties in query
+	_ = csi.Properties.Bottleneck
+	_ = csi.Properties.Date
+
+	var rows *sql.Rows
+
+	rows, err = db.QueryContext(req.Context(), csi.Coordinates.AsWKB())
+	if err != nil {
+		return
+	}
+	defer rows.Close()
+
+	var segments []models.GeoJSONLineCoordinatesZ
+
+	for rows.Next() {
+		var segment models.GeoJSONLineCoordinatesZ
+		if err = rows.Scan(&segment); err != nil {
+			return
+		}
+		segments = append(segments, segment)
+	}
+
+	if err = rows.Err(); err != nil {
+		return
+	}
+
+	// TODO: Join the segments.
+
+	jr = JSONResult{
+		Result: &models.CrossSectionOutput{
+			Type:        "Feature",
+			Geometry:    "MultLineString",
+			Coordinates: segments,
+		},
+	}
 
 	return
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pkg/models/common.go	Thu Sep 13 12:09:23 2018 +0200
@@ -0,0 +1,5 @@
+package models
+
+import "errors"
+
+var errNoString = errors.New("Not a string")
--- a/pkg/models/cross.go	Thu Sep 13 10:31:22 2018 +0200
+++ b/pkg/models/cross.go	Thu Sep 13 12:09:23 2018 +0200
@@ -1,8 +1,13 @@
 package models
 
 import (
+	"bytes"
+	"encoding/binary"
 	"encoding/json"
 	"errors"
+	"fmt"
+	"math"
+	"strings"
 	"time"
 )
 
@@ -21,12 +26,19 @@
 		Lat float64
 	}
 
+	GeoJSONCoordinateZ struct {
+		Lon float64
+		Lat float64
+		Z   float64
+	}
+
 	CrossSectionInputProperties struct {
 		Bottleneck string      `json:"bottleneck"`
 		Date       GeoJSONDate `json:"date"`
 	}
 
-	GeoJSONLineCoordinates []GeoJSONCoordinate
+	GeoJSONLineCoordinates  []GeoJSONCoordinate
+	GeoJSONLineCoordinatesZ []GeoJSONCoordinateZ
 
 	CrossSectionInput struct {
 		Type        GeoJSONFeature              `json:"type"`
@@ -34,6 +46,12 @@
 		Coordinates GeoJSONLineCoordinates      `json:"coordinates"`
 		Properties  CrossSectionInputProperties `json:"properties"`
 	}
+
+	CrossSectionOutput struct {
+		Type        string                    `json:"type"`
+		Geometry    string                    `json:"geometry"`
+		Coordinates []GeoJSONLineCoordinatesZ `json:"coordinates"`
+	}
 )
 
 var (
@@ -103,3 +121,104 @@
 	*t = GeoJSONLineStringType(s)
 	return nil
 }
+
+const (
+	wkbXDR         byte   = 0
+	wkbNDR         byte   = 1
+	wkbPoint       uint32 = 1
+	wkbPointZ      uint32 = 1000 + 1
+	wkbLineString  uint32 = 2
+	wkbLineStringZ uint32 = 1000 + 2
+)
+
+func (lc GeoJSONLineCoordinates) AsWKB() string {
+
+	size := 1 + 4 + 4 + len(lc)*(1+4+2*8)
+
+	buf := bytes.NewBuffer(make([]byte, 0, size))
+
+	binary.Write(buf, binary.LittleEndian, wkbNDR)
+	binary.Write(buf, binary.LittleEndian, wkbLineString)
+	binary.Write(buf, binary.LittleEndian, uint32(len(lc)))
+
+	for i := range lc {
+		c := &lc[i]
+		binary.Write(buf, binary.LittleEndian, wkbNDR)
+		binary.Write(buf, binary.LittleEndian, wkbPoint)
+		binary.Write(buf, binary.LittleEndian, math.Float64bits(c.Lon))
+		binary.Write(buf, binary.LittleEndian, math.Float64bits(c.Lat))
+	}
+
+	return buf.String()
+}
+
+func (cz GeoJSONCoordinateZ) MarshalJSON() ([]byte, error) {
+	var buf bytes.Buffer
+	fmt.Fprintf(&buf, "[%.8f %.8f %.8f]", cz.Lon, cz.Lat, cz.Z)
+	return buf.Bytes(), nil
+}
+
+func (lcz *GeoJSONLineCoordinatesZ) Scan(src interface{}) error {
+	data, ok := src.(string)
+	if !ok {
+		return errNoString
+	}
+	return lcz.FromWKB(data)
+}
+
+func (lcz *GeoJSONLineCoordinatesZ) FromWKB(data string) error {
+
+	r := strings.NewReader(data)
+
+	endian, err := r.ReadByte()
+
+	var order binary.ByteOrder
+
+	switch {
+	case err != nil:
+		return err
+	case endian == wkbNDR:
+		order = binary.BigEndian
+	case endian == wkbXDR:
+		order = binary.LittleEndian
+	default:
+		return fmt.Errorf("unknown byte order %x", endian)
+	}
+
+	var geomType uint32
+	err = binary.Read(r, order, &geomType)
+
+	switch {
+	case err != nil:
+		return err
+	case geomType != wkbLineStringZ:
+		return fmt.Errorf("unknown geometry type %d", geomType)
+	}
+
+	var num uint32
+	if err = binary.Read(r, order, &num); err != nil {
+		return err
+	}
+
+	coords := make(GeoJSONLineCoordinatesZ, num)
+
+	for i := range coords {
+		err = binary.Read(r, order, &geomType)
+		switch {
+		case err != nil:
+			return err
+		case geomType != wkbPointZ:
+			return fmt.Errorf("unknown geometry type %d", geomType)
+		}
+		c := &coords[i]
+		for _, addr := range []*float64{&c.Lat, &c.Lon, &c.Z} {
+			if err = binary.Read(r, order, addr); err != nil {
+				return err
+			}
+		}
+	}
+
+	*lcz = coords
+
+	return nil
+}
--- a/pkg/models/user.go	Thu Sep 13 10:31:22 2018 +0200
+++ b/pkg/models/user.go	Thu Sep 13 12:09:23 2018 +0200
@@ -49,7 +49,6 @@
 			`|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])`)
 
 	errNoEmailAddress = errors.New("Not a valid email address")
-	errNoString       = errors.New("Not a string")
 )
 
 func (e *Email) UnmarshalJSON(data []byte) error {