Mercurial > gemma
view pkg/octree/polygon.go @ 2490:c9164ff98871 octree-diff
Started with point in polygon check.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Sun, 03 Mar 2019 21:10:07 +0100 |
parents | 4f292ff74d9e |
children | 10681749371d |
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 octree import ( "bytes" "encoding/binary" "fmt" "math" "gemma.intevation.de/gemma/pkg/wkb" ) type ring []float64 type Polygon struct { // TODO: Implement me! rings []ring } type IntersectionType byte const ( IntersectionInside IntersectionType = iota IntersectionOutSide IntersectionOverlaps ) func (p *Polygon) IntersectionBox2D(box Box2D) IntersectionType { // TODO: Implement me return IntersectionOutSide } func (p *Polygon) IntersectionWithTriangle(t *Triangle) IntersectionType { // TODO: Implement me return IntersectionOutSide } func (rng ring) isClosed() bool { return (len(rng) / 2) >= 3 } func (rng ring) contains(point []float64) bool { if !rng.isClosed() { return false } contains := intersectsWithRaycast(point, rng[:2], rng[len(rng)-2:len(rng)]) for i := 2; i < len(rng); i += 2 { if intersectsWithRaycast(point, rng[i-2:i], rng[i:i+2]) { contains = !contains } } return contains } // Using the raycast algorithm, this returns whether or not the passed in point // Intersects with the edge drawn by the passed in start and end points. // Original implementation: http://rosettacode.org/wiki/Ray-casting_algorithm#Go func intersectsWithRaycast(point, start, end []float64) bool { // Always ensure that the the first point // has a y coordinate that is less than the second point if start[1] > end[1] { // Switch the points if otherwise. start, end = end, start } // Move the point's y coordinate // outside of the bounds of the testing region // so we can start drawing a ray for point[1] == start[1] || point[1] == end[1] { y := math.Nextafter(point[1], math.Inf(1)) point = []float64{point[0], y} } // If we are outside of the polygon, indicate so. if point[1] < start[1] || point[1] > end[1] { return false } if start[0] > end[0] { if point[0] > start[0] { return false } if point[0] < end[0] { return true } } else { if point[0] > end[0] { return false } if point[0] < start[0] { return true } } raySlope := (point[1] - start[1]) / (point[0] - start[0]) diagSlope := (end[1] - start[1]) / (end[0] - start[0]) return raySlope >= diagSlope } func (p *Polygon) FromWKB(data []byte) error { 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.Polygon: return fmt.Errorf("unknown geometry type %x", geomType) } var numRings uint32 if err = binary.Read(r, order, &numRings); err != nil { return err } rngs := make([]ring, numRings) for rng := uint32(0); rng < numRings; rng++ { var numVertices uint32 if err = binary.Read(r, order, &numVertices); err != nil { return err } numVertices *= 2 vertices := make([]float64, numVertices) for v := uint32(0); v < numVertices; v += 2 { var lat, lon uint64 if err = binary.Read(r, order, &lat); err != nil { return err } if err = binary.Read(r, order, &lon); err != nil { return err } vertices[v] = math.Float64frombits(lat) vertices[v+1] = math.Float64frombits(lon) } rngs[rng] = vertices } p.rings = rngs return nil }