view pkg/wkb/data.go @ 5711:2dd155cc95ec revive-cleanup

Fix all revive issue (w/o machine generated stuff).
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Tue, 20 Feb 2024 22:22:57 +0100
parents 1222b777f51f
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) 2019 by via donau
//   – Österreichische Wasserstraßen-Gesellschaft mbH
// Software engineering by Intevation GmbH
//
// Author(s):
//  * Sascha L. Teichmann <sascha.teichmann@intevation.de>

package wkb

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

type (
	// PointGeom is a 2D point.
	PointGeom struct {
		X float64
		Y float64
	}
	// LinearRingGeom is a ring of points.
	LinearRingGeom []PointGeom
	// PolygonGeom is a polygon.
	PolygonGeom []LinearRingGeom
	// MultiPolygonGeom is a multi polygon.
	MultiPolygonGeom []PolygonGeom
)

// AsWKB serializes a multi polygon as WKB.
func (mpg MultiPolygonGeom) AsWKB() []byte {

	size := 1 + 4 + 4
	for _, pg := range mpg {
		size += 1 + 4 + 4
		for _, r := range pg {
			size += 4 + 2*8*len(r)
		}
	}

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

	binary.Write(buf, binary.LittleEndian, NDR)
	binary.Write(buf, binary.LittleEndian, MultiPolygon)
	binary.Write(buf, binary.LittleEndian, uint32(len(mpg)))

	for _, pg := range mpg {
		binary.Write(buf, binary.LittleEndian, NDR)
		binary.Write(buf, binary.LittleEndian, Polygon)
		binary.Write(buf, binary.LittleEndian, uint32(len(pg)))

		for _, r := range pg {
			binary.Write(buf, binary.LittleEndian, uint32(len(r)))
			for _, p := range r {
				x := math.Float64bits(p.X)
				y := math.Float64bits(p.Y)
				binary.Write(buf, binary.LittleEndian, x)
				binary.Write(buf, binary.LittleEndian, y)
			}
		}
	}

	return buf.Bytes()
}

// FromWKB deserializes a multi polygon from WKB.
func (mpg *MultiPolygonGeom) FromWKB(data []byte) error {
	r := bytes.NewReader(data)

	var order binary.ByteOrder

	switch endian, err := r.ReadByte(); {
	case err != nil:
		return err
	case endian == NDR:
		order = binary.LittleEndian
	case endian == XDR:
		order = binary.BigEndian
	default:
		return fmt.Errorf("unknown byte order %x", endian)
	}

	var geomType uint32

	switch err := binary.Read(r, order, &geomType); {
	case err != nil:
		return err
	case geomType != MultiPolygon:
		return fmt.Errorf("unknown geometry type %x", geomType)
	}

	var numPolygons uint32
	if err := binary.Read(r, order, &numPolygons); err != nil {
		return err
	}

	polygons := make([]PolygonGeom, numPolygons)

	for i := range polygons {
		switch endian, err := r.ReadByte(); {
		case err != nil:
			return err
		case endian == NDR:
			order = binary.LittleEndian
		case endian == XDR:
			order = binary.BigEndian
		default:
			return fmt.Errorf("unknown byte order %x", endian)
		}

		switch err := binary.Read(r, order, &geomType); {
		case err != nil:
			return err
		case geomType != Polygon:
			return fmt.Errorf("unknown geometry type %x", geomType)
		}

		var numRings uint32
		if err := binary.Read(r, order, &numRings); err != nil {
			return err
		}

		rings := make([]LinearRingGeom, numRings)

		for j := range rings {
			var numPoints uint32
			if err := binary.Read(r, order, &numPoints); err != nil {
				return err
			}
			points := make([]PointGeom, numPoints)

			for k := range points {
				var x, y uint64
				if err := binary.Read(r, order, &x); err != nil {
					return err
				}
				if err := binary.Read(r, order, &y); err != nil {
					return err
				}
				points[k] = PointGeom{
					X: math.Float64frombits(x),
					Y: math.Float64frombits(y),
				}
			}
			rings[j] = points
		}
		polygons[i] = rings
	}

	*mpg = polygons
	return nil
}

// CCW tells if the ring is oriented counter clock wise.
func (lr LinearRingGeom) CCW() bool {
	var sum float64
	for i, v1 := range lr {
		v2 := lr[(i+1)%len(lr)]
		sum += (v2.X - v1.X) * (v2.Y + v1.Y)
	}
	return sum > 0
}

// Reverse changes the orientation of the ring.
func (lr LinearRingGeom) Reverse() {
	for i, j := 0, len(lr)-1; i < j; i, j = i+1, j-1 {
		lr[i], lr[j] = lr[j], lr[i]
	}
}