changeset 4296:95786a675d70

WIP: Started with downloading stretches as ESRI shapes. GET /api/data/stretch/shape/{stretch}
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Fri, 30 Aug 2019 18:34:27 +0200
parents 0bb735a412af
children a524e7d7e75f
files pkg/controllers/routes.go pkg/controllers/stretches.go pkg/wkb/data.go pkg/wkb/wkb.go
diffstat 4 files changed, 190 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/pkg/controllers/routes.go	Fri Aug 30 16:54:12 2019 +0200
+++ b/pkg/controllers/routes.go	Fri Aug 30 18:34:27 2019 +0200
@@ -309,6 +309,9 @@
 
 	// Handler to serve data to the client.
 
+	api.Handle("/data/{kind:stretch|section}/shape/{name}", any(
+		mw.DBConn(http.HandlerFunc(stretchShapeDownload)))).Methods(http.MethodGet)
+
 	api.Handle("/data/{kind:stretch|section}/availability/{name}", any(
 		mw.DBConn(http.HandlerFunc(stretchAvailabilty)))).Methods(http.MethodGet)
 
--- a/pkg/controllers/stretches.go	Fri Aug 30 16:54:12 2019 +0200
+++ b/pkg/controllers/stretches.go	Fri Aug 30 18:34:27 2019 +0200
@@ -26,6 +26,7 @@
 	"time"
 
 	"gemma.intevation.de/gemma/pkg/middleware"
+	"gemma.intevation.de/gemma/pkg/wkb"
 	"github.com/gorilla/mux"
 )
 
@@ -656,3 +657,115 @@
 		log.Printf("error: %v\n", err)
 	}
 }
+
+const (
+	selectStretchSQL = `
+SELECT
+  id,
+  isrs_asText(lower(stretch)),
+  isrs_asText(upper(stretch)),
+  ST_AsBinary(area::geometry),
+  objnam,
+  nobjnam,
+  date_info,
+  source_organization
+FROM waterway.stretches WHERE
+  staging_done AND
+  name = $1`
+
+	selectStretchCountriesSQL = `
+SELECT country_code FROM waterway.stretch_countries
+WHERE stretches_id = $1
+ORDER BY country_code`
+)
+
+func stretchShapeDownload(rw http.ResponseWriter, req *http.Request) {
+	vars := mux.Vars(req)
+	stretch := vars["kind"] == "stretch"
+	name := vars["name"]
+
+	// TODO: Implement me!
+	if !stretch {
+		http.Error(
+			rw, "Not implemented, yet!",
+			http.StatusBadRequest)
+		return
+	}
+
+	conn := middleware.GetDBConn(req)
+	ctx := req.Context()
+
+	tx, err := conn.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
+	if err != nil {
+		http.Error(
+			rw, fmt.Sprintf("DB error: %v.", err),
+			http.StatusInternalServerError)
+		return
+	}
+	defer tx.Rollback()
+
+	var (
+		id           int64
+		lower, upper string
+		data         []byte
+		objnam       string
+		nobjnam      sql.NullString
+		dateInfo     time.Time
+		source       string
+	)
+
+	switch err := tx.QueryRowContext(ctx, selectStretchSQL, name).Scan(
+		&id,
+		&lower, &upper,
+		&data,
+		&objnam, &nobjnam,
+		&dateInfo,
+		&source,
+	); {
+	case err == sql.ErrNoRows:
+		http.NotFound(rw, req)
+		return
+	case err != nil:
+		http.Error(
+			rw, fmt.Sprintf("DB error: %v.", err),
+			http.StatusInternalServerError)
+		return
+	}
+
+	var countries []string
+
+	if err := func() error {
+		rows, err := tx.Query(selectStretchCountriesSQL, id)
+		if err != nil {
+			return err
+		}
+		defer rows.Close()
+
+		for rows.Next() {
+			var country string
+			if err := rows.Scan(&country); err != nil {
+				return err
+			}
+			countries = append(countries, country)
+		}
+
+		return rows.Err()
+	}(); err != nil {
+		http.Error(
+			rw, fmt.Sprintf("DB error: %v.", err),
+			http.StatusInternalServerError)
+		return
+	}
+
+	var geom wkb.MultiPolygonGeom
+
+	if err := geom.FromWKB(data); err != nil {
+		http.Error(
+			rw, fmt.Sprintf("Decoding WKB error: %v.", err),
+			http.StatusInternalServerError)
+		return
+	}
+
+	// TODO: Serialize as ESRI shapefile.
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pkg/wkb/data.go	Fri Aug 30 18:34:27 2019 +0200
@@ -0,0 +1,73 @@
+// 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 wkb
+
+import (
+	"bytes"
+	"encoding/binary"
+	"fmt"
+	"log"
+)
+
+type (
+	PointGeom struct {
+		X float64
+		Y float64
+	}
+	LinearRingGeom   []PointGeom
+	PolygonGeom      []LinearRingGeom
+	MultiPolygonGeom []PolygonGeom
+)
+
+func (mpg *MultiPolygonGeom) FromWKB(data []byte) error {
+	r := bytes.NewReader(data)
+
+	endian, err := r.ReadByte()
+
+	var order binary.ByteOrder
+
+	switch {
+	case err != nil:
+		return err
+	case endian == NDR:
+		order = binary.LittleEndian
+	case endian == XDR:
+		order = binary.BigEndian
+	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 != MultiPolygon:
+		return fmt.Errorf("unknown geometry type %x", geomType)
+	}
+
+	var numPolygons int32
+	if err := binary.Read(r, order, &numPolygons); err != nil {
+		return err
+	}
+	log.Printf("info: num polygons: %d\n", numPolygons)
+
+	polygons := make([]PolygonGeom, numPolygons)
+
+	// TODO: Parse the polygons.
+
+	*mpg = polygons
+	return nil
+}
--- a/pkg/wkb/wkb.go	Fri Aug 30 16:54:12 2019 +0200
+++ b/pkg/wkb/wkb.go	Fri Aug 30 18:34:27 2019 +0200
@@ -25,6 +25,7 @@
 	LineString       uint32 = 2
 	Polygon          uint32 = 3
 	MultiLineString  uint32 = 5
+	MultiPolygon     uint32 = 6
 	PointZ           uint32 = 1000 + 1
 	LineStringZ      uint32 = 1000 + 2
 	MultiPointZ      uint32 = 1000 + 4