Don't force the user to specify the metric
[xapian.git] / xapian-core / tests / api_geospatial.cc
blob4d1fa5b8ad3cfb7f6677e00317514ada7a0c85c6
1 /** @file api_geospatial.cc
2 * @brief Tests of geospatial functionality.
3 */
4 /* Copyright 2008 Lemur Consulting Ltd
5 * Copyright 2010,2011 Richard Boulton
6 * Copyright 2012,2016 Olly Betts
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 * USA
24 #include <config.h>
25 #include "api_geospatial.h"
26 #include <xapian.h>
28 #include "apitest.h"
29 #include "testsuite.h"
30 #include "testutils.h"
32 using namespace std;
33 using namespace Xapian;
35 // #######################################################################
36 // # Tests start here
38 static void
39 builddb_coords1(Xapian::WritableDatabase &db, const string &)
41 Xapian::LatLongCoord coord1(10, 10);
42 Xapian::LatLongCoord coord2(20, 10);
43 Xapian::LatLongCoord coord3(30, 10);
45 Xapian::Document doc;
46 doc.add_value(0, coord1.serialise());
47 db.add_document(doc);
49 doc = Xapian::Document();
50 doc.add_value(0, coord2.serialise());
51 db.add_document(doc);
53 doc = Xapian::Document();
54 doc.add_value(0, coord3.serialise());
55 db.add_document(doc);
58 /// Test behaviour of the LatLongPostingSource
59 DEFINE_TESTCASE(latlongpostingsource1, backend && writable && !remote && !inmemory) {
60 Xapian::Database db = get_database("coords1", builddb_coords1, "");
61 Xapian::LatLongCoord coord1(10, 10);
62 Xapian::LatLongCoord coord2(20, 10);
63 Xapian::LatLongCoord coord3(30, 10);
65 Xapian::GreatCircleMetric metric;
66 Xapian::LatLongCoords centre;
67 centre.append(coord1);
68 double coorddist = metric(coord1, coord2);
69 TEST_EQUAL_DOUBLE(coorddist, metric(coord2, coord3));
71 // Test a search with no range restriction.
73 Xapian::LatLongDistancePostingSource ps(0, coord1, metric);
74 ps.init(db);
76 ps.next(0.0);
77 TEST_EQUAL(ps.at_end(), false);
78 TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
79 TEST_EQUAL(ps.get_docid(), 1);
81 ps.next(0.0);
82 TEST_EQUAL(ps.at_end(), false);
83 TEST_EQUAL_DOUBLE(ps.get_weight(), 1000.0 / (1000.0 + coorddist));
84 TEST_EQUAL(ps.get_docid(), 2);
86 ps.next(0.0);
87 TEST_EQUAL(ps.at_end(), false);
88 TEST_EQUAL_DOUBLE(ps.get_weight(), 1000.0 / (1000.0 + coorddist * 2));
89 TEST_EQUAL(ps.get_docid(), 3);
91 ps.next(0.0);
92 TEST_EQUAL(ps.at_end(), true);
95 // Test a search with no range restriction and implicit metric.
97 Xapian::LatLongDistancePostingSource ps(0, coord1);
98 ps.init(db);
100 ps.next(0.0);
101 TEST_EQUAL(ps.at_end(), false);
102 TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
103 TEST_EQUAL(ps.get_docid(), 1);
105 ps.next(0.0);
106 TEST_EQUAL(ps.at_end(), false);
107 TEST_EQUAL_DOUBLE(ps.get_weight(), 1000.0 / (1000.0 + coorddist));
108 TEST_EQUAL(ps.get_docid(), 2);
110 ps.next(0.0);
111 TEST_EQUAL(ps.at_end(), false);
112 TEST_EQUAL_DOUBLE(ps.get_weight(), 1000.0 / (1000.0 + coorddist * 2));
113 TEST_EQUAL(ps.get_docid(), 3);
115 ps.next(0.0);
116 TEST_EQUAL(ps.at_end(), true);
119 // Test a search with a tight range restriction
121 Xapian::LatLongDistancePostingSource ps(0, centre, metric, coorddist * 0.5);
122 ps.init(db);
124 ps.next(0.0);
125 TEST_EQUAL(ps.at_end(), false);
126 TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
128 ps.next(0.0);
129 TEST_EQUAL(ps.at_end(), true);
132 // Test a search with a tight range restriction and implicit metric.
134 Xapian::LatLongDistancePostingSource ps(0, centre, coorddist * 0.5);
135 ps.init(db);
137 ps.next(0.0);
138 TEST_EQUAL(ps.at_end(), false);
139 TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
141 ps.next(0.0);
142 TEST_EQUAL(ps.at_end(), true);
145 // Test a search with a looser range restriction
147 Xapian::LatLongDistancePostingSource ps(0, centre, metric, coorddist);
148 ps.init(db);
150 ps.next(0.0);
151 TEST_EQUAL(ps.at_end(), false);
152 TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
154 ps.next(0.0);
155 TEST_EQUAL(ps.at_end(), false);
156 TEST_EQUAL_DOUBLE(ps.get_weight(), 1000.0 / (1000.0 + coorddist));
157 TEST_EQUAL(ps.get_docid(), 2);
159 ps.next(0.0);
160 TEST_EQUAL(ps.at_end(), true);
163 // Test a search with a looser range restriction and implicit metric.
165 Xapian::LatLongDistancePostingSource ps(0, centre, coorddist);
166 ps.init(db);
168 ps.next(0.0);
169 TEST_EQUAL(ps.at_end(), false);
170 TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
172 ps.next(0.0);
173 TEST_EQUAL(ps.at_end(), false);
174 TEST_EQUAL_DOUBLE(ps.get_weight(), 1000.0 / (1000.0 + coorddist));
175 TEST_EQUAL(ps.get_docid(), 2);
177 ps.next(0.0);
178 TEST_EQUAL(ps.at_end(), true);
181 // Test a search with a looser range restriction, but not enough to return
182 // the next document.
184 Xapian::LatLongDistancePostingSource ps(0, centre, metric, coorddist * 1.5);
185 ps.init(db);
187 ps.next(0.0);
188 TEST_EQUAL(ps.at_end(), false);
189 TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
191 ps.next(0.0);
192 TEST_EQUAL(ps.at_end(), false);
193 TEST_EQUAL_DOUBLE(ps.get_weight(), 1000.0 / (1000.0 + coorddist));
194 TEST_EQUAL(ps.get_docid(), 2);
196 ps.next(0.0);
197 TEST_EQUAL(ps.at_end(), true);
200 // Test a search with a looser range restriction, but not enough to return
201 // the next document and implicit metric.
203 Xapian::LatLongDistancePostingSource ps(0, centre, coorddist * 1.5);
204 ps.init(db);
206 ps.next(0.0);
207 TEST_EQUAL(ps.at_end(), false);
208 TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
210 ps.next(0.0);
211 TEST_EQUAL(ps.at_end(), false);
212 TEST_EQUAL_DOUBLE(ps.get_weight(), 1000.0 / (1000.0 + coorddist));
213 TEST_EQUAL(ps.get_docid(), 2);
215 ps.next(0.0);
216 TEST_EQUAL(ps.at_end(), true);
219 // Test a search with a loose enough range restriction that all docs should
220 // be returned.
222 Xapian::LatLongDistancePostingSource ps(0, centre, metric, coorddist * 2.5);
223 ps.init(db);
225 ps.next(0.0);
226 TEST_EQUAL(ps.at_end(), false);
227 TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
229 ps.next(0.0);
230 TEST_EQUAL(ps.at_end(), false);
231 TEST_EQUAL_DOUBLE(ps.get_weight(), 1000.0 / (1000.0 + coorddist));
232 TEST_EQUAL(ps.get_docid(), 2);
234 ps.next(0.0);
235 TEST_EQUAL(ps.at_end(), false);
236 TEST_EQUAL_DOUBLE(ps.get_weight(), 1000.0 / (1000.0 + coorddist * 2));
237 TEST_EQUAL(ps.get_docid(), 3);
239 ps.next(0.0);
240 TEST_EQUAL(ps.at_end(), true);
243 // Test a search with a loose enough range restriction that all docs should
244 // be returned and implicit metric.
246 Xapian::LatLongDistancePostingSource ps(0, centre, coorddist * 2.5);
247 ps.init(db);
249 ps.next(0.0);
250 TEST_EQUAL(ps.at_end(), false);
251 TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
253 ps.next(0.0);
254 TEST_EQUAL(ps.at_end(), false);
255 TEST_EQUAL_DOUBLE(ps.get_weight(), 1000.0 / (1000.0 + coorddist));
256 TEST_EQUAL(ps.get_docid(), 2);
258 ps.next(0.0);
259 TEST_EQUAL(ps.at_end(), false);
260 TEST_EQUAL_DOUBLE(ps.get_weight(), 1000.0 / (1000.0 + coorddist * 2));
261 TEST_EQUAL(ps.get_docid(), 3);
263 ps.next(0.0);
264 TEST_EQUAL(ps.at_end(), true);
267 return true;
270 // Test various methods of LatLongCoord and LatLongCoords
271 DEFINE_TESTCASE(latlongcoords1, !backend) {
272 LatLongCoord c1(0, 0);
273 LatLongCoord c2(1, 0);
274 LatLongCoord c3(1, 0);
275 LatLongCoord c4(0, 1);
277 // Test comparison
278 TEST_NOT_EQUAL(c1.get_description(), c2.get_description());
279 // Exactly one of these inequalities should be true.
280 TEST((c1 < c2) ^ (c2 < c1));
281 TEST_EQUAL(c2.get_description(), c3.get_description());
282 TEST(!(c2 < c3) && !(c3 < c2));
283 TEST_NOT_EQUAL(c3.get_description(), c4.get_description());
284 // Exactly one of these inequalities should be true. This is a regression
285 // test for bug found prior to 1.3.0.
286 TEST((c3 < c4) ^ (c4 < c3));
288 // Test serialisation
289 std::string s1 = c1.serialise();
290 LatLongCoord c5;
291 c4.unserialise(s1);
292 TEST(!(c1 < c4 || c4 < c1));
293 const char * ptr = s1.data();
294 const char * end = ptr + s1.size();
295 c5.unserialise(&ptr, end);
296 TEST_EQUAL(c1.get_description(), c4.get_description());
297 TEST_EQUAL(c1.get_description(), "Xapian::LatLongCoord(0, 0)");
298 TEST_EQUAL(ptr, end);
300 // Test uninitialised iterator constructor
301 LatLongCoordsIterator i1;
303 // Test building a set of LatLongCoords
304 LatLongCoords g1(c1);
305 TEST(!g1.empty());
306 TEST_EQUAL(g1.size(), 1);
307 TEST_EQUAL(g1.get_description(), "Xapian::LatLongCoords((0, 0))");
308 g1.append(c2);
309 TEST_EQUAL(g1.size(), 2);
310 TEST_EQUAL(g1.get_description(), "Xapian::LatLongCoords((0, 0), (1, 0))");
312 // Test iterating through a set of LatLongCoords
313 i1 = g1.begin();
314 TEST(i1 != g1.end());
315 TEST_EQUAL((*i1).serialise(), c1.serialise());
316 TEST_EQUAL((*(i1++)).serialise(), c1.serialise());
317 TEST(i1 != g1.end());
318 TEST_EQUAL((*i1).serialise(), c2.serialise());
319 i1 = g1.begin();
320 TEST_EQUAL((*(++i1)).serialise(), c2.serialise());
321 TEST(i1 != g1.end());
322 ++i1;
323 TEST(i1 == g1.end());
325 // Test that duplicates are allowed in the list of coordinates, now.
326 g1.append(c3);
327 TEST_EQUAL(g1.size(), 3);
328 TEST_EQUAL(g1.get_description(), "Xapian::LatLongCoords((0, 0), (1, 0), (1, 0))");
330 // Test building an empty LatLongCoords
331 LatLongCoords g2;
332 TEST(g2.empty());
333 TEST_EQUAL(g2.size(), 0);
334 TEST_EQUAL(g2.get_description(), "Xapian::LatLongCoords()");
335 TEST(g2.begin() == g2.end());
337 return true;
340 // Test various methods of LatLongMetric
341 DEFINE_TESTCASE(latlongmetric1, !backend) {
342 LatLongCoord c1(0, 0);
343 LatLongCoord c2(1, 0);
344 Xapian::GreatCircleMetric m1;
345 double d1 = m1(c1, c2);
346 TEST_REL(d1, >, 111226.0);
347 TEST_REL(d1, <, 111227.0);
349 // Let's make another metric, this time using the radius of mars, so
350 // distances should be quite a bit smaller.
351 Xapian::GreatCircleMetric m2(3310000);
352 double d2 = m2(c1, c2);
353 TEST_REL(d2, >, 57770.0);
354 TEST_REL(d2, <, 57771.0);
356 // Check serialise and unserialise.
357 Xapian::Registry registry;
358 std::string s1 = m2.serialise();
359 const Xapian::LatLongMetric * m3;
360 m3 = registry.get_lat_long_metric(m2.name());
361 TEST(m3 != NULL);
362 m3 = m3->unserialise(s1);
363 double d3 = (*m3)(c1, c2);
364 TEST_EQUAL_DOUBLE(d2, d3);
366 delete m3;
368 return true;
371 // Test LatLongMetric on lists of coords.
372 DEFINE_TESTCASE(latlongmetric2, !backend) {
373 LatLongCoord c1(0, 0);
374 LatLongCoord c2(1, 0);
375 LatLongCoords cl1(c1);
376 LatLongCoords cl2(c2);
377 string c2_str = c2.serialise();
378 string cl2_str = cl2.serialise();
379 TEST_EQUAL(c2_str, cl2_str);
381 LatLongCoord c2_check(5, 5);
382 c2_check.unserialise(c2_str);
383 TEST_EQUAL(c2_check.latitude, c2.latitude);
384 TEST_EQUAL(c2_check.longitude, c2.longitude);
386 Xapian::GreatCircleMetric m1;
387 double d1 = m1(c1, c2);
388 double dl1 = m1(cl1, cl2);
389 TEST_EQUAL(d1, dl1);
390 double d1_str = m1(cl1, c2_str);
391 TEST_EQUAL(d1, d1_str);
393 return true;
396 // Test a LatLongDistanceKeyMaker directly.
397 DEFINE_TESTCASE(latlongkeymaker1, !backend) {
398 Xapian::GreatCircleMetric m1(3310000);
399 LatLongCoord c1(0, 0);
400 LatLongCoord c2(1, 0);
401 LatLongCoord c3(2, 0);
402 LatLongCoord c4(3, 0);
404 LatLongCoords g1(c1);
405 g1.append(c2);
407 LatLongDistanceKeyMaker keymaker(0, g1, m1);
408 Xapian::Document doc1;
409 doc1.add_value(0, g1.serialise());
410 Xapian::Document doc2;
411 doc2.add_value(0, c3.serialise());
412 Xapian::Document doc3;
413 doc3.add_value(0, c4.serialise());
414 Xapian::Document doc4;
416 std::string k1 = keymaker(doc1);
417 std::string k2 = keymaker(doc2);
418 std::string k3 = keymaker(doc3);
419 std::string k4 = keymaker(doc4);
420 TEST_REL(k1, <, k2);
421 TEST_REL(k2, <, k3);
422 TEST_REL(k3, <, k4);
424 LatLongDistanceKeyMaker keymaker2(0, g1, m1, 0);
425 std::string k3b = keymaker2(doc3);
426 std::string k4b = keymaker2(doc4);
427 TEST_EQUAL(k3, k3b);
428 TEST_REL(k3b, >, k4b);
430 return true;