view pkg/imports/polygon.go @ 1646:a0982c38eac0

Import queue: Implemented email notifications.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 20 Dec 2018 15:33:29 +0100
parents 20c98c2964f7
children
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 imports

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"math"

	shp "github.com/jonas-p/go-shp"
)

type (
	point struct {
		X float64
		Y float64
	}
	lineString []point
	polygon    []lineString
)

const (
	wkbNDR     byte   = 1
	wkbPolygon uint32 = 3
)

func shapeToPolygon(s shp.Shape) (polygon, error) {
	switch p := s.(type) {
	case *shp.Polygon:
		return toPolygon(p.NumParts, p.Parts, p.Points), nil
	case *shp.PolygonZ:
		return toPolygon(p.NumParts, p.Parts, p.Points), nil
	case *shp.PolygonM:
		return toPolygon(p.NumParts, p.Parts, p.Points), nil
	}
	return nil, fmt.Errorf("Unsupported shape type %T", s)
}

func toPolygon(numParts int32, parts []int32, points []shp.Point) polygon {
	out := make(polygon, numParts)
	var pos int32

	for i := range out {
		var howMany int32
		if i+1 >= len(parts) {
			howMany = int32(len(points)) - pos
		} else {
			howMany = parts[i+1] - parts[i]
		}

		line := make(lineString, howMany)
		for j := int32(0); j < howMany; j, pos = j+1, pos+1 {
			p := &points[pos]
			line[j] = point{p.X, p.Y}
		}
		out[i] = line
	}
	return out
}

func (p polygon) asWKB() []byte {
	if p == nil {
		return nil
	}
	// pre-calculate size to avoid reallocations.
	size := 1 + 4 + 4
	for _, ring := range p {
		size += 4 + len(ring)*2*8
	}

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

	binary.Write(buf, binary.LittleEndian, wkbNDR)
	binary.Write(buf, binary.LittleEndian, wkbPolygon)
	binary.Write(buf, binary.LittleEndian, uint32(len(p)))

	for _, ring := range p {
		binary.Write(buf, binary.LittleEndian, uint32(len(ring)))
		for _, v := range ring {
			binary.Write(buf, binary.LittleEndian, math.Float64bits(v.X))
			binary.Write(buf, binary.LittleEndian, math.Float64bits(v.Y))
		}
	}

	return buf.Bytes()
}