atlas
KDTree.h
1 /*
2  * (C) Copyright 2013 ECMWF.
3  *
4  * This software is licensed under the terms of the Apache Licence Version 2.0
5  * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6  * In applying this licence, ECMWF does not waive the privileges and immunities
7  * granted to it by virtue of its status as an intergovernmental organisation
8  * nor does it submit to any jurisdiction.
9  */
10 
11 #pragma once
12 
13 #include <iosfwd>
14 #include <memory>
15 
16 #include "eckit/container/KDTree.h"
17 
18 #include "atlas/library/config.h"
19 #include "atlas/runtime/Exception.h"
20 #include "atlas/runtime/Log.h"
21 #include "atlas/util/Geometry.h"
22 #include "atlas/util/Object.h"
23 #include "atlas/util/Point.h"
24 
25 namespace atlas {
26 namespace util {
27 namespace detail {
28 
29 //------------------------------------------------------------------------------------------------------
30 
31 // Abstract KDTree, intended to erase the internal KDTree type from eckit (Mapped or Memory)
32 // For usage, see atlas::util::KDTree
33 template <typename PayloadT, typename PointT = Point3>
34 class KDTreeBase : public Object {
35 #define ENABLE_IF_3D_AND_IS_LONLAT( POINT ) \
36  typename = typename std::enable_if < PointT::DIMS == 3 && POINT::DIMS == 2 > ::type
37 
38 private:
39  Geometry geometry_;
40 
41 public:
42  using Payload = PayloadT;
43  using Point = PointT;
44  using PayloadList = std::vector<Payload>;
45 
46  struct KDTreeTraits {
47  using Point = PointT;
48  using Payload = PayloadT;
49  };
50 
51  struct Value {
52  using Point = typename KDTreeTraits::Point;
53  using Payload = typename KDTreeTraits::Payload;
54 
55  template <typename Node>
56  Value( const Node& node ) : Value( node.point(), node.payload(), node.distance() ) {}
57 
58  Value( const Point& point, const Payload& payload ) : point_( point ), payload_( payload ) {}
59 
60  Value( const Point& point, const Payload& payload, double distance ) :
61  point_( point ), payload_( payload ), distance_( distance ) {}
62 
63  const Point& point() const { return point_; }
64  const Payload& payload() const { return payload_; }
65  const double& distance() const { return distance_; }
66 
67  private:
68  Point point_;
69  Payload payload_;
70  double distance_;
71  };
72 
73  class ValueList : public std::vector<Value> {
74  public:
75  using std::vector<Value>::vector;
76  PayloadList payloads() const {
77  PayloadList list;
78  list.reserve( this->size() );
79  for ( auto& item : *this ) {
80  list.emplace_back( item.payload() );
81  }
82  return list;
83  }
84 
85  template <typename NodeList>
86  ValueList( const NodeList& _list ) {
87  this->reserve( _list.size() );
88  for ( const auto& item : _list ) {
89  this->emplace_back( item );
90  }
91  }
92 
93  void print( std::ostream& out ) const {
94  out << "[";
95  for ( size_t i = 0; i < this->size(); ++i ) {
96  const auto& value = this->at( i );
97  out << "{payload:" << value.payload() << ",distance:" << value.distance() << ",point:" << value.point()
98  << "}";
99  if ( i < this->size() - 1 ) {
100  out << ",";
101  }
102  }
103  out << "]";
104  }
105  friend std::ostream& operator<<( std::ostream& out, ValueList& This ) {
106  This.print( out );
107  return out;
108  }
109  };
110 
111 
112 public:
113  KDTreeBase() = default;
114 
115  KDTreeBase( const Geometry& geometry ) : geometry_( geometry ) {}
116 
117  virtual ~KDTreeBase() = default;
118 
119  const Geometry& geometry() const { return geometry_; }
120 
121  bool empty() const { return size() == 0; }
122 
123  virtual idx_t size() const = 0;
124 
125  virtual size_t footprint() const = 0;
126 
129  virtual void reserve( idx_t /*size*/ ) {}
130 
133  template <typename Point>
134  void insert( const Point& p, const Payload& payload ) {
135  do_insert( p, payload );
136  }
137 
140  virtual void insert( const Value& ) = 0;
141 
144  virtual void build() {}
145 
147  virtual void build( std::vector<Value>& values ) = 0;
148 
151  template <typename Longitudes, typename Latitudes, typename Payloads>
152  void build( const Longitudes& longitudes, const Latitudes& latitudes, const Payloads& payloads ) {
153  build( longitudes.begin(), longitudes.end(), latitudes.begin(), latitudes.end(), payloads.begin(),
154  payloads.end() );
155  }
156 
159  template <typename LongitudesIterator, typename LatitudesIterator, typename PayloadsIterator>
160  void build( const LongitudesIterator& longitudes_begin, const LongitudesIterator& longitudes_end,
161  const LatitudesIterator& latitudes_begin, const LatitudesIterator& latitudes_end,
162  const PayloadsIterator& payloads_begin, const PayloadsIterator& payloads_end ) {
163  reserve( std::distance( payloads_begin, payloads_end ) );
164  auto lon = longitudes_begin;
165  auto lat = latitudes_begin;
166  auto payload = payloads_begin;
167  for ( ; lon != longitudes_end && lat != latitudes_end && payload != payloads_end; lon++, lat++, payload++ ) {
168  insert( Point2{*lon, *lat}, *payload );
169  }
170  ATLAS_ASSERT( lon == longitudes_end );
171  ATLAS_ASSERT( lat == latitudes_end );
172  ATLAS_ASSERT( payload == payloads_end );
173  build();
174  }
175 
178  template <typename Points, typename Payloads>
179  void build( const Points& points, const Payloads& payloads ) {
180  build( points.begin(), points.end(), payloads.begin(), payloads.end() );
181  }
182 
185  template <typename PointIterator, typename PayloadsIterator>
186  void build( const PointIterator& points_begin, const PointIterator& points_end,
187  const PayloadsIterator& payloads_begin, const PayloadsIterator& payloads_end ) {
188  reserve( std::distance( points_begin, points_end ) );
189  PointIterator point = points_begin;
190  PayloadsIterator payload = payloads_begin;
191  for ( ; point != points_end && payload != payloads_end; ++point, ++payload ) {
192  insert( *point, *payload );
193  }
194  ATLAS_ASSERT( point == points_end );
195  ATLAS_ASSERT( payload == payloads_end );
196  build();
197  }
198 
199 
201  template <typename Point>
202  ValueList closestPoints( const Point& p, size_t k ) const {
203  return do_closestPoints( p, k );
204  }
205 
207  template <typename Point>
208  Value closestPoint( const Point& p ) const {
209  return do_closestPoint( p );
210  }
211 
213  template <typename Point>
214  ValueList closestPointsWithinRadius( const Point& p, double radius ) const {
215  return do_closestPointsWithinRadius( p, radius );
216  }
217 
218 private:
221  void do_insert( const Point& p, const Payload& payload ) { insert( Value{p, payload} ); }
222 
223 
226  template <typename LonLat, ENABLE_IF_3D_AND_IS_LONLAT( LonLat )>
227  void do_insert( const LonLat& p, const Payload& payload ) {
228  do_insert( make_Point( p ), payload );
229  }
230 
232  virtual ValueList do_closestPoints( const Point&, size_t k ) const = 0;
233 
235  virtual Value do_closestPoint( const Point& ) const = 0;
236 
238  virtual ValueList do_closestPointsWithinRadius( const Point&, double radius ) const = 0;
239 
240 
242  template <typename LonLat, ENABLE_IF_3D_AND_IS_LONLAT( LonLat )>
243  ValueList do_closestPoints( const LonLat& p, size_t k ) const {
244  return do_closestPoints( make_Point( p ), k );
245  }
246 
248  template <typename LonLat, ENABLE_IF_3D_AND_IS_LONLAT( LonLat )>
249  Value do_closestPoint( const LonLat& p ) const {
250  return do_closestPoint( make_Point( p ) );
251  }
252 
254  template <typename LonLat, ENABLE_IF_3D_AND_IS_LONLAT( LonLat )>
255  ValueList do_closestPointsWithinRadius( const LonLat& p, double radius ) const {
256  return do_closestPointsWithinRadius( make_Point( p ), radius );
257  }
258 
259  template <typename LonLat, ENABLE_IF_3D_AND_IS_LONLAT( LonLat )>
260  Point make_Point( const LonLat& lonlat ) const {
261  static_assert( std::is_base_of<Point2, LonLat>::value, "LonLat must be derived from Point2" );
262  static_assert( std::is_base_of<Point3, Point>::value, "Point must be derived from Point3" );
263  Point xyz;
264  geometry().lonlat2xyz( lonlat, xyz );
265  return xyz;
266  }
267 #undef ENABLE_IF_3D_AND_IS_LONLAT
268 };
269 
270 //------------------------------------------------------------------------------------------------------
271 // Concrete implementation
272 
273 template <typename TreeT, typename PayloadT = typename TreeT::Payload, typename PointT = typename TreeT::Point>
274 class KDTree_eckit : public KDTreeBase<PayloadT, PointT> {
275  using Tree = TreeT;
277 
278 public:
279  using Point = typename Base::Point;
280  using Payload = typename Base::Payload;
281  using PayloadList = typename Base::PayloadList;
282  using Value = typename Base::Value;
283  using ValueList = typename Base::ValueList;
284 
285  using Base::build;
286  using Base::closestPoint;
287  using Base::closestPoints;
288  using Base::closestPointsWithinRadius;
289  using Base::insert;
290  using Base::reserve;
291 
292 public:
293  template <typename... Args>
294  KDTree_eckit( const Geometry& geometry, Args... args ) :
295  KDTreeBase<Payload, Point>( geometry ), tree_( new Tree( args... ) ) {
296  static_asserts();
297  }
298 
299 
300  template <typename... Args>
301  KDTree_eckit( Args... args ) : tree_( new Tree( args... ) ) {
302  static_asserts();
303  }
304 
305  KDTree_eckit( const std::shared_ptr<Tree>& tree ) : tree_( tree ) { static_asserts(); }
306 
307  KDTree_eckit( const std::shared_ptr<Tree>& tree, const Geometry& geometry ) :
308  KDTreeBase<Payload, Point>( geometry ), tree_( tree ) {
309  static_asserts();
310  }
311 
312  idx_t size() const override {
313 #if ATLAS_ECKIT_VERSION_INT >= 11302 // v1.13.2
314  return static_cast<idx_t>( tree_->size() );
315 #else
316  // Assume ECKIT-515 not implemented.
317  // Very bad implementation, with a workaround for empty tree
318  idx_t size{0};
319  try {
320  for ( const auto& item : *tree_ ) {
321  size++;
322  }
323  }
324  catch ( const eckit::AssertionFailed& ) {
325  }
326  return size;
327 #endif
328  }
329 
330  size_t footprint() const override { return size() * sizeof( typename Tree::Node ); }
331 
332  void reserve( idx_t size ) override;
333 
334  void build() override;
335 
336  void build( std::vector<Value>& ) override;
337 
340  void insert( const Value& value ) override;
341 
343  ValueList do_closestPoints( const Point&, size_t k ) const override;
344 
346  Value do_closestPoint( const Point& ) const override;
347 
349  ValueList do_closestPointsWithinRadius( const Point&, double radius ) const override;
350 
351  const Tree& tree() const { return *tree_; }
352 
353 private:
354  void assert_built() const;
355 
356  static void static_asserts() {
357  static_assert( std::is_convertible<typename Tree::Payload, Payload>::value,
358  "Tree::Payload must be convertible this Payload type" );
359  static_assert( std::is_convertible<typename Tree::Point, Point>::value,
360  "Tree::Point must be convertible to this Point type" );
361  }
362 
363 private:
364  std::vector<Value> tmp_;
365  mutable std::shared_ptr<Tree> tree_; // mutable because its member functions are non-const...
366 };
367 
368 //------------------------------------------------------------------------------------------------------
369 
370 template <typename Payload, typename Point>
372 
373 //------------------------------------------------------------------------------------------------------
374 
375 template <typename TreeT, typename PayloadT, typename PointT>
377  tmp_.reserve( size );
378 }
379 
380 
381 template <typename TreeT, typename PayloadT, typename PointT>
383  if ( tmp_.capacity() ) {
384  tmp_.emplace_back( value );
385  }
386  else {
387  tree_->insert( value );
388  }
389 }
390 
391 template <typename TreeT, typename PayloadT, typename PointT>
393  if ( tmp_.size() ) {
394  build( tmp_ );
395  tmp_.clear();
396  tmp_.shrink_to_fit();
397  }
398 }
399 
400 template <typename TreeT, typename PayloadT, typename PointT>
401 void KDTree_eckit<TreeT, PayloadT, PointT>::build( std::vector<Value>& values ) {
402  tree_->build( values );
403 }
404 
405 
406 template <typename TreeT, typename PayloadT, typename PointT>
407 typename KDTree_eckit<TreeT, PayloadT, PointT>::ValueList KDTree_eckit<TreeT, PayloadT, PointT>::do_closestPoints(
408  const Point& p, size_t k ) const {
409  assert_built();
410  return tree_->kNearestNeighbours( p, k );
411 }
412 
413 template <typename TreeT, typename PayloadT, typename PointT>
414 typename KDTree_eckit<TreeT, PayloadT, PointT>::Value KDTree_eckit<TreeT, PayloadT, PointT>::do_closestPoint(
415  const Point& p ) const {
416  assert_built();
417  return tree_->nearestNeighbour( p );
418 }
419 
420 template <typename TreeT, typename PayloadT, typename PointT>
421 typename KDTree_eckit<TreeT, PayloadT, PointT>::ValueList
423  assert_built();
424  return tree_->findInSphere( p, radius );
425 }
426 
427 template <typename TreeT, typename PayloadT, typename PointT>
429  if ( tmp_.capacity() ) {
430  throw_AssertionFailed( "KDTree was used before calling build()" );
431  }
432 }
433 
434 //------------------------------------------------------------------------------------------------------
435 
436 } // namespace detail
437 } // namespace util
438 } // namespace atlas
void build(const Longitudes &longitudes, const Latitudes &latitudes, const Payloads &payloads)
Build with spherical points (lon,lat) where longitudes, latitudes, and payloads are separate containe...
Definition: KDTree.h:152
void insert(const Value &value) override
Insert 3D cartesian point (x,y,z) If memory has been reserved with reserve(), insertion will be delay...
Definition: KDTree.h:382
void build() override
Build the kd-tree in one shot, if memory has been reserved, depending on derived class implementation...
Definition: KDTree.h:392
Definition: atlas-benchmark-sorting.cc:55
Value do_closestPoint(const Point &) const override
Find nearest neighbour given a 3D cartesian point (x,y,z)
Definition: KDTree.h:414
Definition: Geometry.h:94
This file contains classes and functions working on points.
Definition: KDTree.h:274
void build(const LongitudesIterator &longitudes_begin, const LongitudesIterator &longitudes_end, const LatitudesIterator &latitudes_begin, const LatitudesIterator &latitudes_end, const PayloadsIterator &payloads_begin, const PayloadsIterator &payloads_end)
Build with spherical points (lon,lat) given separate iterator ranges for longitudes, latitudes, and payloads.
Definition: KDTree.h:160
ValueList do_closestPoints(const Point &, size_t k) const override
Find k nearest neighbours given a 3D cartesian point (x,y,z)
Definition: KDTree.h:407
Definition: KDTree.h:34
ValueList closestPoints(const Point &p, size_t k) const
Find k nearest neighbours given a 3D cartesian point (x,y,z) or 2D lonlat point(lon,lat)
Definition: KDTree.h:202
Definition: Object.h:18
void build(const Points &points, const Payloads &payloads)
Build with spherical points (lon,lat) where longitudes, latitudes, and payloads are separate containe...
Definition: KDTree.h:179
void build(const PointIterator &points_begin, const PointIterator &points_end, const PayloadsIterator &payloads_begin, const PayloadsIterator &payloads_end)
Build with spherical points (lon,lat) given separate iterator ranges for longitudes, latitudes, and payloads.
Definition: KDTree.h:186
virtual void reserve(idx_t)
Reserve memory for building the kdtree in one shot (optional, at cost of extra memory) Implementation...
Definition: KDTree.h:129
void reserve(idx_t size) override
Reserve memory for building the kdtree in one shot (optional, at cost of extra memory) Implementation...
Definition: KDTree.h:376
void insert(const Point &p, const Payload &payload)
Insert spherical point (lon,lat) or 3D cartesian point (x,y,z) If memory has been reserved with reser...
Definition: KDTree.h:134
Value closestPoint(const Point &p) const
Find nearest neighbour given a 3D cartesian point (x,y,z)
Definition: KDTree.h:208
virtual void build()
Build the kd-tree in one shot, if memory has been reserved, depending on derived class implementation...
Definition: KDTree.h:144
Contains all atlas classes and methods.
Definition: atlas-grids.cc:33
long idx_t
Integer type for indices in connectivity tables.
Definition: config.h:42
ValueList do_closestPointsWithinRadius(const Point &, double radius) const override
Find all points within a distance of given radius from a given point (x,y,z)
Definition: KDTree.h:422
ValueList closestPointsWithinRadius(const Point &p, double radius) const
Find all points within a distance of given radius from a given point (x,y,z)
Definition: KDTree.h:214