2 * @brief Geospatial distance metrics.
4 /* Copyright 2008 Lemur Consulting Ltd
5 * Copyright 2011 Richard Boulton
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
25 #include "xapian/geospatial.h"
26 #include "xapian/error.h"
27 #include "serialise-double.h"
31 using namespace Xapian
;
34 /** Quadratic mean radius of the Earth in metres.
36 #define QUAD_EARTH_RADIUS_METRES 6372797.6
38 LatLongMetric::~LatLongMetric()
43 LatLongMetric::operator()(const LatLongCoords
& a
,
44 const LatLongCoords
& b
) const
46 if (a
.empty() || b
.empty()) {
47 throw InvalidArgumentError("Empty coordinate list supplied to LatLongMetric::operator()()");
49 double min_dist
= 0.0;
50 bool have_min
= false;
51 for (LatLongCoordsIterator a_iter
= a
.begin();
55 for (LatLongCoordsIterator b_iter
= b
.begin();
59 double dist
= pointwise_distance(*a_iter
, *b_iter
);
63 } else if (dist
< min_dist
) {
72 LatLongMetric::operator()(const LatLongCoords
& a
,
73 const char* b_ptr
, size_t b_len
) const
75 if (a
.empty() || b_len
== 0) {
76 throw InvalidArgumentError("Empty coordinate list supplied to LatLongMetric::operator()()");
78 double min_dist
= 0.0;
79 bool have_min
= false;
81 const char * b_end
= b_ptr
+ b_len
;
82 while (b_ptr
!= b_end
) {
83 b
.unserialise(&b_ptr
, b_end
);
84 for (LatLongCoordsIterator a_iter
= a
.begin();
88 double dist
= pointwise_distance(*a_iter
, b
);
92 } else if (dist
< min_dist
) {
101 GreatCircleMetric::GreatCircleMetric()
102 : radius(QUAD_EARTH_RADIUS_METRES
)
105 GreatCircleMetric::GreatCircleMetric(double radius_
)
110 GreatCircleMetric::pointwise_distance(const LatLongCoord
& a
,
111 const LatLongCoord
& b
) const
113 double lata
= a
.latitude
* (M_PI
/ 180.0);
114 double latb
= b
.latitude
* (M_PI
/ 180.0);
116 double latdiff
= lata
- latb
;
117 double longdiff
= (a
.longitude
- b
.longitude
) * (M_PI
/ 180.0);
119 double sin_half_lat
= sin(latdiff
/ 2);
120 double sin_half_long
= sin(longdiff
/ 2);
121 double h
= sin_half_lat
* sin_half_lat
+
122 sin_half_long
* sin_half_long
* cos(lata
) * cos(latb
);
124 // Clamp to 1.0, asin(1.0) = M_PI / 2.0.
125 return radius
* M_PI
;
127 return 2 * radius
* asin(sqrt(h
));
131 GreatCircleMetric::clone() const
133 return new GreatCircleMetric(radius
);
137 GreatCircleMetric::name() const
139 return "Xapian::GreatCircleMetric";
143 GreatCircleMetric::serialise() const
145 return serialise_double(radius
);
149 GreatCircleMetric::unserialise(const string
& s
) const
151 const char * p
= s
.data();
152 const char * end
= p
+ s
.size();
154 double new_radius
= unserialise_double(&p
, end
);
156 throw Xapian::NetworkError("Bad serialised GreatCircleMetric - junk at end");
159 return new GreatCircleMetric(new_radius
);