view cmd/octreediff/main.go @ 2468:27cc04bbda46 octree-diff

Bail out if the EPSG codes of the datasets don't match. Need to implement a transformation slow pass to make this work.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Mon, 25 Feb 2019 21:51:39 +0100
parents dbdc50d9e0f8
children 1312953defab
line wrap: on
line source

// 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) 2018 by via donau
//   – Österreichische Wasserstraßen-Gesellschaft mbH
// Software engineering by Intevation GmbH
//
// Author(s):
//  * Sascha L. Teichmann <sascha.teichmann@intevation.de>

package main

import (
	"context"
	"database/sql"
	"errors"
	"flag"
	"fmt"
	"log"
	"runtime"
	"sync"
	"time"

	"gemma.intevation.de/gemma/pkg/common"
	"gemma.intevation.de/gemma/pkg/octree"
)

var (
	bottleneck = flag.String("bottleneck", "", "name of the bottleneck")
	first      = flag.String("first", "", "date of the first sounding result")
	second     = flag.String("second", "", "date of the second sounding result")
)

const (
	loadOctreeSQL = `
SELECT sr.octree_index
FROM waterway.sounding_results sr JOIN waterway.bottlenecks bn
  ON sr.bottleneck_id = bn.id
WHERE bn.bottleneck_id = $1 AND sr.date_info = $2::date
  AND sr.octree_index IS NOT NULL`
)

func check(err error) {
	if err != nil {
		log.Fatalf("error: %v\n", err)
	}
}

func process(bottleneck string, firstDate, secondDate time.Time) error {
	start := time.Now()
	defer func() { log.Printf("processing took %v\n", time.Since(start)) }()

	ctx := context.Background()

	return run(func(db *sql.DB) error {

		conn, err := db.Conn(ctx)
		if err != nil {
			return err
		}
		defer conn.Close()

		type load struct {
			date time.Time
			data []byte
			err  *error
			dst  **octree.Tree
		}

		out := make(chan *load)
		wg := new(sync.WaitGroup)

		n := runtime.NumCPU()
		if n > 2 {
			n = 2
		}

		for i := 0; i < n; i++ {
			wg.Add(1)
			go func() {
				defer wg.Done()
				for l := range out {
					if *l.err == nil {
						*l.dst, *l.err = octree.Deserialize(l.data)
					}
				}
			}()
		}

		var firstErr, secondErr error
		var first, second *octree.Tree

		for _, l := range []*load{
			{date: firstDate, dst: &first, err: &firstErr},
			{date: secondDate, dst: &second, err: &secondErr},
		} {
			var data []byte
			if err := conn.QueryRowContext(
				ctx,
				loadOctreeSQL,
				bottleneck,
				l.date,
			).Scan(&data); err != nil {
				*l.err = err
			} else {
				l.data = data
			}
			out <- l
		}
		close(out)

		wg.Wait()

		if firstErr != nil || secondErr != nil {
			if firstErr != nil && secondErr != nil {
				return fmt.Errorf("%v, %v", firstErr, secondErr)
			}
			if firstErr != nil {
				return firstErr
			}
			return secondErr
		}

		if first.EPSG != second.EPSG {
			return errors.New("EPSG codes mismatch. Needs transformation slow pass.")
		}

		log.Printf("loading took %v\n", time.Since(start))

		setin := time.Now()

		vf := first.Vertices()
		nvf := make([]octree.Vertex, 0, len(vf))
		for i := range vf {
			v := &vf[i]
			if z, found := second.Value(v.X, v.Y); found {
				nvf = append(nvf, octree.Vertex{v.X, v.Y, v.Z - z})
			}
		}

		vs := second.Vertices()
		nsf := make([]octree.Vertex, 0, len(vs))
		for i := range vs {
			v := &vs[i]
			if z, found := first.Value(v.X, v.Y); found {
				nsf = append(nsf, octree.Vertex{v.X, v.Y, z - v.Z})
			}
		}

		log.Printf("setting in took %v\n", time.Since(setin))
		log.Printf("new vertices first: %d\n", len(nvf))
		log.Printf("new vertices second: %d\n", len(nsf))

		return nil
	})
}

func main() {

	flag.Parse()

	firstDate, err := time.Parse(common.DateFormat, *first)
	check(err)
	secondDate, err := time.Parse(common.DateFormat, *second)
	check(err)

	if *bottleneck == "" {
		log.Fatalln("Missing bottleneck name")
	}

	check(process(*bottleneck, firstDate, secondDate))
}