Fix whitespace irregularities in code
[xapian.git] / xapian-core / include / xapian / geospatial.h
blobbaa60336ba7dd0875ae948b4f824961df00ee1a9
1 /** @file geospatial.h
2 * @brief Geospatial search support routines.
3 */
4 /* Copyright 2008,2009 Lemur Consulting Ltd
5 * Copyright 2010,2011 Richard Boulton
6 * Copyright 2012,2013,2014,2015,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 #ifndef XAPIAN_INCLUDED_GEOSPATIAL_H
25 #define XAPIAN_INCLUDED_GEOSPATIAL_H
27 #if !defined XAPIAN_IN_XAPIAN_H && !defined XAPIAN_LIB_BUILD
28 # error "Never use <xapian/geospatial.h> directly; include <xapian.h> instead."
29 #endif
31 #include <iterator>
32 #include <vector>
33 #include <string>
35 #include <xapian/attributes.h>
36 #include <xapian/derefwrapper.h>
37 #include <xapian/keymaker.h>
38 #include <xapian/postingsource.h>
39 #include <xapian/queryparser.h> // For sortable_serialise
40 #include <xapian/visibility.h>
42 namespace Xapian {
44 class Registry;
46 double
47 XAPIAN_NOTHROW(miles_to_metres(double miles)) XAPIAN_CONST_FUNCTION;
49 /** Convert from miles to metres.
51 * Experimental - see https://xapian.org/docs/deprecation#experimental-features
53 inline double
54 miles_to_metres(double miles) XAPIAN_NOEXCEPT
56 return 1609.344 * miles;
59 double
60 XAPIAN_NOTHROW(metres_to_miles(double metres)) XAPIAN_CONST_FUNCTION;
62 /** Convert from metres to miles.
64 * Experimental - see https://xapian.org/docs/deprecation#experimental-features
66 inline double
67 metres_to_miles(double metres) XAPIAN_NOEXCEPT
69 return metres * (1.0 / 1609.344);
72 /** A latitude-longitude coordinate.
74 * Experimental - see https://xapian.org/docs/deprecation#experimental-features
76 * Note that latitude-longitude coordinates are only precisely meaningful if
77 * the datum used to define them is specified. This class ignores this
78 * issue - it is up to the caller to ensure that the datum used for each
79 * coordinate in a system is consistent.
81 struct XAPIAN_VISIBILITY_DEFAULT LatLongCoord {
82 /** A latitude, as decimal degrees.
84 * Should be in the range -90 <= latitude <= 90
86 * Positive latitudes represent the northern hemisphere.
88 double latitude;
90 /** A longitude, as decimal degrees.
92 * Will be wrapped around, so for example, -150 is equal to 210. When
93 * obtained from a serialised form, will be in the range 0 <= longitude <
94 * 360.
96 * Longitudes increase as coordinates move eastwards.
98 double longitude;
100 /** Construct an uninitialised coordinate.
102 XAPIAN_NOTHROW(LatLongCoord()) {}
104 /** Construct a coordinate.
106 * If the supplied longitude is out of the standard range, it will be
107 * normalised to the range 0 <= longitude < 360.
109 * If you want to avoid the checks (for example, you know that your values
110 * are already in range), you can use the alternate constructor to
111 * construct an uninitialised coordinate, and then set the latitude and
112 * longitude directly.
114 * @exception InvalidArgumentError the supplied latitude is out of range.
116 LatLongCoord(double latitude_, double longitude_);
118 /** Unserialise a string and set this object to its coordinate.
120 * @param serialised the string to unserialise the coordinate from.
122 * @exception Xapian::SerialisationError if the string does not contain
123 * a valid serialised latitude-longitude pair, or contains extra data at
124 * the end of it.
126 void unserialise(const std::string & serialised);
128 /** Unserialise a buffer and set this object to its coordinate.
130 * The buffer may contain further data after that for the coordinate.
132 * @param ptr A pointer to the start of the string. This will be updated
133 * to point to the end of the data representing the coordinate.
134 * @param end A pointer to the end of the string.
136 * @exception Xapian::SerialisationError if the string does not start with
137 * a valid serialised latitude-longitude pair.
139 void unserialise(const char ** ptr, const char * end);
141 /** Return a serialised representation of the coordinate.
143 std::string serialise() const;
145 /** Compare with another LatLongCoord.
147 * This is mostly provided so that things like std::map<LatLongCoord> work
148 * - the ordering isn't particularly meaningful.
150 bool XAPIAN_NOTHROW(operator<(const LatLongCoord & other) const)
152 if (latitude < other.latitude) return true;
153 if (latitude > other.latitude) return false;
154 return (longitude < other.longitude);
157 /// Return a string describing this object.
158 std::string get_description() const;
161 /** An iterator across the values in a LatLongCoords object.
163 * Experimental - see https://xapian.org/docs/deprecation#experimental-features
165 class XAPIAN_VISIBILITY_DEFAULT LatLongCoordsIterator {
166 /// Friend class which needs to be able to construct us.
167 friend class LatLongCoords;
169 /// The current position of the iterator.
170 std::vector<LatLongCoord>::const_iterator iter;
172 /// Constructor used by LatLongCoords.
173 LatLongCoordsIterator(std::vector<LatLongCoord>::const_iterator iter_)
174 : iter(iter_) {}
176 public:
177 /// Default constructor. Produces an uninitialised iterator.
178 LatLongCoordsIterator() {}
180 /** Get the LatLongCoord for the current position. */
181 const LatLongCoord & operator*() const {
182 return *iter;
185 /// Advance the iterator to the next position.
186 LatLongCoordsIterator & operator++() {
187 ++iter;
188 return *this;
191 /// Advance the iterator to the next position (postfix version).
192 DerefWrapper_<LatLongCoord> operator++(int) {
193 const LatLongCoord & tmp = **this;
194 ++iter;
195 return DerefWrapper_<LatLongCoord>(tmp);
198 /// Equality test for LatLongCoordsIterator objects.
199 bool operator==(const LatLongCoordsIterator &other) const
201 return iter == other.iter;
204 /** @private @internal LatLongCoordsIterator is what the C++ STL calls an
205 * input_iterator.
207 * The following typedefs allow std::iterator_traits<> to work so that
208 * this iterator can be used with the STL.
210 * These are deliberately hidden from the Doxygen-generated docs, as the
211 * machinery here isn't interesting to API users. They just need to know
212 * that Xapian iterator classes are compatible with the STL.
214 // @{
215 /// @private
216 typedef std::input_iterator_tag iterator_category;
217 /// @private
218 typedef LatLongCoord value_type;
219 /// @private
220 typedef size_t difference_type;
221 /// @private
222 typedef LatLongCoord * pointer;
223 /// @private
224 typedef LatLongCoord & reference;
225 // @}
228 /** A sequence of latitude-longitude coordinates.
230 * Experimental - see https://xapian.org/docs/deprecation#experimental-features
232 class XAPIAN_VISIBILITY_DEFAULT LatLongCoords {
233 /// The coordinates.
234 std::vector<LatLongCoord> coords;
236 public:
237 /// Get a begin iterator for the coordinates.
238 LatLongCoordsIterator begin() const {
239 return LatLongCoordsIterator(coords.begin());
242 /// Get an end iterator for the coordinates.
243 LatLongCoordsIterator end() const {
244 return LatLongCoordsIterator(coords.end());
247 /// Get the number of coordinates in the container.
248 size_t size() const
250 return coords.size();
253 /// Return true if and only if there are no coordinates in the container.
254 bool empty() const
256 return coords.empty();
259 /// Append a coordinate to the end of the sequence.
260 void append(const LatLongCoord & coord)
262 coords.push_back(coord);
265 /// Construct an empty container.
266 LatLongCoords() : coords() {}
268 /// Construct a container holding one coordinate.
269 LatLongCoords(const LatLongCoord & coord) : coords()
271 coords.push_back(coord);
274 /** Unserialise a string and set this object to the coordinates in it.
276 * @param serialised the string to unserialise the coordinates from.
278 * @exception Xapian::SerialisationError if the string does not contain
279 * a valid serialised latitude-longitude pair, or contains junk at the end
280 * of it.
282 void unserialise(const std::string & serialised);
284 /** Return a serialised form of the coordinate list.
286 std::string serialise() const;
288 /// Return a string describing this object.
289 std::string get_description() const;
292 /// Inequality test for LatLongCoordsIterator objects.
293 inline bool
294 operator!=(const LatLongCoordsIterator &a, const LatLongCoordsIterator &b)
296 return !(a == b);
299 /** Base class for calculating distances between two lat/long coordinates.
301 * Experimental - see https://xapian.org/docs/deprecation#experimental-features
303 class XAPIAN_VISIBILITY_DEFAULT LatLongMetric {
304 public:
305 /// Destructor.
306 virtual ~LatLongMetric();
308 /** Return the distance between two coordinates, in metres.
310 virtual double pointwise_distance(const LatLongCoord & a,
311 const LatLongCoord & b) const = 0;
313 /** Return the distance between two coordinate lists, in metres.
315 * The distance between the coordinate lists is defined to be the minimum
316 * pairwise distance between coordinates in the lists.
318 * @exception InvalidArgumentError either of the lists is empty.
320 * @param a The first coordinate list.
321 * @param b The second coordinate list.
323 double operator()(const LatLongCoords & a, const LatLongCoords & b) const;
325 /** Return the distance between two coordinate lists, in metres.
327 * One of the coordinate lists is supplied in serialised form.
329 * The distance between the coordinate lists is defined to be the minimum
330 * pairwise distance between coordinates in the lists.
332 * @exception InvalidArgumentError either of the lists is empty.
334 * @param a The first coordinate list.
335 * @param b The second coordinate list, in serialised form.
337 double operator()(const LatLongCoords & a, const std::string & b) const
339 return (*this)(a, b.data(), b.size());
342 /** Return the distance between two coordinate lists, in metres.
344 * One of the coordinate lists is supplied in serialised form.
346 * The distance between the coordinate lists is defined to be the minimum
347 * pairwise distance between coordinates in the lists.
349 * @exception InvalidArgumentError either of the lists is empty.
351 * @param a The first coordinate list.
352 * @param b_ptr The start of the serialised form of the second coordinate
353 * list.
354 * @param b_len The length of the serialised form of the second coordinate
355 * list.
357 double operator()(const LatLongCoords & a,
358 const char * b_ptr, size_t b_len) const;
360 /** Clone the metric. */
361 virtual LatLongMetric * clone() const = 0;
363 /** Return the full name of the metric.
365 * This is used when serialising and unserialising metrics; for example,
366 * for performing remote searches.
368 * If the subclass is in a C++ namespace, the namespace should be included
369 * in the name, using "::" as a separator. For example, for a
370 * LatLongMetric subclass called "FooLatLongMetric" in the "Xapian"
371 * namespace the result of this call should be "Xapian::FooLatLongMetric".
373 virtual std::string name() const = 0;
375 /** Serialise object parameters into a string.
377 * The serialised parameters should represent the configuration of the
378 * metric.
380 virtual std::string serialise() const = 0;
382 /** Create object given string serialisation returned by serialise().
384 * @param serialised A serialised instance of this LatLongMetric subclass.
386 virtual LatLongMetric * unserialise(const std::string & serialised) const = 0;
389 /** Calculate the great-circle distance between two coordinates on a sphere.
391 * Experimental - see https://xapian.org/docs/deprecation#experimental-features
393 * This uses the haversine formula to calculate the distance. Note that this
394 * formula is subject to inaccuracy due to numerical errors for coordinates on
395 * the opposite side of the sphere.
397 * See http://en.wikipedia.org/wiki/Haversine_formula
399 class XAPIAN_VISIBILITY_DEFAULT GreatCircleMetric : public LatLongMetric {
400 /** The radius of the sphere in metres.
402 double radius;
404 public:
405 /** Construct a GreatCircleMetric.
407 * The (quadratic mean) radius of the Earth will be used by this
408 * calculator.
410 GreatCircleMetric();
412 /** Construct a GreatCircleMetric using a specified radius.
414 * This is useful for data sets in which the points are not on Earth (eg,
415 * a database of features on Mars).
417 * @param radius_ The radius of the sphere to use, in metres.
419 explicit GreatCircleMetric(double radius_);
421 /** Return the great-circle distance between points on the sphere.
423 double pointwise_distance(const LatLongCoord & a,
424 const LatLongCoord &b) const;
426 LatLongMetric * clone() const;
427 std::string name() const;
428 std::string serialise() const;
429 LatLongMetric * unserialise(const std::string & serialised) const;
432 /** Posting source which returns a weight based on geospatial distance.
434 * Experimental - see https://xapian.org/docs/deprecation#experimental-features
436 * Results are weighted by the distance from a fixed point, or list of points,
437 * calculated according to the metric supplied. If multiple points are
438 * supplied (either in the constructor, or in the coordinates stored in a
439 * document), the closest pointwise distance is used.
441 * Documents further away than a specified maximum range (or with no location
442 * stored in the specified slot) will not be returned.
444 * The weight returned is computed from the distance using the formula:
446 * k1 * pow(distance + k1, -k2)
448 * (Where k1 and k2 are (strictly) positive, floating point constants, which
449 * default to 1000 and 1, respectively. Distance is measured in metres, so
450 * this means that something at the centre gets a weight of 1.0, something 1km
451 * away gets a weight of 0.5, and something 3km away gets a weight of 0.25,
452 * etc)
454 class XAPIAN_VISIBILITY_DEFAULT LatLongDistancePostingSource : public ValuePostingSource
456 /// Current distance from centre.
457 double dist;
459 /// Centre, to compute distance from.
460 LatLongCoords centre;
462 /// Metric to compute the distance with.
463 const LatLongMetric * metric;
465 /// Maximum range to allow. If set to 0, there is no maximum range.
466 double max_range;
468 /// Constant used in weighting function.
469 double k1;
471 /// Constant used in weighting function.
472 double k2;
474 /// Calculate the distance for the current document.
475 void calc_distance();
477 /// Internal constructor; used by clone() and serialise().
478 LatLongDistancePostingSource(Xapian::valueno slot_,
479 const LatLongCoords & centre_,
480 const LatLongMetric * metric_,
481 double max_range_,
482 double k1_,
483 double k2_);
485 public:
486 /** Construct a new posting source which returns only documents within
487 * range of one of the central coordinates.
489 * @param slot_ The value slot to read values from.
490 * @param centre_ The centre point to use for distance calculations.
491 * @param metric_ The metric to use for distance calculations.
492 * @param max_range_ The maximum distance for documents which are returned.
493 * @param k1_ The k1 constant to use in the weighting function.
494 * @param k2_ The k2 constant to use in the weighting function.
496 LatLongDistancePostingSource(Xapian::valueno slot_,
497 const LatLongCoords & centre_,
498 const LatLongMetric & metric_,
499 double max_range_ = 0.0,
500 double k1_ = 1000.0,
501 double k2_ = 1.0);
503 /** Construct a new posting source which returns only documents within
504 * range of one of the central coordinates.
506 * @param slot_ The value slot to read values from.
507 * @param centre_ The centre point to use for distance calculations.
508 * @param max_range_ The maximum distance for documents which are returned.
509 * @param k1_ The k1 constant to use in the weighting function.
510 * @param k2_ The k2 constant to use in the weighting function.
512 * Xapian::GreatCircleMetric is used as the metric.
514 LatLongDistancePostingSource(Xapian::valueno slot_,
515 const LatLongCoords & centre_,
516 double max_range_ = 0.0,
517 double k1_ = 1000.0,
518 double k2_ = 1.0);
519 ~LatLongDistancePostingSource();
521 void next(double min_wt);
522 void skip_to(Xapian::docid min_docid, double min_wt);
523 bool check(Xapian::docid min_docid, double min_wt);
525 double get_weight() const;
526 LatLongDistancePostingSource * clone() const;
527 std::string name() const;
528 std::string serialise() const;
529 LatLongDistancePostingSource *
530 unserialise_with_registry(const std::string &serialised,
531 const Registry & registry) const;
532 void init(const Database & db_);
534 std::string get_description() const;
537 /** KeyMaker subclass which sorts by distance from a latitude/longitude.
539 * Experimental - see https://xapian.org/docs/deprecation#experimental-features
541 * Results are ordered by the distance from a fixed point, or list of points,
542 * calculated according to the metric supplied. If multiple points are
543 * supplied (either in the constructor, or in the coordinates stored in a
544 * document), the closest pointwise distance is used.
546 * If a document contains no coordinate stored in the specified slot, a
547 * special value for the distance will be used. This defaults to a large
548 * number, so that such results get a low rank, but may be specified by a
549 * constructor parameter.
551 class XAPIAN_VISIBILITY_DEFAULT LatLongDistanceKeyMaker : public KeyMaker {
553 /// The value slot to read.
554 Xapian::valueno slot;
556 /// The centre point (or points) for distance calculation.
557 LatLongCoords centre;
559 /// The metric to use when calculating distances.
560 const LatLongMetric * metric;
562 /// The default key to return, for documents with no value stored.
563 std::string defkey;
565 public:
566 /** Construct a LatLongDistanceKeyMaker.
568 * @param slot_ Value slot to use.
569 * @param centre_ List of points to calculate distance from
570 * (closest distance is used).
571 * @param metric_ LatLongMetric to use.
572 * @param defdistance Distance to use for docs with no value set.
574 LatLongDistanceKeyMaker(Xapian::valueno slot_,
575 const LatLongCoords & centre_,
576 const LatLongMetric & metric_,
577 double defdistance)
578 : slot(slot_),
579 centre(centre_),
580 metric(metric_.clone()),
581 defkey(sortable_serialise(defdistance))
584 /** Construct a LatLongDistanceKeyMaker.
586 * @param slot_ Value slot to use.
587 * @param centre_ List of points to calculate distance from
588 * (closest distance is used).
589 * @param metric_ LatLongMetric to use.
591 * Documents where no value is set are assumed to be a large distance
592 * away.
594 LatLongDistanceKeyMaker(Xapian::valueno slot_,
595 const LatLongCoords & centre_,
596 const LatLongMetric & metric_)
597 : slot(slot_),
598 centre(centre_),
599 metric(metric_.clone()),
600 defkey(9, '\xff')
603 /** Construct a LatLongDistanceKeyMaker.
605 * @param slot_ Value slot to use.
606 * @param centre_ List of points to calculate distance from
607 * (closest distance is used).
609 * Xapian::GreatCircleMetric is used as the metric.
611 * Documents where no value is set are assumed to be a large distance
612 * away.
614 LatLongDistanceKeyMaker(Xapian::valueno slot_,
615 const LatLongCoords & centre_)
616 : slot(slot_),
617 centre(centre_),
618 metric(new Xapian::GreatCircleMetric()),
619 defkey(9, '\xff')
622 /** Construct a LatLongDistanceKeyMaker.
624 * @param slot_ Value slot to use.
625 * @param centre_ Point to calculate distance from.
626 * @param metric_ LatLongMetric to use.
627 * @param defdistance Distance to use for docs with no value set.
629 LatLongDistanceKeyMaker(Xapian::valueno slot_,
630 const LatLongCoord & centre_,
631 const LatLongMetric & metric_,
632 double defdistance)
633 : slot(slot_),
634 centre(),
635 metric(metric_.clone()),
636 defkey(sortable_serialise(defdistance))
638 centre.append(centre_);
641 /** Construct a LatLongDistanceKeyMaker.
643 * @param slot_ Value slot to use.
644 * @param centre_ Point to calculate distance from.
645 * @param metric_ LatLongMetric to use.
647 * Documents where no value is set are assumed to be a large distance
648 * away.
650 LatLongDistanceKeyMaker(Xapian::valueno slot_,
651 const LatLongCoord & centre_,
652 const LatLongMetric & metric_)
653 : slot(slot_),
654 centre(),
655 metric(metric_.clone()),
656 defkey(9, '\xff')
658 centre.append(centre_);
661 /** Construct a LatLongDistanceKeyMaker.
663 * @param slot_ Value slot to use.
664 * @param centre_ Point to calculate distance from.
666 * Xapian::GreatCircleMetric is used as the metric.
668 * Documents where no value is set are assumed to be a large distance
669 * away.
671 LatLongDistanceKeyMaker(Xapian::valueno slot_,
672 const LatLongCoord & centre_)
673 : slot(slot_),
674 centre(),
675 metric(new Xapian::GreatCircleMetric()),
676 defkey(9, '\xff')
678 centre.append(centre_);
681 ~LatLongDistanceKeyMaker();
683 std::string operator()(const Xapian::Document & doc) const;
688 #endif /* XAPIAN_INCLUDED_GEOSPATIAL_H */