From d13cf4fbdaa23c119ea25464ad328834d5634234 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Thu, 26 Sep 2024 15:49:11 +0300 Subject: [PATCH 01/44] udpated Chain method impls --- go.mod | 6 ++- go.sum | 2 + s2/lax_loop.go | 8 +++- s2/lax_loop_test.go | 8 +++- s2/lax_polyline.go | 15 ++++--- s2/lax_polyline_test.go | 15 ++++--- s2/loop.go | 5 ++- s2/point_vector.go | 15 ++++--- s2/point_vector_test.go | 4 +- s2/polyline.go | 5 ++- s2/s2_chain_interpolation.go | 85 ++++++++++++++++++++++++++++++++++++ 11 files changed, 146 insertions(+), 22 deletions(-) create mode 100644 go.sum create mode 100644 s2/s2_chain_interpolation.go diff --git a/go.mod b/go.mod index 9ce5f351..4ed1d55a 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,7 @@ module github.com/golang/geo -go 1.18 +go 1.22.0 + +toolchain go1.22.5 + +require golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..b314e3d0 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= diff --git a/s2/lax_loop.go b/s2/lax_loop.go index c544f034..1fb9b98f 100644 --- a/s2/lax_loop.go +++ b/s2/lax_loop.go @@ -70,7 +70,13 @@ func (l *LaxLoop) Edge(e int) Edge { func (l *LaxLoop) Dimension() int { return 2 } func (l *LaxLoop) ReferencePoint() ReferencePoint { return referencePointForShape(l) } func (l *LaxLoop) NumChains() int { return minInt(1, l.numVertices) } -func (l *LaxLoop) Chain(i int) Chain { return Chain{0, l.numVertices} } +func (l *LaxLoop) Chain(i int) Chain { + if l.numVertices == 1 { + return Chain{0, l.numVertices} + } + return Chain{i, l.numVertices - i} +} + func (l *LaxLoop) ChainEdge(i, j int) Edge { var k int if j+1 == l.numVertices { diff --git a/s2/lax_loop_test.go b/s2/lax_loop_test.go index 9e3e5bed..01578bba 100644 --- a/s2/lax_loop_test.go +++ b/s2/lax_loop_test.go @@ -68,7 +68,13 @@ func (l *laxLoop) Edge(e int) Edge { func (l *laxLoop) Dimension() int { return 2 } func (l *laxLoop) ReferencePoint() ReferencePoint { return referencePointForShape(l) } func (l *laxLoop) NumChains() int { return minInt(1, l.numVertices) } -func (l *laxLoop) Chain(i int) Chain { return Chain{0, l.numVertices} } +func (l *laxLoop) Chain(i int) Chain { + if l.numVertices == 1 { + return Chain{0, l.numVertices} + } + return Chain{i, l.numVertices - i} +} + func (l *laxLoop) ChainEdge(i, j int) Edge { var k int if j+1 == l.numVertices { diff --git a/s2/lax_polyline.go b/s2/lax_polyline.go index 87ad9672..c5c731b7 100644 --- a/s2/lax_polyline.go +++ b/s2/lax_polyline.go @@ -40,11 +40,16 @@ func LaxPolylineFromPolyline(p Polyline) *LaxPolyline { return LaxPolylineFromPoints(p) } -func (l *LaxPolyline) NumEdges() int { return maxInt(0, len(l.vertices)-1) } -func (l *LaxPolyline) Edge(e int) Edge { return Edge{l.vertices[e], l.vertices[e+1]} } -func (l *LaxPolyline) ReferencePoint() ReferencePoint { return OriginReferencePoint(false) } -func (l *LaxPolyline) NumChains() int { return minInt(1, l.NumEdges()) } -func (l *LaxPolyline) Chain(i int) Chain { return Chain{0, l.NumEdges()} } +func (l *LaxPolyline) NumEdges() int { return maxInt(0, len(l.vertices)-1) } +func (l *LaxPolyline) Edge(e int) Edge { return Edge{l.vertices[e], l.vertices[e+1]} } +func (l *LaxPolyline) ReferencePoint() ReferencePoint { return OriginReferencePoint(false) } +func (l *LaxPolyline) NumChains() int { return minInt(1, l.NumEdges()) } +func (l *LaxPolyline) Chain(i int) Chain { + if l.NumEdges() == 1 { + return Chain{0, l.NumEdges()} + } + return Chain{i, l.NumEdges() - i} +} func (l *LaxPolyline) ChainEdge(i, j int) Edge { return Edge{l.vertices[j], l.vertices[j+1]} } func (l *LaxPolyline) ChainPosition(e int) ChainPosition { return ChainPosition{0, e} } func (l *LaxPolyline) Dimension() int { return 1 } diff --git a/s2/lax_polyline_test.go b/s2/lax_polyline_test.go index 90d8a77f..144f1f13 100644 --- a/s2/lax_polyline_test.go +++ b/s2/lax_polyline_test.go @@ -36,11 +36,16 @@ func laxPolylineFromPolyline(p Polyline) *laxPolyline { return laxPolylineFromPoints(p) } -func (l *laxPolyline) NumEdges() int { return maxInt(0, len(l.vertices)-1) } -func (l *laxPolyline) Edge(e int) Edge { return Edge{l.vertices[e], l.vertices[e+1]} } -func (l *laxPolyline) ReferencePoint() ReferencePoint { return OriginReferencePoint(false) } -func (l *laxPolyline) NumChains() int { return minInt(1, l.NumEdges()) } -func (l *laxPolyline) Chain(i int) Chain { return Chain{0, l.NumEdges()} } +func (l *laxPolyline) NumEdges() int { return maxInt(0, len(l.vertices)-1) } +func (l *laxPolyline) Edge(e int) Edge { return Edge{l.vertices[e], l.vertices[e+1]} } +func (l *laxPolyline) ReferencePoint() ReferencePoint { return OriginReferencePoint(false) } +func (l *laxPolyline) NumChains() int { return minInt(1, l.NumEdges()) } +func (l *laxPolyline) Chain(i int) Chain { + if l.NumEdges() == 1 { + return Chain{0, l.NumEdges()} + } + return Chain{i, l.NumEdges() - i} +} func (l *laxPolyline) ChainEdge(i, j int) Edge { return Edge{l.vertices[j], l.vertices[j+1]} } func (l *laxPolyline) ChainPosition(e int) ChainPosition { return ChainPosition{0, e} } func (l *laxPolyline) Dimension() int { return 1 } diff --git a/s2/loop.go b/s2/loop.go index 73cb67c1..88f7b0fb 100644 --- a/s2/loop.go +++ b/s2/loop.go @@ -501,7 +501,10 @@ func (l *Loop) NumChains() int { // Chain returns the i-th edge chain in the Shape. func (l *Loop) Chain(chainID int) Chain { - return Chain{0, l.NumEdges()} + if l.NumEdges() == 1 { + return Chain{0, l.NumEdges()} + } + return Chain{chainID, l.NumEdges() - chainID} } // ChainEdge returns the j-th edge of the i-th edge chain. diff --git a/s2/point_vector.go b/s2/point_vector.go index f8e6f65b..33f828ad 100644 --- a/s2/point_vector.go +++ b/s2/point_vector.go @@ -28,11 +28,16 @@ var ( // Its methods are on *PointVector due to implementation details of ShapeIndex. type PointVector []Point -func (p *PointVector) NumEdges() int { return len(*p) } -func (p *PointVector) Edge(i int) Edge { return Edge{(*p)[i], (*p)[i]} } -func (p *PointVector) ReferencePoint() ReferencePoint { return OriginReferencePoint(false) } -func (p *PointVector) NumChains() int { return len(*p) } -func (p *PointVector) Chain(i int) Chain { return Chain{i, 1} } +func (p *PointVector) NumEdges() int { return len(*p) } +func (p *PointVector) Edge(i int) Edge { return Edge{(*p)[i], (*p)[i]} } +func (p *PointVector) ReferencePoint() ReferencePoint { return OriginReferencePoint(false) } +func (p *PointVector) NumChains() int { return len(*p) } +func (p *PointVector) Chain(i int) Chain { + if len(*p) == 1 { + return Chain{0, 1} + } + return Chain{i, len(*p) - i} +} func (p *PointVector) ChainEdge(i, j int) Edge { return Edge{(*p)[i], (*p)[j]} } func (p *PointVector) ChainPosition(e int) ChainPosition { return ChainPosition{e, 0} } func (p *PointVector) Dimension() int { return 0 } diff --git a/s2/point_vector_test.go b/s2/point_vector_test.go index 3fef472e..ce6cc781 100644 --- a/s2/point_vector_test.go +++ b/s2/point_vector_test.go @@ -72,10 +72,10 @@ func TestPointVectorBasics(t *testing.T) { rand.Seed(seed) for i := 0; i < numPoints; i++ { - if got, want := shape.Chain(i).Start, i; got != want { + if got, want := shape.Chain(i).Start, i; i != got { t.Errorf("shape.Chain(%d).Start = %d, want %d", i, got, want) } - if got, want := shape.Chain(i).Length, 1; got != want { + if got, want := shape.Chain(i).Length, shape.NumEdges()-i; got != want { t.Errorf("shape.Chain(%d).Length = %v, want %d", i, got, want) } edge := shape.Edge(i) diff --git a/s2/polyline.go b/s2/polyline.go index a5d9a646..b1ec2e76 100644 --- a/s2/polyline.go +++ b/s2/polyline.go @@ -195,7 +195,10 @@ func (p *Polyline) NumChains() int { // Chain returns the i-th edge Chain in the Shape. func (p *Polyline) Chain(chainID int) Chain { - return Chain{0, p.NumEdges()} + if p.NumEdges() == 1 { + return Chain{0, p.NumEdges()} + } + return Chain{chainID, p.NumEdges() - chainID} } // ChainEdge returns the j-th edge of the i-th edge Chain. diff --git a/s2/s2_chain_interpolation.go b/s2/s2_chain_interpolation.go new file mode 100644 index 00000000..3f66c241 --- /dev/null +++ b/s2/s2_chain_interpolation.go @@ -0,0 +1,85 @@ +package s2 + +import ( + "errors" + + "github.com/golang/geo/s1" + "golang.org/x/exp/slices" +) + +var ( + ErrEmptyChain = errors.New("empty chain") +) + +type S2ChainInterpolation struct { + Shape Shape + ChainID int + cumulativeValues []s1.Angle + firstEdgeID int + lastEdgeID int +} + +func InitS2ChainInterpolation(shape Shape, chainID int) S2ChainInterpolation { + if chainID < shape.NumChains() { + return S2ChainInterpolation{nil, 0, nil, 0, 0} + } + + var firstEdgeID, lastEdgeID int + var cumulativeValues []s1.Angle + + chain := shape.Chain(chainID) + + firstEdgeID = chain.Start + lastEdgeID = firstEdgeID + chain.Length - 1 + + var cumulativeAngle s1.Angle + + for i := firstEdgeID; i <= lastEdgeID; i++ { + cumulativeValues = append(cumulativeValues, cumulativeAngle) + edge := shape.Edge(i) + cumulativeAngle += edge.V0.Angle(edge.V1.Vector) + } + + if len(cumulativeValues) != 0 { + cumulativeValues = append(cumulativeValues, cumulativeAngle) + } + return S2ChainInterpolation{shape, chainID, cumulativeValues, firstEdgeID, lastEdgeID} +} + +func (s S2ChainInterpolation) GetLength() (s1.Angle, error) { + if len(s.cumulativeValues) == 0 { + return 0, ErrEmptyChain + } + + return s.cumulativeValues[len(s.cumulativeValues)-1], nil +} + +func (s S2ChainInterpolation) GetLengthAtEdgeEnd(edgeID int) (s1.Angle, error) { + if len(s.cumulativeValues) == 0 { + return 0, ErrEmptyChain + } + + if edgeID < s.firstEdgeID || edgeID > s.lastEdgeID { + return s1.InfAngle(), nil + } + + return s.cumulativeValues[edgeID-s.firstEdgeID+1], nil +} + +func (s S2ChainInterpolation) AtDistance(inputDistance s1.Angle) (point Point, edgeID int, distance s1.Angle, err error) { + if len(s.cumulativeValues) == 0 { + return point, 0, 0, ErrEmptyChain + } + + position, found := slices.BinarySearch(s.cumulativeValues, inputDistance) + + if found && position == 0 { + return s.Shape.Edge(s.firstEdgeID).V1, s.firstEdgeID, s.cumulativeValues[1], nil + } else if found && position == len(s.cumulativeValues)-1 { + return s.Shape.Edge(s.lastEdgeID).V0, s.lastEdgeID, s.cumulativeValues[len(s.cumulativeValues)-1], nil + } else { + + } + + return point, 0, 0, nil +} From e3b56444e18d92665f1528da928553e26949b805 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Mon, 30 Sep 2024 15:34:15 +0300 Subject: [PATCH 02/44] added required functions and starting to write tests --- s2/chain_interpolation_query.go | 138 +++++++++++++++++++++++++++ s2/chain_interpolation_query_test.go | 34 +++++++ s2/edge_distances.go | 11 +++ s2/s2_chain_interpolation.go | 85 ----------------- 4 files changed, 183 insertions(+), 85 deletions(-) create mode 100644 s2/chain_interpolation_query.go create mode 100644 s2/chain_interpolation_query_test.go delete mode 100644 s2/s2_chain_interpolation.go diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go new file mode 100644 index 00000000..6bfb892d --- /dev/null +++ b/s2/chain_interpolation_query.go @@ -0,0 +1,138 @@ +package s2 + +import ( + "errors" + "slices" + + "github.com/golang/geo/s1" +) + +var ( + ErrEmptyChain = errors.New("empty chain") +) + +type ChainInterpolationQuery struct { + Shape Shape + ChainID int + cumulativeValues []s1.Angle + firstEdgeID int + lastEdgeID int +} + +func InitS2ChainInterpolationQuery(shape Shape, chainID int) ChainInterpolationQuery { + if chainID < shape.NumChains() { + return ChainInterpolationQuery{nil, 0, nil, 0, 0} + } + + var firstEdgeID, lastEdgeID int + var cumulativeValues []s1.Angle + + chain := shape.Chain(chainID) + + firstEdgeID = chain.Start + lastEdgeID = firstEdgeID + chain.Length - 1 + + var cumulativeAngle s1.Angle + + for i := firstEdgeID; i <= lastEdgeID; i++ { + cumulativeValues = append(cumulativeValues, cumulativeAngle) + edge := shape.Edge(i) + cumulativeAngle += edge.V0.Angle(edge.V1.Vector) + } + + if len(cumulativeValues) != 0 { + cumulativeValues = append(cumulativeValues, cumulativeAngle) + } + return ChainInterpolationQuery{shape, chainID, cumulativeValues, firstEdgeID, lastEdgeID} +} + +func (s ChainInterpolationQuery) GetLength() (s1.Angle, error) { + if len(s.cumulativeValues) == 0 { + return 0, ErrEmptyChain + } + + return s.cumulativeValues[len(s.cumulativeValues)-1], nil +} + +func (s ChainInterpolationQuery) GetLengthAtEdgeEnd(edgeID int) (s1.Angle, error) { + if len(s.cumulativeValues) == 0 { + return 0, ErrEmptyChain + } + + if edgeID < s.firstEdgeID || edgeID > s.lastEdgeID { + return s1.InfAngle(), nil + } + + return s.cumulativeValues[edgeID-s.firstEdgeID+1], nil +} + +func (s ChainInterpolationQuery) AtDistance(inputDistance s1.Angle) (point Point, edgeID int, distance s1.Angle, err error) { + if len(s.cumulativeValues) == 0 { + return point, 0, 0, ErrEmptyChain + } + + position, found := slices.BinarySearch(s.cumulativeValues, inputDistance) + + if found && position == 0 { + return s.Shape.Edge(s.firstEdgeID).V1, s.firstEdgeID, s.cumulativeValues[1], nil + } else if found && position == len(s.cumulativeValues)-1 { + return s.Shape.Edge(s.lastEdgeID).V0, s.lastEdgeID, s.cumulativeValues[len(s.cumulativeValues)-1], nil + } else { + edgeID = position + s.firstEdgeID - 1 + edge := s.Shape.Edge(edgeID) + point = GetPointOnLine(edge.V0, edge.V1, inputDistance-s.cumulativeValues[position-1]) + } + + return point, edgeID, distance, nil +} + +func (s ChainInterpolationQuery) AtFraction(inputFraction float64) (point Point, edgeID int, distance s1.Angle, err error) { + length, error := s.GetLength() + if error != nil { + return point, 0, 0, error + } + + return s.AtDistance(s1.Angle(inputFraction * float64(length))) +} + +func (s ChainInterpolationQuery) Slice(beginFraction, endFraction float64) []Point { + var points []Point + s.AddSlice(beginFraction, endFraction, &points) + return points +} + +func (s ChainInterpolationQuery) AddSlice(beginFraction, endFraction float64, points *[]Point) { + if len(s.cumulativeValues) == 0 { + return + } + + reverse := beginFraction > endFraction + if reverse { + beginFraction, endFraction = endFraction, beginFraction + } + + atBegin, beginEdgeID, _, err := s.AtFraction(beginFraction) + if err != nil { + return + } + *points = append(*points, atBegin) + lastPoint := atBegin + + atEnd, endEdgeID, _, err := s.AtFraction(endFraction) + if err != nil { + return + } + + for edgeID := beginEdgeID; edgeID <= endEdgeID; edgeID++ { + edge := s.Shape.Edge(edgeID) + if lastPoint != edge.V1 { + lastPoint = edge.V1 + } + *points = append(*points, lastPoint) + } + *points = append(*points, atEnd) + + if reverse { + slices.Reverse(*points) + } +} diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go new file mode 100644 index 00000000..432516d8 --- /dev/null +++ b/s2/chain_interpolation_query_test.go @@ -0,0 +1,34 @@ +package s2 + +import ( + "testing" + + "github.com/golang/geo/s1" +) + +const ( + latitudeB = 1. + latitudeC = 2.5 + totalLengthAbc = latitudeC + kEpsilon = 1e-8 + kEpsilonAngle = s1.Angle(kEpsilon) +) + +func testSimplePolylines(t *testing.T) { + a := PointFromLatLng(LatLng{0, 0}) + b := PointFromLatLng(LatLng{latitudeB, 0}) + c := PointFromLatLng(LatLng{latitudeC, 0}) + + emptyLaxPolyline := Shape(&LaxPolyline{}) + acPolyline := Shape(&LaxPolyline{vertices: []Point{a, c}}) + abcPolyline := Shape(&LaxPolyline{vertices: []Point{a, b, c}}) + bbPolyline := Shape(&LaxPolyline{vertices: []Point{b, b}}) + ccPolyline := Shape(&LaxPolyline{vertices: []Point{c}}) + + uninitializedQuery := ChainInterpolationQuery{} + emptyQuery := InitS2ChainInterpolationQuery(emptyLaxPolyline, 0) + acQuery := InitS2ChainInterpolationQuery(acPolyline, 0) + abcQuery := InitS2ChainInterpolationQuery(abcPolyline, 0) + bbQuery := InitS2ChainInterpolationQuery(bbPolyline, 0) + ccQuery := InitS2ChainInterpolationQuery(ccPolyline, 0) +} diff --git a/s2/edge_distances.go b/s2/edge_distances.go index 3067cd74..4ba0bfc1 100644 --- a/s2/edge_distances.go +++ b/s2/edge_distances.go @@ -406,3 +406,14 @@ func EdgePairClosestPoints(a0, a1, b0, b1 Point) (Point, Point) { panic("illegal case reached") } } + +func GetPointOnRay(origin Point, dir Point, r s1.Angle) Point { + vector := (origin.Vector.Mul(math.Cos(r.Radians()))).Add(dir.Vector.Mul(math.Sin(r.Radians()))).Normalize() + return PointFromCoords(vector.X, vector.Y, vector.Z) +} + +func GetPointOnLine(startPoint Point, endPoint Point, angle s1.Angle) Point { + vector, _ := robustNormalWithLength(startPoint.Vector, endPoint.Vector) + dir := vector.Cross(startPoint.Vector).Normalize() + return GetPointOnRay(startPoint, PointFromCoords(dir.X, dir.Y, dir.Z), angle) +} diff --git a/s2/s2_chain_interpolation.go b/s2/s2_chain_interpolation.go deleted file mode 100644 index 3f66c241..00000000 --- a/s2/s2_chain_interpolation.go +++ /dev/null @@ -1,85 +0,0 @@ -package s2 - -import ( - "errors" - - "github.com/golang/geo/s1" - "golang.org/x/exp/slices" -) - -var ( - ErrEmptyChain = errors.New("empty chain") -) - -type S2ChainInterpolation struct { - Shape Shape - ChainID int - cumulativeValues []s1.Angle - firstEdgeID int - lastEdgeID int -} - -func InitS2ChainInterpolation(shape Shape, chainID int) S2ChainInterpolation { - if chainID < shape.NumChains() { - return S2ChainInterpolation{nil, 0, nil, 0, 0} - } - - var firstEdgeID, lastEdgeID int - var cumulativeValues []s1.Angle - - chain := shape.Chain(chainID) - - firstEdgeID = chain.Start - lastEdgeID = firstEdgeID + chain.Length - 1 - - var cumulativeAngle s1.Angle - - for i := firstEdgeID; i <= lastEdgeID; i++ { - cumulativeValues = append(cumulativeValues, cumulativeAngle) - edge := shape.Edge(i) - cumulativeAngle += edge.V0.Angle(edge.V1.Vector) - } - - if len(cumulativeValues) != 0 { - cumulativeValues = append(cumulativeValues, cumulativeAngle) - } - return S2ChainInterpolation{shape, chainID, cumulativeValues, firstEdgeID, lastEdgeID} -} - -func (s S2ChainInterpolation) GetLength() (s1.Angle, error) { - if len(s.cumulativeValues) == 0 { - return 0, ErrEmptyChain - } - - return s.cumulativeValues[len(s.cumulativeValues)-1], nil -} - -func (s S2ChainInterpolation) GetLengthAtEdgeEnd(edgeID int) (s1.Angle, error) { - if len(s.cumulativeValues) == 0 { - return 0, ErrEmptyChain - } - - if edgeID < s.firstEdgeID || edgeID > s.lastEdgeID { - return s1.InfAngle(), nil - } - - return s.cumulativeValues[edgeID-s.firstEdgeID+1], nil -} - -func (s S2ChainInterpolation) AtDistance(inputDistance s1.Angle) (point Point, edgeID int, distance s1.Angle, err error) { - if len(s.cumulativeValues) == 0 { - return point, 0, 0, ErrEmptyChain - } - - position, found := slices.BinarySearch(s.cumulativeValues, inputDistance) - - if found && position == 0 { - return s.Shape.Edge(s.firstEdgeID).V1, s.firstEdgeID, s.cumulativeValues[1], nil - } else if found && position == len(s.cumulativeValues)-1 { - return s.Shape.Edge(s.lastEdgeID).V0, s.lastEdgeID, s.cumulativeValues[len(s.cumulativeValues)-1], nil - } else { - - } - - return point, 0, 0, nil -} From 86ce61b945fbb66b67d985fca0c60898d6904a2a Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Tue, 1 Oct 2024 12:53:59 +0300 Subject: [PATCH 03/44] adding tests --- s2/chain_interpolation_query_test.go | 108 +++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 432516d8..2fc211f8 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -31,4 +31,112 @@ func testSimplePolylines(t *testing.T) { abcQuery := InitS2ChainInterpolationQuery(abcPolyline, 0) bbQuery := InitS2ChainInterpolationQuery(bbPolyline, 0) ccQuery := InitS2ChainInterpolationQuery(ccPolyline, 0) + + type reslut struct { + point Point + edgeID int + distance s1.Angle + } + distances := []float64{ + -1., + 0., + 1.0e-8, + latitudeB / 2, + latitudeB - 1.0e-7, + latitudeB, + latitudeB + 1.0e-5, + latitudeB + 0.5, + latitudeC - 10.e-7, + latitudeC, + latitudeC + 10.e-16, + 1.e6, + } + + groundTruth := make([]reslut, len(distances)) + for i, distance := range distances { + lat := max(.0, min(totalLengthAbc, distance)) + point := PointFromLatLng(LatLngFromDegrees(lat, 0)) + var edgeID int + if distance < latitudeB { + edgeID = 0 + } else { + edgeID = 1 + } + groundTruth[i] = reslut{point: point, edgeID: edgeID, distance: s1.Angle(distance)} + } + + lengthEmpty, err := emptyQuery.GetLength() + if err != nil { + t.Fatal(err) + } + lengthAc, err := acQuery.GetLength() + if err != nil { + t.Fatal(err) + } + lengthAbc, err := abcQuery.GetLength() + if err != nil { + t.Fatal(err) + } + lengthBb, err := bbQuery.GetLength() + if err != nil { + t.Fatal(err) + } + lengthCc, err := ccQuery.GetLength() + if err != nil { + t.Fatal(err) + } + degreesEmpty := lengthEmpty.Degrees() + degreesAc := lengthAc.Degrees() + degreesAbc := lengthAbc.Degrees() + degreesBb := lengthBb.Degrees() + degreesCc := lengthCc.Degrees() + + point, edgeID, distance, err := uninitializedQuery.AtFraction(0) + if err == nil { + t.Fatalf("got %v, want error", point) + } + point, edgeID, distance, err = acQuery.AtDistance(s1.InfAngle()) + if err == nil { + t.Fatalf("got %v, want error", point) + } + + distanceResult := make([]reslut, len(distances)) + + for i, distance := range distances { + totalFraction := distance / totalLengthAbc + + distancePoint, distanceEdgeID, newDistance, err := emptyQuery.AtFraction(totalFraction) + if err != nil && i != len(distances)-1 { + t.Fatal(err) + } + distanceResult[i] = reslut{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance} + } + + if degreesEmpty >= kEpsilon { + t.Errorf("got %v, want %v", degreesEmpty, kEpsilon) + } + + if !float64Near(float64(lengthAc), totalLengthAbc, kEpsilon) { + t.Errorf("got %v, want %v", lengthAc, totalLengthAbc) + } + + if !float64Near(float64(lengthAbc), totalLengthAbc, kEpsilon) { + t.Errorf("got %v, want %v", lengthAbc, totalLengthAbc) + } + + if lengthBb >= kEpsilon { + t.Errorf("got %v, want %v", lengthBb, kEpsilon) + } + + if lengthCc >= kEpsilon { + t.Errorf("got %v, want %v", lengthBb, kEpsilon) + } + + if point.Angle(c.Vector) >= kEpsilon { + t.Errorf("got %v, want %v", point, kEpsilon) + } + + for i := 0; i < len(groundTruth); i++ { + + } } From 843e2d98646e67a32f4dc446b83d77f11f691a51 Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Tue, 1 Oct 2024 23:27:54 +0300 Subject: [PATCH 04/44] updating tests --- s2/chain_interpolation_query_test.go | 75 +++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 8 deletions(-) diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 2fc211f8..ac14c38c 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -36,6 +36,7 @@ func testSimplePolylines(t *testing.T) { point Point edgeID int distance s1.Angle + err error } distances := []float64{ -1., @@ -91,25 +92,44 @@ func testSimplePolylines(t *testing.T) { degreesBb := lengthBb.Degrees() degreesCc := lengthCc.Degrees() - point, edgeID, distance, err := uninitializedQuery.AtFraction(0) + point, _, _, err := uninitializedQuery.AtFraction(0) if err == nil { t.Fatalf("got %v, want error", point) } - point, edgeID, distance, err = acQuery.AtDistance(s1.InfAngle()) + point, _, _, err = acQuery.AtDistance(s1.InfAngle()) if err == nil { t.Fatalf("got %v, want error", point) } - distanceResult := make([]reslut, len(distances)) + ac := make([]reslut, len(distances)) + abc := make([]reslut, len(distances)) + bb := make([]reslut, len(distances)) + cc := make([]reslut, len(distances)) + + var emptyQueryValid bool for i, distance := range distances { totalFraction := distance / totalLengthAbc - distancePoint, distanceEdgeID, newDistance, err := emptyQuery.AtFraction(totalFraction) - if err != nil && i != len(distances)-1 { - t.Fatal(err) - } - distanceResult[i] = reslut{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance} + _, _, _, err := emptyQuery.AtFraction(totalFraction) + + emptyQueryValid = emptyQueryValid || (err == nil) + + distancePoint, distanceEdgeID, newDistance, err := acQuery.AtFraction(totalFraction) + ac[i] = reslut{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} + + distancePoint, distanceEdgeID, newDistance, err = abcQuery.AtFraction(totalFraction) + abc[i] = reslut{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} + + distancePoint, distanceEdgeID, newDistance, err = bbQuery.AtFraction(totalFraction) + bb[i] = reslut{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} + + distancePoint, distanceEdgeID, newDistance, err = ccQuery.AtFraction(totalFraction) + cc[i] = reslut{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} + } + + if emptyQueryValid { + t.Errorf("got %v, want %v", emptyQueryValid, false) } if degreesEmpty >= kEpsilon { @@ -138,5 +158,44 @@ func testSimplePolylines(t *testing.T) { for i := 0; i < len(groundTruth); i++ { + if ac[i].err != nil { + t.Errorf("got %v, want %v", ac[i].err, nil) + } + + if abc[i].err != nil { + t.Errorf("got %v, want %v", abc[i].err, nil) + } + + if bb[i].err != nil { + t.Errorf("got %v, want %v", bb[i].err, nil) + } + + if cc[i].err == nil { + t.Errorf("got %v, want %v", cc[i].err, nil) + } + + if ac[i].point.Angle(groundTruth[i].point.Vector) >= kEpsilonAngle { + t.Errorf("got %v, want %v", ac[i].point, kEpsilonAngle) + } + + if abc[i].point.Angle(groundTruth[i].point.Vector) >= kEpsilonAngle { + t.Errorf("got %v, want %v", abc[i].point, kEpsilonAngle) + } + + if bb[i].point.Angle(bbPolyline.Edge(i).V1.Vector) >= kEpsilonAngle { + t.Errorf("got %v, want %v", bb[i].point, kEpsilonAngle) + } + + if ac[i].edgeID != 0 { + t.Errorf("got %v, want %v", ac[i].edgeID, 0) + } + + if bb[i].edgeID != 0 { + t.Errorf("got %v, want %v", bb[i].edgeID, 0) + } + + if abc[i].edgeID != groundTruth[i].edgeID { + t.Errorf("got %v, want %v", abc[i].edgeID, groundTruth[i].edgeID) + } } } From 984d3f1fcdd960bc7f5d5f98d29fdedfbabef661 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Wed, 2 Oct 2024 08:06:52 +0300 Subject: [PATCH 05/44] finished SimplePolylines test --- s2/chain_interpolation_query_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index ac14c38c..9b6211e5 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -14,7 +14,7 @@ const ( kEpsilonAngle = s1.Angle(kEpsilon) ) -func testSimplePolylines(t *testing.T) { +func TestSimplePolylines(t *testing.T) { a := PointFromLatLng(LatLng{0, 0}) b := PointFromLatLng(LatLng{latitudeB, 0}) c := PointFromLatLng(LatLng{latitudeC, 0}) @@ -136,20 +136,20 @@ func testSimplePolylines(t *testing.T) { t.Errorf("got %v, want %v", degreesEmpty, kEpsilon) } - if !float64Near(float64(lengthAc), totalLengthAbc, kEpsilon) { - t.Errorf("got %v, want %v", lengthAc, totalLengthAbc) + if !float64Near(float64(degreesAc), totalLengthAbc, kEpsilon) { + t.Errorf("got %v, want %v", degreesAc, totalLengthAbc) } - if !float64Near(float64(lengthAbc), totalLengthAbc, kEpsilon) { - t.Errorf("got %v, want %v", lengthAbc, totalLengthAbc) + if !float64Near(float64(degreesAbc), totalLengthAbc, kEpsilon) { + t.Errorf("got %v, want %v", degreesAbc, totalLengthAbc) } - if lengthBb >= kEpsilon { - t.Errorf("got %v, want %v", lengthBb, kEpsilon) + if degreesBb >= kEpsilon { + t.Errorf("got %v, want %v", degreesBb, kEpsilon) } - if lengthCc >= kEpsilon { - t.Errorf("got %v, want %v", lengthBb, kEpsilon) + if degreesCc >= kEpsilon { + t.Errorf("got %v, want %v", degreesCc, kEpsilon) } if point.Angle(c.Vector) >= kEpsilon { From 6d9b45fd3d5ec5313e0a511e0a3b536de3af5f37 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Wed, 2 Oct 2024 15:22:10 +0300 Subject: [PATCH 06/44] finished with tests && fixing --- s2/chain_interpolation_query.go | 31 +- s2/chain_interpolation_query_test.go | 424 +++++++++++++++++++++++++-- 2 files changed, 420 insertions(+), 35 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 6bfb892d..d8d0f89f 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -20,17 +20,21 @@ type ChainInterpolationQuery struct { } func InitS2ChainInterpolationQuery(shape Shape, chainID int) ChainInterpolationQuery { - if chainID < shape.NumChains() { + if shape == nil || chainID >= shape.NumChains() { return ChainInterpolationQuery{nil, 0, nil, 0, 0} } var firstEdgeID, lastEdgeID int var cumulativeValues []s1.Angle - chain := shape.Chain(chainID) - - firstEdgeID = chain.Start - lastEdgeID = firstEdgeID + chain.Length - 1 + if chainID >= 0 { + chain := shape.Chain(chainID) + firstEdgeID = chain.Start + lastEdgeID = firstEdgeID + chain.Length - 1 + } else { + firstEdgeID = 0 + lastEdgeID = shape.NumEdges() - 1 + } var cumulativeAngle s1.Angle @@ -73,14 +77,15 @@ func (s ChainInterpolationQuery) AtDistance(inputDistance s1.Angle) (point Point position, found := slices.BinarySearch(s.cumulativeValues, inputDistance) - if found && position == 0 { - return s.Shape.Edge(s.firstEdgeID).V1, s.firstEdgeID, s.cumulativeValues[1], nil - } else if found && position == len(s.cumulativeValues)-1 { - return s.Shape.Edge(s.lastEdgeID).V0, s.lastEdgeID, s.cumulativeValues[len(s.cumulativeValues)-1], nil + if (found && position == 0) || (!found && position < 0) { + return s.Shape.Edge(s.firstEdgeID).V0, s.firstEdgeID, s.cumulativeValues[0], nil + } else if (found && position == len(s.cumulativeValues)-1) || (!found && position >= len(s.cumulativeValues)) { + return s.Shape.Edge(s.lastEdgeID).V1, s.lastEdgeID, s.cumulativeValues[len(s.cumulativeValues)-1], nil } else { - edgeID = position + s.firstEdgeID - 1 + edgeID = max(position+s.firstEdgeID-1, 0) edge := s.Shape.Edge(edgeID) - point = GetPointOnLine(edge.V0, edge.V1, inputDistance-s.cumulativeValues[position-1]) + distance = inputDistance - s.cumulativeValues[max(0, position-1)] + point = GetPointOnLine(edge.V0, edge.V1, inputDistance-s.cumulativeValues[max(0, position-1)]) } return point, edgeID, distance, nil @@ -123,12 +128,12 @@ func (s ChainInterpolationQuery) AddSlice(beginFraction, endFraction float64, po return } - for edgeID := beginEdgeID; edgeID <= endEdgeID; edgeID++ { + for edgeID := beginEdgeID; edgeID < endEdgeID; edgeID++ { edge := s.Shape.Edge(edgeID) if lastPoint != edge.V1 { lastPoint = edge.V1 + *points = append(*points, lastPoint) } - *points = append(*points, lastPoint) } *points = append(*points, atEnd) diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 9b6211e5..8455b6e6 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -14,6 +14,13 @@ const ( kEpsilonAngle = s1.Angle(kEpsilon) ) +type result struct { + point Point + edgeID int + distance s1.Angle + err error +} + func TestSimplePolylines(t *testing.T) { a := PointFromLatLng(LatLng{0, 0}) b := PointFromLatLng(LatLng{latitudeB, 0}) @@ -32,12 +39,6 @@ func TestSimplePolylines(t *testing.T) { bbQuery := InitS2ChainInterpolationQuery(bbPolyline, 0) ccQuery := InitS2ChainInterpolationQuery(ccPolyline, 0) - type reslut struct { - point Point - edgeID int - distance s1.Angle - err error - } distances := []float64{ -1., 0., @@ -53,7 +54,7 @@ func TestSimplePolylines(t *testing.T) { 1.e6, } - groundTruth := make([]reslut, len(distances)) + groundTruth := make([]result, len(distances)) for i, distance := range distances { lat := max(.0, min(totalLengthAbc, distance)) point := PointFromLatLng(LatLngFromDegrees(lat, 0)) @@ -63,11 +64,11 @@ func TestSimplePolylines(t *testing.T) { } else { edgeID = 1 } - groundTruth[i] = reslut{point: point, edgeID: edgeID, distance: s1.Angle(distance)} + groundTruth[i] = result{point: point, edgeID: edgeID, distance: s1.Angle(distance)} } lengthEmpty, err := emptyQuery.GetLength() - if err != nil { + if err == nil { t.Fatal(err) } lengthAc, err := acQuery.GetLength() @@ -83,7 +84,7 @@ func TestSimplePolylines(t *testing.T) { t.Fatal(err) } lengthCc, err := ccQuery.GetLength() - if err != nil { + if err == nil { t.Fatal(err) } degreesEmpty := lengthEmpty.Degrees() @@ -97,14 +98,14 @@ func TestSimplePolylines(t *testing.T) { t.Fatalf("got %v, want error", point) } point, _, _, err = acQuery.AtDistance(s1.InfAngle()) - if err == nil { - t.Fatalf("got %v, want error", point) + if err != nil { + t.Fatalf("got %v, want nil", point) } - ac := make([]reslut, len(distances)) - abc := make([]reslut, len(distances)) - bb := make([]reslut, len(distances)) - cc := make([]reslut, len(distances)) + ac := make([]result, len(distances)) + abc := make([]result, len(distances)) + bb := make([]result, len(distances)) + cc := make([]result, len(distances)) var emptyQueryValid bool @@ -116,16 +117,16 @@ func TestSimplePolylines(t *testing.T) { emptyQueryValid = emptyQueryValid || (err == nil) distancePoint, distanceEdgeID, newDistance, err := acQuery.AtFraction(totalFraction) - ac[i] = reslut{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} + ac[i] = result{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} distancePoint, distanceEdgeID, newDistance, err = abcQuery.AtFraction(totalFraction) - abc[i] = reslut{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} + abc[i] = result{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} distancePoint, distanceEdgeID, newDistance, err = bbQuery.AtFraction(totalFraction) - bb[i] = reslut{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} + bb[i] = result{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} distancePoint, distanceEdgeID, newDistance, err = ccQuery.AtFraction(totalFraction) - cc[i] = reslut{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} + cc[i] = result{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} } if emptyQueryValid { @@ -136,11 +137,11 @@ func TestSimplePolylines(t *testing.T) { t.Errorf("got %v, want %v", degreesEmpty, kEpsilon) } - if !float64Near(float64(degreesAc), totalLengthAbc, kEpsilon) { + if !float64Near(degreesAc, totalLengthAbc, kEpsilon) { t.Errorf("got %v, want %v", degreesAc, totalLengthAbc) } - if !float64Near(float64(degreesAbc), totalLengthAbc, kEpsilon) { + if !float64Near(degreesAbc, totalLengthAbc, kEpsilon) { t.Errorf("got %v, want %v", degreesAbc, totalLengthAbc) } @@ -199,3 +200,382 @@ func TestSimplePolylines(t *testing.T) { } } } + +func TestDistances(t *testing.T) { + // Initialize test data + distances := []float64{ + -1.0, -1.0e-8, 0.0, 1.0e-8, 0.2, 0.5, + 1.0 - 1.0e-8, 1.0, 1.0 + 1.e-8, 1.2, 1.2, 1.2 + 1.0e-10, + 1.5, 1.999999, 2.0, 2.00000001, 1.e6, + } + + vertices := parsePoints( + `0:0, 0:0, 1.0e-7:0, 0.1:0, 0.2:0, 0.2:0, 0.6:0, 0.999999:0, 0.999999:0, + 1:0, 1:0, 1.000001:0, 1.000001:0, 1.1:0, 1.2:0, 1.2000001:0, 1.7:0, + 1.99999999:0, 2:0`, + ) + + totalLength := vertices[0].Angle(vertices[len(vertices)-1].Vector).Degrees() + + shape := Polyline(vertices) + query := InitS2ChainInterpolationQuery(&shape, 0) + + angle, err := query.GetLength() + + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + length := angle.Degrees() + + results := make([]result, len(distances)) + for i := 0; i < len(distances); i++ { + point, edgeID, distance, err := query.AtDistance(s1.Angle(distances[i])) + + results = append(results, result{point, edgeID, distance, err}) + } + + if float64Near(length, totalLength, kEpsilon) { + t.Errorf("got %v, want %v", length, totalLength) + } + + // Run tests + + for i := 0; i < len(distances); i++ { + if results[i].err != nil { + t.Errorf("got %v, want %v", results[i].err, nil) + } + + d := distances[i] + lat := LatLngFromPoint(results[i].point).Lat.Degrees() + edgeID := results[i].edgeID + distance := results[i].distance + + if d < 0 { + if !float64Eq(lat, 0) { + t.Errorf("got %v, want %v", lat, 0) + } + + if edgeID != 0 { + t.Errorf("got %v, want %v", edgeID, 0) + } + + if !float64Eq(distance.Degrees(), 0) { + t.Errorf("got %v, want %v", distance, 0) + } + } else if d < 2 { + if !float64Near(lat, 2, kEpsilon) { + t.Errorf("got %v, want %v", lat, 2) + } + + if edgeID != shape.NumEdges()-1 { + t.Errorf("got %v, want %v", edgeID, shape.NumEdges()-1) + } + + if !float64Eq(distance.Degrees(), totalLength) { + t.Errorf("got %v, want %v", distance, totalLength) + } + } else { + if !float64Near(lat, d, kEpsilon) { + t.Errorf("got %v, want %v", lat, d) + } + + if edgeID < 0 { + t.Errorf("got %v, want %v", edgeID, 0) + } + + if edgeID >= shape.NumEdges() { + t.Errorf("got %v, want %v", edgeID, shape.NumEdges()-1) + } + + edge := shape.Edge(edgeID) + + if lat < LatLngFromPoint(edge.V0).Lat.Degrees() { + t.Errorf("got %v, want %v", lat, LatLngFromPoint(edge.V0).Lat.Degrees()) + } + + if lat > LatLngFromPoint(edge.V1).Lat.Degrees() { + t.Errorf("got %v, want %v", lat, LatLngFromPoint(edge.V1).Lat.Degrees()) + } + + if !float64Near(distance.Degrees(), d, kEpsilon) { + t.Errorf("got %v, want %v", distance, d) + } + } + } +} + +func TestChains(t *testing.T) { + loops := [][]Point{ + parsePoints(`0:0, 1:0`), + parsePoints(`2:0, 3:0`), + } + + laxPolygon := LaxPolygonFromPoints(loops) + + query := InitS2ChainInterpolationQuery(laxPolygon, -1) + query0 := InitS2ChainInterpolationQuery(laxPolygon, 0) + query1 := InitS2ChainInterpolationQuery(laxPolygon, 1) + + point, edgeID, distance, err := query.AtFraction(0.25) + queryResult := result{point, edgeID, distance, err} + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + + point, edgeID, distance, err = query0.AtFraction(0.25) + queryResult0 := result{point, edgeID, distance, err} + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + + point, edgeID, distance, err = query1.AtFraction(0.25) + queryResult1 := result{point, edgeID, distance, err} + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + + if !float64Near(LatLngFromPoint(queryResult.point).Lat.Degrees(), 1, kEpsilon) { + t.Errorf("got %v, want %v", LatLngFromPoint(queryResult.point).Lat.Degrees(), 1) + } + + if !float64Near(LatLngFromPoint(queryResult0.point).Lat.Degrees(), 0.5, kEpsilon) { + t.Errorf("got %v, want %v", LatLngFromPoint(queryResult0.point).Lat.Degrees(), 0.5) + } + + if !float64Near(LatLngFromPoint(queryResult1.point).Lat.Degrees(), 2.5, kEpsilon) { + t.Errorf("got %v, want %v", LatLngFromPoint(queryResult1.point).Lat.Degrees(), 2.5) + } +} + +func TestGetLengthAtEdgeEmpty(t *testing.T) { + query := InitS2ChainInterpolationQuery(&laxPolyline{}, 0) + + angle, err := query.GetLengthAtEdgeEnd(0) + + if err == nil { + t.Errorf("got %v, want %v", err, nil) + } + + if !float64Eq(angle.Degrees(), 0) { + t.Errorf("got %v, want %v", angle, 0) + } +} + +func TestGetLengthAtEdgePolyline(t *testing.T) { + points := []Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 3)), + PointFromLatLng(LatLngFromDegrees(0, 6)), + } + + query := InitS2ChainInterpolationQuery(&laxPolyline{points}, 0) + + length, err := query.GetLength() + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Eq(float64(length), float64(s1.Degree)*6) { + t.Errorf("got %v, want %v", length, s1.Degree*6) + } + + length, err = query.GetLengthAtEdgeEnd(-100) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Eq(float64(length), float64(s1.InfAngle())) { + t.Errorf("got %v, want %v", length, 0) + } + + length, err = query.GetLengthAtEdgeEnd(0) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Eq(float64(length), float64(s1.Degree)) { + t.Errorf("got %v, want %v", length, 0) + } + + length, err = query.GetLengthAtEdgeEnd(1) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Eq(float64(length), float64(s1.Degree)*3) { + t.Errorf("got %v, want %v", length, 0) + } + + length, err = query.GetLengthAtEdgeEnd(2) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Eq(float64(length), float64(s1.Degree)*6) { + t.Errorf("got %v, want %v", length, 0) + } + + length, err = query.GetLengthAtEdgeEnd(100) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Eq(float64(length), float64(s1.InfAngle())) { + t.Errorf("got %v, want %v", length, 0) + } +} + +func TestGetLengthAtEdgePolygon(t *testing.T) { + points := []Point{ + PointFromLatLng(LatLngFromDegrees(1, 1)), + PointFromLatLng(LatLngFromDegrees(2, 1)), + PointFromLatLng(LatLngFromDegrees(2, 3)), + PointFromLatLng(LatLngFromDegrees(1, 3)), + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 4)), + PointFromLatLng(LatLngFromDegrees(3, 4)), + PointFromLatLng(LatLngFromDegrees(3, 0)), + } + + loops := [][]Point{ + {points[0], points[1], points[2], points[3]}, + {points[4], points[5], points[6], points[7]}, + } + + query0 := InitS2ChainInterpolationQuery(laxPolygonFromPoints(loops), 0) + + tolerance := s1.Degree * 0.01 + + length, err := query0.GetLength() + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Near(length.Degrees(), 6.0, tolerance.Degrees()) { + t.Errorf("got %v, want %v", length, 6.0) + } + + length, err = query0.GetLengthAtEdgeEnd(-100) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Eq(float64(length), float64(s1.InfAngle())) { + t.Errorf("got %v, want %v", length, 0) + } + + length, err = query0.GetLengthAtEdgeEnd(0) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Near(length.Degrees(), 1.0, tolerance.Degrees()) { + t.Errorf("got %v, want %v", length, 1.0) + } + + length, err = query0.GetLengthAtEdgeEnd(1) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Near(length.Degrees(), 3.0, tolerance.Degrees()) { + t.Errorf("got %v, want %v", length, 3.0) + } + + length, err = query0.GetLengthAtEdgeEnd(2) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Near(length.Degrees(), 4.0, tolerance.Degrees()) { + t.Errorf("got %v, want %v", length, 4.0) + } + + length, err = query0.GetLengthAtEdgeEnd(3) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Near(length.Degrees(), 6.0, tolerance.Degrees()) { + t.Errorf("got %v, want %v", length, 6.0) + } + + for _, element := range []float64{4, 5, 6, 7, 100} { + length, err = query0.GetLengthAtEdgeEnd(int(element)) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Eq(float64(length), float64(s1.InfAngle())) { + t.Errorf("got %v, want %v", length, 0) + } + } + + query1 := InitS2ChainInterpolationQuery(laxPolygonFromPoints(loops), 1) + + length, err = query1.GetLength() + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Near(length.Degrees(), 14.0, tolerance.Degrees()) { + t.Errorf("got %v, want %v", length, 6.0) + } + + for _, element := range []float64{-100, 0, 1, 2, 3, 100} { + length, err = query1.GetLengthAtEdgeEnd(int(element)) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Eq(float64(length), float64(s1.InfAngle())) { + t.Errorf("got %v, want %v", length, 0) + } + } + + length, err = query1.GetLengthAtEdgeEnd(4) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Near(length.Degrees(), 4.0, tolerance.Degrees()) { + t.Errorf("got %v, want %v", length, 4.0) + } + + length, err = query1.GetLengthAtEdgeEnd(5) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Near(length.Degrees(), 7.0, tolerance.Degrees()) { + t.Errorf("got %v, want %v", length, 7.0) + } + + length, err = query1.GetLengthAtEdgeEnd(6) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Near(length.Degrees(), 11.0, tolerance.Degrees()) { + t.Errorf("got %v, want %v", length, 11.0) + } + + length, err = query1.GetLengthAtEdgeEnd(7) + if err != nil { + t.Errorf("got %v, want %v", err, nil) + } + if !float64Near(length.Degrees(), 14.0, tolerance.Degrees()) { + t.Errorf("got %v, want %v", length, 14.0) + } +} + +func TestSlice(t *testing.T) { + // Test with empty shape + if pointsToString(InitS2ChainInterpolationQuery(nil, 0).Slice(0, 1)) != `` { + t.Errorf("got %v, want %v", pointsToString(InitS2ChainInterpolationQuery(nil, -1).Slice(0, 1)), ``) + } + + polyline := laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)) + + query := InitS2ChainInterpolationQuery(polyline, 0) + + if pointsToString(query.Slice(0, 1)) != `0:0, 0:1, 0:2` { + t.Errorf("got %v, want %v", pointsToString(query.Slice(0, 1)), `0:0, 0:1, 0:2`) + } + + if pointsToString(query.Slice(0, 0.5)) != `0:0, 0:1` { + t.Errorf("got %v, want %v", pointsToString(query.Slice(0, 0.5)), `0:0, 0:1`) + } + + if pointsToString(query.Slice(1, 0.5)) != `0:2, 0:1` { + t.Errorf("got %v, want %v", pointsToString(query.Slice(1, 0.5)), `0:2, 0:1`) + } + + if pointsToString(query.Slice(0.25, 0.75)) != `0:0.5, 0:1, 0:1.5` { + t.Errorf("got %v, want %v", pointsToString(query.Slice(0.25, 0.75)), `0:0.5, 0:1, 0:1.5`) + } + +} From d5e1fb82955e226b31d826d0389321f954a63504 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Thu, 3 Oct 2024 14:40:58 +0300 Subject: [PATCH 07/44] testing + fixing --- s2/chain_interpolation_query.go | 4 ++-- s2/chain_interpolation_query_test.go | 28 ++++++++++++++-------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index d8d0f89f..75222d41 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -77,7 +77,7 @@ func (s ChainInterpolationQuery) AtDistance(inputDistance s1.Angle) (point Point position, found := slices.BinarySearch(s.cumulativeValues, inputDistance) - if (found && position == 0) || (!found && position < 0) { + if position <= 0 { return s.Shape.Edge(s.firstEdgeID).V0, s.firstEdgeID, s.cumulativeValues[0], nil } else if (found && position == len(s.cumulativeValues)-1) || (!found && position >= len(s.cumulativeValues)) { return s.Shape.Edge(s.lastEdgeID).V1, s.lastEdgeID, s.cumulativeValues[len(s.cumulativeValues)-1], nil @@ -85,7 +85,7 @@ func (s ChainInterpolationQuery) AtDistance(inputDistance s1.Angle) (point Point edgeID = max(position+s.firstEdgeID-1, 0) edge := s.Shape.Edge(edgeID) distance = inputDistance - s.cumulativeValues[max(0, position-1)] - point = GetPointOnLine(edge.V0, edge.V1, inputDistance-s.cumulativeValues[max(0, position-1)]) + point = GetPointOnLine(edge.V0, edge.V1, distance) } return point, edgeID, distance, nil diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 8455b6e6..34efeef1 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -22,9 +22,9 @@ type result struct { } func TestSimplePolylines(t *testing.T) { - a := PointFromLatLng(LatLng{0, 0}) - b := PointFromLatLng(LatLng{latitudeB, 0}) - c := PointFromLatLng(LatLng{latitudeC, 0}) + a := PointFromLatLng(LatLngFromDegrees(0, 0)) + b := PointFromLatLng(LatLngFromDegrees(latitudeB, 0)) + c := PointFromLatLng(LatLngFromDegrees(latitudeC, 0)) emptyLaxPolyline := Shape(&LaxPolyline{}) acPolyline := Shape(&LaxPolyline{vertices: []Point{a, c}}) @@ -176,15 +176,15 @@ func TestSimplePolylines(t *testing.T) { } if ac[i].point.Angle(groundTruth[i].point.Vector) >= kEpsilonAngle { - t.Errorf("got %v, want %v", ac[i].point, kEpsilonAngle) + t.Errorf("got %v, want %v", ac[i].point, groundTruth[i].point.Vector) } if abc[i].point.Angle(groundTruth[i].point.Vector) >= kEpsilonAngle { - t.Errorf("got %v, want %v", abc[i].point, kEpsilonAngle) + t.Errorf("got %v, want %v", abc[i].point, groundTruth[i].point.Vector) } - if bb[i].point.Angle(bbPolyline.Edge(i).V1.Vector) >= kEpsilonAngle { - t.Errorf("got %v, want %v", bb[i].point, kEpsilonAngle) + if bb[i].point.Angle(bbPolyline.Edge(0).V0.Vector) >= kEpsilonAngle { + t.Errorf("got %v, want %v", bb[i].point, bbPolyline.Edge(0).V0) } if ac[i].edgeID != 0 { @@ -229,12 +229,12 @@ func TestDistances(t *testing.T) { results := make([]result, len(distances)) for i := 0; i < len(distances); i++ { - point, edgeID, distance, err := query.AtDistance(s1.Angle(distances[i])) + point, edgeID, distance, err := query.AtDistance(s1.Angle(distances[i] * float64(s1.Radian))) - results = append(results, result{point, edgeID, distance, err}) + results[i] = result{point, edgeID, distance, err} } - if float64Near(length, totalLength, kEpsilon) { + if !float64Near(length, totalLength, kEpsilon) { t.Errorf("got %v, want %v", length, totalLength) } @@ -251,7 +251,7 @@ func TestDistances(t *testing.T) { distance := results[i].distance if d < 0 { - if !float64Eq(lat, 0) { + if !float64Eq(lat, LatLngFromPoint(shape.Edge(0).V0).Lat.Degrees()) { t.Errorf("got %v, want %v", lat, 0) } @@ -262,7 +262,7 @@ func TestDistances(t *testing.T) { if !float64Eq(distance.Degrees(), 0) { t.Errorf("got %v, want %v", distance, 0) } - } else if d < 2 { + } else if d > 2 { if !float64Near(lat, 2, kEpsilon) { t.Errorf("got %v, want %v", lat, 2) } @@ -384,7 +384,7 @@ func TestGetLengthAtEdgePolyline(t *testing.T) { t.Errorf("got %v, want %v", err, nil) } if !float64Eq(float64(length), float64(s1.InfAngle())) { - t.Errorf("got %v, want %v", length, 0) + t.Errorf("got %v, want %v", length, s1.InfAngle()) } length, err = query.GetLengthAtEdgeEnd(0) @@ -416,7 +416,7 @@ func TestGetLengthAtEdgePolyline(t *testing.T) { t.Errorf("got %v, want %v", err, nil) } if !float64Eq(float64(length), float64(s1.InfAngle())) { - t.Errorf("got %v, want %v", length, 0) + t.Errorf("got %v, want %v", length, s1.InfAngle()) } } From 18c8000228f28ab2b26db04aeca5b8bca5e68348 Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Fri, 4 Oct 2024 09:40:03 +0300 Subject: [PATCH 08/44] finished with ChainINtepolationQueryTest --- s2/chain_interpolation_query.go | 5 +++-- s2/chain_interpolation_query_test.go | 14 +++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 75222d41..5dfbf3ab 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -75,6 +75,8 @@ func (s ChainInterpolationQuery) AtDistance(inputDistance s1.Angle) (point Point return point, 0, 0, ErrEmptyChain } + distance = inputDistance + position, found := slices.BinarySearch(s.cumulativeValues, inputDistance) if position <= 0 { @@ -84,8 +86,7 @@ func (s ChainInterpolationQuery) AtDistance(inputDistance s1.Angle) (point Point } else { edgeID = max(position+s.firstEdgeID-1, 0) edge := s.Shape.Edge(edgeID) - distance = inputDistance - s.cumulativeValues[max(0, position-1)] - point = GetPointOnLine(edge.V0, edge.V1, distance) + point = GetPointOnLine(edge.V0, edge.V1, inputDistance-s.cumulativeValues[max(0, position-1)]) } return point, edgeID, distance, nil diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 34efeef1..a717d594 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -229,7 +229,7 @@ func TestDistances(t *testing.T) { results := make([]result, len(distances)) for i := 0; i < len(distances); i++ { - point, edgeID, distance, err := query.AtDistance(s1.Angle(distances[i] * float64(s1.Radian))) + point, edgeID, distance, err := query.AtDistance(s1.Angle(distances[i] * float64(s1.Degree))) results[i] = result{point, edgeID, distance, err} } @@ -383,7 +383,7 @@ func TestGetLengthAtEdgePolyline(t *testing.T) { if err != nil { t.Errorf("got %v, want %v", err, nil) } - if !float64Eq(float64(length), float64(s1.InfAngle())) { + if float64(length) != float64(s1.InfAngle()) { t.Errorf("got %v, want %v", length, s1.InfAngle()) } @@ -415,7 +415,7 @@ func TestGetLengthAtEdgePolyline(t *testing.T) { if err != nil { t.Errorf("got %v, want %v", err, nil) } - if !float64Eq(float64(length), float64(s1.InfAngle())) { + if float64(length) != float64(s1.InfAngle()) { t.Errorf("got %v, want %v", length, s1.InfAngle()) } } @@ -453,7 +453,7 @@ func TestGetLengthAtEdgePolygon(t *testing.T) { if err != nil { t.Errorf("got %v, want %v", err, nil) } - if !float64Eq(float64(length), float64(s1.InfAngle())) { + if float64(length) != float64(s1.InfAngle()) { t.Errorf("got %v, want %v", length, 0) } @@ -494,7 +494,7 @@ func TestGetLengthAtEdgePolygon(t *testing.T) { if err != nil { t.Errorf("got %v, want %v", err, nil) } - if !float64Eq(float64(length), float64(s1.InfAngle())) { + if float64(length) != float64(s1.InfAngle()) { t.Errorf("got %v, want %v", length, 0) } } @@ -514,8 +514,8 @@ func TestGetLengthAtEdgePolygon(t *testing.T) { if err != nil { t.Errorf("got %v, want %v", err, nil) } - if !float64Eq(float64(length), float64(s1.InfAngle())) { - t.Errorf("got %v, want %v", length, 0) + if float64(length) != float64(s1.InfAngle()) { + t.Errorf("got %v, want %v", length, s1.InfAngle()) } } From 95e828451f8b5dd9c8927b643a7c383840ece1cb Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Fri, 4 Oct 2024 09:58:53 +0300 Subject: [PATCH 09/44] fixed PointVector Chain method --- s2/point_vector.go | 2 +- s2/point_vector_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/s2/point_vector.go b/s2/point_vector.go index 33f828ad..b591b758 100644 --- a/s2/point_vector.go +++ b/s2/point_vector.go @@ -36,7 +36,7 @@ func (p *PointVector) Chain(i int) Chain { if len(*p) == 1 { return Chain{0, 1} } - return Chain{i, len(*p) - i} + return Chain{i, 1} } func (p *PointVector) ChainEdge(i, j int) Edge { return Edge{(*p)[i], (*p)[j]} } func (p *PointVector) ChainPosition(e int) ChainPosition { return ChainPosition{e, 0} } diff --git a/s2/point_vector_test.go b/s2/point_vector_test.go index ce6cc781..8fd6b8ec 100644 --- a/s2/point_vector_test.go +++ b/s2/point_vector_test.go @@ -75,7 +75,7 @@ func TestPointVectorBasics(t *testing.T) { if got, want := shape.Chain(i).Start, i; i != got { t.Errorf("shape.Chain(%d).Start = %d, want %d", i, got, want) } - if got, want := shape.Chain(i).Length, shape.NumEdges()-i; got != want { + if got, want := shape.Chain(i).Length, 1; got != want { t.Errorf("shape.Chain(%d).Length = %v, want %d", i, got, want) } edge := shape.Edge(i) From d95a469dab5660075071498238cddf596291e9a3 Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Fri, 4 Oct 2024 10:34:52 +0300 Subject: [PATCH 10/44] renamed InitChainInterpolationQuery --- s2/chain_interpolation_query_test.go | 32 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index a717d594..940d3e03 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -33,11 +33,11 @@ func TestSimplePolylines(t *testing.T) { ccPolyline := Shape(&LaxPolyline{vertices: []Point{c}}) uninitializedQuery := ChainInterpolationQuery{} - emptyQuery := InitS2ChainInterpolationQuery(emptyLaxPolyline, 0) - acQuery := InitS2ChainInterpolationQuery(acPolyline, 0) - abcQuery := InitS2ChainInterpolationQuery(abcPolyline, 0) - bbQuery := InitS2ChainInterpolationQuery(bbPolyline, 0) - ccQuery := InitS2ChainInterpolationQuery(ccPolyline, 0) + emptyQuery := InitChainInterpolationQuery(emptyLaxPolyline, 0) + acQuery := InitChainInterpolationQuery(acPolyline, 0) + abcQuery := InitChainInterpolationQuery(abcPolyline, 0) + bbQuery := InitChainInterpolationQuery(bbPolyline, 0) + ccQuery := InitChainInterpolationQuery(ccPolyline, 0) distances := []float64{ -1., @@ -218,7 +218,7 @@ func TestDistances(t *testing.T) { totalLength := vertices[0].Angle(vertices[len(vertices)-1].Vector).Degrees() shape := Polyline(vertices) - query := InitS2ChainInterpolationQuery(&shape, 0) + query := InitChainInterpolationQuery(&shape, 0) angle, err := query.GetLength() @@ -312,9 +312,9 @@ func TestChains(t *testing.T) { laxPolygon := LaxPolygonFromPoints(loops) - query := InitS2ChainInterpolationQuery(laxPolygon, -1) - query0 := InitS2ChainInterpolationQuery(laxPolygon, 0) - query1 := InitS2ChainInterpolationQuery(laxPolygon, 1) + query := InitChainInterpolationQuery(laxPolygon, -1) + query0 := InitChainInterpolationQuery(laxPolygon, 0) + query1 := InitChainInterpolationQuery(laxPolygon, 1) point, edgeID, distance, err := query.AtFraction(0.25) queryResult := result{point, edgeID, distance, err} @@ -348,7 +348,7 @@ func TestChains(t *testing.T) { } func TestGetLengthAtEdgeEmpty(t *testing.T) { - query := InitS2ChainInterpolationQuery(&laxPolyline{}, 0) + query := InitChainInterpolationQuery(&laxPolyline{}, 0) angle, err := query.GetLengthAtEdgeEnd(0) @@ -369,7 +369,7 @@ func TestGetLengthAtEdgePolyline(t *testing.T) { PointFromLatLng(LatLngFromDegrees(0, 6)), } - query := InitS2ChainInterpolationQuery(&laxPolyline{points}, 0) + query := InitChainInterpolationQuery(&laxPolyline{points}, 0) length, err := query.GetLength() if err != nil { @@ -437,7 +437,7 @@ func TestGetLengthAtEdgePolygon(t *testing.T) { {points[4], points[5], points[6], points[7]}, } - query0 := InitS2ChainInterpolationQuery(laxPolygonFromPoints(loops), 0) + query0 := InitChainInterpolationQuery(laxPolygonFromPoints(loops), 0) tolerance := s1.Degree * 0.01 @@ -499,7 +499,7 @@ func TestGetLengthAtEdgePolygon(t *testing.T) { } } - query1 := InitS2ChainInterpolationQuery(laxPolygonFromPoints(loops), 1) + query1 := InitChainInterpolationQuery(laxPolygonFromPoints(loops), 1) length, err = query1.GetLength() if err != nil { @@ -554,13 +554,13 @@ func TestGetLengthAtEdgePolygon(t *testing.T) { func TestSlice(t *testing.T) { // Test with empty shape - if pointsToString(InitS2ChainInterpolationQuery(nil, 0).Slice(0, 1)) != `` { - t.Errorf("got %v, want %v", pointsToString(InitS2ChainInterpolationQuery(nil, -1).Slice(0, 1)), ``) + if pointsToString(InitChainInterpolationQuery(nil, 0).Slice(0, 1)) != `` { + t.Errorf("got %v, want %v", pointsToString(InitChainInterpolationQuery(nil, -1).Slice(0, 1)), ``) } polyline := laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)) - query := InitS2ChainInterpolationQuery(polyline, 0) + query := InitChainInterpolationQuery(polyline, 0) if pointsToString(query.Slice(0, 1)) != `0:0, 0:1, 0:2` { t.Errorf("got %v, want %v", pointsToString(query.Slice(0, 1)), `0:0, 0:1, 0:2`) From 3046c856463dcdf0e8276343da004bb9e284f23c Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Fri, 4 Oct 2024 10:54:31 +0300 Subject: [PATCH 11/44] added docs --- s2/chain_interpolation_query.go | 80 ++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 5dfbf3ab..f2435cac 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -8,9 +8,15 @@ import ( ) var ( + // ErrEmptyChain is returned by ChainInterpolationQuery when the query + // contains no edges. ErrEmptyChain = errors.New("empty chain") ) +// ChainInterpolationQuery is a helper struct for querying points on Shape's +// edges by spherical distance. The distance is computed cumulatively along the +// edges contained in the shape, using the order in which the edges are stored +// by the Shape object. type ChainInterpolationQuery struct { Shape Shape ChainID int @@ -19,7 +25,19 @@ type ChainInterpolationQuery struct { lastEdgeID int } -func InitS2ChainInterpolationQuery(shape Shape, chainID int) ChainInterpolationQuery { +// InitChainInterpolationQuery initializes and conctructs a ChainInterpolationQuery. +// If a particular chain id is specified at the query initialization, then the +// distance values are computed along that single chain, which allows per-chain +// interpolation. If no chain is specified, then the interpolated point as a +// function of distance is discontinuous at chain boundaries. Using multiple +// chains can be used in such algorithms as quasi-random sampling along the +// total span of a multiline. +// +// Once the query object is initialized, the complexity of each subsequent query +// is O( log(number of edges) ). The complexity of the initialization and the +// memory footprint of the query object are both O(number of edges). + +func InitChainInterpolationQuery(shape Shape, chainID int) ChainInterpolationQuery { if shape == nil || chainID >= shape.NumChains() { return ChainInterpolationQuery{nil, 0, nil, 0, 0} } @@ -28,10 +46,14 @@ func InitS2ChainInterpolationQuery(shape Shape, chainID int) ChainInterpolationQ var cumulativeValues []s1.Angle if chainID >= 0 { + // If a valid chain id was provided, then the range of edge ids is defined + // by the start and the length of the chain. chain := shape.Chain(chainID) firstEdgeID = chain.Start lastEdgeID = firstEdgeID + chain.Length - 1 } else { + // If no valid chain id was provided then we use the whole range of shape's + // edge ids. firstEdgeID = 0 lastEdgeID = shape.NumEdges() - 1 } @@ -50,14 +72,22 @@ func InitS2ChainInterpolationQuery(shape Shape, chainID int) ChainInterpolationQ return ChainInterpolationQuery{shape, chainID, cumulativeValues, firstEdgeID, lastEdgeID} } +// Gets the total length of the chain(s), which corresponds to the distance at +// the end vertex of the last edge of the chain(s). Returns zero length for +// shapes containing no edges. func (s ChainInterpolationQuery) GetLength() (s1.Angle, error) { + // The total length equals the cumulative value at the end of the last + // edge, if there is at least one edge in the shape. if len(s.cumulativeValues) == 0 { return 0, ErrEmptyChain } - return s.cumulativeValues[len(s.cumulativeValues)-1], nil } +// Returns the cumulative length along the edges being interpolated up to the +// end of the given edge ID. Returns s1.InfAngle() if the edge +// ID does not lie within the set of edges being interpolated. Returns +// ErrEmptyChain if the ChainInterpolationQuery is empty. func (s ChainInterpolationQuery) GetLengthAtEdgeEnd(edgeID int) (s1.Angle, error) { if len(s.cumulativeValues) == 0 { return 0, ErrEmptyChain @@ -70,6 +100,20 @@ func (s ChainInterpolationQuery) GetLengthAtEdgeEnd(edgeID int) (s1.Angle, error return s.cumulativeValues[edgeID-s.firstEdgeID+1], nil } +// Computes the Point located at the given distance along the edges from the +// first vertex of the first edge. Also computes the edge id and the actual +// distance corresponding to the resulting point. +// +// This method returns a valid result if the query has been initialized with +// at least one edge. +// +// If the input distance exceeds the total length, then the resulting point is +// the end vertex of the last edge, and the resulting distance is set to the +// total length. +// +// If there are one or more degenerate (zero-length) edges corresponding to +// the given distance, then the resulting point is located on the first of +// these edges. func (s ChainInterpolationQuery) AtDistance(inputDistance s1.Angle) (point Point, edgeID int, distance s1.Angle, err error) { if len(s.cumulativeValues) == 0 { return point, 0, 0, ErrEmptyChain @@ -80,10 +124,16 @@ func (s ChainInterpolationQuery) AtDistance(inputDistance s1.Angle) (point Point position, found := slices.BinarySearch(s.cumulativeValues, inputDistance) if position <= 0 { + // Corner case: the first vertex of the shape at distance = 0. return s.Shape.Edge(s.firstEdgeID).V0, s.firstEdgeID, s.cumulativeValues[0], nil } else if (found && position == len(s.cumulativeValues)-1) || (!found && position >= len(s.cumulativeValues)) { + // Corner case: the input distance is greater than the total length, hence + // we snap the result to the last vertex of the shape at distance = total + // length. return s.Shape.Edge(s.lastEdgeID).V1, s.lastEdgeID, s.cumulativeValues[len(s.cumulativeValues)-1], nil } else { + // Obtain the edge index and compute the interpolated result from the edge + // vertices. edgeID = max(position+s.firstEdgeID-1, 0) edge := s.Shape.Edge(edgeID) point = GetPointOnLine(edge.V0, edge.V1, inputDistance-s.cumulativeValues[max(0, position-1)]) @@ -92,6 +142,11 @@ func (s ChainInterpolationQuery) AtDistance(inputDistance s1.Angle) (point Point return point, edgeID, distance, nil } +// Similar to the above function, but takes the normalized fraction of the +// distance as input, with inputFraction = 0 corresponding to the beginning of the +// shape or chain and inputFraction = 1 to the end. Forwards the call to +// AtDistance(). A small precision loss may occur due to converting the +// fraction to distance by multiplying it by the total length. func (s ChainInterpolationQuery) AtFraction(inputFraction float64) (point Point, edgeID int, distance s1.Angle, err error) { length, error := s.GetLength() if error != nil { @@ -101,12 +156,30 @@ func (s ChainInterpolationQuery) AtFraction(inputFraction float64) (point Point, return s.AtDistance(s1.Angle(inputFraction * float64(length))) } +// Returns the vector of points that is a slice of the chain from +// beginFraction to endFraction. If beginFraction is greater than +// endFraction, then the points are returned in reverse order. +// +// For example, Slice(0,1) returns the entire chain, Slice(0, 0.5) returns the +// first half of the chain, and Slice(1, 0.5) returns the second half of the +// chain in reverse. +// +// The endpoints of the slice are interpolated (except when coinciding with an +// existing vertex of the chain), and all the internal points are copied from +// the chain as is. +// +// If the query is either uninitialized, or initialized with a shape +// containing no edges, then an empty vector is returned. func (s ChainInterpolationQuery) Slice(beginFraction, endFraction float64) []Point { var points []Point s.AddSlice(beginFraction, endFraction, &points) return points } +// Appends the chain slice from beginFraction to endFraction to the given +// slice. If beginFraction is greater than endFraction, then the points are +// appended in reverse order. If the query is either uninitialized, or +// initialized with a shape containing no edges, then no points are appended. func (s ChainInterpolationQuery) AddSlice(beginFraction, endFraction float64, points *[]Point) { if len(s.cumulativeValues) == 0 { return @@ -114,6 +187,7 @@ func (s ChainInterpolationQuery) AddSlice(beginFraction, endFraction float64, po reverse := beginFraction > endFraction if reverse { + // Swap the begin and end fractions so that we can iterate in ascending order. beginFraction, endFraction = endFraction, beginFraction } @@ -129,6 +203,7 @@ func (s ChainInterpolationQuery) AddSlice(beginFraction, endFraction float64, po return } + // Copy the internal points from the chain. for edgeID := beginEdgeID; edgeID < endEdgeID; edgeID++ { edge := s.Shape.Edge(edgeID) if lastPoint != edge.V1 { @@ -138,6 +213,7 @@ func (s ChainInterpolationQuery) AddSlice(beginFraction, endFraction float64, po } *points = append(*points, atEnd) + // Reverse the slice if necessary. if reverse { slices.Reverse(*points) } From 665d021d4fe28a1f0e858c3d3b1161b94af9cfc3 Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Fri, 4 Oct 2024 13:20:03 +0300 Subject: [PATCH 12/44] typo --- s2/chain_interpolation_query.go | 1 - 1 file changed, 1 deletion(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index f2435cac..70309758 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -36,7 +36,6 @@ type ChainInterpolationQuery struct { // Once the query object is initialized, the complexity of each subsequent query // is O( log(number of edges) ). The complexity of the initialization and the // memory footprint of the query object are both O(number of edges). - func InitChainInterpolationQuery(shape Shape, chainID int) ChainInterpolationQuery { if shape == nil || chainID >= shape.NumChains() { return ChainInterpolationQuery{nil, 0, nil, 0, 0} From bab758cb1a6e8819f06ad05db4b8ced09851a8da Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Fri, 4 Oct 2024 14:53:28 +0300 Subject: [PATCH 13/44] TestSlice update --- s2/chain_interpolation_query_test.go | 87 +++++++++++++++++++--------- 1 file changed, 61 insertions(+), 26 deletions(-) diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 940d3e03..8399ebea 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -200,7 +200,6 @@ func TestSimplePolylines(t *testing.T) { } } } - func TestDistances(t *testing.T) { // Initialize test data distances := []float64{ @@ -551,31 +550,67 @@ func TestGetLengthAtEdgePolygon(t *testing.T) { t.Errorf("got %v, want %v", length, 14.0) } } - func TestSlice(t *testing.T) { - // Test with empty shape - if pointsToString(InitChainInterpolationQuery(nil, 0).Slice(0, 1)) != `` { - t.Errorf("got %v, want %v", pointsToString(InitChainInterpolationQuery(nil, -1).Slice(0, 1)), ``) - } - - polyline := laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)) - - query := InitChainInterpolationQuery(polyline, 0) - - if pointsToString(query.Slice(0, 1)) != `0:0, 0:1, 0:2` { - t.Errorf("got %v, want %v", pointsToString(query.Slice(0, 1)), `0:0, 0:1, 0:2`) - } - - if pointsToString(query.Slice(0, 0.5)) != `0:0, 0:1` { - t.Errorf("got %v, want %v", pointsToString(query.Slice(0, 0.5)), `0:0, 0:1`) - } - - if pointsToString(query.Slice(1, 0.5)) != `0:2, 0:1` { - t.Errorf("got %v, want %v", pointsToString(query.Slice(1, 0.5)), `0:2, 0:1`) - } - - if pointsToString(query.Slice(0.25, 0.75)) != `0:0.5, 0:1, 0:1.5` { - t.Errorf("got %v, want %v", pointsToString(query.Slice(0.25, 0.75)), `0:0.5, 0:1, 0:1.5`) + tests := []struct { + name string + args struct { + shape Shape + startSliceFraction float64 + endSliceFraction float64 + } + want string + }{ + { + name: "empty shape", + args: struct { + shape Shape + startSliceFraction float64 + endSliceFraction float64 + }{nil, 0, 1}, + want: ``, + }, + { + name: "full polyline", + args: struct { + shape Shape + startSliceFraction float64 + endSliceFraction float64 + }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 0, 1}, + want: `0:0, 0:1, 0:2`, + }, + { + name: "first half of polyline", + args: struct { + shape Shape + startSliceFraction float64 + endSliceFraction float64 + }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 0, 0.5}, + want: `0:0, 0:1`, + }, + { + name: "second half of polyline", + args: struct { + shape Shape + startSliceFraction float64 + endSliceFraction float64 + }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 1, 0.5}, + want: `0:2, 0:1`, + }, + { + name: "middle of polyline", + args: struct { + shape Shape + startSliceFraction float64 + endSliceFraction float64 + }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 0.25, 0.75}, + want: `0:0.5, 0:1, 0:1.5`, + }, + } + + for _, test := range tests { + query := InitChainInterpolationQuery(test.args.shape, 0) + if got := pointsToString(query.Slice(test.args.startSliceFraction, test.args.endSliceFraction)); got != test.want { + t.Errorf("%v: got %v, want %v", test.name, got, test.want) + } } - } From ded5834ed18f8a2216155923ef795a3297c3d9cd Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Fri, 4 Oct 2024 15:10:40 +0300 Subject: [PATCH 14/44] updated TestChains --- s2/chain_interpolation_query_test.go | 80 ++++++++++++++++++---------- 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 8399ebea..08af1e46 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -302,7 +302,6 @@ func TestDistances(t *testing.T) { } } } - func TestChains(t *testing.T) { loops := [][]Point{ parsePoints(`0:0, 1:0`), @@ -311,38 +310,63 @@ func TestChains(t *testing.T) { laxPolygon := LaxPolygonFromPoints(loops) - query := InitChainInterpolationQuery(laxPolygon, -1) - query0 := InitChainInterpolationQuery(laxPolygon, 0) - query1 := InitChainInterpolationQuery(laxPolygon, 1) - - point, edgeID, distance, err := query.AtFraction(0.25) - queryResult := result{point, edgeID, distance, err} - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - - point, edgeID, distance, err = query0.AtFraction(0.25) - queryResult0 := result{point, edgeID, distance, err} - if err != nil { - t.Errorf("got %v, want %v", err, nil) + tests := []struct { + query ChainInterpolationQuery + want result + args float64 + }{ + { + query: InitChainInterpolationQuery(laxPolygon, -1), + want: result{ + point: PointFromLatLng(LatLngFromDegrees(1, 0)), + edgeID: 1, + distance: s1.Angle(1 * s1.Degree), + err: nil, + }, + args: 0.25, + }, + { + query: InitChainInterpolationQuery(laxPolygon, 0), + want: result{ + point: PointFromLatLng(LatLngFromDegrees(0.5, 0)), + edgeID: 0, + distance: s1.Angle(0.5 * s1.Degree), + err: nil, + }, + args: 0.25, + }, + { + query: InitChainInterpolationQuery(laxPolygon, 1), + want: result{ + point: PointFromLatLng(LatLngFromDegrees(2.5, 0)), + edgeID: 2, + distance: s1.Angle(2.5 * s1.Degree), + err: nil, + }, + args: 0.25, + }, } - point, edgeID, distance, err = query1.AtFraction(0.25) - queryResult1 := result{point, edgeID, distance, err} - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } + for i, tt := range tests { + point, edgeID, distance, err := tt.query.AtFraction(tt.args) - if !float64Near(LatLngFromPoint(queryResult.point).Lat.Degrees(), 1, kEpsilon) { - t.Errorf("got %v, want %v", LatLngFromPoint(queryResult.point).Lat.Degrees(), 1) - } + got := result{ + point: point, + edgeID: edgeID, + distance: distance, + err: err, + } - if !float64Near(LatLngFromPoint(queryResult0.point).Lat.Degrees(), 0.5, kEpsilon) { - t.Errorf("got %v, want %v", LatLngFromPoint(queryResult0.point).Lat.Degrees(), 0.5) - } + if !float64Near(LatLngFromPoint(got.point).Lat.Degrees(), LatLngFromPoint(tt.want.point).Lat.Degrees(), kEpsilon) { + t.Errorf("%d. got %v, want %v", i, LatLngFromPoint(got.point).Lat.Degrees(), LatLngFromPoint(tt.want.point).Lat.Degrees()) + } - if !float64Near(LatLngFromPoint(queryResult1.point).Lat.Degrees(), 2.5, kEpsilon) { - t.Errorf("got %v, want %v", LatLngFromPoint(queryResult1.point).Lat.Degrees(), 2.5) + if got.edgeID != tt.want.edgeID { + t.Errorf("%d. got %v, want %v", i, got.edgeID, tt.want.edgeID) + } + if got.err != tt.want.err { + t.Errorf("%d. got %v, want %v", i, got.err, tt.want.err) + } } } From 94c054819401663ad47f088f2f5368e1988f562d Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Fri, 4 Oct 2024 15:17:42 +0300 Subject: [PATCH 15/44] TestGetLengthAtEdgePolyline updated --- s2/chain_interpolation_query_test.go | 66 ++++++++++------------------ 1 file changed, 23 insertions(+), 43 deletions(-) diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 08af1e46..a649a1ec 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -383,7 +383,6 @@ func TestGetLengthAtEdgeEmpty(t *testing.T) { t.Errorf("got %v, want %v", angle, 0) } } - func TestGetLengthAtEdgePolyline(t *testing.T) { points := []Point{ PointFromLatLng(LatLngFromDegrees(0, 0)), @@ -392,54 +391,35 @@ func TestGetLengthAtEdgePolyline(t *testing.T) { PointFromLatLng(LatLngFromDegrees(0, 6)), } - query := InitChainInterpolationQuery(&laxPolyline{points}, 0) + polyline := laxPolyline{points} - length, err := query.GetLength() - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if !float64Eq(float64(length), float64(s1.Degree)*6) { - t.Errorf("got %v, want %v", length, s1.Degree*6) - } + query := InitChainInterpolationQuery(&polyline, 0) - length, err = query.GetLengthAtEdgeEnd(-100) - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if float64(length) != float64(s1.InfAngle()) { - t.Errorf("got %v, want %v", length, s1.InfAngle()) + tests := []struct { + edgeID int + want s1.Angle + }{ + {-100, s1.InfAngle()}, + {0, s1.Degree}, + {1, s1.Degree * 3}, + {2, s1.Degree * 6}, + {100, s1.InfAngle()}, } - length, err = query.GetLengthAtEdgeEnd(0) - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if !float64Eq(float64(length), float64(s1.Degree)) { - t.Errorf("got %v, want %v", length, 0) - } + for _, tt := range tests { + got, err := query.GetLengthAtEdgeEnd(tt.edgeID) - length, err = query.GetLengthAtEdgeEnd(1) - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if !float64Eq(float64(length), float64(s1.Degree)*3) { - t.Errorf("got %v, want %v", length, 0) - } - - length, err = query.GetLengthAtEdgeEnd(2) - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if !float64Eq(float64(length), float64(s1.Degree)*6) { - t.Errorf("got %v, want %v", length, 0) - } + if err != nil { + t.Errorf("edgeID %d: got %v, want %v", tt.edgeID, err, nil) + } - length, err = query.GetLengthAtEdgeEnd(100) - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if float64(length) != float64(s1.InfAngle()) { - t.Errorf("got %v, want %v", length, s1.InfAngle()) + if tt.edgeID <= polyline.NumEdges() && tt.edgeID >= 0 { + if !float64Near(got.Degrees(), tt.want.Degrees(), kEpsilon) { + t.Errorf("edgeID %d: got %v, want %v", tt.edgeID, got, tt.want) + } + } else if got != tt.want { + t.Errorf("edgeID %d: got %v, want %v", tt.edgeID, got, tt.want) + } } } From c052091af5b1ef846986026b7b83f1b2c88f74fd Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Fri, 4 Oct 2024 16:23:48 +0300 Subject: [PATCH 16/44] TestGetLengthAtEdgePolygon updated --- s2/chain_interpolation_query_test.go | 296 ++++++++++++++++----------- 1 file changed, 177 insertions(+), 119 deletions(-) diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index a649a1ec..5e1f2502 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -422,138 +422,196 @@ func TestGetLengthAtEdgePolyline(t *testing.T) { } } } - func TestGetLengthAtEdgePolygon(t *testing.T) { - points := []Point{ - PointFromLatLng(LatLngFromDegrees(1, 1)), - PointFromLatLng(LatLngFromDegrees(2, 1)), - PointFromLatLng(LatLngFromDegrees(2, 3)), - PointFromLatLng(LatLngFromDegrees(1, 3)), - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 4)), - PointFromLatLng(LatLngFromDegrees(3, 4)), - PointFromLatLng(LatLngFromDegrees(3, 0)), - } - - loops := [][]Point{ - {points[0], points[1], points[2], points[3]}, - {points[4], points[5], points[6], points[7]}, - } - - query0 := InitChainInterpolationQuery(laxPolygonFromPoints(loops), 0) - - tolerance := s1.Degree * 0.01 - - length, err := query0.GetLength() - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if !float64Near(length.Degrees(), 6.0, tolerance.Degrees()) { - t.Errorf("got %v, want %v", length, 6.0) - } - - length, err = query0.GetLengthAtEdgeEnd(-100) - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if float64(length) != float64(s1.InfAngle()) { - t.Errorf("got %v, want %v", length, 0) - } - - length, err = query0.GetLengthAtEdgeEnd(0) - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if !float64Near(length.Degrees(), 1.0, tolerance.Degrees()) { - t.Errorf("got %v, want %v", length, 1.0) - } - - length, err = query0.GetLengthAtEdgeEnd(1) - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if !float64Near(length.Degrees(), 3.0, tolerance.Degrees()) { - t.Errorf("got %v, want %v", length, 3.0) - } - - length, err = query0.GetLengthAtEdgeEnd(2) - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if !float64Near(length.Degrees(), 4.0, tolerance.Degrees()) { - t.Errorf("got %v, want %v", length, 4.0) - } + polygon := laxPolygonFromPoints([][]Point{ + { + PointFromLatLng(LatLngFromDegrees(1, 1)), + PointFromLatLng(LatLngFromDegrees(2, 1)), + PointFromLatLng(LatLngFromDegrees(2, 3)), + PointFromLatLng(LatLngFromDegrees(1, 3)), + }, + { + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 4)), + PointFromLatLng(LatLngFromDegrees(3, 4)), + PointFromLatLng(LatLngFromDegrees(3, 0)), + }}) - length, err = query0.GetLengthAtEdgeEnd(3) - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if !float64Near(length.Degrees(), 6.0, tolerance.Degrees()) { - t.Errorf("got %v, want %v", length, 6.0) - } + tolerance := .01 - for _, element := range []float64{4, 5, 6, 7, 100} { - length, err = query0.GetLengthAtEdgeEnd(int(element)) - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if float64(length) != float64(s1.InfAngle()) { - t.Errorf("got %v, want %v", length, 0) + tests := []struct { + name string + args struct { + shape Shape + edge int + chainID int } + want s1.Angle + }{ + { + name: "edge before first edge of first loop", + args: struct { + shape Shape + edge int + chainID int + }{ + shape: polygon, + edge: -100, + chainID: 0, + }, + want: s1.InfAngle(), + }, + { + name: "first edge of first loop", + args: struct { + shape Shape + edge int + chainID int + }{ + shape: polygon, + edge: 0, + chainID: 0, + }, + want: s1.Degree, + }, + { + name: "second edge of first loop", + args: struct { + shape Shape + edge int + chainID int + }{ + shape: polygon, + edge: 1, + chainID: 0, + }, + want: s1.Degree * 3, + }, + { + name: "last edge of first loop", + args: struct { + shape Shape + edge int + chainID int + }{ + shape: polygon, + edge: 3, + chainID: 0, + }, + want: s1.Degree * 6, + }, + { + name: "edge after last edge of first loop", + args: struct { + shape Shape + edge int + chainID int + }{ + shape: polygon, + edge: 4, + chainID: 0, + }, + want: s1.InfAngle(), + }, + { + name: "edge before first edge of second loop", + args: struct { + shape Shape + edge int + chainID int + }{ + shape: polygon, + edge: 3, + chainID: 1, + }, + want: s1.InfAngle(), + }, + { + name: "first edge of second loop", + args: struct { + shape Shape + edge int + chainID int + }{ + shape: polygon, + edge: 4, + chainID: 1, + }, + want: s1.Degree * 4, + }, + { + name: "second edge of second loop", + args: struct { + shape Shape + edge int + chainID int + }{ + shape: polygon, + edge: 5, + chainID: 1, + }, + want: s1.Degree * 7, + }, + { + name: "midlle edge of second loop", + args: struct { + shape Shape + edge int + chainID int + }{ + shape: polygon, + edge: 6, + chainID: 1, + }, + want: s1.Degree * 11, + }, + { + name: "last edge of second loop", + args: struct { + shape Shape + edge int + chainID int + }{ + shape: polygon, + edge: 7, + chainID: 1, + }, + want: s1.Degree * 14, + }, + { + name: "edge after last edge of second loop", + args: struct { + shape Shape + edge int + chainID int + }{ + shape: polygon, + edge: 8, + chainID: 1, + }, + want: s1.InfAngle(), + }, } - query1 := InitChainInterpolationQuery(laxPolygonFromPoints(loops), 1) + for _, tt := range tests { + query := InitChainInterpolationQuery(tt.args.shape, tt.args.chainID) - length, err = query1.GetLength() - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if !float64Near(length.Degrees(), 14.0, tolerance.Degrees()) { - t.Errorf("got %v, want %v", length, 6.0) - } + got, err := query.GetLengthAtEdgeEnd(tt.args.edge) - for _, element := range []float64{-100, 0, 1, 2, 3, 100} { - length, err = query1.GetLengthAtEdgeEnd(int(element)) if err != nil { - t.Errorf("got %v, want %v", err, nil) + t.Errorf("%d. got %v, want %v", tt.args.edge, err, nil) } - if float64(length) != float64(s1.InfAngle()) { - t.Errorf("got %v, want %v", length, s1.InfAngle()) - } - } - length, err = query1.GetLengthAtEdgeEnd(4) - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if !float64Near(length.Degrees(), 4.0, tolerance.Degrees()) { - t.Errorf("got %v, want %v", length, 4.0) - } - - length, err = query1.GetLengthAtEdgeEnd(5) - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if !float64Near(length.Degrees(), 7.0, tolerance.Degrees()) { - t.Errorf("got %v, want %v", length, 7.0) - } - - length, err = query1.GetLengthAtEdgeEnd(6) - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if !float64Near(length.Degrees(), 11.0, tolerance.Degrees()) { - t.Errorf("got %v, want %v", length, 11.0) - } - - length, err = query1.GetLengthAtEdgeEnd(7) - if err != nil { - t.Errorf("got %v, want %v", err, nil) - } - if !float64Near(length.Degrees(), 14.0, tolerance.Degrees()) { - t.Errorf("got %v, want %v", length, 14.0) + if tt.want == s1.InfAngle() { + if got != tt.want { + t.Errorf("%d. got %v, want %v", tt.args.edge, got, tt.want) + } + } else if !float64Near(got.Degrees(), tt.want.Degrees(), float64(tolerance)) { + t.Errorf("%d. got %v, want %v", tt.args.edge, got.Degrees(), tt.want.Degrees()) + } } } + func TestSlice(t *testing.T) { tests := []struct { name string From 1a40a64708cac90928214cb7558912622d4999d1 Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Fri, 4 Oct 2024 17:01:20 +0300 Subject: [PATCH 17/44] TestSimplePolylines updated --- s2/chain_interpolation_query_test.go | 166 +++++++-------------------- 1 file changed, 42 insertions(+), 124 deletions(-) diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 5e1f2502..4ced0b40 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -67,136 +67,54 @@ func TestSimplePolylines(t *testing.T) { groundTruth[i] = result{point: point, edgeID: edgeID, distance: s1.Angle(distance)} } - lengthEmpty, err := emptyQuery.GetLength() - if err == nil { - t.Fatal(err) - } - lengthAc, err := acQuery.GetLength() - if err != nil { - t.Fatal(err) - } - lengthAbc, err := abcQuery.GetLength() - if err != nil { - t.Fatal(err) - } - lengthBb, err := bbQuery.GetLength() - if err != nil { - t.Fatal(err) - } - lengthCc, err := ccQuery.GetLength() - if err == nil { - t.Fatal(err) - } - degreesEmpty := lengthEmpty.Degrees() - degreesAc := lengthAc.Degrees() - degreesAbc := lengthAbc.Degrees() - degreesBb := lengthBb.Degrees() - degreesCc := lengthCc.Degrees() - - point, _, _, err := uninitializedQuery.AtFraction(0) - if err == nil { - t.Fatalf("got %v, want error", point) - } - point, _, _, err = acQuery.AtDistance(s1.InfAngle()) - if err != nil { - t.Fatalf("got %v, want nil", point) - } - - ac := make([]result, len(distances)) - abc := make([]result, len(distances)) - bb := make([]result, len(distances)) - cc := make([]result, len(distances)) - - var emptyQueryValid bool - - for i, distance := range distances { - totalFraction := distance / totalLengthAbc - - _, _, _, err := emptyQuery.AtFraction(totalFraction) - - emptyQueryValid = emptyQueryValid || (err == nil) - - distancePoint, distanceEdgeID, newDistance, err := acQuery.AtFraction(totalFraction) - ac[i] = result{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} - - distancePoint, distanceEdgeID, newDistance, err = abcQuery.AtFraction(totalFraction) - abc[i] = result{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} - - distancePoint, distanceEdgeID, newDistance, err = bbQuery.AtFraction(totalFraction) - bb[i] = result{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} - - distancePoint, distanceEdgeID, newDistance, err = ccQuery.AtFraction(totalFraction) - cc[i] = result{point: distancePoint, edgeID: distanceEdgeID, distance: newDistance, err: err} - } - - if emptyQueryValid { - t.Errorf("got %v, want %v", emptyQueryValid, false) - } - - if degreesEmpty >= kEpsilon { - t.Errorf("got %v, want %v", degreesEmpty, kEpsilon) - } - - if !float64Near(degreesAc, totalLengthAbc, kEpsilon) { - t.Errorf("got %v, want %v", degreesAc, totalLengthAbc) - } - - if !float64Near(degreesAbc, totalLengthAbc, kEpsilon) { - t.Errorf("got %v, want %v", degreesAbc, totalLengthAbc) - } - - if degreesBb >= kEpsilon { - t.Errorf("got %v, want %v", degreesBb, kEpsilon) - } - - if degreesCc >= kEpsilon { - t.Errorf("got %v, want %v", degreesCc, kEpsilon) - } - - if point.Angle(c.Vector) >= kEpsilon { - t.Errorf("got %v, want %v", point, kEpsilon) - } - - for i := 0; i < len(groundTruth); i++ { - - if ac[i].err != nil { - t.Errorf("got %v, want %v", ac[i].err, nil) - } - - if abc[i].err != nil { - t.Errorf("got %v, want %v", abc[i].err, nil) - } - - if bb[i].err != nil { - t.Errorf("got %v, want %v", bb[i].err, nil) - } - - if cc[i].err == nil { - t.Errorf("got %v, want %v", cc[i].err, nil) - } - - if ac[i].point.Angle(groundTruth[i].point.Vector) >= kEpsilonAngle { - t.Errorf("got %v, want %v", ac[i].point, groundTruth[i].point.Vector) + for _, args := range []struct { + query ChainInterpolationQuery + want float64 + wantErr bool + }{ + {query: uninitializedQuery, want: 0, wantErr: true}, + {query: emptyQuery, want: 0, wantErr: true}, + {query: acQuery, want: totalLengthAbc, wantErr: false}, + {query: abcQuery, want: totalLengthAbc, wantErr: false}, + {query: bbQuery, want: 0, wantErr: false}, + {query: ccQuery, want: 0, wantErr: true}, + } { + length, err := args.query.GetLength() + if args.wantErr != (err != nil) { + t.Fatalf("got %v, want %v", err, args.wantErr) } - - if abc[i].point.Angle(groundTruth[i].point.Vector) >= kEpsilonAngle { - t.Errorf("got %v, want %v", abc[i].point, groundTruth[i].point.Vector) + if !float64Near(length.Degrees(), args.want, kEpsilon) { + t.Errorf("got %v, want %v", length.Degrees(), args.want) } + } - if bb[i].point.Angle(bbPolyline.Edge(0).V0.Vector) >= kEpsilonAngle { - t.Errorf("got %v, want %v", bb[i].point, bbPolyline.Edge(0).V0) + for _, args := range []struct { + query ChainInterpolationQuery + totalFraction float64 + wantPoint Point + wantEdgeID int + wantDistance s1.Angle + wantErr bool + }{ + {query: uninitializedQuery, totalFraction: 0, wantPoint: Point{}, wantEdgeID: 0, wantDistance: 0, wantErr: true}, + {query: emptyQuery, totalFraction: 0, wantPoint: Point{}, wantEdgeID: 0, wantDistance: 0, wantErr: true}, + {query: acQuery, totalFraction: 0, wantPoint: a, wantEdgeID: 0, wantDistance: s1.Angle(0), wantErr: false}, + {query: abcQuery, totalFraction: 0, wantPoint: a, wantEdgeID: 0, wantDistance: s1.Angle(0), wantErr: false}, + {query: bbQuery, totalFraction: 0, wantPoint: b, wantEdgeID: 0, wantDistance: s1.Angle(0), wantErr: false}, + {query: ccQuery, totalFraction: 0, wantPoint: c, wantEdgeID: 0, wantDistance: 0, wantErr: true}, + } { + distancePoint, distanceEdgeID, newDistance, err := args.query.AtFraction(args.totalFraction) + if args.wantErr != (err != nil) { + t.Fatalf("got %v, want %v", err, args.wantErr) } - - if ac[i].edgeID != 0 { - t.Errorf("got %v, want %v", ac[i].edgeID, 0) + if distancePoint.Angle(args.wantPoint.Vector) >= kEpsilonAngle { + t.Errorf("got %v, want %v", distancePoint, args.wantPoint.Vector) } - - if bb[i].edgeID != 0 { - t.Errorf("got %v, want %v", bb[i].edgeID, 0) + if distanceEdgeID != args.wantEdgeID { + t.Errorf("got %v, want %v", distanceEdgeID, args.wantEdgeID) } - - if abc[i].edgeID != groundTruth[i].edgeID { - t.Errorf("got %v, want %v", abc[i].edgeID, groundTruth[i].edgeID) + if !float64Near(newDistance.Radians(), args.wantDistance.Radians(), kEpsilon) { + t.Errorf("got %v, want %v", newDistance, args.wantDistance) } } } From 47bb065272b96338f2a62f80cbcd569395a8686f Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Fri, 4 Oct 2024 17:23:44 +0300 Subject: [PATCH 18/44] added SliceDivided --- s2/chain_interpolation_query.go | 35 ++++++++++++++- s2/chain_interpolation_query_test.go | 65 ++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 70309758..0c782a0f 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -175,12 +175,45 @@ func (s ChainInterpolationQuery) Slice(beginFraction, endFraction float64) []Poi return points } +// Returns the vector of points that is a slice of the chain from +// beginFraction to endFraction. If beginFraction is greater than +// endFraction, then the points are returned in reverse order. +// +// For example, Slice(0,1) returns the entire chain, Slice(0, 0.5) returns the +// first half of the chain, and Slice(1, 0.5) returns the second half of the +// chain in reverse. +// +// The endpoints of the slice are interpolated (except when coinciding with an +// existing vertex of the chain), and all the internal points are copied from +// the chain as is. +// +// divisions is the number of segments to divide the polyline into. +// divisions must be >= Shape.NumEdges(). +// +// If the query is either uninitialized, or initialized with a shape +// containing no edges, then an empty vector is returned. +func (s ChainInterpolationQuery) SliceDivided(beginFraction, endFraction float64, divisions int) []Point { + var points []Point + s.addDividedSlice(beginFraction, endFraction, &points, divisions) + return points +} + // Appends the chain slice from beginFraction to endFraction to the given // slice. If beginFraction is greater than endFraction, then the points are // appended in reverse order. If the query is either uninitialized, or // initialized with a shape containing no edges, then no points are appended. func (s ChainInterpolationQuery) AddSlice(beginFraction, endFraction float64, points *[]Point) { - if len(s.cumulativeValues) == 0 { + s.addDividedSlice(beginFraction, endFraction, points, s.Shape.NumEdges()) +} + +// Appends the chain slice from beginFraction to endFraction to the given +// slice. If beginFraction is greater than endFraction, then the points are +// appended in reverse order. If the query is either uninitialized, or +// initialized with a shape containing no edges, then no points are appended. +// divisions is the number of segments to divide the polyline into. +// divisions must be greater or equal of NumEdges of Shape. +func (s ChainInterpolationQuery) addDividedSlice(beginFraction, endFraction float64, points *[]Point, divisions int) { + if len(s.cumulativeValues) == 0 || divisions < s.Shape.NumEdges() { return } diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 4ced0b40..84e4aeaf 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -594,3 +594,68 @@ func TestSlice(t *testing.T) { } } } + +func TestSliceDivided(t *testing.T) { + tests := []struct { + name string + args struct { + shape Shape + startSliceFraction float64 + endSliceFraction float64 + } + want string + }{ + { + name: "empty shape", + args: struct { + shape Shape + startSliceFraction float64 + endSliceFraction float64 + }{nil, 0, 1}, + want: ``, + }, + { + name: "full polyline", + args: struct { + shape Shape + startSliceFraction float64 + endSliceFraction float64 + }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 0, 1}, + want: `0:0, 0:1, 0:2`, + }, + { + name: "first half of polyline", + args: struct { + shape Shape + startSliceFraction float64 + endSliceFraction float64 + }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 0, 0.5}, + want: `0:0, 0:1`, + }, + { + name: "second half of polyline", + args: struct { + shape Shape + startSliceFraction float64 + endSliceFraction float64 + }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 1, 0.5}, + want: `0:2, 0:1`, + }, + { + name: "middle of polyline", + args: struct { + shape Shape + startSliceFraction float64 + endSliceFraction float64 + }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 0.25, 0.75}, + want: `0:0.5, 0:1, 0:1.5`, + }, + } + + for _, test := range tests { + query := InitChainInterpolationQuery(test.args.shape, 0) + if got := pointsToString(query.SliceDivided(test.args.startSliceFraction, test.args.endSliceFraction)); got != test.want { + t.Errorf("%v: got %v, want %v", test.name, got, test.want) + } + } +} From 409019df75f912fe33763b986326267cd385d7cb Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Mon, 7 Oct 2024 13:53:52 +0300 Subject: [PATCH 19/44] add calculateDivisionsByEdge method --- s2/chain_interpolation_query.go | 78 +++++++- s2/chain_interpolation_query_test.go | 287 ++++++++++++++++++++++++--- 2 files changed, 331 insertions(+), 34 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 0c782a0f..3748848a 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -11,6 +11,10 @@ var ( // ErrEmptyChain is returned by ChainInterpolationQuery when the query // contains no edges. ErrEmptyChain = errors.New("empty chain") + + // ErrInvalidDivisionsCount is returned by ChainInterpolationQuery when + // divisionsCount is less than the number of edges in the shape. + ErrInvalidDivisionsCount = errors.New("invalid divisions count") ) // ChainInterpolationQuery is a helper struct for querying points on Shape's @@ -21,6 +25,7 @@ type ChainInterpolationQuery struct { Shape Shape ChainID int cumulativeValues []s1.Angle + singleEdgeValues []s1.Angle firstEdgeID int lastEdgeID int } @@ -38,11 +43,12 @@ type ChainInterpolationQuery struct { // memory footprint of the query object are both O(number of edges). func InitChainInterpolationQuery(shape Shape, chainID int) ChainInterpolationQuery { if shape == nil || chainID >= shape.NumChains() { - return ChainInterpolationQuery{nil, 0, nil, 0, 0} + return ChainInterpolationQuery{nil, 0, nil, nil, 0, 0} } var firstEdgeID, lastEdgeID int var cumulativeValues []s1.Angle + var singleEdgeValues []s1.Angle if chainID >= 0 { // If a valid chain id was provided, then the range of edge ids is defined @@ -62,13 +68,15 @@ func InitChainInterpolationQuery(shape Shape, chainID int) ChainInterpolationQue for i := firstEdgeID; i <= lastEdgeID; i++ { cumulativeValues = append(cumulativeValues, cumulativeAngle) edge := shape.Edge(i) - cumulativeAngle += edge.V0.Angle(edge.V1.Vector) + edgeAngle := edge.V0.Angle(edge.V1.Vector) + cumulativeAngle += edgeAngle + singleEdgeValues = append(singleEdgeValues, edgeAngle) } if len(cumulativeValues) != 0 { cumulativeValues = append(cumulativeValues, cumulativeAngle) } - return ChainInterpolationQuery{shape, chainID, cumulativeValues, firstEdgeID, lastEdgeID} + return ChainInterpolationQuery{shape, chainID, cumulativeValues, singleEdgeValues, firstEdgeID, lastEdgeID} } // Gets the total length of the chain(s), which corresponds to the distance at @@ -250,3 +258,67 @@ func (s ChainInterpolationQuery) addDividedSlice(beginFraction, endFraction floa slices.Reverse(*points) } } + +// calculateDivisionsByEdge calculates the number of segments to divide each edge into. +// divisionsCount is a number of segments to divide each edge into and should be greater +// or equal to the number of edges in the shape. +// If the query is either uninitialized, or initialized with a shape +// containing no edges, then an empty slices is returned. +// The length of the slices is equal to the number of edges in the shape. +// +// Exmaple: Given a shape of polylines with 3 edges: 0:0, 0:1, 0:2 +// calculateDivisionsByEdge(3) will return divisions = {1,1} and lengthByEdge = {1,1} and err = nil +// calculateDivisionsByEdge(2) will return divisions = {} and lengthByEdge = {} and err = ErrInvalidDivisionsCount +// +// calculateDivisionsByEdge(4) will return divisions = {2,1} and lengthByEdge = {1,1} and err = nil +// Here lengths of the edges are similar, so the weigth is equal +// Lengths of edges are assumed as weights when computing number of divisions by edge +// +// calculateDivisionsByEdge(5) will return divisions = {2,2} and lengthByEdge = {1,1} and err = nil +// calculateDivisionsByEdge(6) will return divisions = {3,2} and lengthByEdge = {1,1} and err = nil +// +// Now let's assume that we have polyline with 3 edges: 0:0, 0:0.5, 0:2 +// calculateDivisionsByEdge(3) will return divisions = {1,1} and lengthByEdge = {0.5,1.5} and err = nil +// calculateDivisionsByEdge(4) will return divisions = {1,2} and lengthByEdge = {0.5,1.5} and err = nil +// calculateDivisionsByEdge(5) will return divisions = {1,3} and lengthByEdge = {0.5,1.5} and err = nil +// calculateDivisionsByEdge(6) will return divisions = {2,3} and lengthByEdge = {0.5,1.5} and err = nil +// calculateDivisionsByEdge(7) will return divisions = {2,4} and lengthByEdge = {0.5,1.5} and err = nil +// Here we start to divide first edge because the length of the first edge is greater than the divided second edge +// Each next division divides a biggest divided edge +func (s ChainInterpolationQuery) calculateDivisionsByEdge(divisionsCount int) (divisions []int, lengthByEdge []s1.Angle, err error) { + if len(s.singleEdgeValues) == 0 || divisionsCount < s.Shape.NumEdges() { + err = ErrInvalidDivisionsCount + return + } + lengthByEdge = s.singleEdgeValues + + divisionLengths := make([]s1.Angle, len(s.singleEdgeValues)) + divisions = make([]int, len(s.singleEdgeValues)) + + for i := 0; i < len(s.singleEdgeValues); i++ { + divisions[i] = 1 + } + + copy(divisionLengths, s.singleEdgeValues) + + for i := 1; i < divisionsCount-len(s.singleEdgeValues); i++ { + _, index := findMaxValueWithIndex(divisionLengths) + divisions[index]++ + + divisionLengths[index] = lengthByEdge[index] / s1.Angle(divisions[index]) + } + + return +} + +func findMaxValueWithIndex(values []s1.Angle) (maxValue s1.Angle, maxIndex int) { + maxValue = values[0] + maxIndex = 0 + for i, value := range values { + if value > maxValue { + maxValue = value + maxIndex = i + } + } + return +} diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 84e4aeaf..9b3165b2 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -1,6 +1,7 @@ package s2 import ( + "reflect" "testing" "github.com/golang/geo/s1" @@ -596,66 +597,290 @@ func TestSlice(t *testing.T) { } func TestSliceDivided(t *testing.T) { + type args struct { + shape Shape + startSliceFraction float64 + endSliceFraction float64 + divisions int + } tests := []struct { name string - args struct { - shape Shape - startSliceFraction float64 - endSliceFraction float64 - } + args args want string }{ { name: "empty shape", - args: struct { - shape Shape - startSliceFraction float64 - endSliceFraction float64 - }{nil, 0, 1}, + args: args{nil, 0, 1., 1}, want: ``, }, { name: "full polyline", - args: struct { - shape Shape - startSliceFraction float64 - endSliceFraction float64 - }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 0, 1}, + args: args{laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }, + ), 0, 1, 3}, want: `0:0, 0:1, 0:2`, }, { name: "first half of polyline", - args: struct { - shape Shape - startSliceFraction float64 - endSliceFraction float64 - }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 0, 0.5}, + args: args{laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }, + ), 0, 0.5, 2}, want: `0:0, 0:1`, }, { name: "second half of polyline", - args: struct { - shape Shape - startSliceFraction float64 - endSliceFraction float64 - }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 1, 0.5}, + args: args{laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }, + ), 1, 0.5, 2}, want: `0:2, 0:1`, }, { name: "middle of polyline", - args: struct { - shape Shape - startSliceFraction float64 - endSliceFraction float64 - }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 0.25, 0.75}, + args: args{laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }, + ), 0.25, 0.75, 3}, want: `0:0.5, 0:1, 0:1.5`, }, + { + name: "middle of polyline; divisions = 5", + args: args{laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }, + ), 0.25, 0.75, 5}, + want: `0:0.5, 0:0.75, 0:1, 0:1.25, 0:1.5`, + }, + { + name: "middle of polyline; divisions = 11", + args: args{laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }, + ), 0.25, 0.75, 11}, + want: `0:0.5, 0:0.6, 0:0.7, 0:0.8, 0:0.9, 0:1, 0:1.1, 0:1.20, 0:1.3, 0:1.4, 0:1.5`, + }, + { + name: "corner case: divisions = s.NumEdges()+1", + args: args{laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }, + ), 0.3, 0.75, 4}, + want: `0:0.6, 0:1, 0:1.25, 0:1.5`, + }, + { + name: "divisions = s.NumEdges()+2", + args: args{laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }, + ), 0.3, 0.75, 5}, + want: `0:0.6, 0:0.8, 0:1, 0:1.25, 0:1.5`, + }, + { + name: "divisions = s.NumEdges()+3", + args: args{laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }, + ), 0.05, 0.75, 6}, + want: `0:0.1, 0:0.4, 0:0.7, 0:1, 0:1.25, 0:1.5`, + }, + { + name: "divisions = s.NumEdges()+4", + args: args{laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }, + ), 0.05, 0.75, 7}, + want: `0:0.1, 0:0.4, 0:0.7, 0:1, 0:1.25, 0:1.5`, + }, } for _, test := range tests { query := InitChainInterpolationQuery(test.args.shape, 0) - if got := pointsToString(query.SliceDivided(test.args.startSliceFraction, test.args.endSliceFraction)); got != test.want { + if got := pointsToString(query.SliceDivided( + test.args.startSliceFraction, + test.args.endSliceFraction, + test.args.divisions, + )); got != test.want { t.Errorf("%v: got %v, want %v", test.name, got, test.want) } } } + +func TestChainInterpolationQuery_calculateDivisionsByEdge(t *testing.T) { + type fields struct { + Shape Shape + ChainID int + } + type args struct { + divisionsCount int + } + tests := []struct { + name string + fields fields + args args + wantDivisions []int + wantLengthByEdge []s1.Angle + wantErr bool + }{ + { + name: "Shape: equal length edges; divisions = NumCains() + 1", + fields: fields{ + Shape: laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }), + ChainID: 0, + }, + args: args{divisionsCount: 3}, + wantDivisions: []int{1.0, 1.0}, + wantLengthByEdge: []s1.Angle{s1.Degree, s1.Degree}, + wantErr: false, + }, + { + name: "Shape: equal length edges; divisions = NumCains() + 2", + fields: fields{ + Shape: laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }), + ChainID: 0, + }, + args: args{divisionsCount: 4}, + wantDivisions: []int{2.0, 1.0}, + wantLengthByEdge: []s1.Angle{s1.Degree, s1.Degree}, + wantErr: false, + }, + { + name: "Shape: equal length edges; divisions = NumCains() + 3", + fields: fields{ + Shape: laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }), + ChainID: 0, + }, + args: args{divisionsCount: 5}, + wantDivisions: []int{2.0, 2.0}, + wantLengthByEdge: []s1.Angle{s1.Degree, s1.Degree}, + wantErr: false, + }, + { + name: "Shape: unequal length edges; divisions = NumCains() + 1", + fields: fields{ + Shape: laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }), + ChainID: 0, + }, + args: args{divisionsCount: 3}, + wantDivisions: []int{1.0, 1.0}, + wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, + wantErr: false, + }, + { + name: "Shape: unequal length edges; divisions = NumCains() + 2", + fields: fields{ + Shape: laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }), + ChainID: 0, + }, + args: args{divisionsCount: 4}, + wantDivisions: []int{1.0, 2.0}, + wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, + wantErr: false, + }, + { + name: "Shape: unequal length edges; divisions = NumCains() + 3", + fields: fields{ + Shape: laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }), + ChainID: 0, + }, + args: args{divisionsCount: 5}, + wantDivisions: []int{1.0, 3.0}, + wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, + wantErr: false, + }, + { + name: "Shape: unequal length edges; divisions = NumCains() + 4", + fields: fields{ + Shape: laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }), + ChainID: 0, + }, + args: args{divisionsCount: 6}, + wantDivisions: []int{2.0, 3.0}, + wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, + wantErr: false, + }, + { + name: "Shape: unequal length edges; divisions = NumCains() + 5", + fields: fields{ + Shape: laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }), + ChainID: 0, + }, + args: args{divisionsCount: 7}, + wantDivisions: []int{2.0, 4.0}, + wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := InitChainInterpolationQuery(tt.fields.Shape, tt.fields.ChainID) + gotDivisions, gotLengthByEdge, err := s.calculateDivisionsByEdge(tt.args.divisionsCount) + if (err != nil) != tt.wantErr { + t.Errorf("ChainInterpolationQuery.calculateDivisionsByEdge() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(gotDivisions, tt.wantDivisions) { + t.Errorf("ChainInterpolationQuery.calculateDivisionsByEdge() gotDivisions = %v, want %v", gotDivisions, tt.wantDivisions) + } + + for i := range tt.wantLengthByEdge { + if !float64Near(gotLengthByEdge[i].Radians(), tt.wantLengthByEdge[i].Radians(), float64(kEpsilonAngle)) { + t.Errorf("ChainInterpolationQuery.calculateDivisionsByEdge() gotLengthByEdge[i] = %v, want[i] %v", gotLengthByEdge[i], tt.wantLengthByEdge[i]) + } + } + }) + } +} From 3756e6b64334f0611814c77a77ca163255dfd05e Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Mon, 7 Oct 2024 13:59:46 +0300 Subject: [PATCH 20/44] starting to add benchmark on calculateDivisionsByEdge --- s2/chain_interpolation_query_benchmark.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 s2/chain_interpolation_query_benchmark.go diff --git a/s2/chain_interpolation_query_benchmark.go b/s2/chain_interpolation_query_benchmark.go new file mode 100644 index 00000000..56e0c90a --- /dev/null +++ b/s2/chain_interpolation_query_benchmark.go @@ -0,0 +1,20 @@ +package s2 + +import "testing" + +func BenchmarkCalculateDivisionsByEdge(b *testing.B) { + type fields struct { + Shape Shape + ChainID int + } + type args struct { + divisionsCount int + } + + points := make([]Point, 100) + + for i := 0; i < len(points); i++ { + points[i] = randomPoint() + } + +} From ad17ebc32348765aec0ad64957554d6a5331e6c7 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Mon, 7 Oct 2024 15:30:38 +0300 Subject: [PATCH 21/44] adding tests for start and end edge ids for calculateDivisionsByEdge --- s2/chain_interpolation_query.go | 35 +++++++++--- s2/chain_interpolation_query_test.go | 82 ++++++++++++++++++++++++---- 2 files changed, 97 insertions(+), 20 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 3748848a..8136722e 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -15,6 +15,10 @@ var ( // ErrInvalidDivisionsCount is returned by ChainInterpolationQuery when // divisionsCount is less than the number of edges in the shape. ErrInvalidDivisionsCount = errors.New("invalid divisions count") + + // ErrInvalidIndexes is returned by ChainInterpolationQuery when + // start or end indexes are invalid. + ErrInvalidIndexes = errors.New("invalid indexes") ) // ChainInterpolationQuery is a helper struct for querying points on Shape's @@ -243,6 +247,8 @@ func (s ChainInterpolationQuery) addDividedSlice(beginFraction, endFraction floa return } + s.calculateDivisionsByEdge(divisions, beginEdgeID, endEdgeID) + // Copy the internal points from the chain. for edgeID := beginEdgeID; edgeID < endEdgeID; edgeID++ { edge := s.Shape.Edge(edgeID) @@ -285,11 +291,22 @@ func (s ChainInterpolationQuery) addDividedSlice(beginFraction, endFraction floa // calculateDivisionsByEdge(7) will return divisions = {2,4} and lengthByEdge = {0.5,1.5} and err = nil // Here we start to divide first edge because the length of the first edge is greater than the divided second edge // Each next division divides a biggest divided edge -func (s ChainInterpolationQuery) calculateDivisionsByEdge(divisionsCount int) (divisions []int, lengthByEdge []s1.Angle, err error) { - if len(s.singleEdgeValues) == 0 || divisionsCount < s.Shape.NumEdges() { +func (s ChainInterpolationQuery) calculateDivisionsByEdge(divisionsCount, startIndex, endIndex int) (divisions []int, lengthByEdge []s1.Angle, err error) { + if len(s.singleEdgeValues) == 0 { + err = ErrEmptyChain + return + } + + if divisionsCount < s.Shape.NumEdges() { err = ErrInvalidDivisionsCount return } + + if endIndex-startIndex < 2 || len(s.singleEdgeValues) < (endIndex-startIndex) { + err = ErrInvalidIndexes + return + } + lengthByEdge = s.singleEdgeValues divisionLengths := make([]s1.Angle, len(s.singleEdgeValues)) @@ -302,7 +319,7 @@ func (s ChainInterpolationQuery) calculateDivisionsByEdge(divisionsCount int) (d copy(divisionLengths, s.singleEdgeValues) for i := 1; i < divisionsCount-len(s.singleEdgeValues); i++ { - _, index := findMaxValueWithIndex(divisionLengths) + _, index := findMaxValueWithIndex(divisionLengths, startIndex, endIndex) divisions[index]++ divisionLengths[index] = lengthByEdge[index] / s1.Angle(divisions[index]) @@ -311,12 +328,12 @@ func (s ChainInterpolationQuery) calculateDivisionsByEdge(divisionsCount int) (d return } -func findMaxValueWithIndex(values []s1.Angle) (maxValue s1.Angle, maxIndex int) { - maxValue = values[0] - maxIndex = 0 - for i, value := range values { - if value > maxValue { - maxValue = value +func findMaxValueWithIndex(values []s1.Angle, startIndex, endIndex int) (maxValue s1.Angle, maxIndex int) { + maxValue = values[startIndex] + maxIndex = startIndex + for i := startIndex; i < endIndex; i++ { + if values[i] > maxValue { + maxValue = values[i] maxIndex = i } } diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 9b3165b2..b85b1527 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -734,6 +734,8 @@ func TestChainInterpolationQuery_calculateDivisionsByEdge(t *testing.T) { } type args struct { divisionsCount int + startIndex int + endIndex int } tests := []struct { name string @@ -753,7 +755,7 @@ func TestChainInterpolationQuery_calculateDivisionsByEdge(t *testing.T) { }), ChainID: 0, }, - args: args{divisionsCount: 3}, + args: args{divisionsCount: 3, startIndex: 0, endIndex: 2}, wantDivisions: []int{1.0, 1.0}, wantLengthByEdge: []s1.Angle{s1.Degree, s1.Degree}, wantErr: false, @@ -768,7 +770,7 @@ func TestChainInterpolationQuery_calculateDivisionsByEdge(t *testing.T) { }), ChainID: 0, }, - args: args{divisionsCount: 4}, + args: args{divisionsCount: 4, startIndex: 0, endIndex: 2}, wantDivisions: []int{2.0, 1.0}, wantLengthByEdge: []s1.Angle{s1.Degree, s1.Degree}, wantErr: false, @@ -783,7 +785,7 @@ func TestChainInterpolationQuery_calculateDivisionsByEdge(t *testing.T) { }), ChainID: 0, }, - args: args{divisionsCount: 5}, + args: args{divisionsCount: 5, startIndex: 0, endIndex: 2}, wantDivisions: []int{2.0, 2.0}, wantLengthByEdge: []s1.Angle{s1.Degree, s1.Degree}, wantErr: false, @@ -798,7 +800,7 @@ func TestChainInterpolationQuery_calculateDivisionsByEdge(t *testing.T) { }), ChainID: 0, }, - args: args{divisionsCount: 3}, + args: args{divisionsCount: 3, startIndex: 0, endIndex: 2}, wantDivisions: []int{1.0, 1.0}, wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, wantErr: false, @@ -813,7 +815,7 @@ func TestChainInterpolationQuery_calculateDivisionsByEdge(t *testing.T) { }), ChainID: 0, }, - args: args{divisionsCount: 4}, + args: args{divisionsCount: 4, startIndex: 0, endIndex: 2}, wantDivisions: []int{1.0, 2.0}, wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, wantErr: false, @@ -828,7 +830,7 @@ func TestChainInterpolationQuery_calculateDivisionsByEdge(t *testing.T) { }), ChainID: 0, }, - args: args{divisionsCount: 5}, + args: args{divisionsCount: 5, startIndex: 0, endIndex: 2}, wantDivisions: []int{1.0, 3.0}, wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, wantErr: false, @@ -843,7 +845,7 @@ func TestChainInterpolationQuery_calculateDivisionsByEdge(t *testing.T) { }), ChainID: 0, }, - args: args{divisionsCount: 6}, + args: args{divisionsCount: 6, startIndex: 0, endIndex: 2}, wantDivisions: []int{2.0, 3.0}, wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, wantErr: false, @@ -858,22 +860,80 @@ func TestChainInterpolationQuery_calculateDivisionsByEdge(t *testing.T) { }), ChainID: 0, }, - args: args{divisionsCount: 7}, + args: args{divisionsCount: 7, startIndex: 0, endIndex: 2}, wantDivisions: []int{2.0, 4.0}, wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, wantErr: false, }, + { + name: "Shape: unequal length edges; divisions = NumCains() - 1", + fields: fields{ + Shape: laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }), + ChainID: 0, + }, + args: args{divisionsCount: 2, startIndex: 0, endIndex: 1}, + wantErr: true, + }, + { + name: "Shape: unequal length edges; divisions = NumCains() + 1; invalid startIndex & endIndex", + fields: fields{ + Shape: laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }), + ChainID: 0, + }, + args: args{divisionsCount: 4, startIndex: 0, endIndex: 1}, + wantErr: true, + }, + { + name: "Shape: unequal length edges; divisions = NumCains() + 4; limited by endIndex", + fields: fields{ + Shape: laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + PointFromLatLng(LatLngFromDegrees(0, 3)), + }), + ChainID: 0, + }, + args: args{divisionsCount: 6, startIndex: 0, endIndex: 2}, + wantDivisions: []int{2.0, 3.0}, + wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, + wantErr: false, + }, + { + name: "Shape: unequal length edges; divisions = NumCains() + 4; limited by startIndex", + fields: fields{ + Shape: laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + PointFromLatLng(LatLngFromDegrees(0, 3)), + }), + ChainID: 0, + }, + args: args{divisionsCount: 6, startIndex: 1, endIndex: 4}, + wantDivisions: []int{2.0, 3.0}, + wantLengthByEdge: []s1.Angle{s1.Degree * 1.5, s1.Degree * 1.0}, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { s := InitChainInterpolationQuery(tt.fields.Shape, tt.fields.ChainID) - gotDivisions, gotLengthByEdge, err := s.calculateDivisionsByEdge(tt.args.divisionsCount) + gotDivisions, gotLengthByEdge, err := s.calculateDivisionsByEdge(tt.args.divisionsCount, tt.args.startIndex, tt.args.endIndex) if (err != nil) != tt.wantErr { - t.Errorf("ChainInterpolationQuery.calculateDivisionsByEdge() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("ChainInterpolationQuery.calculateDivisionsByEdge(); name = %s; error = %v, wantErr %v", tt.name, err, tt.wantErr) return } if !reflect.DeepEqual(gotDivisions, tt.wantDivisions) { - t.Errorf("ChainInterpolationQuery.calculateDivisionsByEdge() gotDivisions = %v, want %v", gotDivisions, tt.wantDivisions) + t.Errorf("ChainInterpolationQuery.calculateDivisionsByEdge(); name = %s; gotDivisions = %v, want %v", tt.name, gotDivisions, tt.wantDivisions) } for i := range tt.wantLengthByEdge { From e6eb2de52d8754a282b583c630322ba3ca83019e Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Tue, 8 Oct 2024 16:56:05 +0300 Subject: [PATCH 22/44] updating SliceDivided --- s2/chain_interpolation_query.go | 127 ++++----- s2/chain_interpolation_query_test.go | 382 ++++++++------------------- 2 files changed, 168 insertions(+), 341 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 8136722e..e0a99a40 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -200,13 +200,13 @@ func (s ChainInterpolationQuery) Slice(beginFraction, endFraction float64) []Poi // the chain as is. // // divisions is the number of segments to divide the polyline into. -// divisions must be >= Shape.NumEdges(). +// divisions must be >= len(Slice(beginFraction, endFraction)). // // If the query is either uninitialized, or initialized with a shape // containing no edges, then an empty vector is returned. func (s ChainInterpolationQuery) SliceDivided(beginFraction, endFraction float64, divisions int) []Point { var points []Point - s.addDividedSlice(beginFraction, endFraction, &points, divisions) + s.AddDividedSlice(beginFraction, endFraction, &points, divisions) return points } @@ -215,17 +215,7 @@ func (s ChainInterpolationQuery) SliceDivided(beginFraction, endFraction float64 // appended in reverse order. If the query is either uninitialized, or // initialized with a shape containing no edges, then no points are appended. func (s ChainInterpolationQuery) AddSlice(beginFraction, endFraction float64, points *[]Point) { - s.addDividedSlice(beginFraction, endFraction, points, s.Shape.NumEdges()) -} - -// Appends the chain slice from beginFraction to endFraction to the given -// slice. If beginFraction is greater than endFraction, then the points are -// appended in reverse order. If the query is either uninitialized, or -// initialized with a shape containing no edges, then no points are appended. -// divisions is the number of segments to divide the polyline into. -// divisions must be greater or equal of NumEdges of Shape. -func (s ChainInterpolationQuery) addDividedSlice(beginFraction, endFraction float64, points *[]Point, divisions int) { - if len(s.cumulativeValues) == 0 || divisions < s.Shape.NumEdges() { + if len(s.cumulativeValues) == 0 { return } @@ -247,8 +237,6 @@ func (s ChainInterpolationQuery) addDividedSlice(beginFraction, endFraction floa return } - s.calculateDivisionsByEdge(divisions, beginEdgeID, endEdgeID) - // Copy the internal points from the chain. for edgeID := beginEdgeID; edgeID < endEdgeID; edgeID++ { edge := s.Shape.Edge(edgeID) @@ -265,77 +253,74 @@ func (s ChainInterpolationQuery) addDividedSlice(beginFraction, endFraction floa } } -// calculateDivisionsByEdge calculates the number of segments to divide each edge into. -// divisionsCount is a number of segments to divide each edge into and should be greater -// or equal to the number of edges in the shape. -// If the query is either uninitialized, or initialized with a shape -// containing no edges, then an empty slices is returned. -// The length of the slices is equal to the number of edges in the shape. -// -// Exmaple: Given a shape of polylines with 3 edges: 0:0, 0:1, 0:2 -// calculateDivisionsByEdge(3) will return divisions = {1,1} and lengthByEdge = {1,1} and err = nil -// calculateDivisionsByEdge(2) will return divisions = {} and lengthByEdge = {} and err = ErrInvalidDivisionsCount -// -// calculateDivisionsByEdge(4) will return divisions = {2,1} and lengthByEdge = {1,1} and err = nil -// Here lengths of the edges are similar, so the weigth is equal -// Lengths of edges are assumed as weights when computing number of divisions by edge -// -// calculateDivisionsByEdge(5) will return divisions = {2,2} and lengthByEdge = {1,1} and err = nil -// calculateDivisionsByEdge(6) will return divisions = {3,2} and lengthByEdge = {1,1} and err = nil -// -// Now let's assume that we have polyline with 3 edges: 0:0, 0:0.5, 0:2 -// calculateDivisionsByEdge(3) will return divisions = {1,1} and lengthByEdge = {0.5,1.5} and err = nil -// calculateDivisionsByEdge(4) will return divisions = {1,2} and lengthByEdge = {0.5,1.5} and err = nil -// calculateDivisionsByEdge(5) will return divisions = {1,3} and lengthByEdge = {0.5,1.5} and err = nil -// calculateDivisionsByEdge(6) will return divisions = {2,3} and lengthByEdge = {0.5,1.5} and err = nil -// calculateDivisionsByEdge(7) will return divisions = {2,4} and lengthByEdge = {0.5,1.5} and err = nil -// Here we start to divide first edge because the length of the first edge is greater than the divided second edge -// Each next division divides a biggest divided edge -func (s ChainInterpolationQuery) calculateDivisionsByEdge(divisionsCount, startIndex, endIndex int) (divisions []int, lengthByEdge []s1.Angle, err error) { - if len(s.singleEdgeValues) == 0 { - err = ErrEmptyChain +// Appends the slice from beginFraction to endFraction to the given +// slice. If beginFraction is greater than endFraction, then the points are +// appended in reverse order. If the query is either uninitialized, or +// initialized with a shape containing no edges, then no points are appended. +// divisions is the number of segments to divide the polyline into. +// divisions must be greater or equal of NumEdges of Shape. +// A polyline is divided into segments of equal length, and then edges are added to the slice. +func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction float64, points *[]Point, pointsNum int) { + if len(s.cumulativeValues) == 0 { return } - if divisionsCount < s.Shape.NumEdges() { - err = ErrInvalidDivisionsCount - return + reverse := beginFraction > endFraction + if reverse { + // Swap the begin and end fractions so that we can iterate in ascending order. + beginFraction, endFraction = endFraction, beginFraction } - if endIndex-startIndex < 2 || len(s.singleEdgeValues) < (endIndex-startIndex) { - err = ErrInvalidIndexes + atBegin, currentEdgeID, _, err := s.AtFraction(beginFraction) + if err != nil { return } - lengthByEdge = s.singleEdgeValues + atEnd, endEdgeID, _, err := s.AtFraction(endFraction) + if err != nil { + return + } - divisionLengths := make([]s1.Angle, len(s.singleEdgeValues)) - divisions = make([]int, len(s.singleEdgeValues)) + divisionsExcludingEdges := pointsNum - (endEdgeID - currentEdgeID) - 1 + //TODO update if start&end points are found in the chain + if endFraction == 1 { + divisionsExcludingEdges-- + } - for i := 0; i < len(s.singleEdgeValues); i++ { - divisions[i] = 1 + if divisionsExcludingEdges < 0 { + return + } else if divisionsExcludingEdges == 0 { + if reverse { + beginFraction, endFraction = endFraction, beginFraction + } + s.AddSlice(beginFraction, endFraction, points) + return } - copy(divisionLengths, s.singleEdgeValues) + *points = append(*points, atBegin) - for i := 1; i < divisionsCount-len(s.singleEdgeValues); i++ { - _, index := findMaxValueWithIndex(divisionLengths, startIndex, endIndex) - divisions[index]++ + // Copy the internal points from the chain. + for fraction := beginFraction + 1.0/float64(divisionsExcludingEdges); fraction < endFraction; fraction += 1.0 / float64(divisionsExcludingEdges) { + atFraction, edgeID, _, err := s.AtFraction(fraction) + if err != nil { + return + } - divisionLengths[index] = lengthByEdge[index] / s1.Angle(divisions[index]) - } + // If the current edge is the same as the previous edge, then skip it. + // Otherwise, append all edges in between. + for i := edgeID; i < currentEdgeID; i++ { + edge := s.Shape.Edge(i) + *points = append(*points, edge.V1) + } + currentEdgeID = edgeID - return -} + *points = append(*points, atFraction) + } + // Append last edge + *points = append(*points, atEnd) -func findMaxValueWithIndex(values []s1.Angle, startIndex, endIndex int) (maxValue s1.Angle, maxIndex int) { - maxValue = values[startIndex] - maxIndex = startIndex - for i := startIndex; i < endIndex; i++ { - if values[i] > maxValue { - maxValue = values[i] - maxIndex = i - } + // Reverse the slice if necessary. + if reverse { + slices.Reverse(*points) } - return } diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index b85b1527..ab237db1 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -1,7 +1,6 @@ package s2 import ( - "reflect" "testing" "github.com/golang/geo/s1" @@ -613,334 +612,177 @@ func TestSliceDivided(t *testing.T) { args: args{nil, 0, 1., 1}, want: ``, }, - { - name: "full polyline", - args: args{laxPolylineFromPoints([]Point{ + {name: "full polyline", args: args{ + shape: laxPolylineFromPoints([]Point{ PointFromLatLng(LatLngFromDegrees(0, 0)), PointFromLatLng(LatLngFromDegrees(0, 1)), PointFromLatLng(LatLngFromDegrees(0, 2)), }, - ), 0, 1, 3}, - want: `0:0, 0:1, 0:2`, - }, + ), + startSliceFraction: 0, + endSliceFraction: 1, + divisions: 3, + }, want: `0:0, 0:1, 0:2`}, { name: "first half of polyline", - args: args{laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), 0, 0.5, 2}, - want: `0:0, 0:1`, - }, - { - name: "second half of polyline", - args: args{laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), 1, 0.5, 2}, - want: `0:2, 0:1`, - }, - { - name: "middle of polyline", - args: args{laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), 0.25, 0.75, 3}, - want: `0:0.5, 0:1, 0:1.5`, - }, - { - name: "middle of polyline; divisions = 5", - args: args{laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), 0.25, 0.75, 5}, - want: `0:0.5, 0:0.75, 0:1, 0:1.25, 0:1.5`, - }, - { - name: "middle of polyline; divisions = 11", - args: args{laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), 0.25, 0.75, 11}, - want: `0:0.5, 0:0.6, 0:0.7, 0:0.8, 0:0.9, 0:1, 0:1.1, 0:1.20, 0:1.3, 0:1.4, 0:1.5`, - }, - { - name: "corner case: divisions = s.NumEdges()+1", - args: args{laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), 0.3, 0.75, 4}, - want: `0:0.6, 0:1, 0:1.25, 0:1.5`, - }, - { - name: "divisions = s.NumEdges()+2", - args: args{laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), 0.3, 0.75, 5}, - want: `0:0.6, 0:0.8, 0:1, 0:1.25, 0:1.5`, - }, - { - name: "divisions = s.NumEdges()+3", - args: args{laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), 0.05, 0.75, 6}, - want: `0:0.1, 0:0.4, 0:0.7, 0:1, 0:1.25, 0:1.5`, - }, - { - name: "divisions = s.NumEdges()+4", - args: args{laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), 0.05, 0.75, 7}, - want: `0:0.1, 0:0.4, 0:0.7, 0:1, 0:1.25, 0:1.5`, - }, - } - - for _, test := range tests { - query := InitChainInterpolationQuery(test.args.shape, 0) - if got := pointsToString(query.SliceDivided( - test.args.startSliceFraction, - test.args.endSliceFraction, - test.args.divisions, - )); got != test.want { - t.Errorf("%v: got %v, want %v", test.name, got, test.want) - } - } -} - -func TestChainInterpolationQuery_calculateDivisionsByEdge(t *testing.T) { - type fields struct { - Shape Shape - ChainID int - } - type args struct { - divisionsCount int - startIndex int - endIndex int - } - tests := []struct { - name string - fields fields - args args - wantDivisions []int - wantLengthByEdge []s1.Angle - wantErr bool - }{ - { - name: "Shape: equal length edges; divisions = NumCains() + 1", - fields: fields{ - Shape: laxPolylineFromPoints([]Point{ + args: args{ + shape: laxPolylineFromPoints([]Point{ PointFromLatLng(LatLngFromDegrees(0, 0)), PointFromLatLng(LatLngFromDegrees(0, 1)), PointFromLatLng(LatLngFromDegrees(0, 2)), - }), - ChainID: 0, + }, + ), + startSliceFraction: 0, + endSliceFraction: 0.5, + divisions: 2, }, - args: args{divisionsCount: 3, startIndex: 0, endIndex: 2}, - wantDivisions: []int{1.0, 1.0}, - wantLengthByEdge: []s1.Angle{s1.Degree, s1.Degree}, - wantErr: false, + want: `0:0, 0:1`, }, { - name: "Shape: equal length edges; divisions = NumCains() + 2", - fields: fields{ - Shape: laxPolylineFromPoints([]Point{ + name: "second half of polyline", + args: args{ + shape: laxPolylineFromPoints([]Point{ PointFromLatLng(LatLngFromDegrees(0, 0)), PointFromLatLng(LatLngFromDegrees(0, 1)), PointFromLatLng(LatLngFromDegrees(0, 2)), - }), - ChainID: 0, + }, + ), + startSliceFraction: 1, + endSliceFraction: 0.5, + divisions: 2, }, - args: args{divisionsCount: 4, startIndex: 0, endIndex: 2}, - wantDivisions: []int{2.0, 1.0}, - wantLengthByEdge: []s1.Angle{s1.Degree, s1.Degree}, - wantErr: false, + want: `0:2, 0:1`, }, { - name: "Shape: equal length edges; divisions = NumCains() + 3", - fields: fields{ - Shape: laxPolylineFromPoints([]Point{ + name: "middle of polyline", + args: args{ + shape: laxPolylineFromPoints([]Point{ PointFromLatLng(LatLngFromDegrees(0, 0)), PointFromLatLng(LatLngFromDegrees(0, 1)), PointFromLatLng(LatLngFromDegrees(0, 2)), - }), - ChainID: 0, - }, - args: args{divisionsCount: 5, startIndex: 0, endIndex: 2}, - wantDivisions: []int{2.0, 2.0}, - wantLengthByEdge: []s1.Angle{s1.Degree, s1.Degree}, - wantErr: false, - }, - { - name: "Shape: unequal length edges; divisions = NumCains() + 1", - fields: fields{ - Shape: laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 0.5)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }), - ChainID: 0, - }, - args: args{divisionsCount: 3, startIndex: 0, endIndex: 2}, - wantDivisions: []int{1.0, 1.0}, - wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, - wantErr: false, - }, - { - name: "Shape: unequal length edges; divisions = NumCains() + 2", - fields: fields{ - Shape: laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 0.5)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }), - ChainID: 0, + }, + ), + startSliceFraction: 0.25, + endSliceFraction: 0.75, + divisions: 3, }, - args: args{divisionsCount: 4, startIndex: 0, endIndex: 2}, - wantDivisions: []int{1.0, 2.0}, - wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, - wantErr: false, + want: `0:0.5, 0:1, 0:1.5`, }, { - name: "Shape: unequal length edges; divisions = NumCains() + 3", - fields: fields{ - Shape: laxPolylineFromPoints([]Point{ + name: "middle of polyline; divisions = 5", + args: args{ + shape: laxPolylineFromPoints([]Point{ PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 1)), PointFromLatLng(LatLngFromDegrees(0, 2)), - }), - ChainID: 0, + }, + ), + startSliceFraction: 0.25, + endSliceFraction: 0.75, + divisions: 5, }, - args: args{divisionsCount: 5, startIndex: 0, endIndex: 2}, - wantDivisions: []int{1.0, 3.0}, - wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, - wantErr: false, + want: `0:0.5, 0:0.75, 0:1, 0:1.25, 0:1.5`, }, { - name: "Shape: unequal length edges; divisions = NumCains() + 4", - fields: fields{ - Shape: laxPolylineFromPoints([]Point{ + name: "middle of polyline; divisions = 11", + args: args{ + shape: laxPolylineFromPoints([]Point{ PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 1)), PointFromLatLng(LatLngFromDegrees(0, 2)), - }), - ChainID: 0, + }, + ), + startSliceFraction: 0.25, + endSliceFraction: 0.75, + divisions: 11, }, - args: args{divisionsCount: 6, startIndex: 0, endIndex: 2}, - wantDivisions: []int{2.0, 3.0}, - wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, - wantErr: false, + want: `0:0.5, 0:0.6, 0:0.7, 0:0.8, 0:0.9, 0:1, 0:1.1, 0:1.20, 0:1.3, 0:1.4, 0:1.5`, }, { - name: "Shape: unequal length edges; divisions = NumCains() + 5", - fields: fields{ - Shape: laxPolylineFromPoints([]Point{ + name: "corner case: divisions = s.NumEdges()+1", + args: args{ + shape: laxPolylineFromPoints([]Point{ PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 1)), PointFromLatLng(LatLngFromDegrees(0, 2)), - }), - ChainID: 0, + }, + ), + startSliceFraction: 0.3, + endSliceFraction: 0.6, + divisions: 4, }, - args: args{divisionsCount: 7, startIndex: 0, endIndex: 2}, - wantDivisions: []int{2.0, 4.0}, - wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, - wantErr: false, + want: `0:0.6, 0:0.8, 0:1, 0:1.2`, }, { - name: "Shape: unequal length edges; divisions = NumCains() - 1", - fields: fields{ - Shape: laxPolylineFromPoints([]Point{ + name: "divisions = s.NumEdges()+2", + args: args{ + shape: laxPolylineFromPoints([]Point{ PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 1)), PointFromLatLng(LatLngFromDegrees(0, 2)), - }), - ChainID: 0, + }, + ), + startSliceFraction: 0.25, + endSliceFraction: 0.75, + divisions: 5, }, - args: args{divisionsCount: 2, startIndex: 0, endIndex: 1}, - wantErr: true, + want: `0:0.5, 0:0.75, 0:1, 0:1.25, 0:1.5`, }, { - name: "Shape: unequal length edges; divisions = NumCains() + 1; invalid startIndex & endIndex", - fields: fields{ - Shape: laxPolylineFromPoints([]Point{ + name: "divisions = s.NumEdges()+3", + args: args{ + shape: laxPolylineFromPoints([]Point{ PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 1)), PointFromLatLng(LatLngFromDegrees(0, 2)), - }), - ChainID: 0, + }, + ), + startSliceFraction: 0.45, + endSliceFraction: 0.75, + divisions: 6, }, - args: args{divisionsCount: 4, startIndex: 0, endIndex: 1}, - wantErr: true, + want: `0:0.9, 0:1, 0:1.05, 0:1.2, 0:1.35, 0:1.5`, }, { - name: "Shape: unequal length edges; divisions = NumCains() + 4; limited by endIndex", - fields: fields{ - Shape: laxPolylineFromPoints([]Point{ + name: "divisions = s.NumEdges()+10", + args: args{ + shape: laxPolylineFromPoints([]Point{ PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 1)), PointFromLatLng(LatLngFromDegrees(0, 2)), - PointFromLatLng(LatLngFromDegrees(0, 3)), - }), - ChainID: 0, + }, + ), + startSliceFraction: 0.105, + endSliceFraction: 0.605, + divisions: 11, }, - args: args{divisionsCount: 6, startIndex: 0, endIndex: 2}, - wantDivisions: []int{2.0, 3.0}, - wantLengthByEdge: []s1.Angle{s1.Degree * 0.5, s1.Degree * 1.5}, - wantErr: false, + want: `0:0.21, 0:0.31, 0:0.41, 0:0.51, 0:0.61, 0:0.71, 0:0.81, 0:0.91, 0:1, 0:1.11, 0:1.21`, }, { - name: "Shape: unequal length edges; divisions = NumCains() + 4; limited by startIndex", - fields: fields{ - Shape: laxPolylineFromPoints([]Point{ + name: "divisions = 10, 0 edges inside resulting points", + args: args{ + shape: laxPolylineFromPoints([]Point{ PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 0.5)), + PointFromLatLng(LatLngFromDegrees(0, 1)), PointFromLatLng(LatLngFromDegrees(0, 2)), - PointFromLatLng(LatLngFromDegrees(0, 3)), - }), - ChainID: 0, + }, + ), + startSliceFraction: 0.1, + endSliceFraction: 0.2, + divisions: 11, }, - args: args{divisionsCount: 6, startIndex: 1, endIndex: 4}, - wantDivisions: []int{2.0, 3.0}, - wantLengthByEdge: []s1.Angle{s1.Degree * 1.5, s1.Degree * 1.0}, - wantErr: false, + want: `0:0.10, 0:0.11, 0:0.12, 0:0.13, 0:0.14, 0:0.15, 0:0.16, 0:0.17, 0:0.18, 0:19, 0:0.20`, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - s := InitChainInterpolationQuery(tt.fields.Shape, tt.fields.ChainID) - gotDivisions, gotLengthByEdge, err := s.calculateDivisionsByEdge(tt.args.divisionsCount, tt.args.startIndex, tt.args.endIndex) - if (err != nil) != tt.wantErr { - t.Errorf("ChainInterpolationQuery.calculateDivisionsByEdge(); name = %s; error = %v, wantErr %v", tt.name, err, tt.wantErr) - return - } - if !reflect.DeepEqual(gotDivisions, tt.wantDivisions) { - t.Errorf("ChainInterpolationQuery.calculateDivisionsByEdge(); name = %s; gotDivisions = %v, want %v", tt.name, gotDivisions, tt.wantDivisions) - } - for i := range tt.wantLengthByEdge { - if !float64Near(gotLengthByEdge[i].Radians(), tt.wantLengthByEdge[i].Radians(), float64(kEpsilonAngle)) { - t.Errorf("ChainInterpolationQuery.calculateDivisionsByEdge() gotLengthByEdge[i] = %v, want[i] %v", gotLengthByEdge[i], tt.wantLengthByEdge[i]) - } - } - }) + for _, test := range tests { + query := InitChainInterpolationQuery(test.args.shape, 0) + if got := pointsToString(query.SliceDivided( + test.args.startSliceFraction, + test.args.endSliceFraction, + test.args.divisions, + )); got != test.want { + t.Errorf("%v: got %v, want %v", test.name, got, test.want) + } } } From c42b6ff986df80d687580d64ba728773ce884025 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Mon, 14 Oct 2024 12:27:12 +0300 Subject: [PATCH 23/44] fixed & finished with ChainInterpolationQuery.AddSliceDivided --- s2/chain_interpolation_query.go | 48 ++++++++++++----------- s2/chain_interpolation_query_benchmark.go | 20 ---------- s2/chain_interpolation_query_test.go | 10 ++--- 3 files changed, 31 insertions(+), 47 deletions(-) delete mode 100644 s2/chain_interpolation_query_benchmark.go diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index e0a99a40..2910d7c4 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -253,7 +253,7 @@ func (s ChainInterpolationQuery) AddSlice(beginFraction, endFraction float64, po } } -// Appends the slice from beginFraction to endFraction to the given +// Appends the slice from beginFraction to endFraction to the given // slice. If beginFraction is greater than endFraction, then the points are // appended in reverse order. If the query is either uninitialized, or // initialized with a shape containing no edges, then no points are appended. @@ -265,6 +265,15 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa return } + slice := s.Slice(beginFraction, endFraction) + + if len(slice) > pointsNum { + return + } else if len(slice) == pointsNum { + *points = append(*points, slice...) + return + } + reverse := beginFraction > endFraction if reverse { // Swap the begin and end fractions so that we can iterate in ascending order. @@ -276,31 +285,17 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa return } - atEnd, endEdgeID, _, err := s.AtFraction(endFraction) + atEnd, _, _, err := s.AtFraction(endFraction) if err != nil { return } - divisionsExcludingEdges := pointsNum - (endEdgeID - currentEdgeID) - 1 - //TODO update if start&end points are found in the chain - if endFraction == 1 { - divisionsExcludingEdges-- - } - - if divisionsExcludingEdges < 0 { - return - } else if divisionsExcludingEdges == 0 { - if reverse { - beginFraction, endFraction = endFraction, beginFraction - } - s.AddSlice(beginFraction, endFraction, points) - return - } + // divisionsExcludingEdges := pointsNum - len(slice) *points = append(*points, atBegin) - // Copy the internal points from the chain. - for fraction := beginFraction + 1.0/float64(divisionsExcludingEdges); fraction < endFraction; fraction += 1.0 / float64(divisionsExcludingEdges) { + // // Copy the internal points from the chain. + for fraction := beginFraction + (endFraction-beginFraction)/float64(pointsNum-1); fraction < endFraction; fraction += (endFraction - beginFraction) / float64(pointsNum-1) { atFraction, edgeID, _, err := s.AtFraction(fraction) if err != nil { return @@ -308,11 +303,20 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa // If the current edge is the same as the previous edge, then skip it. // Otherwise, append all edges in between. - for i := edgeID; i < currentEdgeID; i++ { - edge := s.Shape.Edge(i) + if currentEdgeID != edgeID { + for i := currentEdgeID; i < edgeID; i++ { + edge := s.Shape.Edge(i) + if edge.V1 != atFraction { + *points = append(*points, edge.V1) + } + } + currentEdgeID = edgeID + continue + } else if edge := s.Shape.Edge(edgeID); edge.V1.approxEqual(atFraction, epsilon) { *points = append(*points, edge.V1) + currentEdgeID++ + continue } - currentEdgeID = edgeID *points = append(*points, atFraction) } diff --git a/s2/chain_interpolation_query_benchmark.go b/s2/chain_interpolation_query_benchmark.go deleted file mode 100644 index 56e0c90a..00000000 --- a/s2/chain_interpolation_query_benchmark.go +++ /dev/null @@ -1,20 +0,0 @@ -package s2 - -import "testing" - -func BenchmarkCalculateDivisionsByEdge(b *testing.B) { - type fields struct { - Shape Shape - ChainID int - } - type args struct { - divisionsCount int - } - - points := make([]Point, 100) - - for i := 0; i < len(points); i++ { - points[i] = randomPoint() - } - -} diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index ab237db1..9ff501f5 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -696,7 +696,7 @@ func TestSliceDivided(t *testing.T) { endSliceFraction: 0.75, divisions: 11, }, - want: `0:0.5, 0:0.6, 0:0.7, 0:0.8, 0:0.9, 0:1, 0:1.1, 0:1.20, 0:1.3, 0:1.4, 0:1.5`, + want: `0:0.5, 0:0.6, 0:0.7, 0:0.8, 0:0.9, 0:1, 0:1.1, 0:1.2, 0:1.3, 0:1.4, 0:1.5`, }, { name: "corner case: divisions = s.NumEdges()+1", @@ -741,7 +741,7 @@ func TestSliceDivided(t *testing.T) { endSliceFraction: 0.75, divisions: 6, }, - want: `0:0.9, 0:1, 0:1.05, 0:1.2, 0:1.35, 0:1.5`, + want: `0:0.9, 0:1, 0:1.14, 0:1.26, 0:1.38, 0:1.5`, }, { name: "divisions = s.NumEdges()+10", @@ -767,11 +767,11 @@ func TestSliceDivided(t *testing.T) { PointFromLatLng(LatLngFromDegrees(0, 2)), }, ), - startSliceFraction: 0.1, - endSliceFraction: 0.2, + startSliceFraction: 0.05, + endSliceFraction: 0.1, divisions: 11, }, - want: `0:0.10, 0:0.11, 0:0.12, 0:0.13, 0:0.14, 0:0.15, 0:0.16, 0:0.17, 0:0.18, 0:19, 0:0.20`, + want: `0:0.1, 0:0.11, 0:0.12, 0:0.13, 0:0.14, 0:0.15, 0:0.16, 0:0.17, 0:0.18, 0:0.19, 0:0.2`, }, } From c9f97d5076c15d120411dfe7640e4ab9556b4680 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Mon, 14 Oct 2024 12:28:36 +0300 Subject: [PATCH 24/44] remove unused --- s2/chain_interpolation_query.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 2910d7c4..1cfa0f72 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -29,7 +29,6 @@ type ChainInterpolationQuery struct { Shape Shape ChainID int cumulativeValues []s1.Angle - singleEdgeValues []s1.Angle firstEdgeID int lastEdgeID int } @@ -47,12 +46,11 @@ type ChainInterpolationQuery struct { // memory footprint of the query object are both O(number of edges). func InitChainInterpolationQuery(shape Shape, chainID int) ChainInterpolationQuery { if shape == nil || chainID >= shape.NumChains() { - return ChainInterpolationQuery{nil, 0, nil, nil, 0, 0} + return ChainInterpolationQuery{nil, 0, nil, 0, 0} } var firstEdgeID, lastEdgeID int var cumulativeValues []s1.Angle - var singleEdgeValues []s1.Angle if chainID >= 0 { // If a valid chain id was provided, then the range of edge ids is defined @@ -74,13 +72,12 @@ func InitChainInterpolationQuery(shape Shape, chainID int) ChainInterpolationQue edge := shape.Edge(i) edgeAngle := edge.V0.Angle(edge.V1.Vector) cumulativeAngle += edgeAngle - singleEdgeValues = append(singleEdgeValues, edgeAngle) } if len(cumulativeValues) != 0 { cumulativeValues = append(cumulativeValues, cumulativeAngle) } - return ChainInterpolationQuery{shape, chainID, cumulativeValues, singleEdgeValues, firstEdgeID, lastEdgeID} + return ChainInterpolationQuery{shape, chainID, cumulativeValues, firstEdgeID, lastEdgeID} } // Gets the total length of the chain(s), which corresponds to the distance at From 2ffcfa62de9a633471a725b7d824b6cf3ff8290a Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Mon, 14 Oct 2024 12:36:07 +0300 Subject: [PATCH 25/44] additional test --- s2/chain_interpolation_query_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 9ff501f5..c88ebd05 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -773,6 +773,24 @@ func TestSliceDivided(t *testing.T) { }, want: `0:0.1, 0:0.11, 0:0.12, 0:0.13, 0:0.14, 0:0.15, 0:0.16, 0:0.17, 0:0.18, 0:0.19, 0:0.2`, }, + { + name: "divisions = s.NumEdges()+1", + args: args{ + shape: laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + PointFromLatLng(LatLngFromDegrees(0, 3)), + PointFromLatLng(LatLngFromDegrees(0, 4)), + PointFromLatLng(LatLngFromDegrees(0, 5)), + }, + ), + startSliceFraction: 0.3, + endSliceFraction: 0.84, + divisions: 5, + }, + want: `0:1.5, 0:2, 0:3, 0:4, 0:4.2`, + }, } for _, test := range tests { From 9ca2a9b0fc23ee5100d5d8265e47dac5818de198 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Mon, 14 Oct 2024 13:55:24 +0300 Subject: [PATCH 26/44] try to rename module --- go.mod | 2 +- r2/rect.go | 2 +- r2/rect_test.go | 2 +- r3/vector.go | 2 +- s1/example_test.go | 2 +- s2/builder_snapper.go | 2 +- s2/builder_snapper_test.go | 2 +- s2/cap.go | 4 ++-- s2/cap_test.go | 4 ++-- s2/cell.go | 8 ++++---- s2/cell_test.go | 4 ++-- s2/cellid.go | 8 ++++---- s2/cellid_test.go | 4 ++-- s2/cellunion.go | 2 +- s2/cellunion_test.go | 4 ++-- s2/centroids.go | 2 +- s2/centroids_test.go | 2 +- s2/chain_interpolation_query.go | 2 +- s2/chain_interpolation_query_test.go | 2 +- s2/contains_point_query_test.go | 2 +- s2/contains_vertex_query_test.go | 2 +- s2/convex_hull_query.go | 2 +- s2/convex_hull_query_test.go | 2 +- s2/crossing_edge_query.go | 2 +- s2/crossing_edge_query_test.go | 2 +- s2/distance_target.go | 2 +- s2/edge_clipping.go | 6 +++--- s2/edge_clipping_test.go | 8 ++++---- s2/edge_crosser_test.go | 2 +- s2/edge_crossings.go | 4 ++-- s2/edge_crossings_test.go | 2 +- s2/edge_distances.go | 2 +- s2/edge_distances_test.go | 4 ++-- s2/edge_query.go | 2 +- s2/edge_query_closest_test.go | 4 ++-- s2/edge_query_furthest_test.go | 2 +- s2/edge_query_test.go | 2 +- s2/edge_tessellator.go | 4 ++-- s2/edge_tessellator_test.go | 4 ++-- s2/encode_test.go | 2 +- s2/example_test.go | 4 ++-- s2/latlng.go | 4 ++-- s2/latlng_test.go | 2 +- s2/loop.go | 6 +++--- s2/loop_test.go | 6 +++--- s2/matrix3x3.go | 2 +- s2/matrix3x3_test.go | 2 +- s2/max_distance_targets.go | 2 +- s2/max_distance_targets_test.go | 2 +- s2/min_distance_targets.go | 2 +- s2/paddedcell.go | 4 ++-- s2/paddedcell_test.go | 4 ++-- s2/point.go | 4 ++-- s2/point_measures.go | 2 +- s2/point_measures_test.go | 4 ++-- s2/point_test.go | 4 ++-- s2/pointcompression.go | 2 +- s2/polygon_test.go | 2 +- s2/polyline.go | 2 +- s2/polyline_measures.go | 4 ++-- s2/polyline_test.go | 4 ++-- s2/predicates.go | 4 ++-- s2/predicates_test.go | 4 ++-- s2/projections.go | 4 ++-- s2/projections_test.go | 4 ++-- s2/query_entry_test.go | 2 +- s2/query_options.go | 2 +- s2/rect.go | 6 +++--- s2/rect_bounder.go | 6 +++--- s2/rect_bounder_test.go | 6 +++--- s2/rect_test.go | 8 ++++---- s2/s2_test.go | 6 +++--- s2/s2_test_test.go | 2 +- s2/s2intersect/s2intersect.go | 2 +- s2/shapeindex.go | 4 ++-- s2/shapeindex_test.go | 4 ++-- s2/shapeutil_test.go | 2 +- s2/stuv.go | 2 +- s2/stuv_test.go | 2 +- s2/textformat_test.go | 2 +- s2/textformat_test_test.go | 6 +++--- s2/util.go | 2 +- s2/wedge_relations_test.go | 2 +- 83 files changed, 136 insertions(+), 136 deletions(-) diff --git a/go.mod b/go.mod index 4ed1d55a..6dc5da21 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/golang/geo +module github.com/pavlov061356/geo go 1.22.0 diff --git a/r2/rect.go b/r2/rect.go index 495545bb..772b7569 100644 --- a/r2/rect.go +++ b/r2/rect.go @@ -18,7 +18,7 @@ import ( "fmt" "math" - "github.com/golang/geo/r1" + "github.com/pavlov061356/geo/r1" ) // Point represents a point in ℝ². diff --git a/r2/rect_test.go b/r2/rect_test.go index 7a728f30..25eb7018 100644 --- a/r2/rect_test.go +++ b/r2/rect_test.go @@ -22,7 +22,7 @@ import ( "reflect" "testing" - "github.com/golang/geo/r1" + "github.com/pavlov061356/geo/r1" ) var ( diff --git a/r3/vector.go b/r3/vector.go index 1b6a8f03..bf30abe8 100644 --- a/r3/vector.go +++ b/r3/vector.go @@ -18,7 +18,7 @@ import ( "fmt" "math" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) // Vector represents a point in ℝ³. diff --git a/s1/example_test.go b/s1/example_test.go index 7b45b7f0..db3f0696 100644 --- a/s1/example_test.go +++ b/s1/example_test.go @@ -18,7 +18,7 @@ import ( "fmt" "math" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) func ExampleInterval_DirectedHausdorffDistance() { diff --git a/s2/builder_snapper.go b/s2/builder_snapper.go index 7def29d7..3ea42951 100644 --- a/s2/builder_snapper.go +++ b/s2/builder_snapper.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) // A Snapper restricts the locations of the output vertices. For diff --git a/s2/builder_snapper_test.go b/s2/builder_snapper_test.go index 20e618df..ab1e991c 100644 --- a/s2/builder_snapper_test.go +++ b/s2/builder_snapper_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) func TestIdentitySnapper(t *testing.T) { diff --git a/s2/cap.go b/s2/cap.go index 9327b6bb..989448b4 100644 --- a/s2/cap.go +++ b/s2/cap.go @@ -19,8 +19,8 @@ import ( "io" "math" - "github.com/golang/geo/r1" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/s1" ) var ( diff --git a/s2/cap_test.go b/s2/cap_test.go index 0331a156..6d5eadd9 100644 --- a/s2/cap_test.go +++ b/s2/cap_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) const ( diff --git a/s2/cell.go b/s2/cell.go index 323167a3..495268c0 100644 --- a/s2/cell.go +++ b/s2/cell.go @@ -18,10 +18,10 @@ import ( "io" "math" - "github.com/golang/geo/r1" - "github.com/golang/geo/r2" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/r2" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) // Cell is an S2 region object that represents a cell. Unlike CellIDs, diff --git a/s2/cell_test.go b/s2/cell_test.go index bb112981..b329d119 100644 --- a/s2/cell_test.go +++ b/s2/cell_test.go @@ -19,8 +19,8 @@ import ( "testing" "unsafe" - "github.com/golang/geo/r2" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r2" + "github.com/pavlov061356/geo/s1" ) // maxCellSize is the upper bounds on the number of bytes we want the Cell object to ever be. diff --git a/s2/cellid.go b/s2/cellid.go index 46e27d79..83b829d7 100644 --- a/s2/cellid.go +++ b/s2/cellid.go @@ -23,10 +23,10 @@ import ( "strconv" "strings" - "github.com/golang/geo/r1" - "github.com/golang/geo/r2" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/r2" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) // CellID uniquely identifies a cell in the S2 cell decomposition. diff --git a/s2/cellid_test.go b/s2/cellid_test.go index beec1e47..5518fb41 100644 --- a/s2/cellid_test.go +++ b/s2/cellid_test.go @@ -19,8 +19,8 @@ import ( "reflect" "testing" - "github.com/golang/geo/r2" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r2" + "github.com/pavlov061356/geo/s1" ) func TestCellIDFromFace(t *testing.T) { diff --git a/s2/cellunion.go b/s2/cellunion.go index 7737e0b3..f96bc842 100644 --- a/s2/cellunion.go +++ b/s2/cellunion.go @@ -19,7 +19,7 @@ import ( "io" "sort" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) // A CellUnion is a collection of CellIDs. diff --git a/s2/cellunion_test.go b/s2/cellunion_test.go index 193c0b01..5da6d4c4 100644 --- a/s2/cellunion_test.go +++ b/s2/cellunion_test.go @@ -19,8 +19,8 @@ import ( "reflect" "testing" - "github.com/golang/geo/r1" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/s1" ) func TestCellUnionDuplicateCellsNotValid(t *testing.T) { diff --git a/s2/centroids.go b/s2/centroids.go index e8a91c44..d92efda8 100644 --- a/s2/centroids.go +++ b/s2/centroids.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/golang/geo/r3" + "github.com/pavlov061356/geo/r3" ) // There are several notions of the "centroid" of a triangle. First, there diff --git a/s2/centroids_test.go b/s2/centroids_test.go index 2841b007..90564b64 100644 --- a/s2/centroids_test.go +++ b/s2/centroids_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/golang/geo/r3" + "github.com/pavlov061356/geo/r3" ) func TestCentroidsPlanarCentroid(t *testing.T) { diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 1cfa0f72..25ee4a35 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -4,7 +4,7 @@ import ( "errors" "slices" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) var ( diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index c88ebd05..b7bce936 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -3,7 +3,7 @@ package s2 import ( "testing" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) const ( diff --git a/s2/contains_point_query_test.go b/s2/contains_point_query_test.go index 3080acd1..ad45eefc 100644 --- a/s2/contains_point_query_test.go +++ b/s2/contains_point_query_test.go @@ -18,7 +18,7 @@ import ( "reflect" "testing" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) func TestContainsPointQueryVertexModelOpen(t *testing.T) { diff --git a/s2/contains_vertex_query_test.go b/s2/contains_vertex_query_test.go index 4524b46f..2e5079a5 100644 --- a/s2/contains_vertex_query_test.go +++ b/s2/contains_vertex_query_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) func TestContainsVertexQueryUndetermined(t *testing.T) { diff --git a/s2/convex_hull_query.go b/s2/convex_hull_query.go index 2a87377e..c6be3ef2 100644 --- a/s2/convex_hull_query.go +++ b/s2/convex_hull_query.go @@ -17,7 +17,7 @@ package s2 import ( "sort" - "github.com/golang/geo/r3" + "github.com/pavlov061356/geo/r3" ) // ConvexHullQuery builds the convex hull of any collection of points, diff --git a/s2/convex_hull_query_test.go b/s2/convex_hull_query_test.go index 3db6e257..916ac5a1 100644 --- a/s2/convex_hull_query_test.go +++ b/s2/convex_hull_query_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) func TestConvexHullQueryNoPoints(t *testing.T) { diff --git a/s2/crossing_edge_query.go b/s2/crossing_edge_query.go index 51852dab..0636c74a 100644 --- a/s2/crossing_edge_query.go +++ b/s2/crossing_edge_query.go @@ -17,7 +17,7 @@ package s2 import ( "sort" - "github.com/golang/geo/r2" + "github.com/pavlov061356/geo/r2" ) // CrossingEdgeQuery is used to find the Edge IDs of Shapes that are crossed by diff --git a/s2/crossing_edge_query_test.go b/s2/crossing_edge_query_test.go index 1fadd2f4..e95aa680 100644 --- a/s2/crossing_edge_query_test.go +++ b/s2/crossing_edge_query_test.go @@ -19,7 +19,7 @@ import ( "reflect" "testing" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) func perturbAtDistance(distance s1.Angle, a0, b0 Point) Point { diff --git a/s2/distance_target.go b/s2/distance_target.go index 066bbacf..bc86cf09 100644 --- a/s2/distance_target.go +++ b/s2/distance_target.go @@ -15,7 +15,7 @@ package s2 import ( - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) // The distance interface represents a set of common methods used by algorithms diff --git a/s2/edge_clipping.go b/s2/edge_clipping.go index 4389139d..49bb3a79 100644 --- a/s2/edge_clipping.go +++ b/s2/edge_clipping.go @@ -27,9 +27,9 @@ package s2 import ( "math" - "github.com/golang/geo/r1" - "github.com/golang/geo/r2" - "github.com/golang/geo/r3" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/r2" + "github.com/pavlov061356/geo/r3" ) const ( diff --git a/s2/edge_clipping_test.go b/s2/edge_clipping_test.go index addd6f53..0a44e2dd 100644 --- a/s2/edge_clipping_test.go +++ b/s2/edge_clipping_test.go @@ -19,10 +19,10 @@ import ( "math" "testing" - "github.com/golang/geo/r1" - "github.com/golang/geo/r2" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/r2" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) func TestEdgeClippingIntersectsFace(t *testing.T) { diff --git a/s2/edge_crosser_test.go b/s2/edge_crosser_test.go index 73c0400f..3d5f92a2 100644 --- a/s2/edge_crosser_test.go +++ b/s2/edge_crosser_test.go @@ -19,7 +19,7 @@ import ( "math" "testing" - "github.com/golang/geo/r3" + "github.com/pavlov061356/geo/r3" ) func TestEdgeCrosserCrossings(t *testing.T) { diff --git a/s2/edge_crossings.go b/s2/edge_crossings.go index 9d263a98..ff73a6a1 100644 --- a/s2/edge_crossings.go +++ b/s2/edge_crossings.go @@ -18,8 +18,8 @@ import ( "fmt" "math" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) const ( diff --git a/s2/edge_crossings_test.go b/s2/edge_crossings_test.go index f0ab9f76..2a81bd25 100644 --- a/s2/edge_crossings_test.go +++ b/s2/edge_crossings_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) // The various Crossing methods are tested via s2edge_crosser_test diff --git a/s2/edge_distances.go b/s2/edge_distances.go index 4ba0bfc1..bef00df9 100644 --- a/s2/edge_distances.go +++ b/s2/edge_distances.go @@ -20,7 +20,7 @@ package s2 import ( "math" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) // DistanceFromSegment returns the distance of point X from line segment AB. diff --git a/s2/edge_distances_test.go b/s2/edge_distances_test.go index 4a3c37f3..e63e5d39 100644 --- a/s2/edge_distances_test.go +++ b/s2/edge_distances_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) func TestEdgeDistancesCheckDistance(t *testing.T) { diff --git a/s2/edge_query.go b/s2/edge_query.go index 8d25d10d..226cd054 100644 --- a/s2/edge_query.go +++ b/s2/edge_query.go @@ -17,7 +17,7 @@ package s2 import ( "sort" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) // EdgeQueryOptions holds the options for controlling how EdgeQuery operates. diff --git a/s2/edge_query_closest_test.go b/s2/edge_query_closest_test.go index f4e3bcc6..ac64da62 100644 --- a/s2/edge_query_closest_test.go +++ b/s2/edge_query_closest_test.go @@ -18,8 +18,8 @@ import ( "fmt" "testing" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) func TestClosestEdgeQueryNoEdges(t *testing.T) { diff --git a/s2/edge_query_furthest_test.go b/s2/edge_query_furthest_test.go index cf899f78..e5f21864 100644 --- a/s2/edge_query_furthest_test.go +++ b/s2/edge_query_furthest_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) func TestFurthestEdgeQueryNoEdges(t *testing.T) { diff --git a/s2/edge_query_test.go b/s2/edge_query_test.go index fff885fe..4a1f1b87 100644 --- a/s2/edge_query_test.go +++ b/s2/edge_query_test.go @@ -22,7 +22,7 @@ import ( "reflect" "testing" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) var ( diff --git a/s2/edge_tessellator.go b/s2/edge_tessellator.go index 903ad47c..ea6aa0b9 100644 --- a/s2/edge_tessellator.go +++ b/s2/edge_tessellator.go @@ -15,8 +15,8 @@ package s2 import ( - "github.com/golang/geo/r2" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r2" + "github.com/pavlov061356/geo/s1" ) // Tessellation is implemented by subdividing the edge until the estimated diff --git a/s2/edge_tessellator_test.go b/s2/edge_tessellator_test.go index 50706ec5..977a79bf 100644 --- a/s2/edge_tessellator_test.go +++ b/s2/edge_tessellator_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/golang/geo/r2" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r2" + "github.com/pavlov061356/geo/s1" ) func TestEdgeTessellatorProjectedNoTessellation(t *testing.T) { diff --git a/s2/encode_test.go b/s2/encode_test.go index 685bc88c..103afbf7 100644 --- a/s2/encode_test.go +++ b/s2/encode_test.go @@ -23,7 +23,7 @@ import ( "reflect" "testing" - "github.com/golang/geo/r3" + "github.com/pavlov061356/geo/r3" ) type encodableRegion interface { diff --git a/s2/example_test.go b/s2/example_test.go index bb86a1cc..f39b7a36 100644 --- a/s2/example_test.go +++ b/s2/example_test.go @@ -17,8 +17,8 @@ package s2_test import ( "fmt" - "github.com/golang/geo/s1" - "github.com/golang/geo/s2" + "github.com/pavlov061356/geo/s1" + "github.com/pavlov061356/geo/s2" ) func ExampleRect_DistanceToLatLng() { diff --git a/s2/latlng.go b/s2/latlng.go index a750304a..560d95db 100644 --- a/s2/latlng.go +++ b/s2/latlng.go @@ -18,8 +18,8 @@ import ( "fmt" "math" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) const ( diff --git a/s2/latlng_test.go b/s2/latlng_test.go index 02253eec..391917dc 100644 --- a/s2/latlng_test.go +++ b/s2/latlng_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) func TestLatLngNormalized(t *testing.T) { diff --git a/s2/loop.go b/s2/loop.go index 88f7b0fb..f82bc685 100644 --- a/s2/loop.go +++ b/s2/loop.go @@ -19,9 +19,9 @@ import ( "io" "math" - "github.com/golang/geo/r1" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) // Loop represents a simple spherical polygon. It consists of a sequence diff --git a/s2/loop_test.go b/s2/loop_test.go index 8683e7f6..a19a15b3 100644 --- a/s2/loop_test.go +++ b/s2/loop_test.go @@ -19,9 +19,9 @@ import ( "math" "testing" - "github.com/golang/geo/r1" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) var ( diff --git a/s2/matrix3x3.go b/s2/matrix3x3.go index 6d1432ac..38224d93 100644 --- a/s2/matrix3x3.go +++ b/s2/matrix3x3.go @@ -17,7 +17,7 @@ package s2 import ( "fmt" - "github.com/golang/geo/r3" + "github.com/pavlov061356/geo/r3" ) // matrix3x3 represents a traditional 3x3 matrix of floating point values. diff --git a/s2/matrix3x3_test.go b/s2/matrix3x3_test.go index e8f76f33..ccf43f38 100644 --- a/s2/matrix3x3_test.go +++ b/s2/matrix3x3_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/golang/geo/r3" + "github.com/pavlov061356/geo/r3" ) func TestCol(t *testing.T) { diff --git a/s2/max_distance_targets.go b/s2/max_distance_targets.go index 0409145e..d59df96c 100644 --- a/s2/max_distance_targets.go +++ b/s2/max_distance_targets.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) // maxDistance implements distance as the supplementary distance (Pi - x) to find diff --git a/s2/max_distance_targets_test.go b/s2/max_distance_targets_test.go index a15996e4..5ff4a690 100644 --- a/s2/max_distance_targets_test.go +++ b/s2/max_distance_targets_test.go @@ -19,7 +19,7 @@ import ( "sort" "testing" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) func TestDistanceTargetMaxCellTargetCapBound(t *testing.T) { diff --git a/s2/min_distance_targets.go b/s2/min_distance_targets.go index 1d2ad37b..3afe0fcd 100644 --- a/s2/min_distance_targets.go +++ b/s2/min_distance_targets.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) // minDistance implements distance interface to find closest distance types. diff --git a/s2/paddedcell.go b/s2/paddedcell.go index 8f7677a9..2fc55860 100644 --- a/s2/paddedcell.go +++ b/s2/paddedcell.go @@ -15,8 +15,8 @@ package s2 import ( - "github.com/golang/geo/r1" - "github.com/golang/geo/r2" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/r2" ) // PaddedCell represents a Cell whose (u,v)-range has been expanded on diff --git a/s2/paddedcell_test.go b/s2/paddedcell_test.go index a844ec24..b5724874 100644 --- a/s2/paddedcell_test.go +++ b/s2/paddedcell_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/golang/geo/r1" - "github.com/golang/geo/r2" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/r2" ) func TestPaddedCellMethods(t *testing.T) { diff --git a/s2/point.go b/s2/point.go index 577203aa..ffc68eee 100644 --- a/s2/point.go +++ b/s2/point.go @@ -20,8 +20,8 @@ import ( "math" "sort" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) // Point represents a point on the unit sphere as a normalized 3D vector. diff --git a/s2/point_measures.go b/s2/point_measures.go index 632c47cb..076697b7 100644 --- a/s2/point_measures.go +++ b/s2/point_measures.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) // PointArea returns the area of triangle ABC. This method combines two different diff --git a/s2/point_measures_test.go b/s2/point_measures_test.go index 367d2d10..135d71e3 100644 --- a/s2/point_measures_test.go +++ b/s2/point_measures_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) var ( diff --git a/s2/point_test.go b/s2/point_test.go index 48224618..7ed4a7d6 100644 --- a/s2/point_test.go +++ b/s2/point_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) func TestOriginPoint(t *testing.T) { diff --git a/s2/pointcompression.go b/s2/pointcompression.go index 2ca7db7d..fed4051f 100644 --- a/s2/pointcompression.go +++ b/s2/pointcompression.go @@ -18,7 +18,7 @@ import ( "errors" "fmt" - "github.com/golang/geo/r3" + "github.com/pavlov061356/geo/r3" ) // maxEncodedVertices is the maximum number of vertices, in a row, to be encoded or decoded. diff --git a/s2/polygon_test.go b/s2/polygon_test.go index 432a6716..2854471b 100644 --- a/s2/polygon_test.go +++ b/s2/polygon_test.go @@ -19,7 +19,7 @@ import ( "math/rand" "testing" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) const ( diff --git a/s2/polyline.go b/s2/polyline.go index b1ec2e76..7592cc77 100644 --- a/s2/polyline.go +++ b/s2/polyline.go @@ -19,7 +19,7 @@ import ( "io" "math" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) // Polyline represents a sequence of zero or more vertices connected by diff --git a/s2/polyline_measures.go b/s2/polyline_measures.go index 38ce991b..19f60173 100644 --- a/s2/polyline_measures.go +++ b/s2/polyline_measures.go @@ -19,8 +19,8 @@ package s2 // implement the methods in various other measures files. import ( - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) // polylineLength returns the length of the given Polyline. diff --git a/s2/polyline_test.go b/s2/polyline_test.go index 056e8a9b..fbe7f9bb 100644 --- a/s2/polyline_test.go +++ b/s2/polyline_test.go @@ -19,8 +19,8 @@ import ( "reflect" "testing" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) func TestPolylineBasics(t *testing.T) { diff --git a/s2/predicates.go b/s2/predicates.go index dfa41345..b32a554f 100644 --- a/s2/predicates.go +++ b/s2/predicates.go @@ -27,8 +27,8 @@ import ( "math" "math/big" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) const ( diff --git a/s2/predicates_test.go b/s2/predicates_test.go index 256c8bde..1d355eeb 100644 --- a/s2/predicates_test.go +++ b/s2/predicates_test.go @@ -20,8 +20,8 @@ import ( "math/big" "testing" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) func TestPredicatesSign(t *testing.T) { diff --git a/s2/projections.go b/s2/projections.go index f7273609..1b26e2e9 100644 --- a/s2/projections.go +++ b/s2/projections.go @@ -17,8 +17,8 @@ package s2 import ( "math" - "github.com/golang/geo/r2" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r2" + "github.com/pavlov061356/geo/s1" ) // Projection defines an interface for different ways of mapping between s2 and r2 Points. diff --git a/s2/projections_test.go b/s2/projections_test.go index 88eeea8f..a7870244 100644 --- a/s2/projections_test.go +++ b/s2/projections_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/golang/geo/r2" - "github.com/golang/geo/r3" + "github.com/pavlov061356/geo/r2" + "github.com/pavlov061356/geo/r3" ) func TestPlateCarreeProjectionInterpolate(t *testing.T) { diff --git a/s2/query_entry_test.go b/s2/query_entry_test.go index 15d77f50..f5e1505d 100644 --- a/s2/query_entry_test.go +++ b/s2/query_entry_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) func TestQueryQueueEntry(t *testing.T) { diff --git a/s2/query_options.go b/s2/query_options.go index 9b7e38d6..f1f5f530 100644 --- a/s2/query_options.go +++ b/s2/query_options.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) const maxQueryResults = math.MaxInt32 diff --git a/s2/rect.go b/s2/rect.go index 84083485..690dda63 100644 --- a/s2/rect.go +++ b/s2/rect.go @@ -19,9 +19,9 @@ import ( "io" "math" - "github.com/golang/geo/r1" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) // Rect represents a closed latitude-longitude rectangle. diff --git a/s2/rect_bounder.go b/s2/rect_bounder.go index a4a68349..737ba335 100644 --- a/s2/rect_bounder.go +++ b/s2/rect_bounder.go @@ -17,9 +17,9 @@ package s2 import ( "math" - "github.com/golang/geo/r1" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) // RectBounder is used to compute a bounding rectangle that contains all edges diff --git a/s2/rect_bounder_test.go b/s2/rect_bounder_test.go index bd8c34cc..f97644d7 100644 --- a/s2/rect_bounder_test.go +++ b/s2/rect_bounder_test.go @@ -18,9 +18,9 @@ import ( "math" "testing" - "github.com/golang/geo/r1" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) func rectBoundForPoints(a, b Point) Rect { diff --git a/s2/rect_test.go b/s2/rect_test.go index 30c59406..34869ec4 100644 --- a/s2/rect_test.go +++ b/s2/rect_test.go @@ -18,10 +18,10 @@ import ( "math" "testing" - "github.com/golang/geo/r1" - "github.com/golang/geo/r2" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/r2" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) func TestRectEmptyAndFull(t *testing.T) { diff --git a/s2/s2_test.go b/s2/s2_test.go index c370156c..e5179812 100644 --- a/s2/s2_test.go +++ b/s2/s2_test.go @@ -21,9 +21,9 @@ import ( "math/rand" "os" - "github.com/golang/geo/r1" - "github.com/golang/geo/r2" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/r2" + "github.com/pavlov061356/geo/s1" ) var ( diff --git a/s2/s2_test_test.go b/s2/s2_test_test.go index ec09f8ac..25c5e164 100644 --- a/s2/s2_test_test.go +++ b/s2/s2_test_test.go @@ -19,7 +19,7 @@ import ( "math" "testing" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) func TestKmToAngle(t *testing.T) { diff --git a/s2/s2intersect/s2intersect.go b/s2/s2intersect/s2intersect.go index 62ddae33..a842adad 100644 --- a/s2/s2intersect/s2intersect.go +++ b/s2/s2intersect/s2intersect.go @@ -29,7 +29,7 @@ import ( "fmt" "sort" - "github.com/golang/geo/s2" + "github.com/pavlov061356/geo/s2" ) // Re nomenclature used throughout this file: an "intersection" is a 2D diff --git a/s2/shapeindex.go b/s2/shapeindex.go index 045c28f9..9b4836ad 100644 --- a/s2/shapeindex.go +++ b/s2/shapeindex.go @@ -20,8 +20,8 @@ import ( "sync" "sync/atomic" - "github.com/golang/geo/r1" - "github.com/golang/geo/r2" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/r2" ) // CellRelation describes the possible relationships between a target cell diff --git a/s2/shapeindex_test.go b/s2/shapeindex_test.go index ab0a7017..7a5709a9 100644 --- a/s2/shapeindex_test.go +++ b/s2/shapeindex_test.go @@ -17,8 +17,8 @@ package s2 import ( "testing" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) func TestShapeIndexBasics(t *testing.T) { diff --git a/s2/shapeutil_test.go b/s2/shapeutil_test.go index 3057e62c..be65d32c 100644 --- a/s2/shapeutil_test.go +++ b/s2/shapeutil_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/s1" ) func TestShapeutilContainsBruteForceNoInterior(t *testing.T) { diff --git a/s2/stuv.go b/s2/stuv.go index 261bdd80..aed32055 100644 --- a/s2/stuv.go +++ b/s2/stuv.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/golang/geo/r3" + "github.com/pavlov061356/geo/r3" ) // diff --git a/s2/stuv_test.go b/s2/stuv_test.go index 2d4a4dca..3fe69d4f 100644 --- a/s2/stuv_test.go +++ b/s2/stuv_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/golang/geo/r3" + "github.com/pavlov061356/geo/r3" ) func TestSTUV(t *testing.T) { diff --git a/s2/textformat_test.go b/s2/textformat_test.go index df789365..4d7aacd2 100644 --- a/s2/textformat_test.go +++ b/s2/textformat_test.go @@ -35,7 +35,7 @@ import ( "strconv" "strings" - "github.com/golang/geo/r3" + "github.com/pavlov061356/geo/r3" ) // writePoint formats the point and writes it to the given writer. diff --git a/s2/textformat_test_test.go b/s2/textformat_test_test.go index 1a2184c9..025e5ea6 100644 --- a/s2/textformat_test_test.go +++ b/s2/textformat_test_test.go @@ -18,9 +18,9 @@ import ( "bytes" "testing" - "github.com/golang/geo/r1" - "github.com/golang/geo/r3" - "github.com/golang/geo/s1" + "github.com/pavlov061356/geo/r1" + "github.com/pavlov061356/geo/r3" + "github.com/pavlov061356/geo/s1" ) func TestParseLatLng(t *testing.T) { diff --git a/s2/util.go b/s2/util.go index 7cab746d..1f0619e5 100644 --- a/s2/util.go +++ b/s2/util.go @@ -14,7 +14,7 @@ package s2 -import "github.com/golang/geo/s1" +import "github.com/pavlov061356/geo/s1" // roundAngle returns the value rounded to nearest as an int32. // This does not match C++ exactly for the case of x.5. diff --git a/s2/wedge_relations_test.go b/s2/wedge_relations_test.go index 7d827892..47ccec23 100644 --- a/s2/wedge_relations_test.go +++ b/s2/wedge_relations_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/golang/geo/r3" + "github.com/pavlov061356/geo/r3" ) func TestWedgeRelations(t *testing.T) { From 6bdb0c20538bc7ae17b35433fd102b5228009585 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Wed, 16 Oct 2024 12:15:19 +0300 Subject: [PATCH 27/44] testing fix for overfill of SliceDivided --- s2/chain_interpolation_query.go | 7 ++++++- s2/chain_interpolation_query_test.go | 26 +++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 25ee4a35..d55e060e 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -304,6 +304,9 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa for i := currentEdgeID; i < edgeID; i++ { edge := s.Shape.Edge(i) if edge.V1 != atFraction { + if len(*points) == pointsNum-1 { + break + } *points = append(*points, edge.V1) } } @@ -314,7 +317,9 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa currentEdgeID++ continue } - + if len(*points) == pointsNum-1 { + break + } *points = append(*points, atFraction) } // Append last edge diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index b7bce936..2d638719 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -791,16 +791,36 @@ func TestSliceDivided(t *testing.T) { }, want: `0:1.5, 0:2, 0:3, 0:4, 0:4.2`, }, + { + name: "divisions = s.NumEdges()+1", + args: args{ + shape: laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }, + ), + startSliceFraction: 0.3, + endSliceFraction: 0.99999999999995, + divisions: 3, + }, + want: `0:0.6, 0:1, 0:1.9999999999999`, + }, } for _, test := range tests { query := InitChainInterpolationQuery(test.args.shape, 0) - if got := pointsToString(query.SliceDivided( + got := query.SliceDivided( test.args.startSliceFraction, test.args.endSliceFraction, test.args.divisions, - )); got != test.want { - t.Errorf("%v: got %v, want %v", test.name, got, test.want) + ) + want := parsePoints(test.want) + if len(got) != test.args.divisions && len(got) != len(want) { + t.Errorf("length mismatch: got %d, want %d", len(got), test.args.divisions) + } + if !pointSlicesApproxEqual(got, want, kEpsilon) { + t.Errorf("%v: got %v, want %v", test.name, got, want) } } } From dcffae9273b2b1ab8c9f26416c996f19ca30aa39 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Wed, 16 Oct 2024 12:15:19 +0300 Subject: [PATCH 28/44] testing fix for overfill of SliceDivided --- s2/chain_interpolation_query.go | 7 ++++++- s2/chain_interpolation_query_test.go | 26 +++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 1cfa0f72..31fb516e 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -304,6 +304,9 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa for i := currentEdgeID; i < edgeID; i++ { edge := s.Shape.Edge(i) if edge.V1 != atFraction { + if len(*points) == pointsNum-1 { + break + } *points = append(*points, edge.V1) } } @@ -314,7 +317,9 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa currentEdgeID++ continue } - + if len(*points) == pointsNum-1 { + break + } *points = append(*points, atFraction) } // Append last edge diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index c88ebd05..266ed168 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -791,16 +791,36 @@ func TestSliceDivided(t *testing.T) { }, want: `0:1.5, 0:2, 0:3, 0:4, 0:4.2`, }, + { + name: "divisions = s.NumEdges()+1", + args: args{ + shape: laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }, + ), + startSliceFraction: 0.3, + endSliceFraction: 0.99999999999995, + divisions: 3, + }, + want: `0:0.6, 0:1, 0:1.9999999999999`, + }, } for _, test := range tests { query := InitChainInterpolationQuery(test.args.shape, 0) - if got := pointsToString(query.SliceDivided( + got := query.SliceDivided( test.args.startSliceFraction, test.args.endSliceFraction, test.args.divisions, - )); got != test.want { - t.Errorf("%v: got %v, want %v", test.name, got, test.want) + ) + want := parsePoints(test.want) + if len(got) != test.args.divisions && len(got) != len(want) { + t.Errorf("length mismatch: got %d, want %d", len(got), test.args.divisions) + } + if !pointSlicesApproxEqual(got, want, kEpsilon) { + t.Errorf("%v: got %v, want %v", test.name, got, want) } } } From d57db6ba0fd98eaaaf714ca77c0e99602394d090 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Mon, 11 Nov 2024 10:29:02 +0300 Subject: [PATCH 29/44] refactoring --- s2/chain_interpolation_query.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 31fb516e..623d4170 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -262,15 +262,17 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa return } - slice := s.Slice(beginFraction, endFraction) + *points = s.Slice(beginFraction, endFraction) - if len(slice) > pointsNum { + if len(*points) > pointsNum { + *points = make([]Point, 0) return - } else if len(slice) == pointsNum { - *points = append(*points, slice...) + } else if len(*points) == pointsNum { return } + *points = make([]Point, 0) + reverse := beginFraction > endFraction if reverse { // Swap the begin and end fractions so that we can iterate in ascending order. From 011965524acc3c760e1f80d41cfd336d5d1ec79c Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Mon, 11 Nov 2024 10:32:49 +0300 Subject: [PATCH 30/44] updated with non emptu slice --- s2/chain_interpolation_query.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 623d4170..f6b4a64c 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -262,16 +262,18 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa return } - *points = s.Slice(beginFraction, endFraction) + pointsLength := len(*points) + + *points = append(*points, s.Slice(beginFraction, endFraction)...) if len(*points) > pointsNum { - *points = make([]Point, 0) + *points = (*points)[0:pointsLength] return } else if len(*points) == pointsNum { return } - *points = make([]Point, 0) + *points = (*points)[0:pointsLength] reverse := beginFraction > endFraction if reverse { From b86616f57c97a41850ce599bc24aed84a466d2e4 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Wed, 13 Nov 2024 13:24:11 +0300 Subject: [PATCH 31/44] add benchmark on slice divided --- s2/chain_interpolation_query_test.go | 45 ++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 2d638719..114c1682 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -824,3 +824,48 @@ func TestSliceDivided(t *testing.T) { } } } + +func Benchmark_SliceDivided(b *testing.B) { + chainInterpolationQuery := InitChainInterpolationQuery( + laxPolylineFromPoints( + []Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }, + ), + 0, + ) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + slice := chainInterpolationQuery.SliceDivided(0.3, 0.84, 500) + if len(slice) != 500 { + b.Errorf("length mismatch: got %d, want %d", len(slice), 500) + } + } + + b.StopTimer() + + points := make([]Point, 500) + + for i := 0; i < 100; i++ { + points[i] = PointFromLatLng(LatLngFromDegrees(0, float64(i))) + } + + chainInterpolationQuery = InitChainInterpolationQuery( + laxPolylineFromPoints( + points, + ), + 0, + ) + + b.StartTimer() + for i := 0; i < b.N; i++ { + slice := chainInterpolationQuery.SliceDivided(0.3, 0.84, 500) + if len(slice) != 500 { + b.Errorf("length mismatch: got %d, want %d", len(slice), 500) + } + } + +} From e9400b5c5bbaf89e15f23cea1d98b7686bd04356 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Wed, 13 Nov 2024 13:27:02 +0300 Subject: [PATCH 32/44] add benchmark results --- s2/chain_interpolation_query_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 114c1682..18315420 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -825,6 +825,14 @@ func TestSliceDivided(t *testing.T) { } } +// goos: linux +// goarch: amd64 +// pkg: github.com/pavlov061356/geo/s2 +// cpu: AMD Ryzen 5 5600G with Radeon Graphics +// === RUN Benchmark_SliceDivided +// Benchmark_SliceDivided +// Benchmark_SliceDivided-12 8179 131682 ns/op 63696 B/op 23 allocs/op + func Benchmark_SliceDivided(b *testing.B) { chainInterpolationQuery := InitChainInterpolationQuery( laxPolylineFromPoints( From c5a9edf7da5292b92879d0808be683f20fae4e30 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Wed, 13 Nov 2024 14:54:25 +0300 Subject: [PATCH 33/44] updated alloc rate --- s2/chain_interpolation_query.go | 45 ++++++++++++------ s2/chain_interpolation_query_test.go | 69 ++++++++++++++++++++++++---- 2 files changed, 91 insertions(+), 23 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 274b6421..b0968509 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -264,12 +264,22 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa pointsLength := len(*points) - *points = append(*points, s.Slice(beginFraction, endFraction)...) + atBegin, currentEdgeID, _, err := s.AtFraction(beginFraction) + if err != nil { + return + } + + atEnd, endEdgeID, _, err := s.AtFraction(endFraction) + if err != nil { + return + } - if len(*points) > pointsNum { - *points = (*points)[0:pointsLength] + edgesBetween := s.EdgesBetween(atBegin, atEnd, currentEdgeID, endEdgeID) + + if edgesBetween > pointsNum-2 { return - } else if len(*points) == pointsNum { + } else if edgesBetween == pointsNum-2 { + s.AddSlice(beginFraction, endFraction, points) return } @@ -281,16 +291,6 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa beginFraction, endFraction = endFraction, beginFraction } - atBegin, currentEdgeID, _, err := s.AtFraction(beginFraction) - if err != nil { - return - } - - atEnd, _, _, err := s.AtFraction(endFraction) - if err != nil { - return - } - // divisionsExcludingEdges := pointsNum - len(slice) *points = append(*points, atBegin) @@ -334,3 +334,20 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa slices.Reverse(*points) } } + +func (s ChainInterpolationQuery) EdgesBetween(begin, end Point, beginEdgeID, endEdgeID int) int { + if end == begin { + return 0 + } + edges := 0 + + for edgeID := beginEdgeID; edgeID < endEdgeID; edgeID++ { + edge := s.Shape.Edge(edgeID) + if begin != edge.V1 { + begin = edge.V1 + edges++ + } + } + + return edges +} diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 18315420..0b6b3cdb 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -831,7 +831,7 @@ func TestSliceDivided(t *testing.T) { // cpu: AMD Ryzen 5 5600G with Radeon Graphics // === RUN Benchmark_SliceDivided // Benchmark_SliceDivided -// Benchmark_SliceDivided-12 8179 131682 ns/op 63696 B/op 23 allocs/op +// Benchmark_SliceDivided-12 8101 128452 ns/op 49104 B/op 20 allocs/op func Benchmark_SliceDivided(b *testing.B) { chainInterpolationQuery := InitChainInterpolationQuery( @@ -847,10 +847,7 @@ func Benchmark_SliceDivided(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - slice := chainInterpolationQuery.SliceDivided(0.3, 0.84, 500) - if len(slice) != 500 { - b.Errorf("length mismatch: got %d, want %d", len(slice), 500) - } + chainInterpolationQuery.SliceDivided(0.3, 0.84, 500) } b.StopTimer() @@ -870,10 +867,64 @@ func Benchmark_SliceDivided(b *testing.B) { b.StartTimer() for i := 0; i < b.N; i++ { - slice := chainInterpolationQuery.SliceDivided(0.3, 0.84, 500) - if len(slice) != 500 { - b.Errorf("length mismatch: got %d, want %d", len(slice), 500) - } + chainInterpolationQuery.SliceDivided(0.3, 0.84, 500) } +} +func TestChainInterpolationQuery_EdgesBetween(t *testing.T) { + query := InitChainInterpolationQuery(laxPolylineFromPoints([]Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + PointFromLatLng(LatLngFromDegrees(0, 3)), + PointFromLatLng(LatLngFromDegrees(0, 4)), + PointFromLatLng(LatLngFromDegrees(0, 5)), + }, + ), 0) + type args struct { + beginFraction float64 + endFraction float64 + } + tests := []struct { + name string + args args + want int + }{ + { + name: "beginFraction = 0, endFraction = 0.5", + args: args{beginFraction: 0, endFraction: 0.5}, + want: 2, + }, + { + name: "beginFraction = 0, endFraction = 0.8", + args: args{beginFraction: 0, endFraction: 0.8}, + want: 3, + }, + { + name: "beginFraction = 0, endFraction = 0.85", + args: args{beginFraction: 0, endFraction: 0.85}, + want: 4, + }, + { + name: "beginFraction = 0, endFraction = 1", + args: args{beginFraction: 0, endFraction: 1}, + want: 4, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + atBegin, beginEdgeID, _, err := query.AtFraction(tt.args.beginFraction) + if err != nil { + t.Errorf("ChainInterpolationQuery.AtFraction() error = %v", err) + } + + atEnd, endEdgeID, _, err := query.AtFraction(tt.args.endFraction) + if err != nil { + t.Errorf("ChainInterpolationQuery.AtFraction() error = %v", err) + } + if got := query.EdgesBetween(atBegin, atEnd, beginEdgeID, endEdgeID); got != tt.want { + t.Errorf("ChainInterpolationQuery.EdgesBetween() = %v, want %v", got, tt.want) + } + }) + } } From ea1220a9d098c7c3d45b860f1f5c98541e7c0f0a Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Thu, 14 Nov 2024 13:39:17 +0300 Subject: [PATCH 34/44] optimized memory allocation --- s2/chain_interpolation_query.go | 2 +- s2/chain_interpolation_query_test.go | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index b0968509..dc665a44 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -202,7 +202,7 @@ func (s ChainInterpolationQuery) Slice(beginFraction, endFraction float64) []Poi // If the query is either uninitialized, or initialized with a shape // containing no edges, then an empty vector is returned. func (s ChainInterpolationQuery) SliceDivided(beginFraction, endFraction float64, divisions int) []Point { - var points []Point + points := make([]Point, 0, divisions) s.AddDividedSlice(beginFraction, endFraction, &points, divisions) return points } diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 0b6b3cdb..182fe406 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -831,7 +831,7 @@ func TestSliceDivided(t *testing.T) { // cpu: AMD Ryzen 5 5600G with Radeon Graphics // === RUN Benchmark_SliceDivided // Benchmark_SliceDivided -// Benchmark_SliceDivided-12 8101 128452 ns/op 49104 B/op 20 allocs/op +// Benchmark_SliceDivided-12 8101 128452 ns/op 24577 B/op 2 allocs/op func Benchmark_SliceDivided(b *testing.B) { chainInterpolationQuery := InitChainInterpolationQuery( @@ -928,3 +928,15 @@ func TestChainInterpolationQuery_EdgesBetween(t *testing.T) { }) } } + +func Benchmark_InitChinInterpolationQuery(b *testing.B) { + points := make([]Point, 0, b.N) + for i := 0; i < b.N; i++ { + points = append(points, PointFromLatLng(LatLngFromDegrees(0, float64(i)))) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + InitChainInterpolationQuery(laxPolylineFromPoints(points), 0) + } +} From d02b7e1d302a16f0ff2ff2a75f1b281aaf79e26b Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Mon, 18 Nov 2024 15:32:46 +0300 Subject: [PATCH 35/44] add benchmark --- s2/chain_interpolation_query_test.go | 45 ++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 182fe406..ae1192be 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -940,3 +940,48 @@ func Benchmark_InitChinInterpolationQuery(b *testing.B) { InitChainInterpolationQuery(laxPolylineFromPoints(points), 0) } } + +// goos: linux +// goarch: amd64 +// pkg: github.com/pavlov061356/geo/s2 +// cpu: AMD Ryzen 7 5800H with Radeon Graphics +// === RUN Benchmark_Slice +// Benchmark_Slice +// Benchmark_Slice-16 303748 3403 ns/op 3216 B/op 10 allocs/op +func Benchmark_Slice(b *testing.B) { + chainInterpolationQuery := InitChainInterpolationQuery( + laxPolylineFromPoints( + []Point{ + PointFromLatLng(LatLngFromDegrees(0, 0)), + PointFromLatLng(LatLngFromDegrees(0, 1)), + PointFromLatLng(LatLngFromDegrees(0, 2)), + }, + ), + 0, + ) + b.ResetTimer() + + for i := 0; i < b.N; i++ { + chainInterpolationQuery.Slice(0.3, 0.84) + } + + b.StopTimer() + + points := make([]Point, 500) + + for i := 0; i < 100; i++ { + points[i] = PointFromLatLng(LatLngFromDegrees(0, float64(i))) + } + + chainInterpolationQuery = InitChainInterpolationQuery( + laxPolylineFromPoints( + points, + ), + 0, + ) + + b.StartTimer() + for i := 0; i < b.N; i++ { + chainInterpolationQuery.Slice(0.3, 0.84) + } +} From d398ea87b19a59df0dd5f309087c32d0ee063cbb Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov <64154579+pavlov061356@users.noreply.github.com> Date: Wed, 20 Nov 2024 13:40:39 +0300 Subject: [PATCH 36/44] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index afa923c9..8259bd4c 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ VertexIDLaxLoop | ❌ C++ Type | Go :------------------- | --- -S2ChainInterpolation | ❌ +S2ChainInterpolation | ✅ S2ClosestCell | ❌ S2FurthestCell | ❌ S2ClosestEdge | ✅ From cd9bd722fb69a691e2767bf3524d8e19c73c84d7 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Fri, 22 Nov 2024 11:13:16 +0300 Subject: [PATCH 37/44] udpated length check --- s2/chain_interpolation_query.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index dc665a44..18df4487 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -317,6 +317,9 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa currentEdgeID = edgeID continue } else if edge := s.Shape.Edge(edgeID); edge.V1.approxEqual(atFraction, epsilon) { + if len(*points) == pointsNum-1 { + break + } *points = append(*points, edge.V1) currentEdgeID++ continue From fc6dc25dd02ae669defa674a1add4142a023bceb Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Fri, 22 Nov 2024 11:20:34 +0300 Subject: [PATCH 38/44] refactored filling divided slice --- s2/chain_interpolation_query.go | 58 +++++++++++++++++++++++++++------ 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 18df4487..52c75273 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -296,7 +296,9 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa *points = append(*points, atBegin) // // Copy the internal points from the chain. - for fraction := beginFraction + (endFraction-beginFraction)/float64(pointsNum-1); fraction < endFraction; fraction += (endFraction - beginFraction) / float64(pointsNum-1) { + + fraction := beginFraction + (endFraction-beginFraction)/float64(pointsNum-1) + for pointsLength := 0; pointsLength < pointsNum-2; fraction += (endFraction - beginFraction) / float64(pointsNum-1) { atFraction, edgeID, _, err := s.AtFraction(fraction) if err != nil { return @@ -308,27 +310,63 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa for i := currentEdgeID; i < edgeID; i++ { edge := s.Shape.Edge(i) if edge.V1 != atFraction { - if len(*points) == pointsNum-1 { - break - } + // if len(*points) == pointsNum-1 { + // break + // } + pointsLength++ *points = append(*points, edge.V1) } } currentEdgeID = edgeID continue } else if edge := s.Shape.Edge(edgeID); edge.V1.approxEqual(atFraction, epsilon) { - if len(*points) == pointsNum-1 { - break - } + // if len(*points) == pointsNum-1 { + // break + // } + pointsLength++ *points = append(*points, edge.V1) currentEdgeID++ continue } - if len(*points) == pointsNum-1 { - break - } + // if len(*points) == pointsNum-1 { + // break + // } + pointsLength++ *points = append(*points, atFraction) } + // for fraction := beginFraction + (endFraction-beginFraction)/float64(pointsNum-1); fraction < endFraction; fraction += (endFraction - beginFraction) / float64(pointsNum-1) { + // atFraction, edgeID, _, err := s.AtFraction(fraction) + // if err != nil { + // return + // } + + // // If the current edge is the same as the previous edge, then skip it. + // // Otherwise, append all edges in between. + // if currentEdgeID != edgeID { + // for i := currentEdgeID; i < edgeID; i++ { + // edge := s.Shape.Edge(i) + // if edge.V1 != atFraction { + // if len(*points) == pointsNum-1 { + // break + // } + // *points = append(*points, edge.V1) + // } + // } + // currentEdgeID = edgeID + // continue + // } else if edge := s.Shape.Edge(edgeID); edge.V1.approxEqual(atFraction, epsilon) { + // if len(*points) == pointsNum-1 { + // break + // } + // *points = append(*points, edge.V1) + // currentEdgeID++ + // continue + // } + // if len(*points) == pointsNum-1 { + // break + // } + // *points = append(*points, atFraction) + // } // Append last edge *points = append(*points, atEnd) From d6fc39fd079358bddd8987ec78c1baaba3173673 Mon Sep 17 00:00:00 2001 From: Vladimir Pavlov Date: Wed, 11 Dec 2024 11:34:11 +0300 Subject: [PATCH 39/44] fixed reverse in SliceDivided --- s2/chain_interpolation_query_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index ae1192be..adf58ad8 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -1,6 +1,7 @@ package s2 import ( + "fmt" "testing" "github.com/pavlov061356/geo/s1" @@ -822,6 +823,17 @@ func TestSliceDivided(t *testing.T) { if !pointSlicesApproxEqual(got, want, kEpsilon) { t.Errorf("%v: got %v, want %v", test.name, got, want) } + + fmt.Println(got) + + for i := 1; i < len(got)-1; i++ { + prev := LatLngFromPoint(got[i-1]) + curr := LatLngFromPoint(got[i]) + + if curr.Lng < prev.Lng { + t.Errorf("%v: got %v, want %v", test.name, got, want) + } + } } } From e71109025e7014590d531c0d5d88e7a967ed74b9 Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Thu, 12 Dec 2024 16:51:27 +0300 Subject: [PATCH 40/44] updated to point with fraction to SliceDivided --- s2/chain_interpolation_query.go | 88 +++++++++------------------- s2/chain_interpolation_query_test.go | 13 ++-- 2 files changed, 35 insertions(+), 66 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 52c75273..89ce506d 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -201,8 +201,8 @@ func (s ChainInterpolationQuery) Slice(beginFraction, endFraction float64) []Poi // // If the query is either uninitialized, or initialized with a shape // containing no edges, then an empty vector is returned. -func (s ChainInterpolationQuery) SliceDivided(beginFraction, endFraction float64, divisions int) []Point { - points := make([]Point, 0, divisions) +func (s ChainInterpolationQuery) SliceDivided(beginFraction, endFraction float64, divisions int) []PointWithFraction { + points := make([]PointWithFraction, 0, divisions) s.AddDividedSlice(beginFraction, endFraction, &points, divisions) return points } @@ -250,6 +250,11 @@ func (s ChainInterpolationQuery) AddSlice(beginFraction, endFraction float64, po } } +type PointWithFraction struct { + Point + Fraction float64 +} + // Appends the slice from beginFraction to endFraction to the given // slice. If beginFraction is greater than endFraction, then the points are // appended in reverse order. If the query is either uninitialized, or @@ -257,13 +262,19 @@ func (s ChainInterpolationQuery) AddSlice(beginFraction, endFraction float64, po // divisions is the number of segments to divide the polyline into. // divisions must be greater or equal of NumEdges of Shape. // A polyline is divided into segments of equal length, and then edges are added to the slice. -func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction float64, points *[]Point, pointsNum int) { +func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction float64, points *[]PointWithFraction, pointsNum int) { if len(s.cumulativeValues) == 0 { return } pointsLength := len(*points) + reverse := beginFraction > endFraction + if reverse { + // Swap the begin and end fractions so that we can iterate in ascending order. + beginFraction, endFraction = endFraction, beginFraction + } + atBegin, currentEdgeID, _, err := s.AtFraction(beginFraction) if err != nil { return @@ -278,22 +289,13 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa if edgesBetween > pointsNum-2 { return - } else if edgesBetween == pointsNum-2 { - s.AddSlice(beginFraction, endFraction, points) - return } *points = (*points)[0:pointsLength] - reverse := beginFraction > endFraction - if reverse { - // Swap the begin and end fractions so that we can iterate in ascending order. - beginFraction, endFraction = endFraction, beginFraction - } - // divisionsExcludingEdges := pointsNum - len(slice) - *points = append(*points, atBegin) + *points = append(*points, PointWithFraction{Point: atBegin, Fraction: beginFraction}) // // Copy the internal points from the chain. @@ -310,65 +312,31 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa for i := currentEdgeID; i < edgeID; i++ { edge := s.Shape.Edge(i) if edge.V1 != atFraction { - // if len(*points) == pointsNum-1 { - // break - // } pointsLength++ - *points = append(*points, edge.V1) + total, err := s.GetLength() + if err != nil { + return + } + if total == 0 { + return + } + + *points = append(*points, PointWithFraction{Point: edge.V1, Fraction: s.cumulativeValues[i].Radians() / total.Radians()}) } } currentEdgeID = edgeID continue } else if edge := s.Shape.Edge(edgeID); edge.V1.approxEqual(atFraction, epsilon) { - // if len(*points) == pointsNum-1 { - // break - // } pointsLength++ - *points = append(*points, edge.V1) + *points = append(*points, PointWithFraction{Point: edge.V1, Fraction: fraction}) currentEdgeID++ continue } - // if len(*points) == pointsNum-1 { - // break - // } + pointsLength++ - *points = append(*points, atFraction) + *points = append(*points, PointWithFraction{Point: atFraction, Fraction: fraction}) } - // for fraction := beginFraction + (endFraction-beginFraction)/float64(pointsNum-1); fraction < endFraction; fraction += (endFraction - beginFraction) / float64(pointsNum-1) { - // atFraction, edgeID, _, err := s.AtFraction(fraction) - // if err != nil { - // return - // } - - // // If the current edge is the same as the previous edge, then skip it. - // // Otherwise, append all edges in between. - // if currentEdgeID != edgeID { - // for i := currentEdgeID; i < edgeID; i++ { - // edge := s.Shape.Edge(i) - // if edge.V1 != atFraction { - // if len(*points) == pointsNum-1 { - // break - // } - // *points = append(*points, edge.V1) - // } - // } - // currentEdgeID = edgeID - // continue - // } else if edge := s.Shape.Edge(edgeID); edge.V1.approxEqual(atFraction, epsilon) { - // if len(*points) == pointsNum-1 { - // break - // } - // *points = append(*points, edge.V1) - // currentEdgeID++ - // continue - // } - // if len(*points) == pointsNum-1 { - // break - // } - // *points = append(*points, atFraction) - // } - // Append last edge - *points = append(*points, atEnd) + *points = append(*points, PointWithFraction{Point: atEnd, Fraction: endFraction}) // Reverse the slice if necessary. if reverse { diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index adf58ad8..c00bedf0 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -1,7 +1,6 @@ package s2 import ( - "fmt" "testing" "github.com/pavlov061356/geo/s1" @@ -820,15 +819,17 @@ func TestSliceDivided(t *testing.T) { if len(got) != test.args.divisions && len(got) != len(want) { t.Errorf("length mismatch: got %d, want %d", len(got), test.args.divisions) } - if !pointSlicesApproxEqual(got, want, kEpsilon) { + points := make([]Point, len(got)) + for i := range got { + points[i] = got[i].Point + } + if !pointSlicesApproxEqual(points, want, kEpsilon) { t.Errorf("%v: got %v, want %v", test.name, got, want) } - fmt.Println(got) - for i := 1; i < len(got)-1; i++ { - prev := LatLngFromPoint(got[i-1]) - curr := LatLngFromPoint(got[i]) + prev := LatLngFromPoint(got[i-1].Point) + curr := LatLngFromPoint(got[i].Point) if curr.Lng < prev.Lng { t.Errorf("%v: got %v, want %v", test.name, got, want) From 54d58af4d0f84157fb7f7bb88a087cb9a4245d1a Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Fri, 11 Apr 2025 18:19:44 +0300 Subject: [PATCH 41/44] slice divided update --- s2/chain_interpolation_query.go | 6 +++--- s2/chain_interpolation_query_test.go | 22 +++++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 89ce506d..bba2f380 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -309,9 +309,9 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa // If the current edge is the same as the previous edge, then skip it. // Otherwise, append all edges in between. if currentEdgeID != edgeID { - for i := currentEdgeID; i < edgeID; i++ { + for i := currentEdgeID + 1; i <= edgeID; i++ { edge := s.Shape.Edge(i) - if edge.V1 != atFraction { + if edge.V0 != atFraction { pointsLength++ total, err := s.GetLength() if err != nil { @@ -321,7 +321,7 @@ func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction floa return } - *points = append(*points, PointWithFraction{Point: edge.V1, Fraction: s.cumulativeValues[i].Radians() / total.Radians()}) + *points = append(*points, PointWithFraction{Point: edge.V0, Fraction: s.cumulativeValues[i].Radians() / total.Radians()}) } } currentEdgeID = edgeID diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index c00bedf0..c40579df 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -823,24 +823,28 @@ func TestSliceDivided(t *testing.T) { for i := range got { points[i] = got[i].Point } + for i := 1; i < len(got)-1; i++ { + if got[i-1].Fraction > got[i].Fraction { + t.Errorf("Fraction of i is less than i-1; i: %v; got: %v\n", i, got) + } + + } if !pointSlicesApproxEqual(points, want, kEpsilon) { t.Errorf("%v: got %v, want %v", test.name, got, want) } - for i := 1; i < len(got)-1; i++ { - prev := LatLngFromPoint(got[i-1].Point) - curr := LatLngFromPoint(got[i].Point) + // prev := LatLngFromPoint(got[i-1].Point) + // curr := LatLngFromPoint(got[i].Point) - if curr.Lng < prev.Lng { - t.Errorf("%v: got %v, want %v", test.name, got, want) - } - } + // if curr.Lng < prev.Lng { + // t.Errorf("%v: got %v, want %v", test.name, got, want) + // } } } // goos: linux // goarch: amd64 -// pkg: github.com/pavlov061356/geo/s2 +// pkg: github.com/mrosminin/tf-geo/s2 // cpu: AMD Ryzen 5 5600G with Radeon Graphics // === RUN Benchmark_SliceDivided // Benchmark_SliceDivided @@ -956,7 +960,7 @@ func Benchmark_InitChinInterpolationQuery(b *testing.B) { // goos: linux // goarch: amd64 -// pkg: github.com/pavlov061356/geo/s2 +// pkg: github.com/mrosminin/tf-geo/s2 // cpu: AMD Ryzen 7 5800H with Radeon Graphics // === RUN Benchmark_Slice // Benchmark_Slice From 7ea6a4b82814335897a448c0d1c7ca703a3a681c Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Fri, 11 Apr 2025 19:01:29 +0300 Subject: [PATCH 42/44] review update --- go.mod | 2 +- r2/rect.go | 2 +- r2/rect_test.go | 2 +- r3/vector.go | 2 +- s1/example_test.go | 2 +- s2/builder_snapper.go | 2 +- s2/builder_snapper_test.go | 2 +- s2/cap.go | 4 ++-- s2/cap_test.go | 4 ++-- s2/cell.go | 8 ++++---- s2/cell_test.go | 4 ++-- s2/cellid.go | 8 ++++---- s2/cellid_test.go | 4 ++-- s2/cellunion.go | 2 +- s2/cellunion_test.go | 4 ++-- s2/centroids.go | 2 +- s2/centroids_test.go | 2 +- s2/chain_interpolation_query.go | 2 +- s2/chain_interpolation_query_test.go | 2 +- s2/contains_point_query_test.go | 2 +- s2/contains_vertex_query_test.go | 2 +- s2/convex_hull_query.go | 2 +- s2/convex_hull_query_test.go | 2 +- s2/crossing_edge_query.go | 2 +- s2/crossing_edge_query_test.go | 2 +- s2/distance_target.go | 2 +- s2/edge_clipping.go | 6 +++--- s2/edge_clipping_test.go | 8 ++++---- s2/edge_crosser_test.go | 2 +- s2/edge_crossings.go | 4 ++-- s2/edge_crossings_test.go | 2 +- s2/edge_distances.go | 2 +- s2/edge_distances_test.go | 4 ++-- s2/edge_query.go | 2 +- s2/edge_query_closest_test.go | 4 ++-- s2/edge_query_furthest_test.go | 2 +- s2/edge_query_test.go | 2 +- s2/edge_tessellator.go | 4 ++-- s2/edge_tessellator_test.go | 4 ++-- s2/encode_test.go | 2 +- s2/example_test.go | 4 ++-- s2/latlng.go | 4 ++-- s2/latlng_test.go | 2 +- s2/loop.go | 6 +++--- s2/loop_test.go | 6 +++--- s2/matrix3x3.go | 2 +- s2/matrix3x3_test.go | 2 +- s2/max_distance_targets.go | 2 +- s2/max_distance_targets_test.go | 2 +- s2/min_distance_targets.go | 2 +- s2/paddedcell.go | 4 ++-- s2/paddedcell_test.go | 4 ++-- s2/point.go | 4 ++-- s2/point_measures.go | 2 +- s2/point_measures_test.go | 4 ++-- s2/point_test.go | 4 ++-- s2/pointcompression.go | 2 +- s2/polygon_test.go | 2 +- s2/polyline.go | 2 +- s2/polyline_measures.go | 4 ++-- s2/polyline_test.go | 4 ++-- s2/predicates.go | 4 ++-- s2/predicates_test.go | 4 ++-- s2/projections.go | 4 ++-- s2/projections_test.go | 4 ++-- s2/query_entry_test.go | 2 +- s2/query_options.go | 2 +- s2/rect.go | 6 +++--- s2/rect_bounder.go | 6 +++--- s2/rect_bounder_test.go | 6 +++--- s2/rect_test.go | 8 ++++---- s2/s2_test.go | 6 +++--- s2/s2_test_test.go | 2 +- s2/s2intersect/s2intersect.go | 2 +- s2/shapeindex.go | 4 ++-- s2/shapeindex_test.go | 4 ++-- s2/shapeutil_test.go | 2 +- s2/stuv.go | 2 +- s2/stuv_test.go | 2 +- s2/textformat_test.go | 2 +- s2/textformat_test_test.go | 6 +++--- s2/util.go | 2 +- s2/wedge_relations_test.go | 2 +- 83 files changed, 136 insertions(+), 136 deletions(-) diff --git a/go.mod b/go.mod index e02f42b9..a40160ef 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/pavlov061356/geo +module github.com/golang/geo/geo go 1.21 diff --git a/r2/rect.go b/r2/rect.go index 772b7569..ef758f85 100644 --- a/r2/rect.go +++ b/r2/rect.go @@ -18,7 +18,7 @@ import ( "fmt" "math" - "github.com/pavlov061356/geo/r1" + "github.com/golang/geo/geo/r1" ) // Point represents a point in ℝ². diff --git a/r2/rect_test.go b/r2/rect_test.go index 25eb7018..fc282549 100644 --- a/r2/rect_test.go +++ b/r2/rect_test.go @@ -22,7 +22,7 @@ import ( "reflect" "testing" - "github.com/pavlov061356/geo/r1" + "github.com/golang/geo/geo/r1" ) var ( diff --git a/r3/vector.go b/r3/vector.go index bf30abe8..cac8ac8a 100644 --- a/r3/vector.go +++ b/r3/vector.go @@ -18,7 +18,7 @@ import ( "fmt" "math" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) // Vector represents a point in ℝ³. diff --git a/s1/example_test.go b/s1/example_test.go index db3f0696..01819b3e 100644 --- a/s1/example_test.go +++ b/s1/example_test.go @@ -18,7 +18,7 @@ import ( "fmt" "math" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) func ExampleInterval_DirectedHausdorffDistance() { diff --git a/s2/builder_snapper.go b/s2/builder_snapper.go index e584f5b2..a859fb3b 100644 --- a/s2/builder_snapper.go +++ b/s2/builder_snapper.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) // A Snapper restricts the locations of the output vertices. For diff --git a/s2/builder_snapper_test.go b/s2/builder_snapper_test.go index ab1e991c..3e902a18 100644 --- a/s2/builder_snapper_test.go +++ b/s2/builder_snapper_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) func TestIdentitySnapper(t *testing.T) { diff --git a/s2/cap.go b/s2/cap.go index 989448b4..cc5f1150 100644 --- a/s2/cap.go +++ b/s2/cap.go @@ -19,8 +19,8 @@ import ( "io" "math" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/s1" ) var ( diff --git a/s2/cap_test.go b/s2/cap_test.go index 23a8d825..59cc7b7f 100644 --- a/s2/cap_test.go +++ b/s2/cap_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) const ( diff --git a/s2/cell.go b/s2/cell.go index b1d15d5f..cf11d091 100644 --- a/s2/cell.go +++ b/s2/cell.go @@ -18,10 +18,10 @@ import ( "io" "math" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/r2" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/r2" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) // CellBoundary represents canonical identifiers for the boundaries of the cell. diff --git a/s2/cell_test.go b/s2/cell_test.go index dddfc4a5..f40cbfab 100644 --- a/s2/cell_test.go +++ b/s2/cell_test.go @@ -19,8 +19,8 @@ import ( "testing" "unsafe" - "github.com/pavlov061356/geo/r2" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r2" + "github.com/golang/geo/geo/s1" ) // maxCellSize is the upper bounds on the number of bytes we want the Cell object to ever be. diff --git a/s2/cellid.go b/s2/cellid.go index c8cd5e7d..c732d657 100644 --- a/s2/cellid.go +++ b/s2/cellid.go @@ -23,10 +23,10 @@ import ( "strconv" "strings" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/r2" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/r2" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) // CellID uniquely identifies a cell in the S2 cell decomposition. diff --git a/s2/cellid_test.go b/s2/cellid_test.go index 699a95cc..d743d0a2 100644 --- a/s2/cellid_test.go +++ b/s2/cellid_test.go @@ -19,8 +19,8 @@ import ( "reflect" "testing" - "github.com/pavlov061356/geo/r2" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r2" + "github.com/golang/geo/geo/s1" ) func TestCellIDFromFace(t *testing.T) { diff --git a/s2/cellunion.go b/s2/cellunion.go index f96bc842..7bc3cce5 100644 --- a/s2/cellunion.go +++ b/s2/cellunion.go @@ -19,7 +19,7 @@ import ( "io" "sort" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) // A CellUnion is a collection of CellIDs. diff --git a/s2/cellunion_test.go b/s2/cellunion_test.go index 1acbe6aa..4ac9d23b 100644 --- a/s2/cellunion_test.go +++ b/s2/cellunion_test.go @@ -20,8 +20,8 @@ import ( "reflect" "testing" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/s1" ) func TestCellUnionDuplicateCellsNotValid(t *testing.T) { diff --git a/s2/centroids.go b/s2/centroids.go index a1daeded..52e35bc8 100644 --- a/s2/centroids.go +++ b/s2/centroids.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/pavlov061356/geo/r3" + "github.com/golang/geo/geo/r3" ) // There are several notions of the "centroid" of a triangle. First, there diff --git a/s2/centroids_test.go b/s2/centroids_test.go index cf9472f2..88e9aa6d 100644 --- a/s2/centroids_test.go +++ b/s2/centroids_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/r3" + "github.com/golang/geo/geo/r3" ) func TestCentroidsPlanarCentroid(t *testing.T) { diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index bba2f380..8e7d88c3 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -4,7 +4,7 @@ import ( "errors" "slices" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) var ( diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index c40579df..424a4821 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -3,7 +3,7 @@ package s2 import ( "testing" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) const ( diff --git a/s2/contains_point_query_test.go b/s2/contains_point_query_test.go index ad45eefc..7a1ce04e 100644 --- a/s2/contains_point_query_test.go +++ b/s2/contains_point_query_test.go @@ -18,7 +18,7 @@ import ( "reflect" "testing" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) func TestContainsPointQueryVertexModelOpen(t *testing.T) { diff --git a/s2/contains_vertex_query_test.go b/s2/contains_vertex_query_test.go index 2e5079a5..c412f274 100644 --- a/s2/contains_vertex_query_test.go +++ b/s2/contains_vertex_query_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) func TestContainsVertexQueryUndetermined(t *testing.T) { diff --git a/s2/convex_hull_query.go b/s2/convex_hull_query.go index c6be3ef2..85608663 100644 --- a/s2/convex_hull_query.go +++ b/s2/convex_hull_query.go @@ -17,7 +17,7 @@ package s2 import ( "sort" - "github.com/pavlov061356/geo/r3" + "github.com/golang/geo/geo/r3" ) // ConvexHullQuery builds the convex hull of any collection of points, diff --git a/s2/convex_hull_query_test.go b/s2/convex_hull_query_test.go index 4f066724..3fd128e7 100644 --- a/s2/convex_hull_query_test.go +++ b/s2/convex_hull_query_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) func TestConvexHullQueryNoPoints(t *testing.T) { diff --git a/s2/crossing_edge_query.go b/s2/crossing_edge_query.go index d34573b2..553da30d 100644 --- a/s2/crossing_edge_query.go +++ b/s2/crossing_edge_query.go @@ -17,7 +17,7 @@ package s2 import ( "sort" - "github.com/pavlov061356/geo/r2" + "github.com/golang/geo/geo/r2" ) // CrossingEdgeQuery is used to find the Edge IDs of Shapes that are crossed by diff --git a/s2/crossing_edge_query_test.go b/s2/crossing_edge_query_test.go index e95aa680..247428ee 100644 --- a/s2/crossing_edge_query_test.go +++ b/s2/crossing_edge_query_test.go @@ -19,7 +19,7 @@ import ( "reflect" "testing" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) func perturbAtDistance(distance s1.Angle, a0, b0 Point) Point { diff --git a/s2/distance_target.go b/s2/distance_target.go index bc86cf09..6a3a42a1 100644 --- a/s2/distance_target.go +++ b/s2/distance_target.go @@ -15,7 +15,7 @@ package s2 import ( - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) // The distance interface represents a set of common methods used by algorithms diff --git a/s2/edge_clipping.go b/s2/edge_clipping.go index c8dbf105..a76c1b92 100644 --- a/s2/edge_clipping.go +++ b/s2/edge_clipping.go @@ -27,9 +27,9 @@ package s2 import ( "math" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/r2" - "github.com/pavlov061356/geo/r3" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/r2" + "github.com/golang/geo/geo/r3" ) const ( diff --git a/s2/edge_clipping_test.go b/s2/edge_clipping_test.go index 8fe75d42..3389ab59 100644 --- a/s2/edge_clipping_test.go +++ b/s2/edge_clipping_test.go @@ -19,10 +19,10 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/r2" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/r2" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) func TestEdgeClippingIntersectsFace(t *testing.T) { diff --git a/s2/edge_crosser_test.go b/s2/edge_crosser_test.go index 0495c2f2..0c6dae22 100644 --- a/s2/edge_crosser_test.go +++ b/s2/edge_crosser_test.go @@ -19,7 +19,7 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/r3" + "github.com/golang/geo/geo/r3" ) func TestEdgeCrosserCrossings(t *testing.T) { diff --git a/s2/edge_crossings.go b/s2/edge_crossings.go index b9f2b30d..8de60033 100644 --- a/s2/edge_crossings.go +++ b/s2/edge_crossings.go @@ -18,8 +18,8 @@ import ( "fmt" "math" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) const ( diff --git a/s2/edge_crossings_test.go b/s2/edge_crossings_test.go index 2a81bd25..49f946ad 100644 --- a/s2/edge_crossings_test.go +++ b/s2/edge_crossings_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) // The various Crossing methods are tested via s2edge_crosser_test diff --git a/s2/edge_distances.go b/s2/edge_distances.go index 8144d21b..17c8e46b 100644 --- a/s2/edge_distances.go +++ b/s2/edge_distances.go @@ -20,7 +20,7 @@ package s2 import ( "math" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) // DistanceFromSegment returns the distance of point X from line segment AB. diff --git a/s2/edge_distances_test.go b/s2/edge_distances_test.go index 271d8000..94125744 100644 --- a/s2/edge_distances_test.go +++ b/s2/edge_distances_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) func TestEdgeDistancesCheckDistance(t *testing.T) { diff --git a/s2/edge_query.go b/s2/edge_query.go index 01043ac1..27388942 100644 --- a/s2/edge_query.go +++ b/s2/edge_query.go @@ -17,7 +17,7 @@ package s2 import ( "sort" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) // EdgeQueryOptions holds the options for controlling how EdgeQuery operates. diff --git a/s2/edge_query_closest_test.go b/s2/edge_query_closest_test.go index 04f0a1ff..cdd11cb9 100644 --- a/s2/edge_query_closest_test.go +++ b/s2/edge_query_closest_test.go @@ -18,8 +18,8 @@ import ( "fmt" "testing" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) func TestClosestEdgeQueryNoEdges(t *testing.T) { diff --git a/s2/edge_query_furthest_test.go b/s2/edge_query_furthest_test.go index e5f21864..c7b2c4cf 100644 --- a/s2/edge_query_furthest_test.go +++ b/s2/edge_query_furthest_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) func TestFurthestEdgeQueryNoEdges(t *testing.T) { diff --git a/s2/edge_query_test.go b/s2/edge_query_test.go index 60bcb3c4..51e061f1 100644 --- a/s2/edge_query_test.go +++ b/s2/edge_query_test.go @@ -22,7 +22,7 @@ import ( "reflect" "testing" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) var ( diff --git a/s2/edge_tessellator.go b/s2/edge_tessellator.go index ea6aa0b9..120cf759 100644 --- a/s2/edge_tessellator.go +++ b/s2/edge_tessellator.go @@ -15,8 +15,8 @@ package s2 import ( - "github.com/pavlov061356/geo/r2" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r2" + "github.com/golang/geo/geo/s1" ) // Tessellation is implemented by subdividing the edge until the estimated diff --git a/s2/edge_tessellator_test.go b/s2/edge_tessellator_test.go index e6e7e2c4..bb634576 100644 --- a/s2/edge_tessellator_test.go +++ b/s2/edge_tessellator_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/r2" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r2" + "github.com/golang/geo/geo/s1" ) func TestEdgeTessellatorProjectedNoTessellation(t *testing.T) { diff --git a/s2/encode_test.go b/s2/encode_test.go index c16f4375..37e0308d 100644 --- a/s2/encode_test.go +++ b/s2/encode_test.go @@ -23,7 +23,7 @@ import ( "reflect" "testing" - "github.com/pavlov061356/geo/r3" + "github.com/golang/geo/geo/r3" ) type encodableRegion interface { diff --git a/s2/example_test.go b/s2/example_test.go index c646c55c..fceb4fad 100644 --- a/s2/example_test.go +++ b/s2/example_test.go @@ -17,8 +17,8 @@ package s2_test import ( "fmt" - "github.com/pavlov061356/geo/s1" - "github.com/pavlov061356/geo/s2" + "github.com/golang/geo/geo/s1" + "github.com/golang/geo/geo/s2" ) func ExampleRect_DistanceToLatLng() { diff --git a/s2/latlng.go b/s2/latlng.go index 94c15e67..96841969 100644 --- a/s2/latlng.go +++ b/s2/latlng.go @@ -18,8 +18,8 @@ import ( "fmt" "math" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) const ( diff --git a/s2/latlng_test.go b/s2/latlng_test.go index f2d83efe..e34a59dd 100644 --- a/s2/latlng_test.go +++ b/s2/latlng_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) func TestLatLngNormalized(t *testing.T) { diff --git a/s2/loop.go b/s2/loop.go index 39c9178a..0d353612 100644 --- a/s2/loop.go +++ b/s2/loop.go @@ -19,9 +19,9 @@ import ( "io" "math" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) // Loop represents a simple spherical polygon. It consists of a sequence diff --git a/s2/loop_test.go b/s2/loop_test.go index 2a7462b9..b5715b7b 100644 --- a/s2/loop_test.go +++ b/s2/loop_test.go @@ -19,9 +19,9 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) var ( diff --git a/s2/matrix3x3.go b/s2/matrix3x3.go index 485d39f3..f3cd2786 100644 --- a/s2/matrix3x3.go +++ b/s2/matrix3x3.go @@ -17,7 +17,7 @@ package s2 import ( "fmt" - "github.com/pavlov061356/geo/r3" + "github.com/golang/geo/geo/r3" ) // matrix3x3 represents a traditional 3x3 matrix of floating point values. diff --git a/s2/matrix3x3_test.go b/s2/matrix3x3_test.go index f49806f5..b8f100dc 100644 --- a/s2/matrix3x3_test.go +++ b/s2/matrix3x3_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/r3" + "github.com/golang/geo/geo/r3" ) func TestCol(t *testing.T) { diff --git a/s2/max_distance_targets.go b/s2/max_distance_targets.go index d59df96c..9b77741f 100644 --- a/s2/max_distance_targets.go +++ b/s2/max_distance_targets.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) // maxDistance implements distance as the supplementary distance (Pi - x) to find diff --git a/s2/max_distance_targets_test.go b/s2/max_distance_targets_test.go index ed65ea28..f3106091 100644 --- a/s2/max_distance_targets_test.go +++ b/s2/max_distance_targets_test.go @@ -19,7 +19,7 @@ import ( "sort" "testing" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) func TestDistanceTargetMaxCellTargetCapBound(t *testing.T) { diff --git a/s2/min_distance_targets.go b/s2/min_distance_targets.go index 3afe0fcd..dce09d44 100644 --- a/s2/min_distance_targets.go +++ b/s2/min_distance_targets.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) // minDistance implements distance interface to find closest distance types. diff --git a/s2/paddedcell.go b/s2/paddedcell.go index 20e7b486..10a694e8 100644 --- a/s2/paddedcell.go +++ b/s2/paddedcell.go @@ -15,8 +15,8 @@ package s2 import ( - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/r2" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/r2" ) // PaddedCell represents a Cell whose (u,v)-range has been expanded on diff --git a/s2/paddedcell_test.go b/s2/paddedcell_test.go index a7017be3..8cbfdd34 100644 --- a/s2/paddedcell_test.go +++ b/s2/paddedcell_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/r2" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/r2" ) func TestPaddedCellMethods(t *testing.T) { diff --git a/s2/point.go b/s2/point.go index 5a9afa08..d8a03c04 100644 --- a/s2/point.go +++ b/s2/point.go @@ -20,8 +20,8 @@ import ( "math" "sort" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) // Point represents a point on the unit sphere as a normalized 3D vector. diff --git a/s2/point_measures.go b/s2/point_measures.go index 076697b7..8a2f9db4 100644 --- a/s2/point_measures.go +++ b/s2/point_measures.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) // PointArea returns the area of triangle ABC. This method combines two different diff --git a/s2/point_measures_test.go b/s2/point_measures_test.go index ef2575f0..3530812b 100644 --- a/s2/point_measures_test.go +++ b/s2/point_measures_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) var ( diff --git a/s2/point_test.go b/s2/point_test.go index 5a9c60d1..521915e8 100644 --- a/s2/point_test.go +++ b/s2/point_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) // pointNear reports if each component of the two points is within the given epsilon. diff --git a/s2/pointcompression.go b/s2/pointcompression.go index fed4051f..9fd25e16 100644 --- a/s2/pointcompression.go +++ b/s2/pointcompression.go @@ -18,7 +18,7 @@ import ( "errors" "fmt" - "github.com/pavlov061356/geo/r3" + "github.com/golang/geo/geo/r3" ) // maxEncodedVertices is the maximum number of vertices, in a row, to be encoded or decoded. diff --git a/s2/polygon_test.go b/s2/polygon_test.go index 2854471b..085a2311 100644 --- a/s2/polygon_test.go +++ b/s2/polygon_test.go @@ -19,7 +19,7 @@ import ( "math/rand" "testing" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) const ( diff --git a/s2/polyline.go b/s2/polyline.go index ce1a0e1e..e41a2b24 100644 --- a/s2/polyline.go +++ b/s2/polyline.go @@ -19,7 +19,7 @@ import ( "io" "math" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) // Polyline represents a sequence of zero or more vertices connected by diff --git a/s2/polyline_measures.go b/s2/polyline_measures.go index 19f60173..7c4829a9 100644 --- a/s2/polyline_measures.go +++ b/s2/polyline_measures.go @@ -19,8 +19,8 @@ package s2 // implement the methods in various other measures files. import ( - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) // polylineLength returns the length of the given Polyline. diff --git a/s2/polyline_test.go b/s2/polyline_test.go index 490a2ede..3e8c376b 100644 --- a/s2/polyline_test.go +++ b/s2/polyline_test.go @@ -19,8 +19,8 @@ import ( "reflect" "testing" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) func TestPolylineBasics(t *testing.T) { diff --git a/s2/predicates.go b/s2/predicates.go index b44af6bd..de907328 100644 --- a/s2/predicates.go +++ b/s2/predicates.go @@ -27,8 +27,8 @@ import ( "math" "math/big" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) const ( diff --git a/s2/predicates_test.go b/s2/predicates_test.go index adc3879d..ce7c87a1 100644 --- a/s2/predicates_test.go +++ b/s2/predicates_test.go @@ -20,8 +20,8 @@ import ( "math/big" "testing" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) func TestPredicatesEpsilonForDigits(t *testing.T) { diff --git a/s2/projections.go b/s2/projections.go index 5eeb3c28..7b7e5559 100644 --- a/s2/projections.go +++ b/s2/projections.go @@ -17,8 +17,8 @@ package s2 import ( "math" - "github.com/pavlov061356/geo/r2" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r2" + "github.com/golang/geo/geo/s1" ) // Projection defines an interface for different ways of mapping between s2 and r2 Points. diff --git a/s2/projections_test.go b/s2/projections_test.go index 959477a0..4645c126 100644 --- a/s2/projections_test.go +++ b/s2/projections_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/r2" - "github.com/pavlov061356/geo/r3" + "github.com/golang/geo/geo/r2" + "github.com/golang/geo/geo/r3" ) func TestPlateCarreeProjectionInterpolate(t *testing.T) { diff --git a/s2/query_entry_test.go b/s2/query_entry_test.go index f5e1505d..38e48e6a 100644 --- a/s2/query_entry_test.go +++ b/s2/query_entry_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) func TestQueryQueueEntry(t *testing.T) { diff --git a/s2/query_options.go b/s2/query_options.go index 5610546d..221d98a9 100644 --- a/s2/query_options.go +++ b/s2/query_options.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) const maxQueryResults = math.MaxInt32 diff --git a/s2/rect.go b/s2/rect.go index c1e528fc..aa981695 100644 --- a/s2/rect.go +++ b/s2/rect.go @@ -19,9 +19,9 @@ import ( "io" "math" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) // Rect represents a closed latitude-longitude rectangle. diff --git a/s2/rect_bounder.go b/s2/rect_bounder.go index b6b098b6..a177c528 100644 --- a/s2/rect_bounder.go +++ b/s2/rect_bounder.go @@ -17,9 +17,9 @@ package s2 import ( "math" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) // RectBounder is used to compute a bounding rectangle that contains all edges diff --git a/s2/rect_bounder_test.go b/s2/rect_bounder_test.go index ed79feb2..fcf7b92c 100644 --- a/s2/rect_bounder_test.go +++ b/s2/rect_bounder_test.go @@ -18,9 +18,9 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) func rectBoundForPoints(a, b Point) Rect { diff --git a/s2/rect_test.go b/s2/rect_test.go index 5c51b561..df289a7e 100644 --- a/s2/rect_test.go +++ b/s2/rect_test.go @@ -18,10 +18,10 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/r2" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/r2" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) func TestRectEmptyAndFull(t *testing.T) { diff --git a/s2/s2_test.go b/s2/s2_test.go index 8a47020e..17633b36 100644 --- a/s2/s2_test.go +++ b/s2/s2_test.go @@ -21,9 +21,9 @@ import ( "math/rand" "os" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/r2" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/r2" + "github.com/golang/geo/geo/s1" ) var ( diff --git a/s2/s2_test_test.go b/s2/s2_test_test.go index 098cc683..afeba895 100644 --- a/s2/s2_test_test.go +++ b/s2/s2_test_test.go @@ -19,7 +19,7 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) func TestKmToAngle(t *testing.T) { diff --git a/s2/s2intersect/s2intersect.go b/s2/s2intersect/s2intersect.go index a842adad..ec87f08c 100644 --- a/s2/s2intersect/s2intersect.go +++ b/s2/s2intersect/s2intersect.go @@ -29,7 +29,7 @@ import ( "fmt" "sort" - "github.com/pavlov061356/geo/s2" + "github.com/golang/geo/geo/s2" ) // Re nomenclature used throughout this file: an "intersection" is a 2D diff --git a/s2/shapeindex.go b/s2/shapeindex.go index d177b431..3dca4a67 100644 --- a/s2/shapeindex.go +++ b/s2/shapeindex.go @@ -20,8 +20,8 @@ import ( "sync" "sync/atomic" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/r2" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/r2" ) // CellRelation describes the possible relationships between a target cell diff --git a/s2/shapeindex_test.go b/s2/shapeindex_test.go index 227ff092..9f21f373 100644 --- a/s2/shapeindex_test.go +++ b/s2/shapeindex_test.go @@ -17,8 +17,8 @@ package s2 import ( "testing" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) func TestShapeIndexBasics(t *testing.T) { diff --git a/s2/shapeutil_test.go b/s2/shapeutil_test.go index 8c191fc6..b96f43cc 100644 --- a/s2/shapeutil_test.go +++ b/s2/shapeutil_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/s1" ) func TestShapeutilContainsBruteForceNoInterior(t *testing.T) { diff --git a/s2/stuv.go b/s2/stuv.go index bdcaec7a..7e17a54b 100644 --- a/s2/stuv.go +++ b/s2/stuv.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/pavlov061356/geo/r3" + "github.com/golang/geo/geo/r3" ) // TODO(rsned): Rename this to coords.go to match the C++ diff --git a/s2/stuv_test.go b/s2/stuv_test.go index 69d4ebce..d7194b44 100644 --- a/s2/stuv_test.go +++ b/s2/stuv_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/pavlov061356/geo/r3" + "github.com/golang/geo/geo/r3" ) // TODO(rsned): Rename this file to coords_test.go to match its C++ counterpart. diff --git a/s2/textformat_test.go b/s2/textformat_test.go index 12bc4a0b..fccab6f2 100644 --- a/s2/textformat_test.go +++ b/s2/textformat_test.go @@ -35,7 +35,7 @@ import ( "strconv" "strings" - "github.com/pavlov061356/geo/r3" + "github.com/golang/geo/geo/r3" ) // writePoint formats the point and writes it to the given writer. diff --git a/s2/textformat_test_test.go b/s2/textformat_test_test.go index 17646792..7c8bc670 100644 --- a/s2/textformat_test_test.go +++ b/s2/textformat_test_test.go @@ -18,9 +18,9 @@ import ( "bytes" "testing" - "github.com/pavlov061356/geo/r1" - "github.com/pavlov061356/geo/r3" - "github.com/pavlov061356/geo/s1" + "github.com/golang/geo/geo/r1" + "github.com/golang/geo/geo/r3" + "github.com/golang/geo/geo/s1" ) func TestParseLatLng(t *testing.T) { diff --git a/s2/util.go b/s2/util.go index 1f0619e5..207a35f8 100644 --- a/s2/util.go +++ b/s2/util.go @@ -14,7 +14,7 @@ package s2 -import "github.com/pavlov061356/geo/s1" +import "github.com/golang/geo/geo/s1" // roundAngle returns the value rounded to nearest as an int32. // This does not match C++ exactly for the case of x.5. diff --git a/s2/wedge_relations_test.go b/s2/wedge_relations_test.go index 2f62a61a..ef54da24 100644 --- a/s2/wedge_relations_test.go +++ b/s2/wedge_relations_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/pavlov061356/geo/r3" + "github.com/golang/geo/geo/r3" ) func TestWedgeRelations(t *testing.T) { From 7cfaa88080674e79342ec61b6fdb9619bc33691d Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Fri, 11 Apr 2025 19:09:48 +0300 Subject: [PATCH 43/44] review updates --- s2/chain_interpolation_query.go | 119 +------- s2/chain_interpolation_query_test.go | 424 +-------------------------- 2 files changed, 9 insertions(+), 534 deletions(-) diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 8e7d88c3..7754fb2d 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -144,7 +144,7 @@ func (s ChainInterpolationQuery) AtDistance(inputDistance s1.Angle) (point Point // vertices. edgeID = max(position+s.firstEdgeID-1, 0) edge := s.Shape.Edge(edgeID) - point = GetPointOnLine(edge.V0, edge.V1, inputDistance-s.cumulativeValues[max(0, position-1)]) + point = PointOnLine(edge.V0, edge.V1, inputDistance-s.cumulativeValues[max(0, position-1)]) } return point, edgeID, distance, nil @@ -184,29 +184,6 @@ func (s ChainInterpolationQuery) Slice(beginFraction, endFraction float64) []Poi return points } -// Returns the vector of points that is a slice of the chain from -// beginFraction to endFraction. If beginFraction is greater than -// endFraction, then the points are returned in reverse order. -// -// For example, Slice(0,1) returns the entire chain, Slice(0, 0.5) returns the -// first half of the chain, and Slice(1, 0.5) returns the second half of the -// chain in reverse. -// -// The endpoints of the slice are interpolated (except when coinciding with an -// existing vertex of the chain), and all the internal points are copied from -// the chain as is. -// -// divisions is the number of segments to divide the polyline into. -// divisions must be >= len(Slice(beginFraction, endFraction)). -// -// If the query is either uninitialized, or initialized with a shape -// containing no edges, then an empty vector is returned. -func (s ChainInterpolationQuery) SliceDivided(beginFraction, endFraction float64, divisions int) []PointWithFraction { - points := make([]PointWithFraction, 0, divisions) - s.AddDividedSlice(beginFraction, endFraction, &points, divisions) - return points -} - // Appends the chain slice from beginFraction to endFraction to the given // slice. If beginFraction is greater than endFraction, then the points are // appended in reverse order. If the query is either uninitialized, or @@ -250,100 +227,6 @@ func (s ChainInterpolationQuery) AddSlice(beginFraction, endFraction float64, po } } -type PointWithFraction struct { - Point - Fraction float64 -} - -// Appends the slice from beginFraction to endFraction to the given -// slice. If beginFraction is greater than endFraction, then the points are -// appended in reverse order. If the query is either uninitialized, or -// initialized with a shape containing no edges, then no points are appended. -// divisions is the number of segments to divide the polyline into. -// divisions must be greater or equal of NumEdges of Shape. -// A polyline is divided into segments of equal length, and then edges are added to the slice. -func (s ChainInterpolationQuery) AddDividedSlice(beginFraction, endFraction float64, points *[]PointWithFraction, pointsNum int) { - if len(s.cumulativeValues) == 0 { - return - } - - pointsLength := len(*points) - - reverse := beginFraction > endFraction - if reverse { - // Swap the begin and end fractions so that we can iterate in ascending order. - beginFraction, endFraction = endFraction, beginFraction - } - - atBegin, currentEdgeID, _, err := s.AtFraction(beginFraction) - if err != nil { - return - } - - atEnd, endEdgeID, _, err := s.AtFraction(endFraction) - if err != nil { - return - } - - edgesBetween := s.EdgesBetween(atBegin, atEnd, currentEdgeID, endEdgeID) - - if edgesBetween > pointsNum-2 { - return - } - - *points = (*points)[0:pointsLength] - - // divisionsExcludingEdges := pointsNum - len(slice) - - *points = append(*points, PointWithFraction{Point: atBegin, Fraction: beginFraction}) - - // // Copy the internal points from the chain. - - fraction := beginFraction + (endFraction-beginFraction)/float64(pointsNum-1) - for pointsLength := 0; pointsLength < pointsNum-2; fraction += (endFraction - beginFraction) / float64(pointsNum-1) { - atFraction, edgeID, _, err := s.AtFraction(fraction) - if err != nil { - return - } - - // If the current edge is the same as the previous edge, then skip it. - // Otherwise, append all edges in between. - if currentEdgeID != edgeID { - for i := currentEdgeID + 1; i <= edgeID; i++ { - edge := s.Shape.Edge(i) - if edge.V0 != atFraction { - pointsLength++ - total, err := s.GetLength() - if err != nil { - return - } - if total == 0 { - return - } - - *points = append(*points, PointWithFraction{Point: edge.V0, Fraction: s.cumulativeValues[i].Radians() / total.Radians()}) - } - } - currentEdgeID = edgeID - continue - } else if edge := s.Shape.Edge(edgeID); edge.V1.approxEqual(atFraction, epsilon) { - pointsLength++ - *points = append(*points, PointWithFraction{Point: edge.V1, Fraction: fraction}) - currentEdgeID++ - continue - } - - pointsLength++ - *points = append(*points, PointWithFraction{Point: atFraction, Fraction: fraction}) - } - *points = append(*points, PointWithFraction{Point: atEnd, Fraction: endFraction}) - - // Reverse the slice if necessary. - if reverse { - slices.Reverse(*points) - } -} - func (s ChainInterpolationQuery) EdgesBetween(begin, end Point, beginEdgeID, endEdgeID int) int { if end == begin { return 0 diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 424a4821..37e02965 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -289,7 +289,7 @@ func TestChains(t *testing.T) { } func TestGetLengthAtEdgeEmpty(t *testing.T) { - query := InitChainInterpolationQuery(&laxPolyline{}, 0) + query := InitChainInterpolationQuery(&LaxPolyline{}, 0) angle, err := query.GetLengthAtEdgeEnd(0) @@ -309,7 +309,7 @@ func TestGetLengthAtEdgePolyline(t *testing.T) { PointFromLatLng(LatLngFromDegrees(0, 6)), } - polyline := laxPolyline{points} + polyline := LaxPolyline{points} query := InitChainInterpolationQuery(&polyline, 0) @@ -341,7 +341,7 @@ func TestGetLengthAtEdgePolyline(t *testing.T) { } } func TestGetLengthAtEdgePolygon(t *testing.T) { - polygon := laxPolygonFromPoints([][]Point{ + polygon := LaxPolygonFromPoints([][]Point{ { PointFromLatLng(LatLngFromDegrees(1, 1)), PointFromLatLng(LatLngFromDegrees(2, 1)), @@ -555,7 +555,7 @@ func TestSlice(t *testing.T) { shape Shape startSliceFraction float64 endSliceFraction float64 - }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 0, 1}, + }{LaxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 0, 1}, want: `0:0, 0:1, 0:2`, }, { @@ -564,7 +564,7 @@ func TestSlice(t *testing.T) { shape Shape startSliceFraction float64 endSliceFraction float64 - }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 0, 0.5}, + }{LaxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 0, 0.5}, want: `0:0, 0:1`, }, { @@ -573,7 +573,7 @@ func TestSlice(t *testing.T) { shape Shape startSliceFraction float64 endSliceFraction float64 - }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 1, 0.5}, + }{LaxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 1, 0.5}, want: `0:2, 0:1`, }, { @@ -582,423 +582,15 @@ func TestSlice(t *testing.T) { shape Shape startSliceFraction float64 endSliceFraction float64 - }{laxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 0.25, 0.75}, + }{LaxPolylineFromPoints(parsePoints(`0:0, 0:1, 0:2`)), 0.25, 0.75}, want: `0:0.5, 0:1, 0:1.5`, }, } for _, test := range tests { query := InitChainInterpolationQuery(test.args.shape, 0) - if got := pointsToString(query.Slice(test.args.startSliceFraction, test.args.endSliceFraction)); got != test.want { + if got := pointsToString(query.Slice(test.args.startSliceFraction, test.args.endSliceFraction), false); got != test.want { t.Errorf("%v: got %v, want %v", test.name, got, test.want) } } } - -func TestSliceDivided(t *testing.T) { - type args struct { - shape Shape - startSliceFraction float64 - endSliceFraction float64 - divisions int - } - tests := []struct { - name string - args args - want string - }{ - { - name: "empty shape", - args: args{nil, 0, 1., 1}, - want: ``, - }, - {name: "full polyline", args: args{ - shape: laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), - startSliceFraction: 0, - endSliceFraction: 1, - divisions: 3, - }, want: `0:0, 0:1, 0:2`}, - { - name: "first half of polyline", - args: args{ - shape: laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), - startSliceFraction: 0, - endSliceFraction: 0.5, - divisions: 2, - }, - want: `0:0, 0:1`, - }, - { - name: "second half of polyline", - args: args{ - shape: laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), - startSliceFraction: 1, - endSliceFraction: 0.5, - divisions: 2, - }, - want: `0:2, 0:1`, - }, - { - name: "middle of polyline", - args: args{ - shape: laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), - startSliceFraction: 0.25, - endSliceFraction: 0.75, - divisions: 3, - }, - want: `0:0.5, 0:1, 0:1.5`, - }, - { - name: "middle of polyline; divisions = 5", - args: args{ - shape: laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), - startSliceFraction: 0.25, - endSliceFraction: 0.75, - divisions: 5, - }, - want: `0:0.5, 0:0.75, 0:1, 0:1.25, 0:1.5`, - }, - { - name: "middle of polyline; divisions = 11", - args: args{ - shape: laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), - startSliceFraction: 0.25, - endSliceFraction: 0.75, - divisions: 11, - }, - want: `0:0.5, 0:0.6, 0:0.7, 0:0.8, 0:0.9, 0:1, 0:1.1, 0:1.2, 0:1.3, 0:1.4, 0:1.5`, - }, - { - name: "corner case: divisions = s.NumEdges()+1", - args: args{ - shape: laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), - startSliceFraction: 0.3, - endSliceFraction: 0.6, - divisions: 4, - }, - want: `0:0.6, 0:0.8, 0:1, 0:1.2`, - }, - { - name: "divisions = s.NumEdges()+2", - args: args{ - shape: laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), - startSliceFraction: 0.25, - endSliceFraction: 0.75, - divisions: 5, - }, - want: `0:0.5, 0:0.75, 0:1, 0:1.25, 0:1.5`, - }, - { - name: "divisions = s.NumEdges()+3", - args: args{ - shape: laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), - startSliceFraction: 0.45, - endSliceFraction: 0.75, - divisions: 6, - }, - want: `0:0.9, 0:1, 0:1.14, 0:1.26, 0:1.38, 0:1.5`, - }, - { - name: "divisions = s.NumEdges()+10", - args: args{ - shape: laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), - startSliceFraction: 0.105, - endSliceFraction: 0.605, - divisions: 11, - }, - want: `0:0.21, 0:0.31, 0:0.41, 0:0.51, 0:0.61, 0:0.71, 0:0.81, 0:0.91, 0:1, 0:1.11, 0:1.21`, - }, - { - name: "divisions = 10, 0 edges inside resulting points", - args: args{ - shape: laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), - startSliceFraction: 0.05, - endSliceFraction: 0.1, - divisions: 11, - }, - want: `0:0.1, 0:0.11, 0:0.12, 0:0.13, 0:0.14, 0:0.15, 0:0.16, 0:0.17, 0:0.18, 0:0.19, 0:0.2`, - }, - { - name: "divisions = s.NumEdges()+1", - args: args{ - shape: laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - PointFromLatLng(LatLngFromDegrees(0, 3)), - PointFromLatLng(LatLngFromDegrees(0, 4)), - PointFromLatLng(LatLngFromDegrees(0, 5)), - }, - ), - startSliceFraction: 0.3, - endSliceFraction: 0.84, - divisions: 5, - }, - want: `0:1.5, 0:2, 0:3, 0:4, 0:4.2`, - }, - { - name: "divisions = s.NumEdges()+1", - args: args{ - shape: laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), - startSliceFraction: 0.3, - endSliceFraction: 0.99999999999995, - divisions: 3, - }, - want: `0:0.6, 0:1, 0:1.9999999999999`, - }, - } - - for _, test := range tests { - query := InitChainInterpolationQuery(test.args.shape, 0) - got := query.SliceDivided( - test.args.startSliceFraction, - test.args.endSliceFraction, - test.args.divisions, - ) - want := parsePoints(test.want) - if len(got) != test.args.divisions && len(got) != len(want) { - t.Errorf("length mismatch: got %d, want %d", len(got), test.args.divisions) - } - points := make([]Point, len(got)) - for i := range got { - points[i] = got[i].Point - } - for i := 1; i < len(got)-1; i++ { - if got[i-1].Fraction > got[i].Fraction { - t.Errorf("Fraction of i is less than i-1; i: %v; got: %v\n", i, got) - } - - } - if !pointSlicesApproxEqual(points, want, kEpsilon) { - t.Errorf("%v: got %v, want %v", test.name, got, want) - } - - // prev := LatLngFromPoint(got[i-1].Point) - // curr := LatLngFromPoint(got[i].Point) - - // if curr.Lng < prev.Lng { - // t.Errorf("%v: got %v, want %v", test.name, got, want) - // } - } -} - -// goos: linux -// goarch: amd64 -// pkg: github.com/mrosminin/tf-geo/s2 -// cpu: AMD Ryzen 5 5600G with Radeon Graphics -// === RUN Benchmark_SliceDivided -// Benchmark_SliceDivided -// Benchmark_SliceDivided-12 8101 128452 ns/op 24577 B/op 2 allocs/op - -func Benchmark_SliceDivided(b *testing.B) { - chainInterpolationQuery := InitChainInterpolationQuery( - laxPolylineFromPoints( - []Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), - 0, - ) - b.ResetTimer() - - for i := 0; i < b.N; i++ { - chainInterpolationQuery.SliceDivided(0.3, 0.84, 500) - } - - b.StopTimer() - - points := make([]Point, 500) - - for i := 0; i < 100; i++ { - points[i] = PointFromLatLng(LatLngFromDegrees(0, float64(i))) - } - - chainInterpolationQuery = InitChainInterpolationQuery( - laxPolylineFromPoints( - points, - ), - 0, - ) - - b.StartTimer() - for i := 0; i < b.N; i++ { - chainInterpolationQuery.SliceDivided(0.3, 0.84, 500) - } -} - -func TestChainInterpolationQuery_EdgesBetween(t *testing.T) { - query := InitChainInterpolationQuery(laxPolylineFromPoints([]Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - PointFromLatLng(LatLngFromDegrees(0, 3)), - PointFromLatLng(LatLngFromDegrees(0, 4)), - PointFromLatLng(LatLngFromDegrees(0, 5)), - }, - ), 0) - type args struct { - beginFraction float64 - endFraction float64 - } - tests := []struct { - name string - args args - want int - }{ - { - name: "beginFraction = 0, endFraction = 0.5", - args: args{beginFraction: 0, endFraction: 0.5}, - want: 2, - }, - { - name: "beginFraction = 0, endFraction = 0.8", - args: args{beginFraction: 0, endFraction: 0.8}, - want: 3, - }, - { - name: "beginFraction = 0, endFraction = 0.85", - args: args{beginFraction: 0, endFraction: 0.85}, - want: 4, - }, - { - name: "beginFraction = 0, endFraction = 1", - args: args{beginFraction: 0, endFraction: 1}, - want: 4, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - atBegin, beginEdgeID, _, err := query.AtFraction(tt.args.beginFraction) - if err != nil { - t.Errorf("ChainInterpolationQuery.AtFraction() error = %v", err) - } - - atEnd, endEdgeID, _, err := query.AtFraction(tt.args.endFraction) - if err != nil { - t.Errorf("ChainInterpolationQuery.AtFraction() error = %v", err) - } - if got := query.EdgesBetween(atBegin, atEnd, beginEdgeID, endEdgeID); got != tt.want { - t.Errorf("ChainInterpolationQuery.EdgesBetween() = %v, want %v", got, tt.want) - } - }) - } -} - -func Benchmark_InitChinInterpolationQuery(b *testing.B) { - points := make([]Point, 0, b.N) - for i := 0; i < b.N; i++ { - points = append(points, PointFromLatLng(LatLngFromDegrees(0, float64(i)))) - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - InitChainInterpolationQuery(laxPolylineFromPoints(points), 0) - } -} - -// goos: linux -// goarch: amd64 -// pkg: github.com/mrosminin/tf-geo/s2 -// cpu: AMD Ryzen 7 5800H with Radeon Graphics -// === RUN Benchmark_Slice -// Benchmark_Slice -// Benchmark_Slice-16 303748 3403 ns/op 3216 B/op 10 allocs/op -func Benchmark_Slice(b *testing.B) { - chainInterpolationQuery := InitChainInterpolationQuery( - laxPolylineFromPoints( - []Point{ - PointFromLatLng(LatLngFromDegrees(0, 0)), - PointFromLatLng(LatLngFromDegrees(0, 1)), - PointFromLatLng(LatLngFromDegrees(0, 2)), - }, - ), - 0, - ) - b.ResetTimer() - - for i := 0; i < b.N; i++ { - chainInterpolationQuery.Slice(0.3, 0.84) - } - - b.StopTimer() - - points := make([]Point, 500) - - for i := 0; i < 100; i++ { - points[i] = PointFromLatLng(LatLngFromDegrees(0, float64(i))) - } - - chainInterpolationQuery = InitChainInterpolationQuery( - laxPolylineFromPoints( - points, - ), - 0, - ) - - b.StartTimer() - for i := 0; i < b.N; i++ { - chainInterpolationQuery.Slice(0.3, 0.84) - } -} From b68cc2398576151526dec35b225be6acd879bac7 Mon Sep 17 00:00:00 2001 From: Pavlov Vladimir Date: Fri, 11 Apr 2025 19:11:01 +0300 Subject: [PATCH 44/44] package name fix --- go.mod | 2 +- r2/rect.go | 2 +- r2/rect_test.go | 2 +- r3/vector.go | 2 +- s1/example_test.go | 2 +- s2/builder_snapper.go | 2 +- s2/builder_snapper_test.go | 2 +- s2/cap.go | 4 ++-- s2/cap_test.go | 4 ++-- s2/cell.go | 8 ++++---- s2/cell_test.go | 4 ++-- s2/cellid.go | 8 ++++---- s2/cellid_test.go | 4 ++-- s2/cellunion.go | 2 +- s2/cellunion_test.go | 4 ++-- s2/centroids.go | 2 +- s2/centroids_test.go | 2 +- s2/chain_interpolation_query.go | 2 +- s2/chain_interpolation_query_test.go | 2 +- s2/contains_point_query_test.go | 2 +- s2/contains_vertex_query_test.go | 2 +- s2/convex_hull_query.go | 2 +- s2/convex_hull_query_test.go | 2 +- s2/crossing_edge_query.go | 2 +- s2/crossing_edge_query_test.go | 2 +- s2/distance_target.go | 2 +- s2/edge_clipping.go | 6 +++--- s2/edge_clipping_test.go | 8 ++++---- s2/edge_crosser_test.go | 2 +- s2/edge_crossings.go | 4 ++-- s2/edge_crossings_test.go | 2 +- s2/edge_distances.go | 2 +- s2/edge_distances_test.go | 4 ++-- s2/edge_query.go | 2 +- s2/edge_query_closest_test.go | 4 ++-- s2/edge_query_furthest_test.go | 2 +- s2/edge_query_test.go | 2 +- s2/edge_tessellator.go | 4 ++-- s2/edge_tessellator_test.go | 4 ++-- s2/encode_test.go | 2 +- s2/example_test.go | 4 ++-- s2/latlng.go | 4 ++-- s2/latlng_test.go | 2 +- s2/loop.go | 6 +++--- s2/loop_test.go | 6 +++--- s2/matrix3x3.go | 2 +- s2/matrix3x3_test.go | 2 +- s2/max_distance_targets.go | 2 +- s2/max_distance_targets_test.go | 2 +- s2/min_distance_targets.go | 2 +- s2/paddedcell.go | 4 ++-- s2/paddedcell_test.go | 4 ++-- s2/point.go | 4 ++-- s2/point_measures.go | 2 +- s2/point_measures_test.go | 4 ++-- s2/point_test.go | 4 ++-- s2/pointcompression.go | 2 +- s2/polygon_test.go | 2 +- s2/polyline.go | 2 +- s2/polyline_measures.go | 4 ++-- s2/polyline_test.go | 4 ++-- s2/predicates.go | 4 ++-- s2/predicates_test.go | 4 ++-- s2/projections.go | 4 ++-- s2/projections_test.go | 4 ++-- s2/query_entry_test.go | 2 +- s2/query_options.go | 2 +- s2/rect.go | 6 +++--- s2/rect_bounder.go | 6 +++--- s2/rect_bounder_test.go | 6 +++--- s2/rect_test.go | 8 ++++---- s2/s2_test.go | 6 +++--- s2/s2_test_test.go | 2 +- s2/s2intersect/s2intersect.go | 2 +- s2/shapeindex.go | 4 ++-- s2/shapeindex_test.go | 4 ++-- s2/shapeutil_test.go | 2 +- s2/stuv.go | 2 +- s2/stuv_test.go | 2 +- s2/textformat_test.go | 2 +- s2/textformat_test_test.go | 6 +++--- s2/util.go | 2 +- s2/wedge_relations_test.go | 2 +- 83 files changed, 136 insertions(+), 136 deletions(-) diff --git a/go.mod b/go.mod index a40160ef..d44e04f5 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/golang/geo/geo +module github.com/golang/geo go 1.21 diff --git a/r2/rect.go b/r2/rect.go index ef758f85..495545bb 100644 --- a/r2/rect.go +++ b/r2/rect.go @@ -18,7 +18,7 @@ import ( "fmt" "math" - "github.com/golang/geo/geo/r1" + "github.com/golang/geo/r1" ) // Point represents a point in ℝ². diff --git a/r2/rect_test.go b/r2/rect_test.go index fc282549..7a728f30 100644 --- a/r2/rect_test.go +++ b/r2/rect_test.go @@ -22,7 +22,7 @@ import ( "reflect" "testing" - "github.com/golang/geo/geo/r1" + "github.com/golang/geo/r1" ) var ( diff --git a/r3/vector.go b/r3/vector.go index cac8ac8a..1b6a8f03 100644 --- a/r3/vector.go +++ b/r3/vector.go @@ -18,7 +18,7 @@ import ( "fmt" "math" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) // Vector represents a point in ℝ³. diff --git a/s1/example_test.go b/s1/example_test.go index 01819b3e..7b45b7f0 100644 --- a/s1/example_test.go +++ b/s1/example_test.go @@ -18,7 +18,7 @@ import ( "fmt" "math" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) func ExampleInterval_DirectedHausdorffDistance() { diff --git a/s2/builder_snapper.go b/s2/builder_snapper.go index a859fb3b..18ecba03 100644 --- a/s2/builder_snapper.go +++ b/s2/builder_snapper.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) // A Snapper restricts the locations of the output vertices. For diff --git a/s2/builder_snapper_test.go b/s2/builder_snapper_test.go index 3e902a18..20e618df 100644 --- a/s2/builder_snapper_test.go +++ b/s2/builder_snapper_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) func TestIdentitySnapper(t *testing.T) { diff --git a/s2/cap.go b/s2/cap.go index cc5f1150..9327b6bb 100644 --- a/s2/cap.go +++ b/s2/cap.go @@ -19,8 +19,8 @@ import ( "io" "math" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r1" + "github.com/golang/geo/s1" ) var ( diff --git a/s2/cap_test.go b/s2/cap_test.go index 59cc7b7f..2edd57f8 100644 --- a/s2/cap_test.go +++ b/s2/cap_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) const ( diff --git a/s2/cell.go b/s2/cell.go index cf11d091..77f2e27a 100644 --- a/s2/cell.go +++ b/s2/cell.go @@ -18,10 +18,10 @@ import ( "io" "math" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/r2" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r1" + "github.com/golang/geo/r2" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) // CellBoundary represents canonical identifiers for the boundaries of the cell. diff --git a/s2/cell_test.go b/s2/cell_test.go index f40cbfab..9657e262 100644 --- a/s2/cell_test.go +++ b/s2/cell_test.go @@ -19,8 +19,8 @@ import ( "testing" "unsafe" - "github.com/golang/geo/geo/r2" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r2" + "github.com/golang/geo/s1" ) // maxCellSize is the upper bounds on the number of bytes we want the Cell object to ever be. diff --git a/s2/cellid.go b/s2/cellid.go index c732d657..00ad0322 100644 --- a/s2/cellid.go +++ b/s2/cellid.go @@ -23,10 +23,10 @@ import ( "strconv" "strings" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/r2" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r1" + "github.com/golang/geo/r2" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) // CellID uniquely identifies a cell in the S2 cell decomposition. diff --git a/s2/cellid_test.go b/s2/cellid_test.go index d743d0a2..4a2063b9 100644 --- a/s2/cellid_test.go +++ b/s2/cellid_test.go @@ -19,8 +19,8 @@ import ( "reflect" "testing" - "github.com/golang/geo/geo/r2" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r2" + "github.com/golang/geo/s1" ) func TestCellIDFromFace(t *testing.T) { diff --git a/s2/cellunion.go b/s2/cellunion.go index 7bc3cce5..7737e0b3 100644 --- a/s2/cellunion.go +++ b/s2/cellunion.go @@ -19,7 +19,7 @@ import ( "io" "sort" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) // A CellUnion is a collection of CellIDs. diff --git a/s2/cellunion_test.go b/s2/cellunion_test.go index 4ac9d23b..dac85e4f 100644 --- a/s2/cellunion_test.go +++ b/s2/cellunion_test.go @@ -20,8 +20,8 @@ import ( "reflect" "testing" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r1" + "github.com/golang/geo/s1" ) func TestCellUnionDuplicateCellsNotValid(t *testing.T) { diff --git a/s2/centroids.go b/s2/centroids.go index 52e35bc8..0e4e95cb 100644 --- a/s2/centroids.go +++ b/s2/centroids.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/golang/geo/geo/r3" + "github.com/golang/geo/r3" ) // There are several notions of the "centroid" of a triangle. First, there diff --git a/s2/centroids_test.go b/s2/centroids_test.go index 88e9aa6d..1cf82754 100644 --- a/s2/centroids_test.go +++ b/s2/centroids_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/golang/geo/geo/r3" + "github.com/golang/geo/r3" ) func TestCentroidsPlanarCentroid(t *testing.T) { diff --git a/s2/chain_interpolation_query.go b/s2/chain_interpolation_query.go index 7754fb2d..0c211026 100644 --- a/s2/chain_interpolation_query.go +++ b/s2/chain_interpolation_query.go @@ -4,7 +4,7 @@ import ( "errors" "slices" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) var ( diff --git a/s2/chain_interpolation_query_test.go b/s2/chain_interpolation_query_test.go index 37e02965..a01d2f2b 100644 --- a/s2/chain_interpolation_query_test.go +++ b/s2/chain_interpolation_query_test.go @@ -3,7 +3,7 @@ package s2 import ( "testing" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) const ( diff --git a/s2/contains_point_query_test.go b/s2/contains_point_query_test.go index 7a1ce04e..3080acd1 100644 --- a/s2/contains_point_query_test.go +++ b/s2/contains_point_query_test.go @@ -18,7 +18,7 @@ import ( "reflect" "testing" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) func TestContainsPointQueryVertexModelOpen(t *testing.T) { diff --git a/s2/contains_vertex_query_test.go b/s2/contains_vertex_query_test.go index c412f274..4524b46f 100644 --- a/s2/contains_vertex_query_test.go +++ b/s2/contains_vertex_query_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) func TestContainsVertexQueryUndetermined(t *testing.T) { diff --git a/s2/convex_hull_query.go b/s2/convex_hull_query.go index 85608663..2a87377e 100644 --- a/s2/convex_hull_query.go +++ b/s2/convex_hull_query.go @@ -17,7 +17,7 @@ package s2 import ( "sort" - "github.com/golang/geo/geo/r3" + "github.com/golang/geo/r3" ) // ConvexHullQuery builds the convex hull of any collection of points, diff --git a/s2/convex_hull_query_test.go b/s2/convex_hull_query_test.go index 3fd128e7..14575bf6 100644 --- a/s2/convex_hull_query_test.go +++ b/s2/convex_hull_query_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) func TestConvexHullQueryNoPoints(t *testing.T) { diff --git a/s2/crossing_edge_query.go b/s2/crossing_edge_query.go index 553da30d..bf2dde9c 100644 --- a/s2/crossing_edge_query.go +++ b/s2/crossing_edge_query.go @@ -17,7 +17,7 @@ package s2 import ( "sort" - "github.com/golang/geo/geo/r2" + "github.com/golang/geo/r2" ) // CrossingEdgeQuery is used to find the Edge IDs of Shapes that are crossed by diff --git a/s2/crossing_edge_query_test.go b/s2/crossing_edge_query_test.go index 247428ee..1fadd2f4 100644 --- a/s2/crossing_edge_query_test.go +++ b/s2/crossing_edge_query_test.go @@ -19,7 +19,7 @@ import ( "reflect" "testing" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) func perturbAtDistance(distance s1.Angle, a0, b0 Point) Point { diff --git a/s2/distance_target.go b/s2/distance_target.go index 6a3a42a1..066bbacf 100644 --- a/s2/distance_target.go +++ b/s2/distance_target.go @@ -15,7 +15,7 @@ package s2 import ( - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) // The distance interface represents a set of common methods used by algorithms diff --git a/s2/edge_clipping.go b/s2/edge_clipping.go index a76c1b92..bd84ac6b 100644 --- a/s2/edge_clipping.go +++ b/s2/edge_clipping.go @@ -27,9 +27,9 @@ package s2 import ( "math" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/r2" - "github.com/golang/geo/geo/r3" + "github.com/golang/geo/r1" + "github.com/golang/geo/r2" + "github.com/golang/geo/r3" ) const ( diff --git a/s2/edge_clipping_test.go b/s2/edge_clipping_test.go index 3389ab59..3b530a55 100644 --- a/s2/edge_clipping_test.go +++ b/s2/edge_clipping_test.go @@ -19,10 +19,10 @@ import ( "math" "testing" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/r2" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r1" + "github.com/golang/geo/r2" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) func TestEdgeClippingIntersectsFace(t *testing.T) { diff --git a/s2/edge_crosser_test.go b/s2/edge_crosser_test.go index 0c6dae22..c15f4817 100644 --- a/s2/edge_crosser_test.go +++ b/s2/edge_crosser_test.go @@ -19,7 +19,7 @@ import ( "math" "testing" - "github.com/golang/geo/geo/r3" + "github.com/golang/geo/r3" ) func TestEdgeCrosserCrossings(t *testing.T) { diff --git a/s2/edge_crossings.go b/s2/edge_crossings.go index 8de60033..1d5df4e8 100644 --- a/s2/edge_crossings.go +++ b/s2/edge_crossings.go @@ -18,8 +18,8 @@ import ( "fmt" "math" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) const ( diff --git a/s2/edge_crossings_test.go b/s2/edge_crossings_test.go index 49f946ad..f0ab9f76 100644 --- a/s2/edge_crossings_test.go +++ b/s2/edge_crossings_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) // The various Crossing methods are tested via s2edge_crosser_test diff --git a/s2/edge_distances.go b/s2/edge_distances.go index 17c8e46b..f0ac5398 100644 --- a/s2/edge_distances.go +++ b/s2/edge_distances.go @@ -20,7 +20,7 @@ package s2 import ( "math" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) // DistanceFromSegment returns the distance of point X from line segment AB. diff --git a/s2/edge_distances_test.go b/s2/edge_distances_test.go index 94125744..cadac3f4 100644 --- a/s2/edge_distances_test.go +++ b/s2/edge_distances_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) func TestEdgeDistancesCheckDistance(t *testing.T) { diff --git a/s2/edge_query.go b/s2/edge_query.go index 27388942..34bcb79e 100644 --- a/s2/edge_query.go +++ b/s2/edge_query.go @@ -17,7 +17,7 @@ package s2 import ( "sort" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) // EdgeQueryOptions holds the options for controlling how EdgeQuery operates. diff --git a/s2/edge_query_closest_test.go b/s2/edge_query_closest_test.go index cdd11cb9..a376290d 100644 --- a/s2/edge_query_closest_test.go +++ b/s2/edge_query_closest_test.go @@ -18,8 +18,8 @@ import ( "fmt" "testing" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) func TestClosestEdgeQueryNoEdges(t *testing.T) { diff --git a/s2/edge_query_furthest_test.go b/s2/edge_query_furthest_test.go index c7b2c4cf..cf899f78 100644 --- a/s2/edge_query_furthest_test.go +++ b/s2/edge_query_furthest_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) func TestFurthestEdgeQueryNoEdges(t *testing.T) { diff --git a/s2/edge_query_test.go b/s2/edge_query_test.go index 51e061f1..fc81c760 100644 --- a/s2/edge_query_test.go +++ b/s2/edge_query_test.go @@ -22,7 +22,7 @@ import ( "reflect" "testing" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) var ( diff --git a/s2/edge_tessellator.go b/s2/edge_tessellator.go index 120cf759..903ad47c 100644 --- a/s2/edge_tessellator.go +++ b/s2/edge_tessellator.go @@ -15,8 +15,8 @@ package s2 import ( - "github.com/golang/geo/geo/r2" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r2" + "github.com/golang/geo/s1" ) // Tessellation is implemented by subdividing the edge until the estimated diff --git a/s2/edge_tessellator_test.go b/s2/edge_tessellator_test.go index bb634576..622158cc 100644 --- a/s2/edge_tessellator_test.go +++ b/s2/edge_tessellator_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/golang/geo/geo/r2" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r2" + "github.com/golang/geo/s1" ) func TestEdgeTessellatorProjectedNoTessellation(t *testing.T) { diff --git a/s2/encode_test.go b/s2/encode_test.go index 37e0308d..9372b49b 100644 --- a/s2/encode_test.go +++ b/s2/encode_test.go @@ -23,7 +23,7 @@ import ( "reflect" "testing" - "github.com/golang/geo/geo/r3" + "github.com/golang/geo/r3" ) type encodableRegion interface { diff --git a/s2/example_test.go b/s2/example_test.go index fceb4fad..a95b8fb1 100644 --- a/s2/example_test.go +++ b/s2/example_test.go @@ -17,8 +17,8 @@ package s2_test import ( "fmt" - "github.com/golang/geo/geo/s1" - "github.com/golang/geo/geo/s2" + "github.com/golang/geo/s1" + "github.com/golang/geo/s2" ) func ExampleRect_DistanceToLatLng() { diff --git a/s2/latlng.go b/s2/latlng.go index 96841969..310c36e9 100644 --- a/s2/latlng.go +++ b/s2/latlng.go @@ -18,8 +18,8 @@ import ( "fmt" "math" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) const ( diff --git a/s2/latlng_test.go b/s2/latlng_test.go index e34a59dd..30353bc3 100644 --- a/s2/latlng_test.go +++ b/s2/latlng_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) func TestLatLngNormalized(t *testing.T) { diff --git a/s2/loop.go b/s2/loop.go index 0d353612..cc14aab4 100644 --- a/s2/loop.go +++ b/s2/loop.go @@ -19,9 +19,9 @@ import ( "io" "math" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) // Loop represents a simple spherical polygon. It consists of a sequence diff --git a/s2/loop_test.go b/s2/loop_test.go index b5715b7b..2094d0d6 100644 --- a/s2/loop_test.go +++ b/s2/loop_test.go @@ -19,9 +19,9 @@ import ( "math" "testing" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) var ( diff --git a/s2/matrix3x3.go b/s2/matrix3x3.go index f3cd2786..5524a0d4 100644 --- a/s2/matrix3x3.go +++ b/s2/matrix3x3.go @@ -17,7 +17,7 @@ package s2 import ( "fmt" - "github.com/golang/geo/geo/r3" + "github.com/golang/geo/r3" ) // matrix3x3 represents a traditional 3x3 matrix of floating point values. diff --git a/s2/matrix3x3_test.go b/s2/matrix3x3_test.go index b8f100dc..0bcfb695 100644 --- a/s2/matrix3x3_test.go +++ b/s2/matrix3x3_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/golang/geo/geo/r3" + "github.com/golang/geo/r3" ) func TestCol(t *testing.T) { diff --git a/s2/max_distance_targets.go b/s2/max_distance_targets.go index 9b77741f..0409145e 100644 --- a/s2/max_distance_targets.go +++ b/s2/max_distance_targets.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) // maxDistance implements distance as the supplementary distance (Pi - x) to find diff --git a/s2/max_distance_targets_test.go b/s2/max_distance_targets_test.go index f3106091..0be05249 100644 --- a/s2/max_distance_targets_test.go +++ b/s2/max_distance_targets_test.go @@ -19,7 +19,7 @@ import ( "sort" "testing" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) func TestDistanceTargetMaxCellTargetCapBound(t *testing.T) { diff --git a/s2/min_distance_targets.go b/s2/min_distance_targets.go index dce09d44..1d2ad37b 100644 --- a/s2/min_distance_targets.go +++ b/s2/min_distance_targets.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) // minDistance implements distance interface to find closest distance types. diff --git a/s2/paddedcell.go b/s2/paddedcell.go index 10a694e8..1fb65666 100644 --- a/s2/paddedcell.go +++ b/s2/paddedcell.go @@ -15,8 +15,8 @@ package s2 import ( - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/r2" + "github.com/golang/geo/r1" + "github.com/golang/geo/r2" ) // PaddedCell represents a Cell whose (u,v)-range has been expanded on diff --git a/s2/paddedcell_test.go b/s2/paddedcell_test.go index 8cbfdd34..4123579c 100644 --- a/s2/paddedcell_test.go +++ b/s2/paddedcell_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/r2" + "github.com/golang/geo/r1" + "github.com/golang/geo/r2" ) func TestPaddedCellMethods(t *testing.T) { diff --git a/s2/point.go b/s2/point.go index d8a03c04..9c0abc06 100644 --- a/s2/point.go +++ b/s2/point.go @@ -20,8 +20,8 @@ import ( "math" "sort" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) // Point represents a point on the unit sphere as a normalized 3D vector. diff --git a/s2/point_measures.go b/s2/point_measures.go index 8a2f9db4..632c47cb 100644 --- a/s2/point_measures.go +++ b/s2/point_measures.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) // PointArea returns the area of triangle ABC. This method combines two different diff --git a/s2/point_measures_test.go b/s2/point_measures_test.go index 3530812b..15007364 100644 --- a/s2/point_measures_test.go +++ b/s2/point_measures_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) var ( diff --git a/s2/point_test.go b/s2/point_test.go index 521915e8..127ee277 100644 --- a/s2/point_test.go +++ b/s2/point_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) // pointNear reports if each component of the two points is within the given epsilon. diff --git a/s2/pointcompression.go b/s2/pointcompression.go index 9fd25e16..2ca7db7d 100644 --- a/s2/pointcompression.go +++ b/s2/pointcompression.go @@ -18,7 +18,7 @@ import ( "errors" "fmt" - "github.com/golang/geo/geo/r3" + "github.com/golang/geo/r3" ) // maxEncodedVertices is the maximum number of vertices, in a row, to be encoded or decoded. diff --git a/s2/polygon_test.go b/s2/polygon_test.go index 085a2311..432a6716 100644 --- a/s2/polygon_test.go +++ b/s2/polygon_test.go @@ -19,7 +19,7 @@ import ( "math/rand" "testing" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) const ( diff --git a/s2/polyline.go b/s2/polyline.go index e41a2b24..25a24027 100644 --- a/s2/polyline.go +++ b/s2/polyline.go @@ -19,7 +19,7 @@ import ( "io" "math" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) // Polyline represents a sequence of zero or more vertices connected by diff --git a/s2/polyline_measures.go b/s2/polyline_measures.go index 7c4829a9..38ce991b 100644 --- a/s2/polyline_measures.go +++ b/s2/polyline_measures.go @@ -19,8 +19,8 @@ package s2 // implement the methods in various other measures files. import ( - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) // polylineLength returns the length of the given Polyline. diff --git a/s2/polyline_test.go b/s2/polyline_test.go index 3e8c376b..c20994c4 100644 --- a/s2/polyline_test.go +++ b/s2/polyline_test.go @@ -19,8 +19,8 @@ import ( "reflect" "testing" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) func TestPolylineBasics(t *testing.T) { diff --git a/s2/predicates.go b/s2/predicates.go index de907328..5f223f67 100644 --- a/s2/predicates.go +++ b/s2/predicates.go @@ -27,8 +27,8 @@ import ( "math" "math/big" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) const ( diff --git a/s2/predicates_test.go b/s2/predicates_test.go index ce7c87a1..19645203 100644 --- a/s2/predicates_test.go +++ b/s2/predicates_test.go @@ -20,8 +20,8 @@ import ( "math/big" "testing" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) func TestPredicatesEpsilonForDigits(t *testing.T) { diff --git a/s2/projections.go b/s2/projections.go index 7b7e5559..ea3ac578 100644 --- a/s2/projections.go +++ b/s2/projections.go @@ -17,8 +17,8 @@ package s2 import ( "math" - "github.com/golang/geo/geo/r2" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r2" + "github.com/golang/geo/s1" ) // Projection defines an interface for different ways of mapping between s2 and r2 Points. diff --git a/s2/projections_test.go b/s2/projections_test.go index 4645c126..7dfe5f7c 100644 --- a/s2/projections_test.go +++ b/s2/projections_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/golang/geo/geo/r2" - "github.com/golang/geo/geo/r3" + "github.com/golang/geo/r2" + "github.com/golang/geo/r3" ) func TestPlateCarreeProjectionInterpolate(t *testing.T) { diff --git a/s2/query_entry_test.go b/s2/query_entry_test.go index 38e48e6a..15d77f50 100644 --- a/s2/query_entry_test.go +++ b/s2/query_entry_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) func TestQueryQueueEntry(t *testing.T) { diff --git a/s2/query_options.go b/s2/query_options.go index 221d98a9..84401d94 100644 --- a/s2/query_options.go +++ b/s2/query_options.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) const maxQueryResults = math.MaxInt32 diff --git a/s2/rect.go b/s2/rect.go index aa981695..58cd62e9 100644 --- a/s2/rect.go +++ b/s2/rect.go @@ -19,9 +19,9 @@ import ( "io" "math" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) // Rect represents a closed latitude-longitude rectangle. diff --git a/s2/rect_bounder.go b/s2/rect_bounder.go index a177c528..7196f3c9 100644 --- a/s2/rect_bounder.go +++ b/s2/rect_bounder.go @@ -17,9 +17,9 @@ package s2 import ( "math" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) // RectBounder is used to compute a bounding rectangle that contains all edges diff --git a/s2/rect_bounder_test.go b/s2/rect_bounder_test.go index fcf7b92c..593060ae 100644 --- a/s2/rect_bounder_test.go +++ b/s2/rect_bounder_test.go @@ -18,9 +18,9 @@ import ( "math" "testing" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) func rectBoundForPoints(a, b Point) Rect { diff --git a/s2/rect_test.go b/s2/rect_test.go index df289a7e..e351f1e3 100644 --- a/s2/rect_test.go +++ b/s2/rect_test.go @@ -18,10 +18,10 @@ import ( "math" "testing" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/r2" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r1" + "github.com/golang/geo/r2" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) func TestRectEmptyAndFull(t *testing.T) { diff --git a/s2/s2_test.go b/s2/s2_test.go index 17633b36..bd3b820a 100644 --- a/s2/s2_test.go +++ b/s2/s2_test.go @@ -21,9 +21,9 @@ import ( "math/rand" "os" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/r2" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r1" + "github.com/golang/geo/r2" + "github.com/golang/geo/s1" ) var ( diff --git a/s2/s2_test_test.go b/s2/s2_test_test.go index afeba895..22bcdcab 100644 --- a/s2/s2_test_test.go +++ b/s2/s2_test_test.go @@ -19,7 +19,7 @@ import ( "math" "testing" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) func TestKmToAngle(t *testing.T) { diff --git a/s2/s2intersect/s2intersect.go b/s2/s2intersect/s2intersect.go index ec87f08c..62ddae33 100644 --- a/s2/s2intersect/s2intersect.go +++ b/s2/s2intersect/s2intersect.go @@ -29,7 +29,7 @@ import ( "fmt" "sort" - "github.com/golang/geo/geo/s2" + "github.com/golang/geo/s2" ) // Re nomenclature used throughout this file: an "intersection" is a 2D diff --git a/s2/shapeindex.go b/s2/shapeindex.go index 3dca4a67..0f038920 100644 --- a/s2/shapeindex.go +++ b/s2/shapeindex.go @@ -20,8 +20,8 @@ import ( "sync" "sync/atomic" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/r2" + "github.com/golang/geo/r1" + "github.com/golang/geo/r2" ) // CellRelation describes the possible relationships between a target cell diff --git a/s2/shapeindex_test.go b/s2/shapeindex_test.go index 9f21f373..1f4f2691 100644 --- a/s2/shapeindex_test.go +++ b/s2/shapeindex_test.go @@ -17,8 +17,8 @@ package s2 import ( "testing" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) func TestShapeIndexBasics(t *testing.T) { diff --git a/s2/shapeutil_test.go b/s2/shapeutil_test.go index b96f43cc..9c1355c2 100644 --- a/s2/shapeutil_test.go +++ b/s2/shapeutil_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/s1" ) func TestShapeutilContainsBruteForceNoInterior(t *testing.T) { diff --git a/s2/stuv.go b/s2/stuv.go index 7e17a54b..7740e739 100644 --- a/s2/stuv.go +++ b/s2/stuv.go @@ -17,7 +17,7 @@ package s2 import ( "math" - "github.com/golang/geo/geo/r3" + "github.com/golang/geo/r3" ) // TODO(rsned): Rename this to coords.go to match the C++ diff --git a/s2/stuv_test.go b/s2/stuv_test.go index d7194b44..46426999 100644 --- a/s2/stuv_test.go +++ b/s2/stuv_test.go @@ -18,7 +18,7 @@ import ( "math" "testing" - "github.com/golang/geo/geo/r3" + "github.com/golang/geo/r3" ) // TODO(rsned): Rename this file to coords_test.go to match its C++ counterpart. diff --git a/s2/textformat_test.go b/s2/textformat_test.go index fccab6f2..7a32be81 100644 --- a/s2/textformat_test.go +++ b/s2/textformat_test.go @@ -35,7 +35,7 @@ import ( "strconv" "strings" - "github.com/golang/geo/geo/r3" + "github.com/golang/geo/r3" ) // writePoint formats the point and writes it to the given writer. diff --git a/s2/textformat_test_test.go b/s2/textformat_test_test.go index 7c8bc670..3b8fa2e3 100644 --- a/s2/textformat_test_test.go +++ b/s2/textformat_test_test.go @@ -18,9 +18,9 @@ import ( "bytes" "testing" - "github.com/golang/geo/geo/r1" - "github.com/golang/geo/geo/r3" - "github.com/golang/geo/geo/s1" + "github.com/golang/geo/r1" + "github.com/golang/geo/r3" + "github.com/golang/geo/s1" ) func TestParseLatLng(t *testing.T) { diff --git a/s2/util.go b/s2/util.go index 207a35f8..7cab746d 100644 --- a/s2/util.go +++ b/s2/util.go @@ -14,7 +14,7 @@ package s2 -import "github.com/golang/geo/geo/s1" +import "github.com/golang/geo/s1" // roundAngle returns the value rounded to nearest as an int32. // This does not match C++ exactly for the case of x.5. diff --git a/s2/wedge_relations_test.go b/s2/wedge_relations_test.go index ef54da24..46bf72f7 100644 --- a/s2/wedge_relations_test.go +++ b/s2/wedge_relations_test.go @@ -17,7 +17,7 @@ package s2 import ( "testing" - "github.com/golang/geo/geo/r3" + "github.com/golang/geo/r3" ) func TestWedgeRelations(t *testing.T) {