Giter VIP home page Giter VIP logo

geo's Introduction

Overview

S2 is a library for spherical geometry that aims to have the same robustness, flexibility, and performance as the best planar geometry libraries.

This is a library for manipulating geometric shapes. Unlike many geometry libraries, S2 is primarily designed to work with spherical geometry, i.e., shapes drawn on a sphere rather than on a planar 2D map. (In fact, the name S2 is derived from the mathematical notation for the unit sphere Sยฒ.) This makes it especially suitable for working with geographic data.

More details about S2 in general are available on the S2 Geometry Website s2geometry.io.

Scope

The library provides the following:

  • Representations of angles, intervals, latitude-longitude points, unit vectors, and so on, and various operations on these types.

  • Geometric shapes over the unit sphere, such as spherical caps ("discs"), latitude-longitude rectangles, polylines, and polygons. These are collectively known as "regions".

  • A hierarchical decomposition of the sphere into regions called "cells". The hierarchy starts with the six faces of a projected cube and recursively subdivides them in a quadtree-like fashion.

  • Robust constructive operations (e.g., union) and boolean predicates (e.g., containment) for arbitrary collections of points, polylines, and polygons.

  • Fast in-memory indexing of collections of points, polylines, and polygons.

  • Algorithms for measuring distances and finding nearby objects.

  • Robust algorithms for snapping and simplifying geometry (with accuracy and topology guarantees).

  • A collection of efficient yet exact mathematical predicates for testing relationships among geometric objects.

  • Support for spatial indexing, including the ability to approximate regions as collections of discrete "S2 cells". This feature makes it easy to build large distributed spatial indexes.

On the other hand, the following are outside the scope of S2:

  • Planar geometry.

  • Conversions to/from common GIS formats.

Robustness

What do we mean by "robust"?

In the S2 library, the core operations are designed to be 100% robust. This means that each operation makes strict mathematical guarantees about its output, and is implemented in such a way that it meets those guarantees for all possible valid inputs. For example, if you compute the intersection of two polygons, not only is the output guaranteed to be topologically correct (up to the creation of degeneracies), but it is also guaranteed that the boundary of the output stays within a user-specified tolerance of true, mathematically exact result.

Robustness is very important when building higher-level algorithms, since unexpected results from low-level operations can be very difficult to handle. S2 achieves this goal using a combination of techniques from computational geometry, including conservative error bounds, exact geometric predicates, and snap rounding.

The implementation attempts to be precise both in terms of mathematical definitions (e.g. whether regions include their boundaries, and how degeneracies are handled) and numerical accuracy (e.g. minimizing cancellation error).

Note that the intent of this library is to represent geometry as a mathematical abstraction. For example, although the unit sphere is obviously a useful approximation for the Earth's surface, functions that are specifically related to geography are not part of the core library (e.g. easting/northing conversions, ellipsoid approximations, geodetic vs. geocentric coordinates, etc).

See http://godoc.org/github.com/golang/geo for specific package documentation.

For an analogous library in C++, see https://github.com/google/s2geometry, in Java, see https://github.com/google/s2-geometry-library-java, and Python, see https://github.com/google/s2geometry/tree/master/src/python

Status of the Go Library

This library is principally a port of the C++ S2 library, adapting to Go idioms where it makes sense. We detail the progress of this port below relative to that C++ library.

Legend:

  • โœ… - Feature Complete
  • ๐ŸŸก - Mostly Complete
  • โŒ - Not available

โ„ยน - One-dimensional Cartesian coordinates

C++ Type Go
R1Interval โœ…

โ„ยฒ - Two-dimensional Cartesian coordinates

C++ Type Go
R2Point โœ…
R2Rect โœ…

โ„ยณ - Three-dimensional Cartesian coordinates

C++ Type Go
R3Vector โœ…
R3ExactVector โœ…
Matrix3x3 โœ…

Sยน - Circular Geometry

C++ Type Go
S1Angle โœ…
S1ChordAngle โœ…
S1Interval โœ…

Sยฒ - Spherical Geometry

Basic Types

C++ Type Go
S2Cap โœ…
S2Cell โœ…
S2CellId โœ…
S2CellIdVector โŒ
S2CellIndex ๐ŸŸก
S2CellUnion โœ…
S2Coords โœ…
S2DensityTree โŒ
S2DistanceTarget โœ…
S2EdgeVector โœ…
S2LatLng โœ…
S2LatLngRect โœ…
S2LaxLoop ๐ŸŸก
S2LaxPolygon ๐ŸŸก
S2LaxPolyline ๐ŸŸก
S2Loop โœ…
S2PaddedCell โœ…
S2Point โœ…
S2PointIndex โŒ
S2PointSpan โŒ
S2PointRegion โŒ
S2PointVector โœ…
S2Polygon ๐ŸŸก
S2Polyline โœ…
S2R2Rect โŒ
S2Region โœ…
S2RegionCoverer โœ…
S2RegionIntersection โŒ
S2RegionUnion โœ…
S2Shape โœ…
S2ShapeIndex โœ…
S2ShapeIndexRegion โŒ
EncodedLaxPolygon โŒ
EncodedLaxPolyline โŒ
EncodedShapeIndex โŒ
EncodedStringVector โŒ
EncodedUintVector โŒ
IdSetLexicon โŒ
ValueSetLexicon โŒ
SequenceLexicon โŒ
LaxClosedPolyline โŒ
VertexIDLaxLoop โŒ

Query Types

C++ Type Go
S2ChainInterpolation โŒ
S2ClosestCell โŒ
S2FurthestCell โŒ
S2ClosestEdge โœ…
S2FurthestEdge โœ…
S2ClosestPoint โŒ
S2FurthestPoint โŒ
S2ContainsPoint โœ…
S2ContainsVertex โœ…
S2ConvexHull โœ…
S2CrossingEdge โœ…
S2HausdorffDistance โŒ
S2ShapeNesting โŒ

Supporting Types

C++ Type Go
S2BooleanOperation โŒ
S2BufferOperation โŒ
S2Builder โŒ
S2BuilderClosedSetNormalizer โŒ
S2BuilderFindPolygonDegeneracies โŒ
S2BuilderGraph โŒ
S2BuilderLayers โŒ
S2BuilderSnapFunctions โŒ
S2BuilderTesting โŒ
S2Builderutil* โŒ
S2Coder โŒ
S2EdgeClipping โœ…
S2EdgeCrosser โœ…
S2EdgeCrossings โœ…
S2EdgeDistances โœ…
S2EdgeTessellator โœ…
S2LoopMeasures โŒ
S2Measures โœ…
S2MemoryTracker โŒ
S2Metrics โŒ
S2PointUtil ๐ŸŸก
S2PolygonBuilder โŒ
S2PolylineAlignment โŒ
S2PolylineMeasures โœ…
S2PolylineSimplifier โŒ
S2Predicates โœ…
S2Projections โŒ
S2rectBounder โŒ
S2RegionTermIndexer โŒ
S2ShapeIndexMeasures โŒ
S2ShapeIndexUtil* ๐ŸŸก
S2ShapeMeasures โŒ
S2ShapeUtil* ๐ŸŸก
S2Stats โŒ
S2Testing โœ…
S2TextFormat โœ…
S2WedgeRelations โœ…
S2WindingOperation โŒ

Encode/Decode

Encoding and decoding of S2 types is fully implemented and interoperable with C++ and Java.

geo's People

Contributors

domob1812 avatar dsymonds avatar gofz avatar keijak avatar lascap avatar nes1983 avatar rsned avatar sambeckman avatar sandesh247 avatar shishberg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

geo's Issues

SimpleCrossing: differences from C++ tests

I noticed a difference in the expected behavior of SimpleCrossing between the C++ and Go version of this library.

C++ test is here: https://code.google.com/p/s2-geometry-library/source/browse/geometry/s2edgeutil_test.cc#85
Go test: https://github.com/golang/geo/blob/master/s2/edgeutil_test.go#L48

The C++ test expects true while the go code expects false. I noticed similar behavior with the same test while trying to implement and test RobustCrossing.

Is this a bug in the go library or a change in behavior at edge conditions in the RobustSign function?

The go test for RobustCrossing here: https://github.com/rlmcpherson/geo/blob/master/s2/edgeutil_test.go#L233

[question] distance between points

I'm loving the library, really well put together!

I'd like to find the distance in meters between two points.

What did I try:

a: = s2.PointFromLatLng()
b: = s2.PointFromLatLng()
distance := a.Distance(b) // <- how do i convert this to mm/m/km

Not even sure if I'm using the correct methods.

Radian == 57.2957795 ?

Hello,

I am encountering a confusing issue. s1.Radian is clearly defined as 1, but when I print it from within a program it equals 57.2957795 and s1.Degree equals 1.

Any ideas what the issue could be, or what I'm misunderstanding here?

$ cat example.go 
package main

import(
"fmt"
"github.com/golang/geo/s1"
)

func main() {
fmt.Println(s1.Radian)
}

$ go mod init example
go: creating new go.mod: module example

$ go run example.go 
go: finding module for package github.com/golang/geo/s1
go: found github.com/golang/geo/s1 in github.com/golang/geo v0.0.0-20200319012246-673a6f80352d
57.2957795

$ go version
go version go1.14.2 linux/amd64

Thanks!

Support for Polygon Contains and Intersects method

I wanted to check if its possible to add support for Contains/Intersects for Polygon. Since the library doesn't yet support those methods for Polygon/MultiPolygon we just use the outer s2.Loop and do Intersects/Contains using that. That is error prone because it ignores the holes in the polygon.

If it's not very complicated then I could try porting them over from the C++ library and send over a PR?

Inconsistent result using RegionCoverer over Loop

I've come up with some results using the CellUnion method of RegionCoverer which are unexpected to me. I've created this gist to reproduce what I'm observing.

I'm basically comparing the cells covered by a Polyline containing 3 points and a Loop which is defined using the same 3 points. Using a RegionCoverer, it identifies 3 cells for the Polyline but returns an empty list of cells in the case of the Loop.

I've done some digging and although the FastCovering call identifies the same initial candidates in both cases, Loop's IntersectsCell method results in a "Disjoint" CellRelation for each of the 4 identified candidates.

I would have expected similar cells returning for both geometries or at least a non empty slice in the case of the Loop.

Strange results

Hi,

just realized that LatLngFromDegrees does not convert argumens:

package main
import (
    "fmt"
    "github.com/golang/geo/s2"
    "github.com/golang/geo/s1"
)

func main() {
    fmt.Println(  s1.Degree, float64(s1.Degree), s2.LatLngFromDegrees(1, 1))  
}

result:

1.0000000 0.017453292519943295 [1.0000000, 1.0000000]

go version
go version go1.6 linux/amd64

go compiled from source

Typo on testcase output

It seems that VertexNeighbors should be CommonAncestorLevel.

geo/s2/cellid_test.go

Lines 544 to 546 in ff7962d

if got, ok := test.ci.CommonAncestorLevel(test.other); ok != test.wantOk || got != test.want {
t.Errorf("CellID(%v).VertexNeighbors(%v) = %d, %t; want %d, %t", test.ci, test.other, got, ok, test.want, test.wantOk)
}

Status and what's missing.

Hej Devs,
I am wondering what are the plans for this repo.

I'd be more than willing to collaborate and bring it up (feature wise to the c++ lib), but I'd love some guidance :D

cell containment discrepancy

There isn't agreement between the Cell, Loop, and Polygon implementations of ContainsCell as required by the Region interface.

testRegions := []s2.Region{
	cell, // s2.Cell
	s2.LoopFromCell(cell),
	s2.PolygonFromCell(cell),
}

for _, region := range testRegions {
	fmt.Println(region.ContainsCell(cell))
}

// true
// false
// false

I don't know whether or not this is intentional, but the discrepancy seems related to whether or not regions can be said to contain cells with which they share an edge.

CellUnion.Intersects gives incorrect results

Currently, CellUnion.Intersects is implemented in terms of CellUnion.ContainsCellID. It should be implemented in terms of CellUnion.IntersectsCellID, as it is in the C++ library. With the current implementation, the method gives wrong results.

Is the `.id()` equivalent in s2geometry-node equivalent to `.pos()`

I'm trying to use this package for a project over s2geometry-node

Which has this:

var origin = new s2.S2CellId(new s2.S2LatLng(lat, lng)).parent(15);
var walk = [origin.id()];

Which does this: https://github.com/billyriantono/s2geometry-node/blob/master/API.md#cellidid---number

I'm trying to achieve similar results by doing:

ll := s2.LatLngFromDegrees(location.Latitude, location.Longitude)
cid := s2.CellIDFromLatLng(ll).Parent(15)
cid.Pos()

Polygon and RegionCoverer not working

Hello

I created a polygon/triangle by using LoopFromPoints() and PolygonFromLoops() with 1 loop and tried to get the cells by using RegionCoverer() and SimpleRegionCovering(). Unfortunately it isn't working, the result: 6.3 Mio. level 13 cells. Expected result: 2

It works fine by using 3 polylines though.

The check polygon.ContainsPoint() fails as well for a given point, which should be inside the polygon.

It seems the problem is similar to #29.

I'm using go1.11 on MacOS 10.14.3.

Regards
Alex

Deadlock during call to ShapeIndex.Iterator()

This is the call chain for (s *ShapeIndex)

Call to s.Iterator
--> s.maybeApplyUpdates()
  --> Acquires s.mu.Lock()
    --> s.applyUpdatesInternal()
      --> s.updateFaceEdges()
        --> s.skipCellRange()
          --> s.updateEdges()
            --> s.Iterator()
              --> s.maybeApplyUpdates()
                --> Tries to acquire s.mu.Lock() --> DEADLOCK

I'm using version v0.0.0-20200319012246-673a6f80352d

stuv_test.go: dead test code

There's dead test code on stuv_test.go [1]

siRandom and tiRandom is initialized to zero, siRandom > maxSiTi || tiRandom > maxSiTi is always false, therefore siRandom and tiRandom will be always zero for the testcase.

Here's simple fix.

siRandom = uint64(maxSiTi)
tiRansom = uint64(maxSiTi)

geo/s2/stuv_test.go

Lines 273 to 278 in d6335c7

var siRandom, tiRandom uint64
mask := -1 << uint64(maxLevel-level)
for siRandom > maxSiTi || tiRandom > maxSiTi {
siRandom = uint64(randomUint32() & uint32(mask))
tiRandom = uint64(randomUint32() & uint32(mask))
}

Don't Swallow Error on Loop Validity

Ran into an issue with a degenerate loop yesterday and found myself having to edit source code to show me useful errors for what was causing it to be invalid. Seems like there's a perfectly reasonable place to return a useful error here rather than swallow it, wonder if this is something that can be looked at?

geo/s2/loop.go

Line 207 in a852329

func (l *Loop) IsValid() bool {

Loop edge self intersection not detected

From the s2geometry.io docs (emphasis mine):

Loops are not allowed to have any duplicate vertices (whether adjacent or not), and non-adjacent edges are not allowed to intersect. Loops must have at least 3 vertices (except for the โ€œemptyโ€ and โ€œfullโ€ loops discussed below). These restrictions make it possible to implement exact polygon-polygon containment and intersection tests very efficiently. See S2Builder if your data does not meet these requirements.

Whereas running this sample code does not return an error although the second and last (i.e. implicit) edges intersect

package main

import (
	"log"
	"github.com/golang/geo/s2"
)

func main() {
	vertices := [][]float64{{0, 0}, {0, 1}, {1, 0}, {1, 1}}
	pnts := []s2.Point{}
	for _, v := range vertices {
		ll := s2.LatLngFromDegrees(v[1], v[0])
		pnts = append(pnts, s2.PointFromLatLng(ll))
	}
	loop := s2.LoopFromPoints(pnts)
	err := loop.Validate()
	if err != nil {
		log.Printf("loop validate: %v", err)
	} else {
		log.Printf("all ok")
	}
}

Is this the expected behavior?

Inverted Loops don't contain expected Points

This issue might just be me not understanding the semantics of this library's Loop.Invert function, but my expectation is that a Loop which doesn't contain a Point p should then contain p when it is .Invert'ed. This is not currently the case in this library.

I've put together a small playground demonstrating this behaviour below:

https://play.golang.org/p/1touvyS49sE

math/bits benchmarks

Hey all. I recently opened a pull request that removed some of the redundancies with Go 1.9's math/bits package. Addressing some of the feedback there, I've written a few benchmarks to demonstrate that there is indeed a performance improvement to be had by moving to this implementation.

https://github.com/topos-ai/go-s2bits

goos: darwin
goarch: amd64
pkg: github.com/topos-ai/go-s2bits
BenchmarkS2CellIDLevel/30-4         	2000000000	         1.03 ns/op
BenchmarkS2CellIDLevel/29-4         	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/28-4         	2000000000	         1.05 ns/op
BenchmarkS2CellIDLevel/27-4         	2000000000	         1.05 ns/op
BenchmarkS2CellIDLevel/26-4         	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/25-4         	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/24-4         	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/23-4         	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/22-4         	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/21-4         	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/20-4         	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/19-4         	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/18-4         	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/17-4         	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/16-4         	2000000000	         1.03 ns/op
BenchmarkS2CellIDLevel/15-4         	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/14-4         	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/13-4         	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/12-4         	2000000000	         1.05 ns/op
BenchmarkS2CellIDLevel/11-4         	2000000000	         1.03 ns/op
BenchmarkS2CellIDLevel/10-4         	2000000000	         1.03 ns/op
BenchmarkS2CellIDLevel/9-4          	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/8-4          	2000000000	         1.05 ns/op
BenchmarkS2CellIDLevel/7-4          	2000000000	         1.03 ns/op
BenchmarkS2CellIDLevel/6-4          	2000000000	         1.03 ns/op
BenchmarkS2CellIDLevel/5-4          	2000000000	         1.04 ns/op
BenchmarkS2CellIDLevel/4-4          	2000000000	         1.02 ns/op
BenchmarkS2CellIDLevel/3-4          	2000000000	         1.02 ns/op
BenchmarkS2CellIDLevel/2-4          	2000000000	         1.03 ns/op
BenchmarkS2CellIDLevel/1-4          	2000000000	         1.03 ns/op
BenchmarkS2CellIDLevel/0-4          	2000000000	         1.04 ns/op
BenchmarkCellIDLevel/30-4           	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/29-4           	2000000000	         0.36 ns/op
BenchmarkCellIDLevel/28-4           	2000000000	         0.34 ns/op
BenchmarkCellIDLevel/27-4           	2000000000	         0.34 ns/op
BenchmarkCellIDLevel/26-4           	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/25-4           	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/24-4           	2000000000	         0.34 ns/op
BenchmarkCellIDLevel/23-4           	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/22-4           	2000000000	         0.34 ns/op
BenchmarkCellIDLevel/21-4           	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/20-4           	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/19-4           	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/18-4           	2000000000	         0.34 ns/op
BenchmarkCellIDLevel/17-4           	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/16-4           	2000000000	         0.34 ns/op
BenchmarkCellIDLevel/15-4           	2000000000	         0.34 ns/op
BenchmarkCellIDLevel/14-4           	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/13-4           	2000000000	         0.34 ns/op
BenchmarkCellIDLevel/12-4           	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/11-4           	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/10-4           	2000000000	         0.34 ns/op
BenchmarkCellIDLevel/9-4            	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/8-4            	2000000000	         0.34 ns/op
BenchmarkCellIDLevel/7-4            	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/6-4            	2000000000	         0.34 ns/op
BenchmarkCellIDLevel/5-4            	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/4-4            	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/3-4            	2000000000	         0.34 ns/op
BenchmarkCellIDLevel/2-4            	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/1-4            	2000000000	         0.35 ns/op
BenchmarkCellIDLevel/0-4            	2000000000	         0.35 ns/op
PASS
ok  	github.com/topos-ai/go-s2bits	90.225s

Understanding that the maintainers of this library want to continue to support Go 1.8, I'm adding this as an issue that can be referenced in the future.

Thanks!

Inconsistency in ContainsPoint implementation from f65fe01

Commit f65fe01 added several method implementations to s2's Loop structure, such that it now fully implements the Region interface. This commit also included a modification to Loop's ContainsPoint method, splitting the implementation into cases by number of vertices in the Loop. This implementation change introduced situations where results returned by ContainsPoint and bruteForceContainsPoint may disagree.

The method bruteForceContainsPoint is equivalent to the ContainsPoint implementation from previous commits. In new versions, bruteForceContainsPoint is only invoked if the Loop has less than 32 vertices. I'll provide a bit more discussion about isolating the issue, and the bottom of this message contains a test case which shows that bruteForceContainsPoint may sometimes disagree with the new implementation of ContainsPoint.

The precise issue with ContainsPoint appears to be a false negative. It occurs when using the LocatePoint method of an iterator. To recreate the error reporting, consider the following code snippet adapted from the implementation of ContainsPoint

/// ^^^^ omitted code ^^^^^

// For small loops, and during initial construction, it is faster to just
// check all the crossing.
const maxBruteForceVertices = 32
if len(l.vertices) < maxBruteForceVertices || l.index == nil {
	return l.bruteForceContainsPoint(p)
}

// Otherwise, look up the point in the index.
it := l.index.Iterator()
if !it.LocatePoint(p) {
	//>>>>>>> begin added code
	if l.bruteForceContainsPoint(p) {
		// Error reporting can go here:
		// bruteForceContainsPoint would return true, but if
		// we are at this point in the function, then
		// LocatePoint would have caused a false result
	}
	//>>>>>> end of added code
	return false
}
return l.iteratorContainsPoint(it, p)

/// vvv code continues below vvv

This is how I was able to isolate the issue. I added similar print/error checking statements to the line

return l.iteratorContainsPoint(it, p)

but I encountered no situations where this caused an error.

As promised, here is a test case which can be added to loop_test.go to provide a failure. Some remarks

  • The vectors look pretty lamentable; they were extracted with a crude print/panic statement inserted as suggested in the snippet above, and the error occurred at some point in the code where the points in the loop had already been normalized to unit length.
  • This example was originally 41 lines long, but I commented some out to make the example smaller while allowing the test to still fail. Of course it is still very unwieldy, and it may be possible to isolate the issue to a more manageable size by (temporarily) writing a separate "contains point"-esque method for Loop which avoids the call to bruteForceContainsPoint, regardless of the loop size.
     func TestLoopAgreesBrute(t *testing.T) {
     	failLoop := LoopFromPoints([]Point{
     		Point{r3.Vector{0.165641708683984922867438, -0.682335499717461102520133, 0.712026045990996392376360}},
     		Point{r3.Vector{0.166979327473334382414549, -0.682904418669755708393154, 0.711167673026464219887544}},
     		Point{r3.Vector{0.170033862370230215432798, -0.684512796839880532928646, 0.708894009432937788339757}},
     		Point{r3.Vector{0.173220846601894046257186, -0.686324802909040188048095, 0.706365913117550037192416}},
     		Point{r3.Vector{0.174132592687058745939410, -0.685648723780406754713113, 0.706798180347390614386427}},
     		Point{r3.Vector{0.174311093817664880534224, -0.685348749932649936589257, 0.707045071786688406056953}},
     		Point{r3.Vector{0.174788277726536822598291, -0.684972450974164481785067, 0.707291877074688768445299}},
     		Point{r3.Vector{0.175010683840933378707660, -0.684597055988098723844359, 0.707600262488615072697939}},
     		Point{r3.Vector{0.178440325586863041129604, -0.682113826909010900756414, 0.709140167628373951913545}},
     		Point{r3.Vector{0.178421498442325326960756, -0.681798846893779120215129, 0.709447743859904389474025}},
     		Point{r3.Vector{0.177363651320893683793756, -0.680666029071524625315703, 0.710799473872992471079613}},
     		Point{r3.Vector{0.178797103397320800155512, -0.679841802240863102291257, 0.711229020599288497272994}},
     		Point{r3.Vector{0.179208354282632764675753, -0.679476714451123453564207, 0.711474356722725964630172}},
     		Point{r3.Vector{0.179160846488811170695854, -0.679296586466655694103167, 0.711658301925974789980955}},
     		Point{r3.Vector{0.177754755617451076776803, -0.677801803870706653931677, 0.713433922325674485520608}},
     		Point{r3.Vector{0.177407707407642972752271, -0.677442089150445014844593, 0.713861836211913458605238}},
     		Point{r3.Vector{0.177198812539136635324510, -0.677367964597019756034513, 0.713984048401930637695045}},
     		Point{r3.Vector{0.176713912637369380265184, -0.677687776492058135957564, 0.713800721962119077979025}},
     		Point{r3.Vector{0.175562077462614568590382, -0.678115763127744464178193, 0.713678477155285651001293}},
     		Point{r3.Vector{0.174399255619088117130744, -0.678994258659042504433501, 0.713128106547179396734748}},
     		Point{r3.Vector{0.173303962271755457313915, -0.679403082290932269771133, 0.713005742217052929099452}},
     		Point{r3.Vector{0.173112863740829431291601, -0.679644400955237593109359, 0.712822154999159129928898}},
     		Point{r3.Vector{0.173070942545166334136653, -0.679975956971311856769091, 0.712516067739862535290740}},
     		Point{r3.Vector{0.172611481966008672994661, -0.680156883220664032485558, 0.712454834007777759730118}},
     		Point{r3.Vector{0.172375750124909493665726, -0.680473171917579389145203, 0.712209844827564220182126}},
     		Point{r3.Vector{0.172406271752544054676193, -0.680593659565949526957240, 0.712087317692862931117759}},
     		Point{r3.Vector{0.172036150809872556699531, -0.680879570099696906915199, 0.711903486320564882205986}},
     		Point{r3.Vector{0.170830245913837386328993, -0.680606447647954415636207, 0.712454834007777759730118}},
     		Point{r3.Vector{0.170650943370862256953657, -0.680394989174694653222275, 0.712699736377530812525549}},
     		Point{r3.Vector{0.170055147795025896062526, -0.680031099872446809229132, 0.713189280566303929198568}},
     		Point{r3.Vector{0.169624646201347706320206, -0.680074463704104847927567, 0.713250449154181564992427}},
     		Point{r3.Vector{0.168510702717778293457940, -0.679902327040342591146782, 0.713678477155285651001293}},
     		// Point{r3.Vector{0.167012134825664543269497, -0.680207855705349917485592, 0.713739602276421192250666}},
     		// Point{r3.Vector{0.166730147144508150658382, -0.680341156537279445615241, 0.713678477155285651001293}},
     		// Point{r3.Vector{0.166537056793038762414483, -0.680580777692820770852222, 0.713495069183906371890203}},
     		// Point{r3.Vector{0.166566405708508558092262, -0.680958101605218635299366, 0.713128106547179396734748}},
     		// Point{r3.Vector{0.166046269466772172807012, -0.681149171530156971599013, 0.713066927097273817182099}},
     		// Point{r3.Vector{0.165986282410054197811178, -0.681419936158346062349267, 0.712822154999159129928898}},
     		// Point{r3.Vector{0.165539070261747467416313, -0.681912696549804686618756, 0.712454834007777759730118}},
     		// Point{r3.Vector{0.165464015353066290492379, -0.682122818382738138609511, 0.712271100259466050097501}},
     		// Point{r3.Vector{0.165641708683984922867438, -0.682335499717461102520133, 0.712026045990996392376360}},
     	})
     
     	failPt := Point{r3.Vector{0.171317369856565732133546, -0.681796830037278533964695, 0.711198594864013489136312}}
     
     	if failLoop.ContainsPoint(failPt) != failLoop.bruteForceContainsPoint(failPt) {
     		t.Errorf("ContainsPoint should coincide with bruteForceContainsPoint")
     	}
     
     }

ShapeIndex Questions

Hello @rsned
Are all index query-methods thread safe?
Is it possible to add a method to force build of an index? Can I make a pull request?

Polygon Intetersection didn't work for triangle and rectangle

So I use S2.Polygon Intersects methods.

My points as a triangle and another rectangle (when drawing) have no intersection. However, the intersect methods will return true.

I put all the points inside of s2.Point and construct loop using list of s2.point and construct polygon using list of loop. Could some one tell me if I use them wrong or if the algorithm has some problems.

Coverer.CellUnion(Point) can have multiple cells?

In S2, in some cases, a Coverer.CellUnion() of a Point can contain two cells. Is that the correct behavior? If so, what's the story?

Example:

package main

import (
	"fmt"

	"github.com/golang/geo/s2"
)

func main() {
	var (
		level   = 7
		coverer = s2.RegionCoverer{
			MinLevel: level,
			MaxLevel: level,
		}
		ll    = s2.LatLng{Lat: -0.8248776721189366, Lng: -2.038611228784389}
		p     = s2.PointFromLatLng(ll)
		union = coverer.CellUnion(p)
	)
	fmt.Println(len(union))
}

At the playground.

NewClosestEdgeQuery problem

Hello @rsned

I use your golang package for finding the nearest polygon from geo point & i try to make it following this:

index := s2.NewShapeIndex()

// More than 4000 polygons of cities added to index
// ...

q := s2.NewClosestEdgeQuery(index, s2.NewClosestEdgeQueryOptions().MaxResults(1).IncludeInteriors(true))
target := s2.NewMinDistanceToPointTarget(s2.PointFromLatLng(s2.LatLngFromDegrees(40.853544, -73.099544)))
res := q.FindEdges(target)

I've got a problem, the method's implementation time is about 30 sec which is quite slow.
Maybe i did smth wrong or it is an expected result?
if I exactly did smth wrong, could you please tell me how to realize it in a right way?

Question: Overflow converting `CellID.Pos()` to float64 is possible?

Hi

I have to convert the latlongs of a polyline into list of s2CellId Pos(uint64) and store the POS() value in redis as sorted set(which needs value as float64)
Now I am a bit afraid of overflow while converting POS from uint64 to float64 because below code overflows -

	numUint64 := math.MaxUint64
	numFloat64 := float64(numUint64)

However, the documentation of CellID.Pos() confirms the range as [0,2^posBits-1].
If we calculate, posBits = 2<<maxLevel + 1, given maxLevel = 30 below code works fine without overflow.

        posBits := (2<<30+1) - 1
	floatEq := float64(posBits)

Now, is it safe to say CellID.Pos() conversion to float64 will never overflow?

returned cap from CapBound for Polyline is too big

Hi,
i have case that performance using CapBound is in times slower compared to suggested replacement.

func (p *Polyline) CapBound() Cap {

Please change from

// CapBound returns the bounding Cap for this Polyline.
func (p *Polyline) CapBound() Cap {
	return p.RectBound().CapBound()
}

to

// CapBound returns the bounding Cap for this Polyline.
func (p *Polyline) CapBound() Cap {
	c := s2.EmptyCap()
	for _, v := range *p {
		c.AddPoint(v)
	}
	return c
}

I have a question about code

In cellid.go ,faceIJOrientation() ,

if ci.lsb()&0x1111111111111110 != 0 {
, I do not understand why we should add this judgment -- ci.lsb()&0x1111111111111110 != 0 ๏ผŸ I think faceIJOrientation and cellIDFromFaceIJ reverse each other, but faceIJOrientation have one more calculation of orientation than cellIDFromFaceIJ . Can you help me answer my doubts , why add 504 line's judgment ? (I think for a long time, really did not figure out here)

polylines match

Hi,
what is the recommended way to find two polylines (intersecting each other) are in same direction.
Is there any function written for it?

Polyline intersection

As already highlighted in #44 - there are quite a few C++ boolean functions missing from this port. e.g. one that I was looking at right now was Polyline intersecting another Polyline(and others soon)

I see the following:

// TODO(roberts): Differences from C++.
// Suffix
// Interpolate/UnInterpolate
// Intersects(Polyline) --> One I am interested in 

Do we have a list of all remaining tasks? Is anyone actively working on them? Or, is there any rough timeline of when we plan to add these features? :)

Loop.Intersects gives incorrect results

I have two geojson objects
Polygon 1:
[[[-74.93335056304832,39.283340980346445],
[-74.93335056305031,40.30000217770297],
[-75.91667795181374,40.30000217770097],
[-75.91667795181175,40.28334098034444],
[-74.93335056304832,39.283340980346445]]]
Polygon 2:
[[[-74.3122237229173, 39.9541747627284],
[-74.1955570274039, 40.0833414585103],
[-74.5497238128233, 40.1958414684098],
[-74.7163905245327, 40.0583414352459],
[-74.8663905578957, 39.8208413826612],
[-74.4163904122584,39.7708413884741],
[-74.3122237229173, 39.9541747627284]]]

I converted [][][]float64 array to Loop using Function:
func convertFloatListToPolygon(floatList [][][]float64) *s2.Loop {
points := floatList[0]
pointList := make([]s2.Point, 0)
for _, point := range points {
s2Point := s2.PointFromLatLng(
s2.LatLng{
Lat:s1.Angle(point[1]), Lng:s1.Angle(point[0]), }) pointList = append(pointList, s2Point) } return s2.LoopFromPoints(pointList) }`

poly = convertFloatListToPolygon(p1)
comparedPoly = convertFloatListToPolygon(p2)
poly.Intersects(comparedPoly)

The results is true. However, when I draw those two polygon in geojson.io, it's not intersected.

Could someone tell me if I use function wrong or there is a bug in intersection?

S2: Regioncoverer slightly intersecting another face doesn't cover whole region

I created a regioncoverer from a rectangle as following:

var regionCoverer s2.RegionCoverer

regionCoverer.MaxCells = 10000
regionCoverer.MinLevel = 7
regionCoverer.MaxLevel = 7

topleft := s2.LatLngFromDegrees(72.0000064, -141.0000000)
topright := s2.LatLngFromDegrees(72.0000064, -55.6152420)
bottomleft := s2.LatLngFromDegrees(41.9017143, -141.0000000)
bottomright := s2.LatLngFromDegrees(41.9017143, -55.6152420)

rect = s2.Rect{}
rect = rect.AddPoint(bottomleft)
rect = rect.AddPoint(bottomright)
rect = rect.AddPoint(topright)
rect = rect.AddPoint(topleft)

covering := regionCoverer.Covering(s2.Region(rect))

The result is not 100% what I was expecting. A cellUnion of CellID's covering the whole rectangle should be the result but the small part of the rectangle that lies on a different face does not get any cellID's returned for it. I visualized all cell's in QGIS so that you can see what I mean:

Image of  regioncoverer error

The part where it looks like somebody took a bite out of it lies on a different face (face 4 vs. face 2).

Slightly lowering the bottomleft and bottomright latitude solves the problem.

Implement Polygon.Intersection

Since it seems acceptable to use this space to indicate interest in specific features, I would like to request polygon intersections. The Go language currently lacks a high quality library for boolean operations on geographic features, and having one would help me out greatly. (I am one of the maintainers of a low-to-medium quality library.) I greatly appreciate all the work that has happened so far to port this library to Go.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.