1 #![cfg(feature = "geospatial")]
2 
3 use assert_approx_eq::assert_approx_eq;
4 
5 use redis::geo::{Coord, RadiusOptions, RadiusOrder, RadiusSearchResult, Unit};
6 use redis::{Commands, RedisResult};
7 
8 mod support;
9 use crate::support::*;
10 
11 const PALERMO: (&str, &str, &str) = ("13.361389", "38.115556", "Palermo");
12 const CATANIA: (&str, &str, &str) = ("15.087269", "37.502669", "Catania");
13 const AGRIGENTO: (&str, &str, &str) = ("13.5833332", "37.316667", "Agrigento");
14 
15 #[test]
test_geoadd_single_tuple()16 fn test_geoadd_single_tuple() {
17     let ctx = TestContext::new();
18     let mut con = ctx.connection();
19 
20     assert_eq!(con.geo_add("my_gis", PALERMO), Ok(1));
21 }
22 
23 #[test]
test_geoadd_multiple_tuples()24 fn test_geoadd_multiple_tuples() {
25     let ctx = TestContext::new();
26     let mut con = ctx.connection();
27 
28     assert_eq!(con.geo_add("my_gis", &[PALERMO, CATANIA]), Ok(2));
29 }
30 
31 #[test]
test_geodist_existing_members()32 fn test_geodist_existing_members() {
33     let ctx = TestContext::new();
34     let mut con = ctx.connection();
35 
36     assert_eq!(con.geo_add("my_gis", &[PALERMO, CATANIA]), Ok(2));
37 
38     let dist: f64 = con
39         .geo_dist("my_gis", PALERMO.2, CATANIA.2, Unit::Kilometers)
40         .unwrap();
41     assert_approx_eq!(dist, 166.2742, 0.001);
42 }
43 
44 #[test]
test_geodist_support_option()45 fn test_geodist_support_option() {
46     let ctx = TestContext::new();
47     let mut con = ctx.connection();
48 
49     assert_eq!(con.geo_add("my_gis", &[PALERMO, CATANIA]), Ok(2));
50 
51     // We should be able to extract the value as an Option<_>, so we can detect
52     // if a member is missing
53 
54     let result: RedisResult<Option<f64>> = con.geo_dist("my_gis", PALERMO.2, "none", Unit::Meters);
55     assert_eq!(result, Ok(None));
56 
57     let result: RedisResult<Option<f64>> =
58         con.geo_dist("my_gis", PALERMO.2, CATANIA.2, Unit::Meters);
59     assert_ne!(result, Ok(None));
60 
61     let dist = result.unwrap().unwrap();
62     assert_approx_eq!(dist, 166_274.151_6, 0.01);
63 }
64 
65 #[test]
test_geohash()66 fn test_geohash() {
67     let ctx = TestContext::new();
68     let mut con = ctx.connection();
69 
70     assert_eq!(con.geo_add("my_gis", &[PALERMO, CATANIA]), Ok(2));
71     let result: RedisResult<Vec<String>> = con.geo_hash("my_gis", PALERMO.2);
72     assert_eq!(result, Ok(vec![String::from("sqc8b49rny0")]));
73 
74     let result: RedisResult<Vec<String>> = con.geo_hash("my_gis", &[PALERMO.2, CATANIA.2]);
75     assert_eq!(
76         result,
77         Ok(vec![
78             String::from("sqc8b49rny0"),
79             String::from("sqdtr74hyu0"),
80         ])
81     );
82 }
83 
84 #[test]
test_geopos()85 fn test_geopos() {
86     let ctx = TestContext::new();
87     let mut con = ctx.connection();
88 
89     assert_eq!(con.geo_add("my_gis", &[PALERMO, CATANIA]), Ok(2));
90 
91     let result: Vec<Vec<f64>> = con.geo_pos("my_gis", &[PALERMO.2]).unwrap();
92     assert_eq!(result.len(), 1);
93 
94     assert_approx_eq!(result[0][0], 13.36138, 0.0001);
95     assert_approx_eq!(result[0][1], 38.11555, 0.0001);
96 
97     // Using the Coord struct
98     let result: Vec<Coord<f64>> = con.geo_pos("my_gis", &[PALERMO.2, CATANIA.2]).unwrap();
99     assert_eq!(result.len(), 2);
100 
101     assert_approx_eq!(result[0].longitude, 13.36138, 0.0001);
102     assert_approx_eq!(result[0].latitude, 38.11555, 0.0001);
103 
104     assert_approx_eq!(result[1].longitude, 15.08726, 0.0001);
105     assert_approx_eq!(result[1].latitude, 37.50266, 0.0001);
106 }
107 
108 #[test]
test_use_coord_struct()109 fn test_use_coord_struct() {
110     let ctx = TestContext::new();
111     let mut con = ctx.connection();
112 
113     assert_eq!(
114         con.geo_add(
115             "my_gis",
116             (Coord::lon_lat(13.361_389, 38.115_556), "Palermo")
117         ),
118         Ok(1)
119     );
120 
121     let result: Vec<Coord<f64>> = con.geo_pos("my_gis", "Palermo").unwrap();
122     assert_eq!(result.len(), 1);
123 
124     assert_approx_eq!(result[0].longitude, 13.36138, 0.0001);
125     assert_approx_eq!(result[0].latitude, 38.11555, 0.0001);
126 }
127 
128 #[test]
test_georadius()129 fn test_georadius() {
130     let ctx = TestContext::new();
131     let mut con = ctx.connection();
132 
133     assert_eq!(con.geo_add("my_gis", &[PALERMO, CATANIA]), Ok(2));
134 
135     let mut geo_radius = |opts: RadiusOptions| -> Vec<RadiusSearchResult> {
136         con.geo_radius("my_gis", 15.0, 37.0, 200.0, Unit::Kilometers, opts)
137             .unwrap()
138     };
139 
140     // Simple request, without extra data
141     let mut result = geo_radius(RadiusOptions::default());
142     result.sort_by(|a, b| Ord::cmp(&a.name, &b.name));
143 
144     assert_eq!(result.len(), 2);
145 
146     assert_eq!(result[0].name.as_str(), "Catania");
147     assert_eq!(result[0].coord, None);
148     assert_eq!(result[0].dist, None);
149 
150     assert_eq!(result[1].name.as_str(), "Palermo");
151     assert_eq!(result[1].coord, None);
152     assert_eq!(result[1].dist, None);
153 
154     // Get data with multiple fields
155     let result = geo_radius(RadiusOptions::default().with_dist().order(RadiusOrder::Asc));
156 
157     assert_eq!(result.len(), 2);
158 
159     assert_eq!(result[0].name.as_str(), "Catania");
160     assert_eq!(result[0].coord, None);
161     assert_approx_eq!(result[0].dist.unwrap(), 56.4413, 0.001);
162 
163     assert_eq!(result[1].name.as_str(), "Palermo");
164     assert_eq!(result[1].coord, None);
165     assert_approx_eq!(result[1].dist.unwrap(), 190.4424, 0.001);
166 
167     let result = geo_radius(
168         RadiusOptions::default()
169             .with_coord()
170             .order(RadiusOrder::Desc)
171             .limit(1),
172     );
173 
174     assert_eq!(result.len(), 1);
175 
176     assert_eq!(result[0].name.as_str(), "Palermo");
177     assert_approx_eq!(result[0].coord.as_ref().unwrap().longitude, 13.361_389);
178     assert_approx_eq!(result[0].coord.as_ref().unwrap().latitude, 38.115_556);
179     assert_eq!(result[0].dist, None);
180 }
181 
182 #[test]
test_georadius_by_member()183 fn test_georadius_by_member() {
184     let ctx = TestContext::new();
185     let mut con = ctx.connection();
186 
187     assert_eq!(con.geo_add("my_gis", &[PALERMO, CATANIA, AGRIGENTO]), Ok(3));
188 
189     // Simple request, without extra data
190     let opts = RadiusOptions::default().order(RadiusOrder::Asc);
191     let result: Vec<RadiusSearchResult> = con
192         .geo_radius_by_member("my_gis", AGRIGENTO.2, 100.0, Unit::Kilometers, opts)
193         .unwrap();
194     let names: Vec<_> = result.iter().map(|c| c.name.as_str()).collect();
195 
196     assert_eq!(names, vec!["Agrigento", "Palermo"]);
197 }
198