Don't force the user to specify the metric
[xapian.git] / xapian-core / geospatial / latlong_posting_source.cc
blobea34f92bf4beeedd264b1b2824a519d58bde26b1
1 /** @file latlong_posting_source.cc
2 * @brief LatLongPostingSource implementation.
3 */
4 /* Copyright 2008 Lemur Consulting Ltd
5 * Copyright 2010,2011 Richard Boulton
6 * Copyright 2012,2015 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 #include <config.h>
26 #include "xapian/geospatial.h"
28 #include "xapian/error.h"
29 #include "xapian/registry.h"
31 #include "net/length.h"
32 #include "serialise-double.h"
33 #include "str.h"
35 #include <cmath>
37 using namespace Xapian;
38 using namespace std;
40 static double
41 weight_from_distance(double dist, double k1, double k2)
43 return k1 * pow(dist + k1, -k2);
46 void
47 LatLongDistancePostingSource::calc_distance()
49 dist = (*metric)(centre, get_value());
52 /// Validate the parameters supplied to LatLongDistancePostingSource.
53 static void
54 validate_postingsource_params(double k1, double k2) {
55 if (k1 <= 0) {
56 string msg("k1 parameter to LatLongDistancePostingSource must be "
57 "greater than 0; was ");
58 msg += str(k1);
59 throw InvalidArgumentError(msg);
61 if (k2 <= 0) {
62 string msg("k2 parameter to LatLongDistancePostingSource must be "
63 "greater than 0; was ");
64 msg += str(k2);
65 throw InvalidArgumentError(msg);
69 LatLongDistancePostingSource::LatLongDistancePostingSource(
70 valueno slot_,
71 const LatLongCoords & centre_,
72 const LatLongMetric * metric_,
73 double max_range_,
74 double k1_,
75 double k2_)
76 : ValuePostingSource(slot_),
77 centre(centre_),
78 metric(metric_),
79 max_range(max_range_),
80 k1(k1_),
81 k2(k2_)
83 validate_postingsource_params(k1, k2);
84 set_maxweight(weight_from_distance(0, k1, k2));
87 LatLongDistancePostingSource::LatLongDistancePostingSource(
88 valueno slot_,
89 const LatLongCoords & centre_,
90 const LatLongMetric & metric_,
91 double max_range_,
92 double k1_,
93 double k2_)
94 : ValuePostingSource(slot_),
95 centre(centre_),
96 metric(metric_.clone()),
97 max_range(max_range_),
98 k1(k1_),
99 k2(k2_)
101 validate_postingsource_params(k1, k2);
102 set_maxweight(weight_from_distance(0, k1, k2));
105 LatLongDistancePostingSource::LatLongDistancePostingSource(
106 valueno slot_,
107 const LatLongCoords & centre_,
108 double max_range_,
109 double k1_,
110 double k2_)
111 : ValuePostingSource(slot_),
112 centre(centre_),
113 metric(new Xapian::GreatCircleMetric()),
114 max_range(max_range_),
115 k1(k1_),
116 k2(k2_)
118 validate_postingsource_params(k1, k2);
119 set_maxweight(weight_from_distance(0, k1, k2));
122 LatLongDistancePostingSource::~LatLongDistancePostingSource()
124 delete metric;
127 void
128 LatLongDistancePostingSource::next(double min_wt)
130 ValuePostingSource::next(min_wt);
132 while (!ValuePostingSource::at_end()) {
133 calc_distance();
134 if (max_range == 0 || dist <= max_range)
135 break;
136 ValuePostingSource::next(min_wt);
140 void
141 LatLongDistancePostingSource::skip_to(docid min_docid,
142 double min_wt)
144 ValuePostingSource::skip_to(min_docid, min_wt);
146 while (!ValuePostingSource::at_end()) {
147 calc_distance();
148 if (max_range == 0 || dist <= max_range)
149 break;
150 ValuePostingSource::next(min_wt);
154 bool
155 LatLongDistancePostingSource::check(docid min_docid,
156 double min_wt)
158 if (!ValuePostingSource::check(min_docid, min_wt)) {
159 // check returned false, so we know the document is not in the source.
160 return false;
162 if (ValuePostingSource::at_end()) {
163 // return true, since we're definitely at the end of the list.
164 return true;
167 calc_distance();
168 if (max_range > 0 && dist > max_range) {
169 return false;
171 return true;
174 double
175 LatLongDistancePostingSource::get_weight() const
177 return weight_from_distance(dist, k1, k2);
180 LatLongDistancePostingSource *
181 LatLongDistancePostingSource::clone() const
183 return new LatLongDistancePostingSource(get_slot(), centre,
184 metric->clone(),
185 max_range, k1, k2);
188 string
189 LatLongDistancePostingSource::name() const
191 return "Xapian::LatLongDistancePostingSource";
194 string
195 LatLongDistancePostingSource::serialise() const
197 string serialised_centre = centre.serialise();
198 string metric_name = metric->name();
199 string serialised_metric = metric->serialise();
201 string result = encode_length(get_slot());
202 result += encode_length(serialised_centre.size());
203 result += serialised_centre;
204 result += encode_length(metric_name.size());
205 result += metric_name;
206 result += encode_length(serialised_metric.size());
207 result += serialised_metric;
208 result += serialise_double(max_range);
209 result += serialise_double(k1);
210 result += serialise_double(k2);
211 return result;
214 LatLongDistancePostingSource *
215 LatLongDistancePostingSource::unserialise_with_registry(const string &s,
216 const Registry & registry) const
218 const char * p = s.data();
219 const char * end = p + s.size();
221 valueno new_slot;
222 decode_length(&p, end, new_slot);
223 size_t len;
224 decode_length_and_check(&p, end, len);
225 string new_serialised_centre(p, len);
226 p += len;
227 decode_length_and_check(&p, end, len);
228 string new_metric_name(p, len);
229 p += len;
230 decode_length_and_check(&p, end, len);
231 string new_serialised_metric(p, len);
232 p += len;
233 double new_max_range = unserialise_double(&p, end);
234 double new_k1 = unserialise_double(&p, end);
235 double new_k2 = unserialise_double(&p, end);
236 if (p != end) {
237 throw NetworkError("Bad serialised LatLongDistancePostingSource - junk at end");
240 LatLongCoords new_centre;
241 new_centre.unserialise(new_serialised_centre);
243 const Xapian::LatLongMetric * metric_type =
244 registry.get_lat_long_metric(new_metric_name);
245 if (metric_type == NULL) {
246 string msg("LatLongMetric ");
247 msg += new_metric_name;
248 msg += " not registered";
249 throw InvalidArgumentError(msg);
251 LatLongMetric * new_metric =
252 metric_type->unserialise(new_serialised_metric);
254 return new LatLongDistancePostingSource(new_slot, new_centre,
255 new_metric,
256 new_max_range, new_k1, new_k2);
259 void
260 LatLongDistancePostingSource::init(const Database & db_)
262 ValuePostingSource::init(db_);
263 if (max_range > 0.0) {
264 // Possible that no documents are in range.
265 set_termfreq_min(0);
266 // Note - would be good to improve termfreq_est here, too, but
267 // I can't think of anything we can do with the information
268 // available.
272 string
273 LatLongDistancePostingSource::get_description() const
275 string result("Xapian::LatLongDistancePostingSource(slot=");
276 result += str(get_slot());
277 result += ")";
278 return result;