10#if !defined(GEOGRAPHICLIB_NEARESTNEIGHBOR_HPP)
11#define GEOGRAPHICLIB_NEARESTNEIGHBOR_HPP 1
24#if defined(GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION) && \
25 GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION
26#include <boost/serialization/nvp.hpp>
27#include <boost/serialization/split_member.hpp>
28#include <boost/serialization/array.hpp>
29#include <boost/serialization/vector.hpp>
34# pragma warning (push)
35# pragma warning (disable: 4127)
101 template<
typename dist_t,
typename pos_t,
class distfun_t>
104 static const int version = 1;
107 static const int maxbucket =
108 (2 + ((4 *
sizeof(dist_t)) /
sizeof(int) >= 2 ?
109 (4 *
sizeof(dist_t)) /
sizeof(
int) : 2));
169 void Initialize(
const std::vector<pos_t>& pts,
const distfun_t& dist,
171 static_assert(std::numeric_limits<dist_t>::is_signed,
172 "dist_t must be a signed type");
173 if (!( 0 <= bucket && bucket <= maxbucket ))
175 (
"bucket must lie in [0, 2 + 4*sizeof(dist_t)/sizeof(int)]");
176 if (pts.size() > size_t(std::numeric_limits<int>::max()))
179 std::vector<item> ids(pts.size());
180 for (
int k =
int(ids.size()); k--;)
181 ids[k] = std::make_pair(dist_t(0), k);
183 std::vector<Node> tree;
184 init(pts, dist, bucket, tree, ids, cost,
185 0,
int(ids.size()),
int(ids.size()/2));
187 _numpoints = int(pts.size());
190 _cost = cost; _c1 = _k = _cmax = 0;
191 _cmin = std::numeric_limits<int>::max();
257 dist_t
Search(
const std::vector<pos_t>& pts,
const distfun_t& dist,
259 std::vector<int>& ind,
261 dist_t maxdist = std::numeric_limits<dist_t>::max(),
263 bool exhaustive =
true,
264 dist_t tol = 0)
const {
265 if (_numpoints !=
int(pts.size()))
267 std::priority_queue<item> results;
268 if (_numpoints > 0 && k > 0 && maxdist > mindist) {
270 dist_t tau = maxdist;
274 std::priority_queue<item> todo;
275 todo.push(std::make_pair(dist_t(1),
int(_tree.size()) - 1));
277 while (!todo.empty()) {
278 int n = todo.top().second;
279 dist_t d = -todo.top().first;
281 dist_t tau1 = tau - tol;
283 if (!( n >= 0 && tau1 >= d ))
continue;
284 const Node& current = _tree[n];
286 bool exitflag =
false, leaf = current.index < 0;
287 for (
int i = 0; i < (leaf ? _bucket : 1); ++i) {
288 int index = leaf ? current.leaves[i] : current.index;
289 if (index < 0)
break;
290 dst = dist(pts[index], query);
293 if (dst > mindist && dst <= tau) {
294 if (
int(results.size()) == k) results.pop();
295 results.push(std::make_pair(dst, index));
296 if (
int(results.size()) == k) {
298 tau = results.top().first;
312 if (current.index < 0)
continue;
314 for (
int l = 0; l < 2; ++l) {
315 if (current.data.child[l] >= 0 &&
316 dst + current.data.upper[l] >= mindist) {
317 if (dst < current.data.lower[l]) {
318 d = current.data.lower[l] - dst;
320 todo.push(std::make_pair(-d, current.data.child[l]));
321 }
else if (dst > current.data.upper[l]) {
322 d = dst - current.data.upper[l];
324 todo.push(std::make_pair(-d, current.data.child[l]));
326 todo.push(std::make_pair(dist_t(1), current.data.child[l]));
333 _mc += (c - omc) / _k;
334 _sc += (c - omc) * (c - _mc);
335 if (c > _cmax) _cmax = c;
336 if (c < _cmin) _cmin = c;
340 ind.resize(results.size());
342 for (
int i =
int(ind.size()); i--;) {
343 ind[i] = int(results.top().second);
344 if (i == 0) d = results.top().first;
373 void Save(std::ostream& os,
bool bin =
true)
const {
374 int realspec = std::numeric_limits<dist_t>::digits *
375 (std::numeric_limits<dist_t>::is_integer ? -1 : 1);
377 char id[] =
"NearestNeighbor_";
384 buf[4] = int(_tree.size());
386 os.write(
reinterpret_cast<const char *
>(buf), 6 *
sizeof(
int));
387 for (
int i = 0; i < int(_tree.size()); ++i) {
388 const Node& node = _tree[i];
389 os.write(
reinterpret_cast<const char *
>(&node.index),
sizeof(int));
390 if (node.index >= 0) {
391 os.write(
reinterpret_cast<const char *
>(node.data.lower),
393 os.write(
reinterpret_cast<const char *
>(node.data.upper),
395 os.write(
reinterpret_cast<const char *
>(node.data.child),
398 os.write(
reinterpret_cast<const char *
>(node.leaves),
399 _bucket *
sizeof(int));
403 std::stringstream ostring;
406 if (!std::numeric_limits<dist_t>::is_integer) {
407 static const int prec
408 = int(std::ceil(std::numeric_limits<dist_t>::digits *
409 std::log10(2.0) + 1));
410 ostring.precision(prec);
412 ostring << version <<
" " << realspec <<
" " << _bucket <<
" "
413 << _numpoints <<
" " << _tree.size() <<
" " << _cost;
414 for (
int i = 0; i < int(_tree.size()); ++i) {
415 const Node& node = _tree[i];
416 ostring <<
"\n" << node.index;
417 if (node.index >= 0) {
418 for (
int l = 0; l < 2; ++l)
419 ostring <<
" " << node.data.lower[l] <<
" " << node.data.upper[l]
420 <<
" " << node.data.child[l];
422 for (
int l = 0; l < _bucket; ++l)
423 ostring <<
" " << node.leaves[l];
451 void Load(std::istream& is,
bool bin =
true) {
452 int version1, realspec, bucket, numpoints, treesize, cost;
457 if (!(std::strcmp(
id,
"NearestNeighbor_") == 0))
459 is.read(
reinterpret_cast<char *
>(&version1),
sizeof(
int));
460 is.read(
reinterpret_cast<char *
>(&realspec),
sizeof(
int));
461 is.read(
reinterpret_cast<char *
>(&bucket),
sizeof(
int));
462 is.read(
reinterpret_cast<char *
>(&numpoints),
sizeof(
int));
463 is.read(
reinterpret_cast<char *
>(&treesize),
sizeof(
int));
464 is.read(
reinterpret_cast<char *
>(&cost),
sizeof(
int));
466 if (!( is >> version1 >> realspec >> bucket >> numpoints >> treesize
470 if (!( version1 == version ))
472 if (!( realspec == std::numeric_limits<dist_t>::digits *
473 (std::numeric_limits<dist_t>::is_integer ? -1 : 1) ))
475 if (!( 0 <= bucket && bucket <= maxbucket ))
477 if (!( 0 <= treesize && treesize <= numpoints ))
482 std::vector<Node> tree;
483 tree.reserve(treesize);
484 for (
int i = 0; i < treesize; ++i) {
487 is.read(
reinterpret_cast<char *
>(&node.index),
sizeof(
int));
488 if (node.index >= 0) {
489 is.read(
reinterpret_cast<char *
>(node.data.lower),
491 is.read(
reinterpret_cast<char *
>(node.data.upper),
493 is.read(
reinterpret_cast<char *
>(node.data.child),
496 is.read(
reinterpret_cast<char *
>(node.leaves),
497 bucket *
sizeof(
int));
498 for (
int l = bucket; l < maxbucket; ++l)
502 if (!( is >> node.index ))
504 if (node.index >= 0) {
505 for (
int l = 0; l < 2; ++l) {
506 if (!( is >> node.data.lower[l] >> node.data.upper[l]
507 >> node.data.child[l] ))
513 for (
int l = 0; l < bucket; ++l) {
514 if (!( is >> node.leaves[l] ))
517 for (
int l = bucket; l < maxbucket; ++l)
521 node.Check(numpoints, treesize, bucket);
522 tree.push_back(node);
525 _numpoints = numpoints;
528 _cost = cost; _c1 = _k = _cmax = 0;
529 _cmin = std::numeric_limits<int>::max();
541 { t.
Save(os,
false);
return os; }
552 { t.
Load(is,
false);
return is; }
586 void Statistics(
int& setupcost,
int& numsearches,
int& searchcost,
587 int& mincost,
int& maxcost,
588 double& mean,
double& sd)
const {
589 setupcost = _cost; numsearches = _k; searchcost = _c1;
590 mincost = _cmin; maxcost = _cmax;
591 mean = _mc; sd = std::sqrt(_sc / (_k - 1));
600 _c1 = _k = _cmax = 0;
601 _cmin = std::numeric_limits<int>::max();
607 typedef std::pair<dist_t, int> item;
612 dist_t lower[2], upper[2];
617 int leaves[maxbucket];
624 for (
int i = 0; i < 2; ++i) {
625 data.lower[i] = data.upper[i] = 0;
631 void Check(
int numpoints,
int treesize,
int bucket)
const {
632 if (!( -1 <= index && index < numpoints ))
635 if (!( -1 <= data.child[0] && data.child[0] < treesize &&
636 -1 <= data.child[1] && data.child[1] < treesize ))
638 if (!( 0 <= data.lower[0] && data.lower[0] <= data.upper[0] &&
639 data.upper[0] <= data.lower[1] &&
640 data.lower[1] <= data.upper[1] ))
646 for (
int l = 0; l < bucket; ++l) {
648 ((l == 0 ? 0 : -1) <= leaves[l] && leaves[l] < numpoints) :
651 start = leaves[l] >= 0;
653 for (
int l = bucket; l < maxbucket; ++l) {
660#if defined(GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION) && \
661 GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION
662 friend class boost::serialization::access;
663 template<
class Archive>
664 void save(Archive& ar,
const unsigned int)
const {
665 ar & boost::serialization::make_nvp(
"index", index);
667 ar & boost::serialization::make_nvp(
"leaves", leaves);
669 ar & boost::serialization::make_nvp(
"lower", data.lower)
670 & boost::serialization::make_nvp(
"upper", data.upper)
671 & boost::serialization::make_nvp(
"child", data.child);
673 template<
class Archive>
674 void load(Archive& ar,
const unsigned int) {
675 ar & boost::serialization::make_nvp(
"index", index);
677 ar & boost::serialization::make_nvp(
"leaves", leaves);
679 ar & boost::serialization::make_nvp(
"lower", data.lower)
680 & boost::serialization::make_nvp(
"upper", data.upper)
681 & boost::serialization::make_nvp(
"child", data.child);
683 template<
class Archive>
684 void serialize(Archive& ar,
const unsigned int file_version)
685 { boost::serialization::split_member(ar, *
this, file_version); }
689#if defined(GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION) && \
690 GEOGRAPHICLIB_HAVE_BOOST_SERIALIZATION
691 friend class boost::serialization::access;
692 template<
class Archive>
void save(Archive& ar,
const unsigned)
const {
693 int realspec = std::numeric_limits<dist_t>::digits *
694 (std::numeric_limits<dist_t>::is_integer ? -1 : 1);
697 int version1 = version;
698 ar & boost::serialization::make_nvp(
"version", version1)
699 & boost::serialization::make_nvp(
"realspec", realspec)
700 & boost::serialization::make_nvp(
"bucket", _bucket)
701 & boost::serialization::make_nvp(
"numpoints", _numpoints)
702 & boost::serialization::make_nvp(
"cost", _cost)
703 & boost::serialization::make_nvp(
"tree", _tree);
705 template<
class Archive>
void load(Archive& ar,
const unsigned) {
706 int version1, realspec, bucket, numpoints, cost;
707 ar & boost::serialization::make_nvp(
"version", version1);
708 if (version1 != version)
710 std::vector<Node> tree;
711 ar & boost::serialization::make_nvp(
"realspec", realspec);
712 if (!( realspec == std::numeric_limits<dist_t>::digits *
713 (std::numeric_limits<dist_t>::is_integer ? -1 : 1) ))
715 ar & boost::serialization::make_nvp(
"bucket", bucket);
716 if (!( 0 <= bucket && bucket <= maxbucket ))
718 ar & boost::serialization::make_nvp(
"numpoints", numpoints)
719 & boost::serialization::make_nvp(
"cost", cost)
720 & boost::serialization::make_nvp(
"tree", tree);
721 if (!( 0 <=
int(tree.size()) &&
int(tree.size()) <= numpoints ))
724 for (
int i = 0; i < int(tree.size()); ++i)
725 tree[i].Check(numpoints,
int(tree.size()), bucket);
727 _numpoints = numpoints;
730 _cost = cost; _c1 = _k = _cmax = 0;
731 _cmin = std::numeric_limits<int>::max();
733 template<
class Archive>
734 void serialize(Archive& ar,
const unsigned int file_version)
735 { boost::serialization::split_member(ar, *
this, file_version); }
738 int _numpoints, _bucket, _cost;
739 std::vector<Node> _tree;
741 mutable double _mc, _sc;
742 mutable int _c1, _k, _cmin, _cmax;
744 int init(
const std::vector<pos_t>& pts,
const distfun_t& dist,
int bucket,
745 std::vector<Node>& tree, std::vector<item>& ids,
int& cost,
746 int l,
int u,
int vp) {
752 if (u - l > (bucket == 0 ? 1 : bucket)) {
758 int m = (u + l + 1) / 2;
760 for (
int k = l + 1; k < u; ++k) {
761 ids[k].first = dist(pts[ids[l].second], pts[ids[k].second]);
765 std::nth_element(ids.begin() + l + 1,
768 node.index = ids[l].second;
770 typename std::vector<item>::iterator
771 t = std::min_element(ids.begin() + l + 1, ids.begin() + m);
772 node.data.lower[0] = t->first;
773 t = std::max_element(ids.begin() + l + 1, ids.begin() + m);
774 node.data.upper[0] = t->first;
777 node.data.child[0] = init(pts, dist, bucket, tree, ids, cost,
778 l + 1, m,
int(t - ids.begin()));
780 typename std::vector<item>::iterator
781 t = std::max_element(ids.begin() + m, ids.begin() + u);
782 node.data.lower[1] = ids[m].first;
783 node.data.upper[1] = t->first;
785 node.data.child[1] = init(pts, dist, bucket, tree, ids, cost,
786 m, u,
int(t - ids.begin()));
789 node.index = ids[l].second;
794 std::sort(ids.begin() + l, ids.begin() + u);
795 for (
int i = l; i < u; ++i)
796 node.leaves[i-l] = ids[i].second;
797 for (
int i = u - l; i < bucket; ++i)
799 for (
int i = bucket; i < maxbucket; ++i)
804 tree.push_back(node);
805 return int(tree.size()) - 1;
824 template<
typename dist_t,
typename pos_t,
class distfun_t>
833# pragma warning (pop)
Header for GeographicLib::Constants class.
Exception handling for GeographicLib.
Nearest-neighbor calculations.
void Statistics(int &setupcost, int &numsearches, int &searchcost, int &mincost, int &maxcost, double &mean, double &sd) const
friend std::istream & operator>>(std::istream &is, NearestNeighbor &t)
dist_t Search(const std::vector< pos_t > &pts, const distfun_t &dist, const pos_t &query, std::vector< int > &ind, int k=1, dist_t maxdist=std::numeric_limits< dist_t >::max(), dist_t mindist=-1, bool exhaustive=true, dist_t tol=0) const
void Initialize(const std::vector< pos_t > &pts, const distfun_t &dist, int bucket=4)
void Load(std::istream &is, bool bin=true)
void Save(std::ostream &os, bool bin=true) const
void ResetStatistics() const
NearestNeighbor(const std::vector< pos_t > &pts, const distfun_t &dist, int bucket=4)
void swap(NearestNeighbor &t)
friend std::ostream & operator<<(std::ostream &os, const NearestNeighbor &t)
Namespace for GeographicLib.
void swap(GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &a, GeographicLib::NearestNeighbor< dist_t, pos_t, distfun_t > &b)