view cmd/tin2octree/main.go @ 904:e4b72a199258

New default bottleneck colors Mainly to make the stroke color one actually selectable in the ui. In addition the pink does better match the collors used on the ECDIS layer.
author Sascha Wilde <wilde@intevation.de>
date Tue, 02 Oct 2018 13:34:59 +0200
parents aeaa2adf5a8b
children a4fe07a21ba7
line wrap: on
line source

package main

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

	"github.com/golang/snappy"
	"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")
	utm        = flag.Bool("utm", true, "fetch in matchin UTM zone")

	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 (
	tinSQL = `
SELECT ST_AsBinary(ST_DelaunayTriangles(point_cloud::geometry, 0, 2))
FROM waterway.sounding_results
WHERE bottleneck_id = $1 AND date_info = $2
`
	tinUTMSQL = `
SELECT ST_AsBinary(
  ST_DelaunayTriangles(
	  ST_Transform(point_cloud::geometry, $3::int), 0, 2))
FROM waterway.sounding_results
WHERE bottleneck_id = $1 AND date_info = $2
`
	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) int {
	var pref int
	if y > 0 {
		pref = 32600
	} else {
		pref = 32700
	}
	zone := int(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 tin

	if err := run(func(db *sql.DB) error {
		var utmZ int

		if *utm {
			var cx, cy float64
			err := db.QueryRow(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()
		var err error
		if *utm {
			err = db.QueryRow(tinUTMSQL, *bottleneck, dateInfo, utmZ).Scan(&t)
		} else {
			err = db.QueryRow(tinSQL, *bottleneck, dateInfo).Scan(&t)
		}
		switch {
		case err == sql.ErrNoRows:
			return nil
		case err != nil:
			return err
		}
		log.Printf("query took: %s\n", time.Since(start))

		if *utm {
			t.epsg = uint32(utmZ)
		}

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

	tb := &treeBuilder{t: &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 := write(&buf, tb); 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 = write(f, tb)
		if err2 := f.Close(); err == nil {
			if err != nil {
				err = err2
			}
		}
		if err != nil {
			log.Fatalf("error: %v\n", err)
		}
	}
}

func write(w io.Writer, tb *treeBuilder) error {
	out := snappy.NewBufferedWriter(w)
	if err := tb.t.Serialize(out); err != nil {
		return err
	}
	if err := tb.Serialize(out); err != nil {
		return err
	}
	return out.Flush()
}