Skip to content

Commit df15212

Browse files
authored
EdgeQuery.initCovering: Fix bug causing skipped faces (#247)
Remove extra `break` causing intermediate faces to be skipped sometimes. C++ does not have this `break`: https://github.com/google/s2geometry/blob/98f435222f24cd3865619f2df9e02f630b8d5d53/src/s2/s2closest_edge_query_base.h#L814 Improve test coverage.
1 parent f7ee789 commit df15212

File tree

2 files changed

+97
-3
lines changed

2 files changed

+97
-3
lines changed

s2/edge_query.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,7 @@ func (e *EdgeQuery) initCovering() {
697697
if next.CellID() != last.CellID() {
698698
// The index has at least two cells. Choose a level such that the entire
699699
// index can be spanned with at most 6 cells (if the index spans multiple
700-
// faces) or 4 cells (it the index spans a single face).
700+
// faces) or 4 cells (if the index spans a single face).
701701
level, ok := next.CellID().CommonAncestorLevel(last.CellID())
702702
if !ok {
703703
level = 0
@@ -720,9 +720,7 @@ func (e *EdgeQuery) initCovering() {
720720
cellLast := next.clone()
721721
cellLast.Prev()
722722
e.addInitialRange(cellFirst, cellLast)
723-
break
724723
}
725-
726724
}
727725
e.addInitialRange(next, last)
728726
}

s2/edge_query_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,102 @@ func TestEdgeQuerySortAndUnique(t *testing.T) {
177177
}
178178
}
179179

180+
func TestEdgeQueryOptimized(t *testing.T) {
181+
tests := []struct {
182+
name string
183+
locs []struct{ latitude, longitude float64 }
184+
}{
185+
{
186+
// 0 intermediate faces: first and last handled directly
187+
"faces 0,4",
188+
[]struct{ latitude, longitude float64 }{{20, 20}, {40, -100}},
189+
},
190+
191+
{
192+
"faces 0,4 (3+1 shapes)",
193+
[]struct{ latitude, longitude float64 }{{20, 20}, {25, 25}, {15, 15}, {40, -100}},
194+
},
195+
196+
{
197+
// 1 intermediate face with 1 shape each
198+
"faces 0,1,4",
199+
[]struct{ latitude, longitude float64 }{{20, 20}, {40, 90}, {40, -100}},
200+
},
201+
202+
{
203+
// 1 intermediate face but multiple shapes on first face
204+
"faces 0,1,4 (3+1+1 shapes)",
205+
[]struct{ latitude, longitude float64 }{{20, 20}, {25, 25}, {15, 15}, {40, 90}, {40, -100}},
206+
},
207+
208+
{
209+
// 2 intermediate faces
210+
"faces 0,1,3,4",
211+
[]struct{ latitude, longitude float64 }{{20, 20}, {40, 90}, {0, 170}, {40, -100}},
212+
},
213+
214+
{
215+
// All 6 faces: extreme case with 4 intermediate faces
216+
"all 6 faces",
217+
[]struct{ latitude, longitude float64 }{
218+
{20, 20}, {40, 90}, {90, 0}, {0, 170}, {40, -100}, {-90, 0},
219+
},
220+
},
221+
222+
{
223+
// Non-zero starting face with 1 intermediate
224+
"faces 1,3,4",
225+
[]struct{ latitude, longitude float64 }{{40, 90}, {0, 170}, {40, -100}},
226+
},
227+
228+
{
229+
// Non-zero starting face with 2 intermediate faces
230+
"faces 1,2,3,4",
231+
[]struct{ latitude, longitude float64 }{{40, 90}, {90, 0}, {0, 170}, {40, -100}},
232+
},
233+
}
234+
235+
for _, test := range tests {
236+
t.Run(test.name, func(t *testing.T) {
237+
index := NewShapeIndex()
238+
for _, location := range test.locs {
239+
center := PointFromLatLng(LatLngFromDegrees(location.latitude, location.longitude))
240+
loop := RegularLoop(center, s1.Degree, 8)
241+
index.Add(PolygonFromLoops([]*Loop{loop}))
242+
}
243+
244+
queryPoint := PointFromLatLng(LatLngFromDegrees(0, 10))
245+
246+
optimized := NewClosestEdgeQueryOptions().IncludeInteriors(true)
247+
got := NewClosestEdgeQuery(index, optimized).FindEdges(
248+
NewMinDistanceToPointTarget(queryPoint),
249+
)
250+
251+
bruteForce := NewClosestEdgeQueryOptions().IncludeInteriors(true).UseBruteForce(true)
252+
want := NewClosestEdgeQuery(index, bruteForce).FindEdges(
253+
NewMinDistanceToPointTarget(queryPoint),
254+
)
255+
256+
shapesGot, shapesWant := make(map[int32]bool), make(map[int32]bool)
257+
for _, r := range got {
258+
shapesGot[r.ShapeID()] = true
259+
}
260+
for _, r := range want {
261+
shapesWant[r.ShapeID()] = true
262+
}
263+
264+
if len(shapesGot) != len(shapesWant) {
265+
t.Errorf(
266+
"%s: optimized found %d shapes, brute force found %d",
267+
test.name,
268+
len(shapesGot),
269+
len(shapesWant),
270+
)
271+
}
272+
})
273+
}
274+
}
275+
180276
// For various tests and benchmarks on the edge query code, there are a number of
181277
// ShapeIndex generators that can be used.
182278
type shapeIndexGeneratorFunc func(c Cap, numEdges int, index *ShapeIndex)

0 commit comments

Comments
 (0)