view cmd/soundingresults/points.go @ 715:23b68cd4bed3

client: re-enable loading of full river bank marks * Remove the bboxFilter that restricted loading of external distance_marks to the extend of the Austrian Danbue. Because of the improved loading strategy, this is more feasable, but still there maybe issue when trying to display everything.
author Bernhard Reiter <bernhard@intevation.de>
date Fri, 21 Sep 2018 13:57:40 +0200
parents bc2b7da07d60
children
line wrap: on
line source

package main

import (
	"bufio"
	"bytes"
	"compress/bzip2"
	"compress/gzip"
	"encoding/binary"
	"io"
	"log"
	"math"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

type point3d struct {
	x float64
	y float64
	z float64
}

type points3d []*point3d

func parseXYZ(fname string) (points3d, error) {
	f, err := os.Open(fname)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	r, err := wrap(fname, f)
	if err != nil {
		return nil, err
	}
	return readXYZ(r)
}

func wrap(fname string, f io.Reader) (io.Reader, error) {

	switch strings.ToLower(filepath.Ext(fname)) {
	case ".gz":
		return gzip.NewReader(f)
	case ".bz2":
		return bzip2.NewReader(f), nil
	}

	return bufio.NewReader(f), nil
}

func readXYZ(r io.Reader) (points3d, error) {

	// Alloc in larger chunks to reduce pressure on memory management.
	var chunk []point3d
	alloc := func() *point3d {
		if len(chunk) == 0 {
			chunk = make([]point3d, 8*1024)
		}
		p := &chunk[0]
		chunk = chunk[1:]
		return p
	}

	var points points3d

	s := bufio.NewScanner(r)
	if s.Scan() { // Skip header line.
		for line := 2; s.Scan(); line++ {
			p := alloc()
			text := s.Text()
			// fmt.Sscanf(text, "%f,%f,%f") is 4 times slower.
			idx := strings.IndexByte(text, ',')
			if idx == -1 {
				log.Printf("format error in line %d\n", line)
				continue
			}
			var err error
			if p.x, err = strconv.ParseFloat(text[:idx], 64); err != nil {
				log.Printf("format error in line %d: %v\n", line, err)
				continue
			}
			text = text[idx+1:]
			if idx = strings.IndexByte(text, ','); idx == -1 {
				log.Printf("format error in line %d\n", line)
				continue
			}
			if p.y, err = strconv.ParseFloat(text[:idx], 64); err != nil {
				log.Printf("format error in line %d: %v\n", line, err)
				continue
			}
			text = text[idx+1:]
			if p.z, err = strconv.ParseFloat(text, 64); err != nil {
				log.Printf("format error in line %d: %v\n", line, err)
				continue
			}
			points = append(points, p)
		}
	}

	return points, s.Err()
}

const (
	wkbNDR         byte   = 1
	wkbPointZ      uint32 = 1 + 1000
	wkbMultiPointZ uint32 = 4 + 1000
)

func (ps points3d) asWKB() string {

	size := 1 + 4 + 4 + len(ps)*(1+4+3*8)

	buf := bytes.NewBuffer(make([]byte, 0, size))

	binary.Write(buf, binary.LittleEndian, wkbNDR)
	binary.Write(buf, binary.LittleEndian, wkbMultiPointZ)
	binary.Write(buf, binary.LittleEndian, uint32(len(ps)))

	for _, p := range ps {
		binary.Write(buf, binary.LittleEndian, wkbNDR)
		binary.Write(buf, binary.LittleEndian, wkbPointZ)
		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.String()
}