@@ -177,6 +177,103 @@ 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: addInitialRange covers adequately
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: first top-level cell
204+ // doesn't cover all shapes, and addInitialRange misses earlier ones
205+ "faces 0,1,4 (3+1+1 shapes)" ,
206+ []struct { latitude , longitude float64 }{{20 , 20 }, {25 , 25 }, {15 , 15 }, {40 , 90 }, {40 , - 100 }},
207+ },
208+
209+ {
210+ // 2 intermediate faces: covering too coarse without fix
211+ "faces 0,1,3,4" ,
212+ []struct { latitude , longitude float64 }{{20 , 20 }, {40 , 90 }, {0 , 170 }, {40 , - 100 }},
213+ },
214+
215+ {
216+ // All 6 faces: extreme case with 4 intermediate faces
217+ "all 6 faces" ,
218+ []struct { latitude , longitude float64 }{
219+ {20 , 20 }, {40 , 90 }, {90 , 0 }, {0 , 170 }, {40 , - 100 }, {- 90 , 0 },
220+ },
221+ },
222+
223+ {
224+ // Non-zero starting face with 1 intermediate: confirms pattern holds
225+ "faces 1,3,4" ,
226+ []struct { latitude , longitude float64 }{{40 , 90 }, {0 , 170 }, {40 , - 100 }},
227+ },
228+
229+ {
230+ // Non-zero starting face with 2 intermediate faces
231+ "faces 1,2,3,4" ,
232+ []struct { latitude , longitude float64 }{{40 , 90 }, {90 , 0 }, {0 , 170 }, {40 , - 100 }},
233+ },
234+ }
235+
236+ for _ , test := range tests {
237+ t .Run (test .name , func (t * testing.T ) {
238+ index := NewShapeIndex ()
239+ for _ , location := range test .locs {
240+ center := PointFromLatLng (LatLngFromDegrees (location .latitude , location .longitude ))
241+ loop := RegularLoop (center , s1 .Degree , 8 )
242+ index .Add (PolygonFromLoops ([]* Loop {loop }))
243+ }
244+
245+ queryPoint := PointFromLatLng (LatLngFromDegrees (0 , 10 ))
246+
247+ optimized := NewClosestEdgeQueryOptions ().IncludeInteriors (true )
248+ resultA := NewClosestEdgeQuery (index , optimized ).FindEdges (
249+ NewMinDistanceToPointTarget (queryPoint ),
250+ )
251+
252+ bruteForce := NewClosestEdgeQueryOptions ().IncludeInteriors (true ).UseBruteForce (true )
253+ resultB := NewClosestEdgeQuery (index , bruteForce ).FindEdges (
254+ NewMinDistanceToPointTarget (queryPoint ),
255+ )
256+
257+ shapesA , shapesB := make (map [int32 ]bool ), make (map [int32 ]bool )
258+ for _ , r := range resultA {
259+ shapesA [r .ShapeID ()] = true
260+ }
261+ for _ , r := range resultB {
262+ shapesB [r .ShapeID ()] = true
263+ }
264+
265+ if len (shapesA ) != len (shapesB ) {
266+ t .Errorf (
267+ "%s: optimized found %d shapes, brute force found %d" ,
268+ test .name ,
269+ len (shapesA ),
270+ len (shapesB ),
271+ )
272+ }
273+ })
274+ }
275+ }
276+
180277// For various tests and benchmarks on the edge query code, there are a number of
181278// ShapeIndex generators that can be used.
182279type shapeIndexGeneratorFunc func (c Cap , numEdges int , index * ShapeIndex )
0 commit comments