summaryrefslogtreecommitdiff
path: root/examples/redis-unstable/tests/unit/geo.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'examples/redis-unstable/tests/unit/geo.tcl')
-rw-r--r--examples/redis-unstable/tests/unit/geo.tcl768
1 files changed, 768 insertions, 0 deletions
diff --git a/examples/redis-unstable/tests/unit/geo.tcl b/examples/redis-unstable/tests/unit/geo.tcl
new file mode 100644
index 0000000..6175329
--- /dev/null
+++ b/examples/redis-unstable/tests/unit/geo.tcl
@@ -0,0 +1,768 @@
1# Helper functions to simulate search-in-radius in the Tcl side in order to
2# verify the Redis implementation with a fuzzy test.
3proc geo_degrad deg {expr {$deg*(atan(1)*8/360)}}
4proc geo_raddeg rad {expr {$rad/(atan(1)*8/360)}}
5
6proc geo_distance {lon1d lat1d lon2d lat2d} {
7 set lon1r [geo_degrad $lon1d]
8 set lat1r [geo_degrad $lat1d]
9 set lon2r [geo_degrad $lon2d]
10 set lat2r [geo_degrad $lat2d]
11 set v [expr {sin(($lon2r - $lon1r) / 2)}]
12 set u [expr {sin(($lat2r - $lat1r) / 2)}]
13 expr {2.0 * 6372797.560856 * \
14 asin(sqrt($u * $u + cos($lat1r) * cos($lat2r) * $v * $v))}
15}
16
17proc geo_random_point {lonvar latvar} {
18 upvar 1 $lonvar lon
19 upvar 1 $latvar lat
20 # Note that the actual latitude limit should be -85 to +85, we restrict
21 # the test to -70 to +70 since in this range the algorithm is more precise
22 # while outside this range occasionally some element may be missing.
23 set lon [expr {-180 + rand()*360}]
24 set lat [expr {-70 + rand()*140}]
25}
26
27# Return elements non common to both the lists.
28# This code is from http://wiki.tcl.tk/15489
29proc compare_lists {List1 List2} {
30 set DiffList {}
31 foreach Item $List1 {
32 if {[lsearch -exact $List2 $Item] == -1} {
33 lappend DiffList $Item
34 }
35 }
36 foreach Item $List2 {
37 if {[lsearch -exact $List1 $Item] == -1} {
38 if {[lsearch -exact $DiffList $Item] == -1} {
39 lappend DiffList $Item
40 }
41 }
42 }
43 return $DiffList
44}
45
46# return true If a point in circle.
47# search_lon and search_lat define the center of the circle,
48# and lon, lat define the point being searched.
49proc pointInCircle {radius_km lon lat search_lon search_lat} {
50 set radius_m [expr {$radius_km*1000}]
51 set distance [geo_distance $lon $lat $search_lon $search_lat]
52 if {$distance < $radius_m} {
53 return true
54 }
55 return false
56}
57
58# return true If a point in rectangle.
59# search_lon and search_lat define the center of the rectangle,
60# and lon, lat define the point being searched.
61# error: can adjust the width and height of the rectangle according to the error
62proc pointInRectangle {width_km height_km lon lat search_lon search_lat error} {
63 set width_m [expr {$width_km*1000*$error/2}]
64 set height_m [expr {$height_km*1000*$error/2}]
65 set lon_distance [geo_distance $lon $lat $search_lon $lat]
66 set lat_distance [geo_distance $lon $lat $lon $search_lat]
67
68 if {$lon_distance > $width_m || $lat_distance > $height_m} {
69 return false
70 }
71 return true
72}
73
74proc verify_geo_edge_response_bylonlat {expected_response expected_store_response} {
75 catch {r georadius src{t} 1 1 1 km} response
76 assert_match $expected_response $response
77
78 catch {r georadius src{t} 1 1 1 km store dest{t}} response
79 assert_match $expected_store_response $response
80
81 catch {r geosearch src{t} fromlonlat 0 0 byradius 1 km} response
82 assert_match $expected_response $response
83
84 catch {r geosearchstore dest{t} src{t} fromlonlat 0 0 byradius 1 km} response
85 assert_match $expected_store_response $response
86}
87
88proc verify_geo_edge_response_bymember {expected_response expected_store_response} {
89 catch {r georadiusbymember src{t} member 1 km} response
90 assert_match $expected_response $response
91
92 catch {r georadiusbymember src{t} member 1 km store dest{t}} response
93 assert_match $expected_store_response $response
94
95 catch {r geosearch src{t} frommember member bybox 1 1 km} response
96 assert_match $expected_response $response
97
98 catch {r geosearchstore dest{t} src{t} frommember member bybox 1 1 m} response
99 assert_match $expected_store_response $response
100}
101
102proc verify_geo_edge_response_generic {expected_response} {
103 catch {r geodist src{t} member 1 km} response
104 assert_match $expected_response $response
105
106 catch {r geohash src{t} member} response
107 assert_match $expected_response $response
108
109 catch {r geopos src{t} member} response
110 assert_match $expected_response $response
111}
112
113
114# The following list represents sets of random seed, search position
115# and radius that caused bugs in the past. It is used by the randomized
116# test later as a starting point. When the regression vectors are scanned
117# the code reverts to using random data.
118#
119# The format is: seed km lon lat
120set regression_vectors {
121 {1482225976969 7083 81.634948934258375 30.561509253718668}
122 {1482340074151 5416 -70.863281847379767 -46.347003465679947}
123 {1499014685896 6064 -89.818768962202014 -40.463868561416803}
124 {1412 156 149.29737817929004 15.95807862745508}
125 {441574 143 59.235461856813856 66.269555127373678}
126 {160645 187 -101.88575239939883 49.061997951502917}
127 {750269 154 -90.187939661642517 66.615930412251487}
128 {342880 145 163.03472387745728 64.012747720821181}
129 {729955 143 137.86663517256579 63.986745399416776}
130 {939895 151 59.149620271823181 65.204186651485145}
131 {1412 156 149.29737817929004 15.95807862745508}
132 {564862 149 84.062063109158544 -65.685403922426232}
133 {1546032440391 16751 -1.8175081637769495 20.665668878082954}
134}
135set rv_idx 0
136
137start_server {tags {"geo"}} {
138 test {GEO with wrong type src key} {
139 r set src{t} wrong_type
140
141 verify_geo_edge_response_bylonlat "WRONGTYPE*" "WRONGTYPE*"
142 verify_geo_edge_response_bymember "WRONGTYPE*" "WRONGTYPE*"
143 verify_geo_edge_response_generic "WRONGTYPE*"
144 }
145
146 test {GEO with non existing src key} {
147 r del src{t}
148
149 verify_geo_edge_response_bylonlat {} 0
150 verify_geo_edge_response_bymember {} 0
151 }
152
153 test {GEO BYLONLAT with empty search} {
154 r del src{t}
155 r geoadd src{t} 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
156
157 verify_geo_edge_response_bylonlat {} 0
158 }
159
160 test {GEO BYMEMBER with non existing member} {
161 r del src{t}
162 r geoadd src{t} 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
163
164 verify_geo_edge_response_bymember "ERR*" "ERR*"
165 }
166
167 test {GEOADD create} {
168 r geoadd nyc -73.9454966 40.747533 "lic market"
169 } {1}
170
171 test {GEOADD update} {
172 r geoadd nyc -73.9454966 40.747533 "lic market"
173 } {0}
174
175 test {GEOADD update with CH option} {
176 assert_equal 1 [r geoadd nyc CH 40.747533 -73.9454966 "lic market"]
177 lassign [lindex [r geopos nyc "lic market"] 0] x1 y1
178 assert {abs($x1) - 40.747 < 0.001}
179 assert {abs($y1) - 73.945 < 0.001}
180 } {}
181
182 test {GEOADD update with NX option} {
183 assert_equal 0 [r geoadd nyc NX -73.9454966 40.747533 "lic market"]
184 lassign [lindex [r geopos nyc "lic market"] 0] x1 y1
185 assert {abs($x1) - 40.747 < 0.001}
186 assert {abs($y1) - 73.945 < 0.001}
187 } {}
188
189 test {GEOADD update with XX option} {
190 assert_equal 0 [r geoadd nyc XX -83.9454966 40.747533 "lic market"]
191 lassign [lindex [r geopos nyc "lic market"] 0] x1 y1
192 assert {abs($x1) - 83.945 < 0.001}
193 assert {abs($y1) - 40.747 < 0.001}
194 } {}
195
196 test {GEOADD update with CH NX option} {
197 r geoadd nyc CH NX -73.9454966 40.747533 "lic market"
198 } {0}
199
200 test {GEOADD update with CH XX option} {
201 r geoadd nyc CH XX -73.9454966 40.747533 "lic market"
202 } {1}
203
204 test {GEOADD update with XX NX option will return syntax error} {
205 catch {
206 r geoadd nyc xx nx -73.9454966 40.747533 "lic market"
207 } err
208 set err
209 } {ERR *syntax*}
210
211 test {GEOADD update with invalid option} {
212 catch {
213 r geoadd nyc ch xx foo -73.9454966 40.747533 "lic market"
214 } err
215 set err
216 } {ERR *syntax*}
217
218 test {GEOADD invalid coordinates} {
219 catch {
220 r geoadd nyc -73.9454966 40.747533 "lic market" \
221 foo bar "luck market"
222 } err
223 set err
224 } {*valid*}
225
226 test {GEOADD multi add} {
227 r geoadd nyc -73.9733487 40.7648057 "central park n/q/r" -73.9903085 40.7362513 "union square" -74.0131604 40.7126674 "wtc one" -73.7858139 40.6428986 "jfk" -73.9375699 40.7498929 "q4" -73.9564142 40.7480973 4545
228 } {6}
229
230 test {Check geoset values} {
231 r zrange nyc 0 -1 withscores
232 } {{wtc one} 1791873972053020 {union square} 1791875485187452 {central park n/q/r} 1791875761332224 4545 1791875796750882 {lic market} 1791875804419201 q4 1791875830079666 jfk 1791895905559723}
233
234 test {GEORADIUS simple (sorted)} {
235 r georadius nyc -73.9798091 40.7598464 3 km asc
236 } {{central park n/q/r} 4545 {union square}}
237
238 test {GEORADIUS_RO simple (sorted)} {
239 r georadius_ro nyc -73.9798091 40.7598464 3 km asc
240 } {{central park n/q/r} 4545 {union square}}
241
242 test {GEOSEARCH simple (sorted)} {
243 r geosearch nyc fromlonlat -73.9798091 40.7598464 bybox 6 6 km asc
244 } {{central park n/q/r} 4545 {union square} {lic market}}
245
246 test {GEOSEARCH FROMLONLAT and FROMMEMBER cannot exist at the same time} {
247 catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 frommember xxx bybox 6 6 km asc} e
248 set e
249 } {ERR *syntax*}
250
251 test {GEOSEARCH FROMLONLAT and FROMMEMBER one must exist} {
252 catch {r geosearch nyc bybox 3 3 km asc desc withhash withdist withcoord} e
253 set e
254 } {ERR *exactly one of FROMMEMBER or FROMLONLAT*}
255
256 test {GEOSEARCH BYRADIUS and BYBOX cannot exist at the same time} {
257 catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 byradius 3 km bybox 3 3 km asc} e
258 set e
259 } {ERR *syntax*}
260
261 test {GEOSEARCH BYRADIUS and BYBOX one must exist} {
262 catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 asc desc withhash withdist withcoord} e
263 set e
264 } {ERR *exactly one of BYRADIUS and BYBOX*}
265
266 test {GEOSEARCH with STOREDIST option} {
267 catch {r geosearch nyc fromlonlat -73.9798091 40.7598464 bybox 6 6 km asc storedist} e
268 set e
269 } {ERR *syntax*}
270
271 test {GEORADIUS withdist (sorted)} {
272 r georadius nyc -73.9798091 40.7598464 3 km withdist asc
273 } {{{central park n/q/r} 0.7750} {4545 2.3651} {{union square} 2.7697}}
274
275 test {GEOSEARCH withdist (sorted)} {
276 r geosearch nyc fromlonlat -73.9798091 40.7598464 bybox 6 6 km withdist asc
277 } {{{central park n/q/r} 0.7750} {4545 2.3651} {{union square} 2.7697} {{lic market} 3.1991}}
278
279 test {GEORADIUS with COUNT} {
280 r georadius nyc -73.9798091 40.7598464 10 km COUNT 3
281 } {{central park n/q/r} 4545 {union square}}
282
283 test {GEORADIUS with multiple WITH* tokens} {
284 assert_match {{{central park n/q/r} 1791875761332224 {-73.97334* 40.76480*}} {4545 1791875796750882 {-73.95641* 40.74809*}}} [r georadius nyc -73.9798091 40.7598464 10 km WITHCOORD WITHHASH COUNT 2]
285 assert_match {{{central park n/q/r} 1791875761332224 {-73.97334* 40.76480*}} {4545 1791875796750882 {-73.95641* 40.74809*}}} [r georadius nyc -73.9798091 40.7598464 10 km WITHHASH WITHCOORD COUNT 2]
286 assert_match {{{central park n/q/r} 0.7750 1791875761332224 {-73.97334* 40.76480*}} {4545 2.3651 1791875796750882 {-73.95641* 40.74809*}}} [r georadius nyc -73.9798091 40.7598464 10 km WITHDIST WITHHASH WITHCOORD COUNT 2]
287 }
288
289 test {GEORADIUS with ANY not sorted by default} {
290 r georadius nyc -73.9798091 40.7598464 10 km COUNT 3 ANY
291 } {{wtc one} {union square} {central park n/q/r}}
292
293 test {GEORADIUS with ANY sorted by ASC} {
294 r georadius nyc -73.9798091 40.7598464 10 km COUNT 3 ANY ASC
295 } {{central park n/q/r} {union square} {wtc one}}
296
297 test {GEORADIUS with ANY but no COUNT} {
298 catch {r georadius nyc -73.9798091 40.7598464 10 km ANY ASC} e
299 set e
300 } {ERR *ANY*requires*COUNT*}
301
302 test {GEORADIUS with COUNT but missing integer argument} {
303 catch {r georadius nyc -73.9798091 40.7598464 10 km COUNT} e
304 set e
305 } {ERR *syntax*}
306
307 test {GEORADIUS with COUNT DESC} {
308 r georadius nyc -73.9798091 40.7598464 10 km COUNT 2 DESC
309 } {{wtc one} q4}
310
311 test {GEORADIUS HUGE, issue #2767} {
312 r geoadd users -47.271613776683807 -54.534504198047678 user_000000
313 llength [r GEORADIUS users 0 0 50000 km WITHCOORD]
314 } {1}
315
316 test {GEORADIUSBYMEMBER simple (sorted)} {
317 r georadiusbymember nyc "wtc one" 7 km
318 } {{wtc one} {union square} {central park n/q/r} 4545 {lic market}}
319
320 test {GEORADIUSBYMEMBER_RO simple (sorted)} {
321 r georadiusbymember_ro nyc "wtc one" 7 km
322 } {{wtc one} {union square} {central park n/q/r} 4545 {lic market}}
323
324 test {GEORADIUSBYMEMBER search areas contain satisfied points in oblique direction} {
325 r del k1
326
327 r geoadd k1 -0.15307903289794921875 85 n1 0.3515625 85.00019260486917005437 n2
328 set ret1 [r GEORADIUSBYMEMBER k1 n1 4891.94 m]
329 assert_equal $ret1 {n1 n2}
330
331 r zrem k1 n1 n2
332 r geoadd k1 -4.95211958885192871094 85 n3 11.25 85.0511 n4
333 set ret2 [r GEORADIUSBYMEMBER k1 n3 156544 m]
334 assert_equal $ret2 {n3 n4}
335
336 r zrem k1 n3 n4
337 r geoadd k1 -45 65.50900022111811438208 n5 90 85.0511 n6
338 set ret3 [r GEORADIUSBYMEMBER k1 n5 5009431 m]
339 assert_equal $ret3 {n5 n6}
340 }
341
342 test {GEORADIUSBYMEMBER crossing pole search} {
343 r del k1
344 r geoadd k1 45 65 n1 -135 85.05 n2
345 set ret [r GEORADIUSBYMEMBER k1 n1 5009431 m]
346 assert_equal $ret {n1 n2}
347 }
348
349 test {GEOSEARCH FROMMEMBER simple (sorted)} {
350 r geosearch nyc frommember "wtc one" bybox 14 14 km
351 } {{wtc one} {union square} {central park n/q/r} 4545 {lic market} q4}
352
353 test {GEOSEARCH vs GEORADIUS} {
354 r del Sicily
355 r geoadd Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
356 r geoadd Sicily 12.758489 38.788135 "edge1" 17.241510 38.788135 "eage2"
357 set ret1 [r georadius Sicily 15 37 200 km asc]
358 assert_equal $ret1 {Catania Palermo}
359 set ret2 [r geosearch Sicily fromlonlat 15 37 bybox 400 400 km asc]
360 assert_equal $ret2 {Catania Palermo eage2 edge1}
361 }
362
363 test {GEOSEARCH non square, long and narrow} {
364 r del Sicily
365 r geoadd Sicily 12.75 36.995 "test1"
366 r geoadd Sicily 12.75 36.50 "test2"
367 r geoadd Sicily 13.00 36.50 "test3"
368 # box height=2km width=400km
369 set ret1 [r geosearch Sicily fromlonlat 15 37 bybox 400 2 km]
370 assert_equal $ret1 {test1}
371
372 # Add a western Hemisphere point
373 r geoadd Sicily -1 37.00 "test3"
374 set ret2 [r geosearch Sicily fromlonlat 15 37 bybox 3000 2 km asc]
375 assert_equal $ret2 {test1 test3}
376 }
377
378 test {GEOSEARCH corner point test} {
379 r del Sicily
380 r geoadd Sicily 12.758489 38.788135 edge1 17.241510 38.788135 edge2 17.250000 35.202000 edge3 12.750000 35.202000 edge4 12.748489955781654 37 edge5 15 38.798135872540925 edge6 17.251510044218346 37 edge7 15 35.201864127459075 edge8 12.692799634687903 38.798135872540925 corner1 12.692799634687903 38.798135872540925 corner2 17.200560937451133 35.201864127459075 corner3 12.799439062548865 35.201864127459075 corner4
381 set ret [lsort [r geosearch Sicily fromlonlat 15 37 bybox 400 400 km asc]]
382 assert_equal $ret {edge1 edge2 edge5 edge7}
383 }
384
385 test {GEORADIUSBYMEMBER withdist (sorted)} {
386 r georadiusbymember nyc "wtc one" 7 km withdist
387 } {{{wtc one} 0.0000} {{union square} 3.2544} {{central park n/q/r} 6.7000} {4545 6.1975} {{lic market} 6.8969}}
388
389 test {GEOHASH is able to return geohash strings} {
390 # Example from Wikipedia.
391 r del points
392 r geoadd points -5.6 42.6 test
393 lindex [r geohash points test] 0
394 } {ezs42e44yx0}
395
396 test {GEOHASH with only key as argument} {
397 r del points
398 r geoadd points 10 20 a 30 40 b
399 set result [r geohash points]
400 assert {$result eq {}}
401 }
402
403 test {GEOPOS simple} {
404 r del points
405 r geoadd points 10 20 a 30 40 b
406 lassign [lindex [r geopos points a b] 0] x1 y1
407 lassign [lindex [r geopos points a b] 1] x2 y2
408 assert {abs($x1 - 10) < 0.001}
409 assert {abs($y1 - 20) < 0.001}
410 assert {abs($x2 - 30) < 0.001}
411 assert {abs($y2 - 40) < 0.001}
412 }
413
414 test {GEOPOS missing element} {
415 r del points
416 r geoadd points 10 20 a 30 40 b
417 lindex [r geopos points a x b] 1
418 } {}
419
420 test {GEOPOS with only key as argument} {
421 r del points
422 r geoadd points 10 20 a 30 40 b
423 set result [r geopos points]
424 assert {$result eq {}}
425 }
426
427 test {GEODIST simple & unit} {
428 r del points
429 r geoadd points 13.361389 38.115556 "Palermo" \
430 15.087269 37.502669 "Catania"
431 set m [r geodist points Palermo Catania]
432 assert {$m > 166274 && $m < 166275}
433 set km [r geodist points Palermo Catania km]
434 assert {$km > 166.2 && $km < 166.3}
435 set dist [r geodist points Palermo Palermo]
436 assert {$dist eq 0.0000}
437 }
438
439 test {GEODIST missing elements} {
440 r del points
441 r geoadd points 13.361389 38.115556 "Palermo" \
442 15.087269 37.502669 "Catania"
443 set m [r geodist points Palermo Agrigento]
444 assert {$m eq {}}
445 set m [r geodist points Ragusa Agrigento]
446 assert {$m eq {}}
447 set m [r geodist empty_key Palermo Catania]
448 assert {$m eq {}}
449 }
450
451 test {GEORADIUS STORE option: syntax error} {
452 r del points{t}
453 r geoadd points{t} 13.361389 38.115556 "Palermo" \
454 15.087269 37.502669 "Catania"
455 catch {r georadius points{t} 13.361389 38.115556 50 km store} e
456 set e
457 } {*ERR*syntax*}
458
459 test {GEOSEARCHSTORE STORE option: syntax error} {
460 catch {r geosearchstore abc{t} points{t} fromlonlat 13.361389 38.115556 byradius 50 km store abc{t}} e
461 set e
462 } {*ERR*syntax*}
463
464 test {GEORANGE STORE option: incompatible options} {
465 r del points{t}
466 r geoadd points{t} 13.361389 38.115556 "Palermo" \
467 15.087269 37.502669 "Catania"
468 catch {r georadius points{t} 13.361389 38.115556 50 km store points2{t} withdist} e
469 assert_match {*ERR*} $e
470 catch {r georadius points{t} 13.361389 38.115556 50 km store points2{t} withhash} e
471 assert_match {*ERR*} $e
472 catch {r georadius points{t} 13.361389 38.115556 50 km store points2{t} withcoords} e
473 assert_match {*ERR*} $e
474 }
475
476 test {GEORANGE STORE option: plain usage} {
477 r del points{t}
478 r geoadd points{t} 13.361389 38.115556 "Palermo" \
479 15.087269 37.502669 "Catania"
480 r georadius points{t} 13.361389 38.115556 500 km store points2{t}
481 assert_equal [r zrange points{t} 0 -1] [r zrange points2{t} 0 -1]
482 }
483
484 test {GEORADIUSBYMEMBER STORE/STOREDIST option: plain usage} {
485 r del points{t}
486 r geoadd points{t} 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
487
488 r georadiusbymember points{t} Palermo 500 km store points2{t}
489 assert_equal {Palermo Catania} [r zrange points2{t} 0 -1]
490
491 r georadiusbymember points{t} Catania 500 km storedist points2{t}
492 assert_equal {Catania Palermo} [r zrange points2{t} 0 -1]
493
494 set res [r zrange points2{t} 0 -1 withscores]
495 assert {[lindex $res 1] < 1}
496 assert {[lindex $res 3] > 166}
497 }
498
499 test {GEOSEARCHSTORE STORE option: plain usage} {
500 r geosearchstore points2{t} points{t} fromlonlat 13.361389 38.115556 byradius 500 km
501 assert_equal [r zrange points{t} 0 -1] [r zrange points2{t} 0 -1]
502 }
503
504 test {GEORANGE STOREDIST option: plain usage} {
505 r del points{t}
506 r geoadd points{t} 13.361389 38.115556 "Palermo" \
507 15.087269 37.502669 "Catania"
508 r georadius points{t} 13.361389 38.115556 500 km storedist points2{t}
509 set res [r zrange points2{t} 0 -1 withscores]
510 assert {[lindex $res 1] < 1}
511 assert {[lindex $res 3] > 166}
512 assert {[lindex $res 3] < 167}
513 }
514
515 test {GEOSEARCHSTORE STOREDIST option: plain usage} {
516 r geosearchstore points2{t} points{t} fromlonlat 13.361389 38.115556 byradius 500 km storedist
517 set res [r zrange points2{t} 0 -1 withscores]
518 assert {[lindex $res 1] < 1}
519 assert {[lindex $res 3] > 166}
520 assert {[lindex $res 3] < 167}
521 }
522
523 test {GEORANGE STOREDIST option: COUNT ASC and DESC} {
524 r del points{t}
525 r geoadd points{t} 13.361389 38.115556 "Palermo" \
526 15.087269 37.502669 "Catania"
527 r georadius points{t} 13.361389 38.115556 500 km storedist points2{t} asc count 1
528 assert {[r zcard points2{t}] == 1}
529 set res [r zrange points2{t} 0 -1 withscores]
530 assert {[lindex $res 0] eq "Palermo"}
531
532 r georadius points{t} 13.361389 38.115556 500 km storedist points2{t} desc count 1
533 assert {[r zcard points2{t}] == 1}
534 set res [r zrange points2{t} 0 -1 withscores]
535 assert {[lindex $res 0] eq "Catania"}
536 }
537
538 test {GEOSEARCH the box spans -180° or 180°} {
539 r del points
540 r geoadd points 179.5 36 point1
541 r geoadd points -179.5 36 point2
542 assert_equal {point1 point2} [r geosearch points fromlonlat 179 37 bybox 400 400 km asc]
543 assert_equal {point2 point1} [r geosearch points fromlonlat -179 37 bybox 400 400 km asc]
544 }
545
546 test {GEOSEARCH with small distance} {
547 r del points
548 r geoadd points -122.407107 37.794300 1
549 r geoadd points -122.227336 37.794300 2
550 assert_equal {{1 0.0001} {2 9.8182}} [r GEORADIUS points -122.407107 37.794300 30 mi ASC WITHDIST]
551 }
552
553 foreach {type} {byradius bybox} {
554 test "GEOSEARCH fuzzy test - $type" {
555 if {$::accurate} { set attempt 300 } else { set attempt 30 }
556 while {[incr attempt -1]} {
557 set rv [lindex $regression_vectors $rv_idx]
558 incr rv_idx
559
560 set radius_km 0; set width_km 0; set height_km 0
561 unset -nocomplain debuginfo
562 set srand_seed [clock milliseconds]
563 if {$rv ne {}} {set srand_seed [lindex $rv 0]}
564 lappend debuginfo "srand_seed is $srand_seed"
565 expr {srand($srand_seed)} ; # If you need a reproducible run
566 r del mypoints
567
568 if {[randomInt 10] == 0} {
569 # From time to time use very big radiuses
570 if {$type == "byradius"} {
571 set radius_km [expr {[randomInt 5000]+10}]
572 } elseif {$type == "bybox"} {
573 set width_km [expr {[randomInt 5000]+10}]
574 set height_km [expr {[randomInt 5000]+10}]
575 }
576 } else {
577 # Normally use a few - ~200km radiuses to stress
578 # test the code the most in edge cases.
579 if {$type == "byradius"} {
580 set radius_km [expr {[randomInt 200]+10}]
581 } elseif {$type == "bybox"} {
582 set width_km [expr {[randomInt 200]+10}]
583 set height_km [expr {[randomInt 200]+10}]
584 }
585 }
586 if {$rv ne {}} {
587 set radius_km [lindex $rv 1]
588 set width_km [lindex $rv 1]
589 set height_km [lindex $rv 1]
590 }
591 geo_random_point search_lon search_lat
592 if {$rv ne {}} {
593 set search_lon [lindex $rv 2]
594 set search_lat [lindex $rv 3]
595 }
596 lappend debuginfo "Search area: $search_lon,$search_lat $radius_km $width_km $height_km km"
597 set tcl_result {}
598 set argv {}
599 for {set j 0} {$j < 20000} {incr j} {
600 geo_random_point lon lat
601 lappend argv $lon $lat "place:$j"
602 if {$type == "byradius"} {
603 if {[pointInCircle $radius_km $lon $lat $search_lon $search_lat]} {
604 lappend tcl_result "place:$j"
605 }
606 } elseif {$type == "bybox"} {
607 if {[pointInRectangle $width_km $height_km $lon $lat $search_lon $search_lat 1]} {
608 lappend tcl_result "place:$j"
609 }
610 }
611 lappend debuginfo "place:$j $lon $lat"
612 }
613 r geoadd mypoints {*}$argv
614 if {$type == "byradius"} {
615 set res [lsort [r geosearch mypoints fromlonlat $search_lon $search_lat byradius $radius_km km]]
616 } elseif {$type == "bybox"} {
617 set res [lsort [r geosearch mypoints fromlonlat $search_lon $search_lat bybox $width_km $height_km km]]
618 }
619 set res2 [lsort $tcl_result]
620 set test_result OK
621
622 if {$res != $res2} {
623 set rounding_errors 0
624 set diff [compare_lists $res $res2]
625 foreach place $diff {
626 lassign [lindex [r geopos mypoints $place] 0] lon lat
627 set mydist [geo_distance $lon $lat $search_lon $search_lat]
628 set mydist [expr $mydist/1000]
629 if {$type == "byradius"} {
630 if {($mydist / $radius_km) > 0.999} {
631 incr rounding_errors
632 continue
633 }
634 if {$mydist < [expr {$radius_km*1000}]} {
635 # This is a false positive for redis since given the
636 # same points the higher precision calculation provided
637 # by TCL shows the point within range
638 incr rounding_errors
639 continue
640 }
641 } elseif {$type == "bybox"} {
642 # we add 0.1% error for floating point calculation error
643 if {[pointInRectangle $width_km $height_km $lon $lat $search_lon $search_lat 1.001]} {
644 incr rounding_errors
645 continue
646 }
647 }
648 }
649
650 # Make sure this is a real error and not a rounidng issue.
651 if {[llength $diff] == $rounding_errors} {
652 set res $res2; # Error silenced
653 }
654 }
655
656 if {$res != $res2} {
657 set diff [compare_lists $res $res2]
658 puts "*** Possible problem in GEO radius query ***"
659 puts "Redis: $res"
660 puts "Tcl : $res2"
661 puts "Diff : $diff"
662 puts [join $debuginfo "\n"]
663 foreach place $diff {
664 if {[lsearch -exact $res2 $place] != -1} {
665 set where "(only in Tcl)"
666 } else {
667 set where "(only in Redis)"
668 }
669 lassign [lindex [r geopos mypoints $place] 0] lon lat
670 set mydist [geo_distance $lon $lat $search_lon $search_lat]
671 set mydist [expr $mydist/1000]
672 puts "$place -> [r geopos mypoints $place] $mydist $where"
673 }
674 set test_result FAIL
675 }
676 unset -nocomplain debuginfo
677 if {$test_result ne {OK}} break
678 }
679 set test_result
680 } {OK}
681 }
682
683 test {GEOSEARCH box edges fuzzy test} {
684 if {$::accurate} { set attempt 300 } else { set attempt 30 }
685 while {[incr attempt -1]} {
686 unset -nocomplain debuginfo
687 set srand_seed [clock milliseconds]
688 lappend debuginfo "srand_seed is $srand_seed"
689 expr {srand($srand_seed)} ; # If you need a reproducible run
690 r del mypoints
691
692 geo_random_point search_lon search_lat
693 set width_m [expr {[randomInt 10000]+10}]
694 set height_m [expr {[randomInt 10000]+10}]
695 set lat_delta [geo_raddeg [expr {$height_m/2/6372797.560856}]]
696 set long_delta_top [geo_raddeg [expr {$width_m/2/6372797.560856/cos([geo_degrad [expr {$search_lat+$lat_delta}]])}]]
697 set long_delta_middle [geo_raddeg [expr {$width_m/2/6372797.560856/cos([geo_degrad $search_lat])}]]
698 set long_delta_bottom [geo_raddeg [expr {$width_m/2/6372797.560856/cos([geo_degrad [expr {$search_lat-$lat_delta}]])}]]
699
700 # Total of 8 points are generated, which are located at each vertex and the center of each side
701 set points(north) [list $search_lon [expr {$search_lat+$lat_delta}]]
702 set points(south) [list $search_lon [expr {$search_lat-$lat_delta}]]
703 set points(east) [list [expr {$search_lon+$long_delta_middle}] $search_lat]
704 set points(west) [list [expr {$search_lon-$long_delta_middle}] $search_lat]
705 set points(north_east) [list [expr {$search_lon+$long_delta_top}] [expr {$search_lat+$lat_delta}]]
706 set points(north_west) [list [expr {$search_lon-$long_delta_top}] [expr {$search_lat+$lat_delta}]]
707 set points(south_east) [list [expr {$search_lon+$long_delta_bottom}] [expr {$search_lat-$lat_delta}]]
708 set points(south_west) [list [expr {$search_lon-$long_delta_bottom}] [expr {$search_lat-$lat_delta}]]
709
710 lappend debuginfo "Search area: geosearch mypoints fromlonlat $search_lon $search_lat bybox $width_m $height_m m"
711 set tcl_result {}
712 foreach name [array names points] {
713 set x [lindex $points($name) 0]
714 set y [lindex $points($name) 1]
715 # If longitude crosses -180° or 180°, we need to convert it.
716 # latitude doesn't have this problem, because it's scope is -70~70, see geo_random_point
717 if {$x > 180} {
718 set x [expr {$x-360}]
719 } elseif {$x < -180} {
720 set x [expr {$x+360}]
721 }
722 r geoadd mypoints $x $y place:$name
723 lappend tcl_result "place:$name"
724 lappend debuginfo "geoadd mypoints $x $y place:$name"
725 }
726
727 set res2 [lsort $tcl_result]
728
729 # make the box larger by two meter in each direction to put the coordinate slightly inside the box.
730 set height_new [expr {$height_m+4}]
731 set width_new [expr {$width_m+4}]
732 set res [lsort [r geosearch mypoints fromlonlat $search_lon $search_lat bybox $width_new $height_new m]]
733 if {$res != $res2} {
734 set diff [compare_lists $res $res2]
735 lappend debuginfo "res: $res, res2: $res2, diff: $diff"
736 fail "place should be found, debuginfo: $debuginfo, height_new: $height_new width_new: $width_new"
737 }
738
739 # The width decreases and the height increases. Only north and south are found
740 set width_new [expr {$width_m-4}]
741 set height_new [expr {$height_m+4}]
742 set res [lsort [r geosearch mypoints fromlonlat $search_lon $search_lat bybox $width_new $height_new m]]
743 if {$res != {place:north place:south}} {
744 lappend debuginfo "res: $res"
745 fail "place should not be found, debuginfo: $debuginfo, height_new: $height_new width_new: $width_new"
746 }
747
748 # The width increases and the height decreases. Only ease and west are found
749 set width_new [expr {$width_m+4}]
750 set height_new [expr {$height_m-4}]
751 set res [lsort [r geosearch mypoints fromlonlat $search_lon $search_lat bybox $width_new $height_new m]]
752 if {$res != {place:east place:west}} {
753 lappend debuginfo "res: $res"
754 fail "place should not be found, debuginfo: $debuginfo, height_new: $height_new width_new: $width_new"
755 }
756
757 # make the box smaller by two meter in each direction to put the coordinate slightly outside the box.
758 set height_new [expr {$height_m-4}]
759 set width_new [expr {$width_m-4}]
760 set res [r geosearch mypoints fromlonlat $search_lon $search_lat bybox $width_new $height_new m]
761 if {$res != ""} {
762 lappend debuginfo "res: $res"
763 fail "place should not be found, debuginfo: $debuginfo, height_new: $height_new width_new: $width_new"
764 }
765 unset -nocomplain debuginfo
766 }
767 }
768}