xapian-core  1.4.25
api_geospatial.cc
Go to the documentation of this file.
1 
4 /* Copyright 2008 Lemur Consulting Ltd
5  * Copyright 2010,2011 Richard Boulton
6  * Copyright 2012,2016 Olly Betts
7  *
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.
12  *
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.
17  *
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
22  */
23 
24 #include <config.h>
25 #include "api_geospatial.h"
26 #include <xapian.h>
27 
28 #include "apitest.h"
29 #include "testsuite.h"
30 #include "testutils.h"
31 
32 using namespace std;
33 using namespace Xapian;
34 
35 // #######################################################################
36 // # Tests start here
37 
38 static void
40 {
41  Xapian::LatLongCoord coord1(10, 10);
42  Xapian::LatLongCoord coord2(20, 10);
43  Xapian::LatLongCoord coord3(30, 10);
44 
45  Xapian::Document doc;
46  doc.add_value(0, coord1.serialise());
47  db.add_document(doc);
48 
49  doc = Xapian::Document();
50  doc.add_value(0, coord2.serialise());
51  db.add_document(doc);
52 
53  doc = Xapian::Document();
54  doc.add_value(0, coord3.serialise());
55  db.add_document(doc);
56 }
57 
59 DEFINE_TESTCASE(latlongpostingsource1, backend && !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);
64 
66  Xapian::LatLongCoords centre;
67  centre.append(coord1);
68  double coorddist = metric(coord1, coord2);
69  TEST_EQUAL_DOUBLE(coorddist, metric(coord2, coord3));
70 
71  // Test a search with no range restriction.
72  {
73  Xapian::LatLongDistancePostingSource ps(0, coord1, metric);
74  ps.init(db);
75 
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);
80 
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);
85 
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);
90 
91  ps.next(0.0);
92  TEST_EQUAL(ps.at_end(), true);
93  }
94 
95  // Test a search with no range restriction and implicit metric.
96  {
98  ps.init(db);
99 
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);
104 
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);
109 
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);
114 
115  ps.next(0.0);
116  TEST_EQUAL(ps.at_end(), true);
117  }
118 
119  // Test a search with a tight range restriction
120  {
121  Xapian::LatLongDistancePostingSource ps(0, centre, metric, coorddist * 0.5);
122  ps.init(db);
123 
124  ps.next(0.0);
125  TEST_EQUAL(ps.at_end(), false);
126  TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
127 
128  ps.next(0.0);
129  TEST_EQUAL(ps.at_end(), true);
130  }
131 
132  // Test a search with a tight range restriction and implicit metric.
133  {
134  Xapian::LatLongDistancePostingSource ps(0, centre, coorddist * 0.5);
135  ps.init(db);
136 
137  ps.next(0.0);
138  TEST_EQUAL(ps.at_end(), false);
139  TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
140 
141  ps.next(0.0);
142  TEST_EQUAL(ps.at_end(), true);
143  }
144 
145  // Test a search with a looser range restriction
146  {
147  Xapian::LatLongDistancePostingSource ps(0, centre, metric, coorddist);
148  ps.init(db);
149 
150  ps.next(0.0);
151  TEST_EQUAL(ps.at_end(), false);
152  TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
153 
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);
158 
159  ps.next(0.0);
160  TEST_EQUAL(ps.at_end(), true);
161  }
162 
163  // Test a search with a looser range restriction and implicit metric.
164  {
165  Xapian::LatLongDistancePostingSource ps(0, centre, coorddist);
166  ps.init(db);
167 
168  ps.next(0.0);
169  TEST_EQUAL(ps.at_end(), false);
170  TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
171 
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);
176 
177  ps.next(0.0);
178  TEST_EQUAL(ps.at_end(), true);
179  }
180 
181  // Test a search with a looser range restriction, but not enough to return
182  // the next document.
183  {
184  Xapian::LatLongDistancePostingSource ps(0, centre, metric, coorddist * 1.5);
185  ps.init(db);
186 
187  ps.next(0.0);
188  TEST_EQUAL(ps.at_end(), false);
189  TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
190 
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);
195 
196  ps.next(0.0);
197  TEST_EQUAL(ps.at_end(), true);
198  }
199 
200  // Test a search with a looser range restriction, but not enough to return
201  // the next document and implicit metric.
202  {
203  Xapian::LatLongDistancePostingSource ps(0, centre, coorddist * 1.5);
204  ps.init(db);
205 
206  ps.next(0.0);
207  TEST_EQUAL(ps.at_end(), false);
208  TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
209 
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);
214 
215  ps.next(0.0);
216  TEST_EQUAL(ps.at_end(), true);
217  }
218 
219  // Test a search with a loose enough range restriction that all docs should
220  // be returned.
221  {
222  Xapian::LatLongDistancePostingSource ps(0, centre, metric, coorddist * 2.5);
223  ps.init(db);
224 
225  ps.next(0.0);
226  TEST_EQUAL(ps.at_end(), false);
227  TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
228 
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);
233 
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);
238 
239  ps.next(0.0);
240  TEST_EQUAL(ps.at_end(), true);
241  }
242 
243  // Test a search with a loose enough range restriction that all docs should
244  // be returned and implicit metric.
245  {
246  Xapian::LatLongDistancePostingSource ps(0, centre, coorddist * 2.5);
247  ps.init(db);
248 
249  ps.next(0.0);
250  TEST_EQUAL(ps.at_end(), false);
251  TEST_EQUAL_DOUBLE(ps.get_weight(), 1.0);
252 
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);
257 
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);
262 
263  ps.next(0.0);
264  TEST_EQUAL(ps.at_end(), true);
265  }
266 }
267 
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);
274 
275  // Test comparison
277  // Exactly one of these inequalities should be true.
278  TEST((c1 < c2) ^ (c2 < c1));
280  TEST(!(c2 < c3) && !(c3 < c2));
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));
285 
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);
295  TEST_EQUAL(c1.get_description(), "Xapian::LatLongCoord(0, 0)");
296  TEST_EQUAL(ptr, end);
297 
298  // Test uninitialised iterator constructor
300 
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))");
309 
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());
324 
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))");
329 
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());
336 }
337 
338 // Test various methods of LatLongMetric
339 DEFINE_TESTCASE(latlongmetric1, !backend) {
340  LatLongCoord c1(0, 0);
341  LatLongCoord c2(1, 0);
343  double d1 = m1(c1, c2);
344  TEST_REL(d1, >, 111226.0);
345  TEST_REL(d1, <, 111227.0);
346 
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);
353 
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);
363 
364  delete m3;
365 }
366 
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);
376 
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);
381 
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);
388 }
389 
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);
397 
398  LatLongCoords g1(c1);
399  g1.append(c2);
400 
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;
409 
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);
417 
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);
423 }
The Xapian namespace contains public interfaces for the Xapian library.
Definition: compactor.cc:80
Xapian::docid add_document(const Xapian::Document &document)
Add a new document to the database.
Definition: omdatabase.cc:902
void add_value(Xapian::valueno slot, const std::string &value)
Add a new value.
Definition: omdocument.cc:107
static void builddb_coords1(Xapian::WritableDatabase &db, const string &)
#define TEST(a)
Test a condition, without an additional explanation for failure.
Definition: testsuite.h:275
This class is used to access a database, or a group of databases.
Definition: database.h:68
bool empty() const
Return true if and only if there are no coordinates in the container.
Definition: geospatial.h:254
Xapian::docid get_docid() const
Return the current docid.
std::string get_description() const
Return a string describing this object.
std::string serialise() const
Serialise object parameters into a string.
bool at_end() const
Return true if the current position is past the last entry in this list.
a generic test suite engine
STL namespace.
std::string serialise() const
Return a serialised representation of the coordinate.
Definition: latlongcoord.cc:75
std::string serialise() const
Return a serialised form of the coordinate list.
std::string name() const
Return the full name of the metric.
test functionality of the Xapian API
#define TEST_REL(A, REL, B)
Test a relation holds,e.g. TEST_REL(a,>,b);.
Definition: testmacros.h:32
#define TEST_NOT_EQUAL(a, b)
Test for non-equality of two things.
Definition: testsuite.h:305
Posting source which returns a weight based on geospatial distance.
Definition: geospatial.h:454
LatLongCoordsIterator begin() const
Get a begin iterator for the coordinates.
Definition: geospatial.h:238
A latitude-longitude coordinate.
Definition: geospatial.h:81
This class provides read/write access to a database.
Definition: database.h:789
void unserialise(const std::string &serialised)
Unserialise a string and set this object to its coordinate.
Definition: latlongcoord.cc:47
Registry for user subclasses.
Definition: registry.h:47
Public interfaces for the Xapian library.
double latitude
A latitude, as decimal degrees.
Definition: geospatial.h:88
virtual LatLongMetric * unserialise(const std::string &serialised) const =0
Create object given string serialisation returned by serialise().
void init(const Database &db_)
Set this PostingSource to the start of the list of postings.
Base class for calculating distances between two lat/long coordinates.
Definition: geospatial.h:303
A sequence of latitude-longitude coordinates.
Definition: geospatial.h:232
#define TEST_EQUAL_DOUBLE(a, b)
Test two doubles for near equality.
Definition: testsuite.h:295
KeyMaker subclass which sorts by distance from a latitude/longitude.
Definition: geospatial.h:551
double get_weight() const
Return the weight contribution for the current document.
Xapian::Database get_database(const string &dbname)
Definition: apitest.cc:48
void append(const LatLongCoord &coord)
Append a coordinate to the end of the sequence.
Definition: geospatial.h:260
An iterator across the values in a LatLongCoords object.
Definition: geospatial.h:165
std::string get_description() const
Return a string describing this object.
Definition: latlongcoord.cc:83
Xapian-specific test helper functions and macros.
LatLongCoordsIterator end() const
Get an end iterator for the coordinates.
Definition: geospatial.h:243
Calculate the great-circle distance between two coordinates on a sphere.
Definition: geospatial.h:399
DEFINE_TESTCASE(latlongpostingsource1, backend &&!remote &&!inmemory)
Test behaviour of the LatLongDistancePostingSource.
#define TEST_EQUAL(a, b)
Test for equality of two things.
Definition: testsuite.h:278
void next(double min_wt)
Advance the current position to the next matching document.
A handle representing a document in a Xapian database.
Definition: document.h:61
size_t size() const
Get the number of coordinates in the container.
Definition: geospatial.h:248
double longitude
A longitude, as decimal degrees.
Definition: geospatial.h:98
const Xapian::LatLongMetric * get_lat_long_metric(const std::string &name) const
Get a lat-long metric given a name.
Definition: registry.cc:314