Update for 1.4.20
[xapian.git] / xapian-core / tests / api_geospatial.cc
blob38a4d5f86e53367b12fdb20becac74f8e371b6a7
1 /** @file
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 LatLongDistancePostingSource
59 DEFINE_TESTCASE(latlongpostingsource1, generated && !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);
268 // Test various methods of LatLongCoord and LatLongCoords
269 DEFINE_TESTCASE(latlongcoords1, !backend) {
270 LatLongCoord c1(0, 0);
271 LatLongCoord c2(1, 0);
272 LatLongCoord c3(1, 0);
273 LatLongCoord c4(0, 1);
275 // Test comparison
276 TEST_NOT_EQUAL(c1.get_description(), c2.get_description());
277 // Exactly one of these inequalities should be true.
278 TEST((c1 < c2) ^ (c2 < c1));
279 TEST_EQUAL(c2.get_description(), c3.get_description());
280 TEST(!(c2 < c3) && !(c3 < c2));
281 TEST_NOT_EQUAL(c3.get_description(), c4.get_description());
282 // Exactly one of these inequalities should be true. This is a regression
283 // test for bug found prior to 1.3.0.
284 TEST((c3 < c4) ^ (c4 < c3));
286 // Test serialisation
287 std::string s1 = c1.serialise();
288 LatLongCoord c5;
289 c4.unserialise(s1);
290 TEST(!(c1 < c4 || c4 < c1));
291 const char * ptr = s1.data();
292 const char * end = ptr + s1.size();
293 c5.unserialise(&ptr, end);
294 TEST_EQUAL(c1.get_description(), c4.get_description());
295 TEST_EQUAL(c1.get_description(), "Xapian::LatLongCoord(0, 0)");
296 TEST_EQUAL(ptr, end);
298 // Test uninitialised iterator constructor
299 LatLongCoordsIterator i1;
301 // Test building a set of LatLongCoords
302 LatLongCoords g1(c1);
303 TEST(!g1.empty());
304 TEST_EQUAL(g1.size(), 1);
305 TEST_EQUAL(g1.get_description(), "Xapian::LatLongCoords((0, 0))");
306 g1.append(c2);
307 TEST_EQUAL(g1.size(), 2);
308 TEST_EQUAL(g1.get_description(), "Xapian::LatLongCoords((0, 0), (1, 0))");
310 // Test iterating through a set of LatLongCoords
311 i1 = g1.begin();
312 TEST(i1 != g1.end());
313 TEST_EQUAL((*i1).serialise(), c1.serialise());
314 TEST_EQUAL((*i1).serialise(), c1.serialise());
315 ++i1;
316 TEST(i1 != g1.end());
317 TEST_EQUAL((*i1).serialise(), c2.serialise());
318 i1 = g1.begin();
319 ++i1;
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());
338 // Test various methods of LatLongMetric
339 DEFINE_TESTCASE(latlongmetric1, !backend) {
340 LatLongCoord c1(0, 0);
341 LatLongCoord c2(1, 0);
342 Xapian::GreatCircleMetric m1;
343 double d1 = m1(c1, c2);
344 TEST_REL(d1, >, 111226.0);
345 TEST_REL(d1, <, 111227.0);
347 // Let's make another metric, this time using the radius of mars, so
348 // distances should be quite a bit smaller.
349 Xapian::GreatCircleMetric m2(3310000);
350 double d2 = m2(c1, c2);
351 TEST_REL(d2, >, 57770.0);
352 TEST_REL(d2, <, 57771.0);
354 // Check serialise and unserialise.
355 Xapian::Registry registry;
356 std::string s1 = m2.serialise();
357 const Xapian::LatLongMetric * m3;
358 m3 = registry.get_lat_long_metric(m2.name());
359 TEST(m3 != NULL);
360 m3 = m3->unserialise(s1);
361 double d3 = (*m3)(c1, c2);
362 TEST_EQUAL_DOUBLE(d2, d3);
364 delete m3;
367 // Test LatLongMetric on lists of coords.
368 DEFINE_TESTCASE(latlongmetric2, !backend) {
369 LatLongCoord c1(0, 0);
370 LatLongCoord c2(1, 0);
371 LatLongCoords cl1(c1);
372 LatLongCoords cl2(c2);
373 string c2_str = c2.serialise();
374 string cl2_str = cl2.serialise();
375 TEST_EQUAL(c2_str, cl2_str);
377 LatLongCoord c2_check(5, 5);
378 c2_check.unserialise(c2_str);
379 TEST_EQUAL(c2_check.latitude, c2.latitude);
380 TEST_EQUAL(c2_check.longitude, c2.longitude);
382 Xapian::GreatCircleMetric m1;
383 double d1 = m1(c1, c2);
384 double dl1 = m1(cl1, cl2);
385 TEST_EQUAL(d1, dl1);
386 double d1_str = m1(cl1, c2_str);
387 TEST_EQUAL(d1, d1_str);
390 // Test a LatLongDistanceKeyMaker directly.
391 DEFINE_TESTCASE(latlongkeymaker1, !backend) {
392 Xapian::GreatCircleMetric m1(3310000);
393 LatLongCoord c1(0, 0);
394 LatLongCoord c2(1, 0);
395 LatLongCoord c3(2, 0);
396 LatLongCoord c4(3, 0);
398 LatLongCoords g1(c1);
399 g1.append(c2);
401 LatLongDistanceKeyMaker keymaker(0, g1, m1);
402 Xapian::Document doc1;
403 doc1.add_value(0, g1.serialise());
404 Xapian::Document doc2;
405 doc2.add_value(0, c3.serialise());
406 Xapian::Document doc3;
407 doc3.add_value(0, c4.serialise());
408 Xapian::Document doc4;
410 std::string k1 = keymaker(doc1);
411 std::string k2 = keymaker(doc2);
412 std::string k3 = keymaker(doc3);
413 std::string k4 = keymaker(doc4);
414 TEST_REL(k1, <, k2);
415 TEST_REL(k2, <, k3);
416 TEST_REL(k3, <, k4);
418 LatLongDistanceKeyMaker keymaker2(0, g1, m1, 0);
419 std::string k3b = keymaker2(doc3);
420 std::string k4b = keymaker2(doc4);
421 TEST_EQUAL(k3, k3b);
422 TEST_REL(k3b, >, k4b);