2 * @brief Geospatial search support routines.
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
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.
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>
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
54 miles_to_metres(double miles
) XAPIAN_NOEXCEPT
56 return 1609.344 * miles
;
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
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.
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 <
96 * Longitudes increase as coordinates move eastwards.
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
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_
)
177 /// Default constructor. Produces an uninitialised iterator.
178 LatLongCoordsIterator() {}
180 /** Get the LatLongCoord for the current position. */
181 const LatLongCoord
& operator*() const {
185 /// Advance the iterator to the next position.
186 LatLongCoordsIterator
& operator++() {
191 /// Advance the iterator to the next position (postfix version).
192 DerefWrapper_
<LatLongCoord
> operator++(int) {
193 const LatLongCoord
& tmp
= **this;
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
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.
216 typedef std::input_iterator_tag iterator_category
;
218 typedef LatLongCoord value_type
;
220 typedef size_t difference_type
;
222 typedef LatLongCoord
* pointer
;
224 typedef LatLongCoord
& reference
;
228 /** A sequence of latitude-longitude coordinates.
230 * Experimental - see https://xapian.org/docs/deprecation#experimental-features
232 class XAPIAN_VISIBILITY_DEFAULT LatLongCoords
{
234 std::vector
<LatLongCoord
> coords
;
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.
250 return coords
.size();
253 /// Return true if and only if there are no coordinates in the container.
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
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.
294 operator!=(const LatLongCoordsIterator
&a
, const LatLongCoordsIterator
&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
{
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
354 * @param b_len The length of the serialised form of the second coordinate
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
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 https://en.wikipedia.org/wiki/Haversine_formula
399 class XAPIAN_VISIBILITY_DEFAULT GreatCircleMetric
: public LatLongMetric
{
400 /** The radius of the sphere in metres.
405 /** Construct a GreatCircleMetric.
407 * The (quadratic mean) radius of the Earth will be used by this
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,
454 class XAPIAN_VISIBILITY_DEFAULT LatLongDistancePostingSource
: public ValuePostingSource
456 /// Current distance from centre.
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.
468 /// Constant used in weighting function.
471 /// Constant used in weighting function.
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_
,
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,
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,
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.
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_
,
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
594 LatLongDistanceKeyMaker(Xapian::valueno slot_
,
595 const LatLongCoords
& centre_
,
596 const LatLongMetric
& metric_
)
599 metric(metric_
.clone()),
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
614 LatLongDistanceKeyMaker(Xapian::valueno slot_
,
615 const LatLongCoords
& centre_
)
618 metric(new Xapian::GreatCircleMetric()),
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_
,
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
650 LatLongDistanceKeyMaker(Xapian::valueno slot_
,
651 const LatLongCoord
& centre_
,
652 const LatLongMetric
& metric_
)
655 metric(metric_
.clone()),
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
671 LatLongDistanceKeyMaker(Xapian::valueno slot_
,
672 const LatLongCoord
& centre_
)
675 metric(new Xapian::GreatCircleMetric()),
678 centre
.append(centre_
);
681 ~LatLongDistanceKeyMaker();
683 std::string
operator()(const Xapian::Document
& doc
) const;
688 #endif /* XAPIAN_INCLUDED_GEOSPATIAL_H */