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 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
);
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);
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);
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();
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
);
304 TEST_EQUAL(g1
.size(), 1);
305 TEST_EQUAL(g1
.get_description(), "Xapian::LatLongCoords((0, 0))");
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
312 TEST(i1
!= g1
.end());
313 TEST_EQUAL((*i1
).serialise(), c1
.serialise());
314 TEST_EQUAL((*i1
).serialise(), c1
.serialise());
316 TEST(i1
!= g1
.end());
317 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());
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());
360 m3
= m3
->unserialise(s1
);
361 double d3
= (*m3
)(c1
, c2
);
362 TEST_EQUAL_DOUBLE(d2
, d3
);
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
);
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
);
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
);
418 LatLongDistanceKeyMaker
keymaker2(0, g1
, m1
, 0);
419 std::string k3b
= keymaker2(doc3
);
420 std::string k4b
= keymaker2(doc4
);
422 TEST_REL(k3b
, >, k4b
);