Mercurial > gemma
view pkg/mesh/tin.go @ 5718:3d497077f888 uploadwg
Implemented direct file upload as alternative import method for WG.
For testing and data corrections it is useful to be able to import
waterway gauges data directly by uploading a xml file.
author | Sascha Wilde <wilde@sha-bang.de> |
---|---|
date | Thu, 18 Apr 2024 19:23:19 +0200 |
parents | 6270951dda28 |
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 mesh import ( "bytes" "encoding/binary" "errors" "fmt" "math" "gemma.intevation.de/gemma/pkg/log" "gemma.intevation.de/gemma/pkg/models" "gemma.intevation.de/gemma/pkg/wkb" ) var ( errNoByteSlice = errors.New("not a byte slice") errTooLessPoints = errors.New("too less points") ) // Tin stores a mesh of triangles with common vertices. type Tin struct { // EPSG holds the projection. EPSG uint32 // Vertices are the shared vertices. Vertices []Vertex // Triangles are the triangles. Triangles [][]int32 // Min is the lower left corner of the bbox. Min Vertex // Max is the upper right corner of the bbox. Max Vertex } // Clip returns a map of ids of triangles which are not inside the // given polygon. func (t *Tin) Clip(polygon *Polygon) map[int32]struct{} { var tree STRTree tree.Build(t) return tree.Clip(polygon) } // FromWKB constructs the TIN from a WKB representation. // Shared vertices are identified and referenced by the // same index. func (t *Tin) FromWKB(data []byte) error { log.Infof("data length %d\n", len(data)) r := bytes.NewReader(data) endian, err := r.ReadByte() var order binary.ByteOrder switch { case err != nil: return err case endian == wkb.NDR: order = binary.LittleEndian case endian == wkb.XDR: order = binary.BigEndian default: return fmt.Errorf("unknown byte order %x", endian) } var geomType uint32 err = binary.Read(r, order, &geomType) switch { case err != nil: return err case geomType != wkb.TinZ: return fmt.Errorf("unknown geometry type %x", geomType) } var num uint32 if err = binary.Read(r, order, &num); err != nil { return err } vertices := make([]Vertex, 0, 100000) var v Vertex v2i := make(map[Vertex]int32, 100000) var indexPool []int32 allocIndices := func() []int32 { if len(indexPool) == 0 { indexPool = make([]int32, 3*8*1024) } ids := indexPool[:3] indexPool = indexPool[3:] return ids } var triangles [][]int32 min := Vertex{math.MaxFloat64, math.MaxFloat64, math.MaxFloat64} max := Vertex{-math.MaxFloat64, -math.MaxFloat64, -math.MaxFloat64} for i := uint32(0); i < num; i++ { endian, err = r.ReadByte() switch { case err != nil: return err case endian == wkb.NDR: order = binary.LittleEndian case endian == wkb.XDR: order = binary.BigEndian default: return fmt.Errorf("unknown byte order %x", endian) } err = binary.Read(r, order, &geomType) switch { case err != nil: return err case geomType != wkb.TriangleZ: return fmt.Errorf("unknown geometry type %d", geomType) } var rings uint32 if err = binary.Read(r, order, &rings); err != nil { return err } triangle := allocIndices() for ring := uint32(0); ring < rings; ring++ { var npoints uint32 if err = binary.Read(r, order, &npoints); err != nil { return err } if npoints < 3 { return errTooLessPoints } for p := uint32(0); p < npoints; p++ { var x, y, z uint64 for _, addr := range []*uint64{&x, &y, &z} { if err = binary.Read(r, order, addr); err != nil { return err } } if p >= 3 || ring >= 1 { // Don't store the forth point. continue } // Do this conversion later to spare reflect calls // and allocs in binary.Read. v.X = math.Float64frombits(x) v.Y = math.Float64frombits(y) v.Z = math.Float64frombits(z) idx, found := v2i[v] if !found { idx = int32(len(vertices)) v2i[v] = idx vertices = append(vertices, v) min.Minimize(v) max.Maximize(v) } triangle[p] = idx } } triangles = append(triangles, triangle) } log.Infof("bbox: [[%f, %f], [%f, %f]]\n", min.X, min.Y, max.X, max.Y) *t = Tin{ EPSG: models.WGS84, Vertices: vertices, Triangles: triangles, Min: min, Max: max, } return nil } // Scan implements the sql.Scanner interface. func (t *Tin) Scan(raw any) error { if raw == nil { return nil } data, ok := raw.([]byte) if !ok { return errNoByteSlice } return t.FromWKB(data) }