Mercurial > gemma
view pkg/wkb/data.go @ 5736:55892008ec96 default tip
Fixed a bunch of corner cases in WG import.
author | Sascha Wilde <wilde@sha-bang.de> |
---|---|
date | Wed, 29 May 2024 19:02:42 +0200 |
parents | 2dd155cc95ec |
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] } }