changeset 808:e10a8a682297

Cross sections: Clip result against area of the sounding result. This also includes the re-projections back to WGS84 and encoding the final result as GeoJSON.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 27 Sep 2018 16:30:15 +0200
parents 34b2cfcab24c
children 13f80b9033c8
files pkg/controllers/cross.go pkg/models/cross.go
diffstat 2 files changed, 133 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/pkg/controllers/cross.go	Thu Sep 27 16:13:59 2018 +0200
+++ b/pkg/controllers/cross.go	Thu Sep 27 16:30:15 2018 +0200
@@ -54,6 +54,32 @@
 	return out, nil
 }
 
+const clipSQL = `
+SELECT ST_AsBinary(ST_Transform(ST_Multi(ST_Intersection(
+  ST_Transform(area::geometry, $1::integer),
+  ST_GeomFromWKB($2, $1::integer))), 4326))
+FROM waterway.sounding_results
+WHERE bottleneck_id = $3 AND date_info = $4::date`
+
+func clipAgainstArea(
+	line octree.MultiLineStringZ,
+	epsg uint32,
+	bottleneck string,
+	dateInfo time.Time,
+	conn *sql.Conn,
+	ctx context.Context,
+) (models.GeoJSONMultiLineCoordinatesZ, error) {
+
+	var mls models.GeoJSONMultiLineCoordinatesZ
+	err := conn.QueryRowContext(
+		ctx, clipSQL,
+		epsg, line.AsWKB(),
+		bottleneck, dateInfo,
+	).Scan(&mls)
+
+	return mls, err
+}
+
 func crossSection(
 	input interface{},
 	req *http.Request,
@@ -124,15 +150,16 @@
 	}
 	log.Printf("octree traversal took: %s\n", time.Since(start))
 
-	// The result have to be WGS84. So project the result back.
 	start = time.Now()
 
-	rp.FromEPSG, rp.ToEPSG = tree.EPSG, WGS84
+	var joined models.GeoJSONMultiLineCoordinatesZ
+	joined, err = clipAgainstArea(
+		segments, tree.EPSG,
+		csi.Properties.Bottleneck, csi.Properties.Date.Time,
+		conn, req.Context(),
+	)
 
-	var joined models.GeoJSONMultiLineCoordinatesZ
-	joined, err = projectBack(rp, segments, req.Context())
-
-	log.Printf("projecting back took: %s\n", time.Since(start))
+	log.Printf("clipping and projecting back took: %s\n", time.Since(start))
 	if err != nil {
 		return
 	}
--- a/pkg/models/cross.go	Thu Sep 27 16:13:59 2018 +0200
+++ b/pkg/models/cross.go	Thu Sep 27 16:30:15 2018 +0200
@@ -133,10 +133,11 @@
 }
 
 const (
-	wkbXDR         byte   = 0
-	wkbNDR         byte   = 1
-	wkbLineString  uint32 = 2
-	wkbLineStringZ uint32 = 1000 + 2
+	wkbXDR              byte   = 0
+	wkbNDR              byte   = 1
+	wkbLineString       uint32 = 2
+	wkbLineStringZ      uint32 = 1000 + 2
+	wkbMultiLineStringZ uint32 = 1000 + 5
 )
 
 func (lc GeoJSONLineCoordinates) AsWKB() []byte {
@@ -256,3 +257,98 @@
 	x := dLng * math.Cos(deg2rad((cz.Lat+other.Lat)/2.0))
 	return math.Sqrt(dLat*dLat+x*x) * EarthRadius
 }
+
+func (mls *GeoJSONMultiLineCoordinatesZ) FromWKB(data []byte) error {
+	r := bytes.NewReader(data)
+
+	var order binary.ByteOrder
+
+	endian, err := r.ReadByte()
+
+	switch {
+	case err != nil:
+		return err
+	case endian == wkbNDR:
+		order = binary.LittleEndian
+	case endian == wkbXDR:
+		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 != wkbMultiLineStringZ:
+		return fmt.Errorf("unknown geometry type %d", geomType)
+	}
+
+	var numLines uint32
+	if err = binary.Read(r, order, &numLines); err != nil {
+		return err
+	}
+
+	lines := make(GeoJSONMultiLineCoordinatesZ, numLines)
+
+	for i := range lines {
+		endian, err = r.ReadByte()
+
+		switch {
+		case err != nil:
+			return err
+		case endian == wkbNDR:
+			order = binary.LittleEndian
+		case endian == wkbXDR:
+			order = binary.BigEndian
+		default:
+			return fmt.Errorf("unknown byte order %x", endian)
+		}
+
+		err = binary.Read(r, order, &geomType)
+		switch {
+		case err != nil:
+			return err
+		case geomType != wkbLineStringZ:
+			return fmt.Errorf("unknown geometry type %d", geomType)
+		}
+
+		var numPoints uint32
+		if err = binary.Read(r, order, &numPoints); err != nil {
+			return err
+		}
+
+		points := make(GeoJSONLineCoordinatesZ, numPoints)
+		for j := range points {
+			var lat, lon, z uint64
+			if err = binary.Read(r, order, &lat); err != nil {
+				return err
+			}
+			if err = binary.Read(r, order, &lon); err != nil {
+				return err
+			}
+			if err = binary.Read(r, order, &z); err != nil {
+				return err
+			}
+			c := &points[j]
+			c.Lat = math.Float64frombits(lat)
+			c.Lon = math.Float64frombits(lon)
+			c.Z = math.Float64frombits(z)
+		}
+		lines[i] = points
+	}
+
+	*mls = lines
+
+	return nil
+}
+
+func (mls *GeoJSONMultiLineCoordinatesZ) Scan(src interface{}) error {
+	data, ok := src.([]byte)
+	if !ok {
+		return errNoByteSlice
+	}
+	return mls.FromWKB(data)
+}