/src/s2geometry/src/s2/s2point_index.h
Line | Count | Source |
1 | | // Copyright 2015 Google Inc. All Rights Reserved. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | | // you may not use this file except in compliance with the License. |
5 | | // You may obtain a copy of the License at |
6 | | // |
7 | | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | | // |
9 | | // Unless required by applicable law or agreed to in writing, software |
10 | | // distributed under the License is distributed on an "AS-IS" BASIS, |
11 | | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | | // See the License for the specific language governing permissions and |
13 | | // limitations under the License. |
14 | | // |
15 | | |
16 | | // Author: ericv@google.com (Eric Veach) |
17 | | |
18 | | #ifndef S2_S2POINT_INDEX_H_ |
19 | | #define S2_S2POINT_INDEX_H_ |
20 | | |
21 | | #include <cstddef> |
22 | | #include <tuple> |
23 | | #include <type_traits> |
24 | | #include <utility> |
25 | | |
26 | | #include "absl/container/btree_map.h" |
27 | | #include "absl/log/absl_check.h" |
28 | | |
29 | | #include "s2/_fp_contract_off.h" // IWYU pragma: keep |
30 | | #include "s2/s2cell_id.h" |
31 | | #include "s2/s2cell_iterator.h" |
32 | | #include "s2/s2point.h" |
33 | | |
34 | | namespace s2internal { |
35 | | // Hack to expose bytes_used. |
36 | | template <typename Key, typename Value> |
37 | | class BTreeMultimap : public absl::btree_multimap<Key, Value> { |
38 | | public: |
39 | 0 | size_t bytes_used() const { return this->tree_.bytes_used(); } |
40 | | }; |
41 | | } // namespace s2internal |
42 | | |
43 | | |
44 | | // S2PointIndex maintains an index of points sorted by leaf S2CellId. Each |
45 | | // point can optionally store auxiliary data such as an integer or pointer. |
46 | | // This can be used to map results back to client data structures. |
47 | | // |
48 | | // The class supports adding or removing points dynamically, and provides a |
49 | | // seekable iterator interface for navigating the index. |
50 | | // |
51 | | // You can use this class in conjunction with S2ClosestPointQuery to find the |
52 | | // closest index points to a given query point. For example: |
53 | | // |
54 | | // void Test(const vector<S2Point>& index_points, |
55 | | // const vector<S2Point>& target_points) { |
56 | | // // The template argument allows auxiliary data to be attached to each |
57 | | // // point (in this case, the array index). |
58 | | // S2PointIndex<int> index; |
59 | | // for (int i = 0; i < index_points.size(); ++i) { |
60 | | // index.Add(index_points[i], i); |
61 | | // } |
62 | | // S2ClosestPointQuery<int> query(&index); |
63 | | // query.mutable_options()->set_max_results(5); |
64 | | // for (const S2Point& target_point : target_points) { |
65 | | // S2ClosestPointQueryPointTarget target(target_point); |
66 | | // for (const auto& result : query.FindClosestPoints(&target)) { |
67 | | // // The Result class contains the following methods: |
68 | | // // distance() is the distance to the target. |
69 | | // // point() is the indexed point. |
70 | | // // data() is the auxiliary data. |
71 | | // DoSomething(target_point, result); |
72 | | // } |
73 | | // } |
74 | | // } |
75 | | // |
76 | | // The Data argument defaults to an empty class, which uses no additional |
77 | | // space beyond the S2Point itself. In this case the Data argument is |
78 | | // required. For example: |
79 | | // |
80 | | // S2PointIndex<> index; |
81 | | // index.Add(point); |
82 | | // |
83 | | // Points can be added or removed from the index at any time by calling Add() |
84 | | // or Remove(). However when the index is modified, you must call Init() on |
85 | | // each iterator before using it again (or simply create a new iterator). |
86 | | // |
87 | | // index.Add(new_point, 123456); |
88 | | // it.Init(&index); |
89 | | // it.Seek(target.range_min()); |
90 | | // |
91 | | // You can also access the index directly using the iterator interface. For |
92 | | // example, here is how to iterate through all the points in a given S2CellId |
93 | | // "target_id": |
94 | | // |
95 | | // S2PointIndex<int>::Iterator it(&index); |
96 | | // it.Seek(target_id.range_min()); |
97 | | // for (; !it.done() && it.id() <= target_id.range_max(); it.Next()) { |
98 | | // DoSomething(it.id(), it.point(), it.data()); |
99 | | // } |
100 | | // |
101 | | // TODO(ericv): Consider adding an S2PointIndexRegion class, which could be |
102 | | // used to efficiently compute coverings of a collection of S2Points. |
103 | | // |
104 | | // REQUIRES: "Data" has default and copy constructors. |
105 | | // REQUIRES: "Data" has operator== and operator<. |
106 | | template <class Data = std::tuple<> /*empty class*/> |
107 | | class S2PointIndex { |
108 | | public: |
109 | | // PointData is essentially std::pair with named fields. It stores an |
110 | | // S2Point and its associated data, taking advantage of the "empty base |
111 | | // optimization" to ensure that no extra space is used when Data is empty. |
112 | | class PointData { |
113 | | public: |
114 | | PointData() = default; // Needed by STL |
115 | 0 | PointData(const S2Point& point, const Data& data) : tuple_(point, data) {} |
116 | | |
117 | 0 | const S2Point& point() const { return std::get<0>(tuple_); } |
118 | 0 | const Data& data() const { return std::get<1>(tuple_); } |
119 | | |
120 | | friend bool operator==(const PointData& x, const PointData& y) { |
121 | | return x.tuple_ == y.tuple_; |
122 | | } |
123 | | friend bool operator<(const PointData& x, const PointData& y) { |
124 | | return x.tuple_ < y.tuple_; |
125 | | } |
126 | | |
127 | | private: |
128 | | // Note that std::tuple has special template magic to ensure that Data |
129 | | // doesn't take up any space when it is empty. (This is not true if you |
130 | | // simply declare a member of type Data.) |
131 | | std::tuple<S2Point, Data> tuple_; |
132 | | }; |
133 | | |
134 | | // Default constructor. |
135 | | // TODO(b/252809194): Move definition back to .cc file. |
136 | 0 | S2PointIndex() = default; |
137 | | |
138 | | // Returns the number of points in the index. |
139 | | int num_points() const; |
140 | | |
141 | | // Adds the given point to the index. Invalidates all iterators. |
142 | | void Add(const S2Point& point, const Data& data); |
143 | | void Add(const PointData& point_data); |
144 | | |
145 | | // Convenience function for the case when Data is an empty class. |
146 | | void Add(const S2Point& point); |
147 | | |
148 | | // Removes the given point from the index. Both the "point" and "data" |
149 | | // fields must match the point to be removed. Returns false if the given |
150 | | // point was not present. Invalidates all iterators. |
151 | | bool Remove(const S2Point& point, const Data& data); |
152 | | bool Remove(const PointData& point_data); |
153 | | |
154 | | // Convenience function for the case when Data is an empty class. |
155 | | void Remove(const S2Point& point); |
156 | | |
157 | | // Resets the index to its original empty state. Invalidates all iterators. |
158 | | void Clear(); |
159 | | |
160 | | // Returns the number of bytes currently occupied by the index. |
161 | | size_t SpaceUsed() const; |
162 | | |
163 | | private: |
164 | | // Defined here because the Iterator class below uses it. |
165 | | using Map = s2internal::BTreeMultimap<S2CellId, PointData>; |
166 | | |
167 | | public: |
168 | | class Iterator final : public S2CellIterator { |
169 | | public: |
170 | | // Default constructor; must be followed by a call to Init(). |
171 | | Iterator(); |
172 | | |
173 | | // Convenience constructor that calls Init(). |
174 | | explicit Iterator(const S2PointIndex* index); |
175 | | |
176 | | // Initializes an iterator for the given S2PointIndex. If the index is |
177 | | // non-empty, the iterator is positioned at the first cell. |
178 | | // |
179 | | // This method may be called multiple times, e.g. to make an iterator |
180 | | // valid again after the index is modified. |
181 | | void Init(const S2PointIndex* index); |
182 | | |
183 | | // The S2CellId for the current index entry. |
184 | | // REQUIRES: !done() |
185 | | S2CellId id() const override; |
186 | | |
187 | | // The point associated with the current index entry. |
188 | | // REQUIRES: !done() |
189 | | const S2Point& point() const; |
190 | | |
191 | | // The client-supplied data associated with the current index entry. |
192 | | // REQUIRES: !done() |
193 | | const Data& data() const; |
194 | | |
195 | | // The (S2Point, data) pair associated with the current index entry. |
196 | | const PointData& point_data() const; |
197 | | |
198 | | // Returns true if the iterator is positioned past the last index entry. |
199 | | bool done() const override; |
200 | | |
201 | | // Positions the iterator at the first index entry (if any). |
202 | | void Begin() override; |
203 | | |
204 | | // Positions the iterator so that done() is true. |
205 | | void Finish() override; |
206 | | |
207 | | // Advances the iterator to the next index entry. |
208 | | // REQUIRES: !done() |
209 | | void Next() override; |
210 | | |
211 | | // If the iterator is already positioned at the beginning, returns false. |
212 | | // Otherwise positions the iterator at the previous entry and returns true. |
213 | | bool Prev() override; |
214 | | |
215 | | // Positions the iterator at the first entry with id() >= target, or at the |
216 | | // end of the index if no such entry exists. |
217 | | void Seek(S2CellId target) override; |
218 | | |
219 | | // Positions the iterator at the cell containing target and returns true. If |
220 | | // no such cell exists, return false and leave the iterator in an undefined |
221 | | // (but valid) state. |
222 | 0 | bool Locate(const S2Point& target) override { |
223 | 0 | return LocateImpl(*this, target); |
224 | 0 | } |
225 | | |
226 | | // Let T be the target S2CellId. If T is contained by some index cell I |
227 | | // (including equality), this method positions the iterator at I and returns |
228 | | // INDEXED. Otherwise if T contains one or more (smaller) index cells, it |
229 | | // positions the iterator at the first such cell I and returns SUBDIVIDED. |
230 | | // Otherwise it returns DISJOINT and leaves the iterator in an undefined |
231 | | // (but valid) state. |
232 | 0 | S2CellRelation Locate(S2CellId target) override { |
233 | 0 | return LocateImpl(*this, target); |
234 | 0 | } |
235 | | |
236 | | private: |
237 | | const Map* map_ = nullptr; |
238 | | typename Map::const_iterator iter_, end_; |
239 | | }; |
240 | | |
241 | | private: |
242 | | friend class Iterator; |
243 | | Map map_; |
244 | | |
245 | | S2PointIndex(const S2PointIndex&) = delete; |
246 | | void operator=(const S2PointIndex&) = delete; |
247 | | }; |
248 | | |
249 | | |
250 | | ////////////////// Implementation details follow //////////////////// |
251 | | |
252 | | template <class Data> |
253 | 0 | inline int S2PointIndex<Data>::num_points() const { |
254 | 0 | return map_.size(); |
255 | 0 | } |
256 | | |
257 | | template <class Data> |
258 | 0 | void S2PointIndex<Data>::Add(const PointData& point_data) { |
259 | 0 | S2CellId id(point_data.point()); |
260 | 0 | map_.insert(std::make_pair(id, point_data)); |
261 | 0 | } |
262 | | |
263 | | template <class Data> |
264 | 0 | void S2PointIndex<Data>::Add(const S2Point& point, const Data& data) { |
265 | 0 | Add(PointData(point, data)); |
266 | 0 | } |
267 | | |
268 | | template <class Data> |
269 | | void S2PointIndex<Data>::Add(const S2Point& point) { |
270 | | static_assert(std::is_empty<Data>::value, "Data must be empty"); |
271 | | Add(point, {}); |
272 | | } |
273 | | |
274 | | template <class Data> |
275 | | bool S2PointIndex<Data>::Remove(const PointData& point_data) { |
276 | | S2CellId id(point_data.point()); |
277 | | for (typename Map::iterator it = map_.lower_bound(id), end = map_.end(); |
278 | | it != end && it->first == id; ++it) { |
279 | | if (it->second == point_data) { |
280 | | map_.erase(it); |
281 | | return true; |
282 | | } |
283 | | } |
284 | | return false; |
285 | | } |
286 | | |
287 | | template <class Data> |
288 | | bool S2PointIndex<Data>::Remove(const S2Point& point, const Data& data) { |
289 | | return Remove(PointData(point, data)); |
290 | | } |
291 | | |
292 | | template <class Data> |
293 | | void S2PointIndex<Data>::Remove(const S2Point& point) { |
294 | | static_assert(std::is_empty<Data>::value, "Data must be empty"); |
295 | | Remove(point, {}); |
296 | | } |
297 | | |
298 | | template <class Data> |
299 | | void S2PointIndex<Data>::Clear() { |
300 | | map_.clear(); |
301 | | } |
302 | | |
303 | | template <class Data> |
304 | 0 | size_t S2PointIndex<Data>::SpaceUsed() const { |
305 | 0 | return sizeof(*this) - sizeof(map_) + map_.bytes_used(); |
306 | 0 | } |
307 | | |
308 | | template <class Data> |
309 | 0 | inline S2PointIndex<Data>::Iterator::Iterator() = default; |
310 | | |
311 | | template <class Data> |
312 | | inline S2PointIndex<Data>::Iterator::Iterator( |
313 | | const S2PointIndex<Data>* index) { |
314 | | Init(index); |
315 | | } |
316 | | |
317 | | template <class Data> |
318 | | inline void S2PointIndex<Data>::Iterator::Init( |
319 | 0 | const S2PointIndex<Data>* index) { |
320 | 0 | map_ = &index->map_; |
321 | 0 | iter_ = map_->begin(); |
322 | 0 | end_ = map_->end(); |
323 | 0 | } |
324 | | |
325 | | template <class Data> |
326 | 0 | inline S2CellId S2PointIndex<Data>::Iterator::id() const { |
327 | 0 | ABSL_DCHECK(!done()); |
328 | 0 | return iter_->first; |
329 | 0 | } |
330 | | |
331 | | template <class Data> |
332 | | inline const S2Point& S2PointIndex<Data>::Iterator::point() const { |
333 | | ABSL_DCHECK(!done()); |
334 | | return iter_->second.point(); |
335 | | } |
336 | | |
337 | | template <class Data> |
338 | | inline const Data& S2PointIndex<Data>::Iterator::data() const { |
339 | | ABSL_DCHECK(!done()); |
340 | | return iter_->second.data(); |
341 | | } |
342 | | |
343 | | template <class Data> |
344 | | inline const typename S2PointIndex<Data>::PointData& |
345 | 0 | S2PointIndex<Data>::Iterator::point_data() const { |
346 | 0 | ABSL_DCHECK(!done()); |
347 | 0 | return iter_->second; |
348 | 0 | } |
349 | | |
350 | | template <class Data> |
351 | 0 | inline bool S2PointIndex<Data>::Iterator::done() const { |
352 | 0 | return iter_ == end_; |
353 | 0 | } |
354 | | |
355 | | template <class Data> |
356 | 0 | inline void S2PointIndex<Data>::Iterator::Begin() { |
357 | 0 | iter_ = map_->begin(); |
358 | 0 | } |
359 | | |
360 | | template <class Data> |
361 | 0 | inline void S2PointIndex<Data>::Iterator::Finish() { |
362 | 0 | iter_ = end_; |
363 | 0 | } |
364 | | |
365 | | template <class Data> |
366 | 0 | inline void S2PointIndex<Data>::Iterator::Next() { |
367 | 0 | ABSL_DCHECK(!done()); |
368 | 0 | ++iter_; |
369 | 0 | } |
370 | | |
371 | | template <class Data> |
372 | 0 | inline bool S2PointIndex<Data>::Iterator::Prev() { |
373 | 0 | if (iter_ == map_->begin()) return false; |
374 | 0 | --iter_; |
375 | 0 | return true; |
376 | 0 | } |
377 | | |
378 | | template <class Data> |
379 | 0 | inline void S2PointIndex<Data>::Iterator::Seek(S2CellId target) { |
380 | 0 | iter_ = map_->lower_bound(target); |
381 | 0 | } |
382 | | |
383 | | #endif // S2_S2POINT_INDEX_H_ |