1 /** @file api_geospatial.cc
2 * @brief Tests of geospatial functionality.
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
25 #include "api_geospatial.h"
29 #include "testsuite.h"
30 #include "testutils.h"
33 using namespace Xapian
;
35 // #######################################################################
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);
46 doc
.add_value(0, coord1
.serialise());
49 doc
= Xapian::Document();
50 doc
.add_value(0, coord2
.serialise());
53 doc
= Xapian::Document();
54 doc
.add_value(0, coord3
.serialise());
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
);
77 TEST_EQUAL(ps
.at_end(), false);
78 TEST_EQUAL_DOUBLE(ps
.get_weight(), 1.0);
79 TEST_EQUAL(ps
.get_docid(), 1);
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);
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);
92 TEST_EQUAL(ps
.at_end(), true);
95 // Test a search with no range restriction and implicit metric.
97 Xapian::LatLongDistancePostingSource
ps(0, coord1
);
101 TEST_EQUAL(ps
.at_end(), false);
102 TEST_EQUAL_DOUBLE(ps
.get_weight(), 1.0);
103 TEST_EQUAL(ps
.get_docid(), 1);
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);
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);
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);
125 TEST_EQUAL(ps
.at_end(), false);
126 TEST_EQUAL_DOUBLE(ps
.get_weight(), 1.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);
138 TEST_EQUAL(ps
.at_end(), false);
139 TEST_EQUAL_DOUBLE(ps
.get_weight(), 1.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
);
151 TEST_EQUAL(ps
.at_end(), false);
152 TEST_EQUAL_DOUBLE(ps
.get_weight(), 1.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);
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
);
169 TEST_EQUAL(ps
.at_end(), false);
170 TEST_EQUAL_DOUBLE(ps
.get_weight(), 1.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);
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);
188 TEST_EQUAL(ps
.at_end(), false);
189 TEST_EQUAL_DOUBLE(ps
.get_weight(), 1.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);
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);
207 TEST_EQUAL(ps
.at_end(), false);
208 TEST_EQUAL_DOUBLE(ps
.get_weight(), 1.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);
216 TEST_EQUAL(ps
.at_end(), true);
219 // Test a search with a loose enough range restriction that all docs should
222 Xapian::LatLongDistancePostingSource
ps(0, centre
, metric
, coorddist
* 2.5);
226 TEST_EQUAL(ps
.at_end(), false);
227 TEST_EQUAL_DOUBLE(ps
.get_weight(), 1.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);
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);
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);
250 TEST_EQUAL(ps
.at_end(), false);
251 TEST_EQUAL_DOUBLE(ps
.get_weight(), 1.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);
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);
264 TEST_EQUAL(ps
.at_end(), 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);
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();
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
);
306 TEST_EQUAL(g1
.size(), 1);
307 TEST_EQUAL(g1
.get_description(), "Xapian::LatLongCoords((0, 0))");
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
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());
320 TEST_EQUAL((*(++i1
)).serialise(), c2
.serialise());
321 TEST(i1
!= g1
.end());
323 TEST(i1
== g1
.end());
325 // Test that duplicates are allowed in the list of coordinates, now.
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
333 TEST_EQUAL(g2
.size(), 0);
334 TEST_EQUAL(g2
.get_description(), "Xapian::LatLongCoords()");
335 TEST(g2
.begin() == g2
.end());
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());
362 m3
= m3
->unserialise(s1
);
363 double d3
= (*m3
)(c1
, c2
);
364 TEST_EQUAL_DOUBLE(d2
, d3
);
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
);
390 double d1_str
= m1(cl1
, c2_str
);
391 TEST_EQUAL(d1
, d1_str
);
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
);
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
);
424 LatLongDistanceKeyMaker
keymaker2(0, g1
, m1
, 0);
425 std::string k3b
= keymaker2(doc3
);
426 std::string k4b
= keymaker2(doc4
);
428 TEST_REL(k3b
, >, k4b
);