view cmd/tin2octree/main.go @ 971:f9fb6c399f3f

Moved generating of tins to octree package.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 18 Oct 2018 11:52:13 +0200
parents a4e003ba0074
children
line wrap: on
line source

package main

import (
	"bytes"
	"context"
	"crypto/sha1"
	"database/sql"
	"encoding/base64"
	"flag"
	"fmt"
	"io"
	"log"
	"math"
	"os"
	"time"

	"gemma.intevation.de/gemma/pkg/octree"
	"github.com/jackc/pgx"
	"github.com/jackc/pgx/stdlib"
)

var (
	bottleneck = flag.String("bottleneck", "", "bottleneck id")
	date       = flag.String("date", "", "date info")
	file       = flag.String("file", "", "save to file")
	insert     = flag.Bool("insert", false, "write as SQL insert statement")

	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)
}

const (
	centroidSQL = `
SELECT ST_X(ST_Centroid(point_cloud::geometry)), ST_Y(ST_Centroid(point_cloud::geometry))
FROM waterway.sounding_results
WHERE bottleneck_id = $1 AND date_info = $2
`
)

func utmZone(x, y float64) uint32 {
	var pref uint32
	if y > 0 {
		pref = 32600
	} else {
		pref = 32700
	}
	zone := uint32(math.Floor((x+180)/6)) + 1
	return zone + pref
}

func main() {
	flag.Parse()

	if *bottleneck == "" || *date == "" {
		log.Fatalln("missing bottleneck or date option.")
	}

	dateInfo, err := time.Parse("2006-01-02", *date)
	if err != nil {
		log.Fatalf("error: %v\n", err)
	}

	var t *octree.Tin

	ctx := context.Background()

	if err := run(func(db *sql.DB) error {
		conn, err := db.Conn(ctx)
		if err != nil {
			return err
		}
		defer conn.Close()

		var utmZ uint32
		var cx, cy float64

		conn.QueryRowContext(ctx, centroidSQL, *bottleneck, dateInfo).Scan(&cx, &cy)
		switch {
		case err == sql.ErrNoRows:
			return nil
		case err != nil:
			return err
		}
		log.Printf("lat/lon: [%f, %f]\n", cx, cy)
		utmZ = utmZone(cx, cy)
		log.Printf("UTM zone: %d\n", utmZ)

		start := time.Now()
		t, err = octree.GenerateTin(conn, ctx, *bottleneck, dateInfo, utmZ)
		log.Printf("query took: %s\n", time.Since(start))
		return err
	}); err != nil {
		log.Fatalf("error: %v\n", err)
	}

	if t == nil {
		log.Fatalf("error: No such sounding result (%s, %s)\n",
			*bottleneck, dateInfo)
	}

	tb := octree.NewBuilder(t)
	tb.Build()

	if *insert {
		var w io.Writer
		var f *os.File

		if *file != "" {
			if f, err = os.Create(*file); err != nil {
				log.Fatalf("error: %v\n", err)
			}
			w = f
		} else {
			w = os.Stdout
		}

		var buf bytes.Buffer
		if err := tb.WriteTo(&buf); err != nil {
			log.Fatalf("error: %v\n", err)
		}
		data := buf.String()
		h := sha1.New()
		buf.WriteTo(h)
		fmt.Fprintln(w, "BEGIN;")
		fmt.Fprintln(w, "INSERT INTO waterway.octrees")
		fmt.Fprintf(w, "SELECT sr.id, '%x',\n", h.Sum(nil))
		fmt.Fprint(w, "decode('")
		fmt.Fprintf(w, "%s", base64.StdEncoding.EncodeToString([]byte(data)))
		fmt.Fprintln(w, "', 'base64')")
		fmt.Fprintln(w, "FROM waterway.sounding_results sr")
		fmt.Fprintf(w,
			"WHERE sr.bottleneck_id = '%s' AND sr.date_info = '%s'::date;\n",
			*bottleneck, *date)
		fmt.Fprintln(w, "END;")

		if f != nil {
			if err := f.Close(); err != nil {
				log.Fatalf("error: %v\n", err)
			}
		}
		return
	}

	if *file != "" {
		f, err := os.Create(*file)
		if err != nil {
			log.Printf("error: %v\n", err)
		}
		err = tb.WriteTo(f)
		if err2 := f.Close(); err == nil {
			if err != nil {
				err = err2
			}
		}
		if err != nil {
			log.Fatalf("error: %v\n", err)
		}
	}
}