changeset 694:a9783d8f74ed octree

octree: Store contour lines into postgres/postgis.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 20 Sep 2018 15:50:07 +0200
parents 614135d69823
children 37c08d8487c3
files cmd/octree2contour/db.go cmd/octree2contour/main.go cmd/octree2contour/store.go
diffstat 3 files changed, 118 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmd/octree2contour/db.go	Thu Sep 20 15:50:07 2018 +0200
@@ -0,0 +1,39 @@
+package main
+
+import (
+	"database/sql"
+	"flag"
+
+	"github.com/jackc/pgx"
+	"github.com/jackc/pgx/stdlib"
+)
+
+var (
+	dbhost     = flag.String("dbhost", "localhost", "database host")
+	dbport     = flag.Uint("dbport", 5432, "database port")
+	dbname     = flag.String("dbname", "gemma", "database user")
+	dbuser     = flag.String("dbuser", "scott", "database user")
+	dbpassword = flag.String("dbpw", "tiger", "database password")
+	dbssl      = flag.String("dbssl", "prefer", "database SSL mode")
+)
+
+func run(fn func(*sql.DB) error) error {
+
+	// To ease SSL config ride a bit on parsing.
+	cc, err := pgx.ParseConnectionString("sslmode=" + *dbssl)
+	if err != nil {
+		return err
+	}
+
+	// Do the rest manually to allow whitespace in user/password.
+	cc.Host = *dbhost
+	cc.Port = uint16(*dbport)
+	cc.User = *dbuser
+	cc.Password = *dbpassword
+	cc.Database = *dbname
+
+	db := stdlib.OpenDB(cc)
+	defer db.Close()
+
+	return fn(db)
+}
--- a/cmd/octree2contour/main.go	Thu Sep 20 13:19:49 2018 +0200
+++ b/cmd/octree2contour/main.go	Thu Sep 20 15:50:07 2018 +0200
@@ -15,11 +15,6 @@
 	max  = flag.Float64("m", 10, "max height from lowest point")
 )
 
-type result struct {
-	h     float64
-	lines [][]vertex
-}
-
 func processLevels(
 	tree *octree,
 	jobs <-chan float64,
@@ -28,7 +23,7 @@
 ) {
 	defer wg.Done()
 	for h := range jobs {
-		var lines [][]vertex
+		var lines multiLineStringZ
 		tree.horizontal(h, func(t *triangle) {
 			line := t.intersectH(h)
 			if len(line) > 1 {
@@ -42,7 +37,7 @@
 func process(tree *octree) []result {
 
 	if *one {
-		var lines [][]vertex
+		var lines multiLineStringZ
 		tree.horizontal(*step, func(t *triangle) {
 			line := t.intersectH(*step)
 			if len(line) > 0 {
@@ -87,21 +82,10 @@
 	return all
 }
 
-func store(all []result, fname string) error {
-	for i := range all {
-		a := &all[i]
-		log.Printf("level %f: %d\n", a.h, len(a.lines))
-	}
-	return nil
-}
-
 func main() {
 	flag.Parse()
 
-	files := flag.Args()
-
-	for i := 0; i < len(files); i += 2 {
-		fname := files[i]
+	for _, fname := range flag.Args() {
 		log.Printf("processing %s\n", fname)
 		start := time.Now()
 		tree, err := loadOctree(fname)
@@ -113,14 +97,8 @@
 		start = time.Now()
 		all := process(tree)
 		log.Printf("processing took: %v\n", time.Since(start))
-		var outname string
-		if i+1 < len(files) {
-			outname = files[i+1]
-		} else {
-			outname = "out.shp"
-		}
 		start = time.Now()
-		if err = store(all, outname); err != nil {
+		if err = store(all, tree.epsg); err != nil {
 			log.Printf("error: %v\n", err)
 		}
 		log.Printf("storing took: %v\n", time.Since(start))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmd/octree2contour/store.go	Thu Sep 20 15:50:07 2018 +0200
@@ -0,0 +1,75 @@
+package main
+
+import (
+	"bytes"
+	"database/sql"
+	"encoding/binary"
+	"math"
+)
+
+type multiLineStringZ [][]vertex
+
+const (
+	wkbNDR              byte   = 1
+	wkbPointZ           uint32 = 1000 + 1
+	wkbLineStringZ      uint32 = 1000 + 2
+	wkbMultiLineStringZ uint32 = 1000 + 5
+)
+
+type result struct {
+	h     float64
+	lines multiLineStringZ
+}
+
+const insertSQL = `
+INSERT INTO waterway.contour_lines (height, geom)
+VALUES ($1, ST_Transform(
+	ST_SetSRID(ST_GeomFromWKB($2), $3), 4326)::geography)
+`
+
+func store(all []result, epsg uint32) error {
+
+	return run(func(db *sql.DB) error {
+
+		tx, err := db.Begin()
+		if err != nil {
+			return err
+		}
+		defer tx.Rollback()
+
+		stmt, err := tx.Prepare(insertSQL)
+		if err != nil {
+			return err
+		}
+
+		for _, r := range all {
+			if _, err := stmt.Exec(r.h, r.lines.asWKB(), epsg); err != nil {
+				return err
+			}
+		}
+
+		return tx.Commit()
+	})
+}
+
+func (mls multiLineStringZ) asWKB() []byte {
+
+	var buf bytes.Buffer
+
+	binary.Write(&buf, binary.LittleEndian, wkbNDR)
+	binary.Write(&buf, binary.LittleEndian, wkbMultiLineStringZ)
+	binary.Write(&buf, binary.LittleEndian, uint32(len(mls)))
+
+	for _, ml := range mls {
+		binary.Write(&buf, binary.LittleEndian, wkbNDR)
+		binary.Write(&buf, binary.LittleEndian, wkbLineStringZ)
+		binary.Write(&buf, binary.LittleEndian, uint32(len(ml)))
+		for _, p := range ml {
+			binary.Write(&buf, binary.LittleEndian, math.Float64bits(p.x))
+			binary.Write(&buf, binary.LittleEndian, math.Float64bits(p.y))
+			binary.Write(&buf, binary.LittleEndian, math.Float64bits(p.z))
+		}
+	}
+
+	return buf.Bytes()
+}