/src/s2geometry/src/s2/s2closest_edge_query_base.h
Line | Count | Source |
1 | | // Copyright 2017 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_S2CLOSEST_EDGE_QUERY_BASE_H_ |
19 | | #define S2_S2CLOSEST_EDGE_QUERY_BASE_H_ |
20 | | |
21 | | #include <algorithm> |
22 | | #include <cstdint> |
23 | | #include <cstdlib> |
24 | | #include <iterator> |
25 | | #include <limits> |
26 | | #include <memory> |
27 | | #include <optional> |
28 | | #include <queue> |
29 | | #include <type_traits> |
30 | | #include <utility> |
31 | | #include <vector> |
32 | | |
33 | | #include "absl/container/btree_set.h" |
34 | | #include "absl/container/flat_hash_set.h" |
35 | | #include "absl/container/inlined_vector.h" |
36 | | #include "absl/functional/function_ref.h" |
37 | | #include "absl/log/absl_check.h" |
38 | | #include "absl/log/absl_log.h" |
39 | | #include "s2/_fp_contract_off.h" // IWYU pragma: keep |
40 | | #include "s2/s1angle.h" |
41 | | #include "s2/s1chord_angle.h" |
42 | | #include "s2/s2cap.h" |
43 | | #include "s2/s2cell.h" |
44 | | #include "s2/s2cell_id.h" |
45 | | #include "s2/s2cell_union.h" |
46 | | #include "s2/s2distance_target.h" |
47 | | #include "s2/s2point.h" |
48 | | #include "s2/s2region_coverer.h" |
49 | | #include "s2/s2shape.h" |
50 | | #include "s2/s2shape_index.h" |
51 | | #include "s2/s2shapeutil_count_edges.h" |
52 | | #include "s2/s2shapeutil_shape_edge_id.h" |
53 | | |
54 | | // S2ClosestEdgeQueryBase is a templatized class for finding the closest |
55 | | // edge(s) between two geometries. It is not intended to be used directly, |
56 | | // but rather to serve as the implementation of various specialized classes |
57 | | // with more convenient APIs (such as S2ClosestEdgeQuery). It is flexible |
58 | | // enough so that it can be adapted to compute maximum distances and even |
59 | | // potentially Hausdorff distances. |
60 | | // |
61 | | // By using the appropriate options, this class can answer questions such as: |
62 | | // |
63 | | // - Find the minimum distance between two geometries A and B. |
64 | | // - Find all edges of geometry A that are within a distance D of geometry B. |
65 | | // - Find the k edges of geometry A that are closest to a given point P. |
66 | | // |
67 | | // You can also specify whether polygons should include their interiors (i.e., |
68 | | // if a point is contained by a polygon, should the distance be zero or should |
69 | | // it be measured to the polygon boundary?) |
70 | | // |
71 | | // The input geometries may consist of any number of points, polylines, and |
72 | | // polygons (collectively referred to as "shapes"). Shapes do not need to be |
73 | | // disjoint; they may overlap or intersect arbitrarily. The implementation is |
74 | | // designed to be fast for both simple and complex geometries. |
75 | | // |
76 | | // The Distance template argument is used to represent distances. Usually it |
77 | | // is a thin wrapper around S1ChordAngle, but another distance type may be |
78 | | // used as long as it implements the Distance concept described in |
79 | | // s2distance_target.h. For example this can be used to measure maximum |
80 | | // distances, to get more accuracy, or to measure non-spheroidal distances. |
81 | | template <class Distance> |
82 | | class S2ClosestEdgeQueryBase { |
83 | | public: |
84 | | // Each "Result" object represents a closest edge. Note the following |
85 | | // special cases: |
86 | | // |
87 | | // - (shape_id() >= 0) && (edge_id() < 0) represents the interior of a shape. |
88 | | // Such results may be returned when options.include_interiors() is true. |
89 | | // Such results can be identified using the is_interior() method. |
90 | | // |
91 | | // - (shape_id() < 0) && (edge_id() < 0) is returned by FindClosestEdge() |
92 | | // to indicate that no edge satisfies the given query options. Such |
93 | | // results can be identified using is_empty() method. |
94 | | class Result { |
95 | | public: |
96 | | // The default constructor yields an empty result, with a distance() of |
97 | | // Infinity() and shape_id == edge_id == -1. |
98 | 0 | Result() : distance_(Distance::Infinity()), shape_id_(-1), edge_id_(-1) {} |
99 | | |
100 | | // Constructs a Result object for the given arguments. |
101 | | Result(Distance distance, int32_t shape_id, int32_t edge_id) |
102 | 0 | : distance_(distance), shape_id_(shape_id), edge_id_(edge_id) {} |
103 | | |
104 | | // The distance from the target to this edge. |
105 | 0 | Distance distance() const { return distance_; } |
106 | | |
107 | | // Identifies an indexed shape. |
108 | 0 | int32_t shape_id() const { return shape_id_; } |
109 | | |
110 | | // Identifies an edge within the shape. |
111 | 0 | int32_t edge_id() const { return edge_id_; } |
112 | | |
113 | | // Returns true if this Result object represents the interior of a shape. |
114 | | // (Such results may be returned when options.include_interiors() is true.) |
115 | | bool is_interior() const { return shape_id_ >= 0 && edge_id_ < 0; } |
116 | | |
117 | | // Returns true if this Result object indicates that no edge satisfies the |
118 | | // given query options. (This result is only returned in one special |
119 | | // case, namely when FindClosestEdge() does not find any suitable edges. |
120 | | // It is never returned by methods that return a vector of results.) |
121 | 0 | bool is_empty() const { return shape_id_ < 0; } |
122 | | |
123 | | // Returns true if two Result objects are identical. |
124 | 0 | friend bool operator==(const Result& x, const Result& y) { |
125 | 0 | return (x.distance_ == y.distance_ && x.shape_id_ == y.shape_id_ && |
126 | 0 | x.edge_id_ == y.edge_id_); |
127 | 0 | } |
128 | | |
129 | | // Compares edges first by distance, then by (shape_id, edge_id). |
130 | 0 | friend bool operator<(const Result& x, const Result& y) { |
131 | 0 | if (x.distance_ < y.distance_) return true; |
132 | 0 | if (y.distance_ < x.distance_) return false; |
133 | 0 | if (x.shape_id_ < y.shape_id_) return true; |
134 | 0 | if (y.shape_id_ < x.shape_id_) return false; |
135 | 0 | return x.edge_id_ < y.edge_id_; |
136 | 0 | } |
137 | | |
138 | | // Indicates that linear rather than binary search should be used when this |
139 | | // type is used as the key in gtl::btree data structures. |
140 | | using absl_btree_prefer_linear_node_search = std::true_type; |
141 | | |
142 | | private: |
143 | | Distance distance_; // The distance from the target to this edge. |
144 | | int32_t shape_id_; // Identifies an indexed shape. |
145 | | int32_t edge_id_; // Identifies an edge within the shape. |
146 | | }; |
147 | | |
148 | | using Delta = typename Distance::Delta; |
149 | | using ShapeFilter = std::optional<absl::FunctionRef<bool(int)>>; |
150 | | using ResultVisitor = absl::FunctionRef<bool(const Result&)>; |
151 | | |
152 | | // Options that control the set of edges returned. Note that by default |
153 | | // *all* edges are returned, so you will always want to set either the |
154 | | // max_results() option or the max_distance() option (or both). |
155 | | class Options { |
156 | | public: |
157 | | Options(); |
158 | | |
159 | | // Specifies that at most "max_results" edges should be returned. |
160 | | // |
161 | | // REQUIRES: max_results >= 1 |
162 | | // DEFAULT: kMaxMaxResults |
163 | | int max_results() const; |
164 | | void set_max_results(int max_results); |
165 | | static constexpr int kMaxMaxResults = std::numeric_limits<int>::max(); |
166 | | |
167 | | // Specifies that only edges whose distance to the target is less than |
168 | | // "max_distance" should be returned. |
169 | | // |
170 | | // Note that edges whose distance is exactly equal to "max_distance" are |
171 | | // not returned. In most cases this doesn't matter (since distances are |
172 | | // not computed exactly in the first place), but if such edges are needed |
173 | | // then you can retrieve them by specifying "max_distance" as the next |
174 | | // largest representable Distance. For example, if Distance is an |
175 | | // S1ChordAngle then you can specify max_distance.Successor(). |
176 | | // |
177 | | // DEFAULT: Distance::Infinity() |
178 | | Distance max_distance() const; |
179 | | void set_max_distance(Distance max_distance); |
180 | | |
181 | | // Specifies that edges up to max_error() further away than the true |
182 | | // closest edges may be substituted in the result set, as long as such |
183 | | // edges satisfy all the remaining search criteria (such as max_distance). |
184 | | // This option only has an effect if max_results() is also specified; |
185 | | // otherwise all edges closer than max_distance() will always be returned. |
186 | | // |
187 | | // Note that this does not affect how the distance between edges is |
188 | | // computed; it simply gives the algorithm permission to stop the search |
189 | | // early as soon as the best possible improvement drops below max_error(). |
190 | | // |
191 | | // This can be used to implement distance predicates efficiently. For |
192 | | // example, to determine whether the minimum distance is less than D, set |
193 | | // max_results() == 1 and max_distance() == max_error() == D. This causes |
194 | | // the algorithm to terminate as soon as it finds any edge whose distance |
195 | | // is less than D, rather than continuing to search for an edge that is |
196 | | // even closer. |
197 | | // |
198 | | // DEFAULT: Distance::Delta::Zero() |
199 | | Delta max_error() const; |
200 | | void set_max_error(Delta max_error); |
201 | | |
202 | | // Specifies that polygon interiors should be included when measuring |
203 | | // distances. In other words, polygons that contain the target should |
204 | | // have a distance of zero. (For targets consisting of multiple connected |
205 | | // components, the distance is zero if any component is contained.) This |
206 | | // is indicated in the results by returning a (shape_id, edge_id) pair |
207 | | // with edge_id == -1, i.e. this value denotes the polygons's interior. |
208 | | // |
209 | | // Note that for efficiency, any polygon that intersects the target may or |
210 | | // may not have an (edge_id == -1) result. Such results are optional |
211 | | // because in that case the distance to the polygon is already zero. |
212 | | // |
213 | | // DEFAULT: true |
214 | | bool include_interiors() const; |
215 | | void set_include_interiors(bool include_interiors); |
216 | | |
217 | | // Specifies that distances should be computed by examining every edge |
218 | | // rather than using the S2ShapeIndex. This is useful for testing, |
219 | | // benchmarking, and debugging. |
220 | | // |
221 | | // DEFAULT: false |
222 | | bool use_brute_force() const; |
223 | | void set_use_brute_force(bool use_brute_force); |
224 | | |
225 | | private: |
226 | | Distance max_distance_ = Distance::Infinity(); |
227 | | Delta max_error_ = Delta::Zero(); |
228 | | int max_results_ = kMaxMaxResults; |
229 | | bool include_interiors_ = true; |
230 | | bool use_brute_force_ = false; |
231 | | }; |
232 | | |
233 | | // The Target class represents the geometry to which the distance is |
234 | | // measured. For example, there can be subtypes for measuring the distance |
235 | | // to a point, an edge, or to an S2ShapeIndex (an arbitrary collection of |
236 | | // geometry). |
237 | | // |
238 | | // Implementations do *not* need to be thread-safe. They may cache data or |
239 | | // allocate temporary data structures in order to improve performance. |
240 | | using Target = S2DistanceTarget<Distance>; |
241 | | |
242 | | // Default constructor; requires Init() to be called. |
243 | | S2ClosestEdgeQueryBase(); |
244 | | ~S2ClosestEdgeQueryBase(); |
245 | | |
246 | | // Convenience constructor that calls Init(). |
247 | | explicit S2ClosestEdgeQueryBase(const S2ShapeIndex* index); |
248 | | |
249 | | // S2ClosestEdgeQueryBase is not copyable. |
250 | | S2ClosestEdgeQueryBase(const S2ClosestEdgeQueryBase&) = delete; |
251 | | void operator=(const S2ClosestEdgeQueryBase&) = delete; |
252 | | |
253 | | // Initializes the query. |
254 | | // REQUIRES: ReInit() must be called if "index" is modified. |
255 | | void Init(const S2ShapeIndex* index); |
256 | | |
257 | | // Reinitializes the query. This method must be called whenever the |
258 | | // underlying index is modified. |
259 | | void ReInit(); |
260 | | |
261 | | // Returns a reference to the underlying S2ShapeIndex. |
262 | | const S2ShapeIndex& index() const; |
263 | | |
264 | | // Returns the closest edges to the given target that satisfy the given |
265 | | // options. This method may be called multiple times. |
266 | | // |
267 | | // Note that if options().include_interiors() is true, the result vector may |
268 | | // include some entries with edge_id == -1. This indicates that the target |
269 | | // intersects the indexed polygon with the given shape_id. |
270 | | std::vector<Result> FindClosestEdges(Target* target, const Options& options, |
271 | | ShapeFilter filter = {}); |
272 | | |
273 | | // This version can be more efficient when this method is called many times, |
274 | | // since it does not require allocating a new vector on each call. |
275 | | void FindClosestEdges(Target* target, const Options& options, |
276 | | std::vector<Result>* results, ShapeFilter filter = {}); |
277 | | |
278 | | // Calls a callback with the closest edges to the given target that satisfy |
279 | | // the given options. Edges are reported in order of increasing distance. |
280 | | // |
281 | | // Updating the state that the ShapeFilter accesses while visiting is allowed |
282 | | // and can be used to disable reporting of results on the fly. |
283 | | void VisitClosestEdges(Target* target, Options options, ResultVisitor visitor, |
284 | | ShapeFilter filter = {}); |
285 | | |
286 | | // Convenience method that returns exactly one edge. If no edges satisfy |
287 | | // the given search criteria, then a Result with distance == Infinity() and |
288 | | // shape_id == edge_id == -1 is returned. |
289 | | // |
290 | | // Note that if options.include_interiors() is true, edge_id == -1 is also |
291 | | // used to indicate that the target intersects an indexed polygon (but in |
292 | | // that case distance == Zero() and shape_id >= 0). |
293 | | // |
294 | | // REQUIRES: options.max_results() == 1 |
295 | | Result FindClosestEdge(Target* target, const Options& options, |
296 | | ShapeFilter filter = {}); |
297 | | |
298 | | private: |
299 | | struct QueueEntry; |
300 | | |
301 | 0 | const Options& options() const { return *options_; } |
302 | | void FindClosestEdgesInternal(Target* target, const Options& options, |
303 | | std::optional<ResultVisitor> visitor = {}); |
304 | | void FindClosestEdgesBruteForce(std::optional<ResultVisitor> visitor = {}); |
305 | | void FindClosestEdgesOptimized(std::optional<ResultVisitor> visitor = {}); |
306 | | void InitQueue(); |
307 | | void InitCovering(); |
308 | | void AddInitialRange(const S2ShapeIndex::Iterator& first, |
309 | | const S2ShapeIndex::Iterator& last); |
310 | | void MaybeAddResult(const S2Shape& shape, int shape_id, int edge_id); |
311 | | void AddResult(const Result& result); |
312 | | void ProcessEdges(const QueueEntry& entry); |
313 | | void ProcessOrEnqueue(S2CellId id); |
314 | | void ProcessOrEnqueue(S2CellId id, const S2ShapeIndexCell* index_cell); |
315 | | |
316 | | // An optional call back for filtering shapes out as we scan the index. This |
317 | | // is only set temporarily while a query is running. |
318 | | ShapeFilter shape_filter_; |
319 | | |
320 | | // Reports results that are less than cell_distance to the visitor. |
321 | | // |
322 | | // Returns true if result generation should continue, false otherwise. |
323 | 0 | bool ReportResults(ResultVisitor visitor, const Distance& cell_distance) { |
324 | 0 | auto iter = result_set_.begin(); |
325 | 0 | while (iter != result_set_.end() && iter->distance() < cell_distance) { |
326 | | // Re-check the shape filtering in case the user is updating it as we go. |
327 | 0 | if (!shape_filter_ || (*shape_filter_)(iter->shape_id())) { |
328 | 0 | if (!visitor(*iter)) { |
329 | 0 | return false; |
330 | 0 | } |
331 | 0 | } |
332 | 0 | iter = result_set_.erase(iter); |
333 | 0 | } |
334 | 0 | return true; |
335 | 0 | } |
336 | | |
337 | | const S2ShapeIndex* index_; |
338 | | const Options* options_; |
339 | | Target* target_; |
340 | | |
341 | | // Indicates we're sending results to a visitor instead of accumulating them |
342 | | // entirely in internal storage. |
343 | | // |
344 | | // Placed here in padding intentionally to avoid increasing class size. |
345 | | bool visiting_ = false; |
346 | | |
347 | | // True if max_error() must be subtracted from priority queue cell distances |
348 | | // in order to ensure that such distances are measured conservatively. This |
349 | | // is true only if the target takes advantage of max_error() in order to |
350 | | // return faster results, and 0 < max_error() < distance_limit_. |
351 | | bool use_conservative_cell_distance_; |
352 | | |
353 | | // For the optimized algorithm we precompute the top-level S2CellIds that |
354 | | // will be added to the priority queue. There can be at most 6 of these |
355 | | // cells. Essentially this is just a covering of the indexed edges, except |
356 | | // that we also store pointers to the corresponding S2ShapeIndexCells to |
357 | | // reduce the number of index seeks required. |
358 | | // |
359 | | // The covering needs to be stored in a std::vector so that we can use |
360 | | // S2CellUnion::GetIntersection(). |
361 | | std::vector<S2CellId> index_covering_; |
362 | | absl::InlinedVector<const S2ShapeIndexCell*, 6> index_cells_; |
363 | | |
364 | | // The decision about whether to use the brute force algorithm is based on |
365 | | // counting the total number of edges in the index. However if the index |
366 | | // contains a large number of shapes, this in itself might take too long. |
367 | | // So instead we only count edges up to (max_brute_force_index_size() + 1) |
368 | | // for the current target type (stored as index_num_edges_limit_). |
369 | | int index_num_edges_; |
370 | | int index_num_edges_limit_; |
371 | | |
372 | | // The distance beyond which we can safely ignore further candidate edges. |
373 | | // (Candidates that are exactly at the limit are ignored; this is more |
374 | | // efficient for UpdateMinDistance() and should not affect clients since |
375 | | // distance measurements have a small amount of error anyway.) |
376 | | // |
377 | | // Initially this is the same as the maximum distance specified by the user, |
378 | | // but it can also be updated by the algorithm (see MaybeAddResult). |
379 | | Distance distance_limit_; |
380 | | |
381 | | // The current result set is stored in one of three ways: |
382 | | // |
383 | | // - If max_results() == 1, the best result is kept in result_singleton_. |
384 | | // |
385 | | // - If max_results() == "infinity", results are appended to result_vector_ |
386 | | // and sorted/uniqued at the end. |
387 | | // |
388 | | // - Otherwise results are kept in a btree_set so that we can progressively |
389 | | // reduce the distance limit once max_results() results have been found. |
390 | | // (A priority queue is not sufficient because we need to be able to |
391 | | // check whether a candidate edge is already in the result set.) |
392 | | // |
393 | | // TODO(ericv): Check whether it would be faster to use avoid_duplicates_ |
394 | | // when result_set_ is used so that we could use a priority queue instead. |
395 | | Result result_singleton_; |
396 | | std::vector<Result> result_vector_; |
397 | | absl::btree_set<Result> result_set_; |
398 | | |
399 | | // When the result edges are stored in a btree_set (see above), usually |
400 | | // duplicates can be removed simply by inserting candidate edges in the |
401 | | // current set. However this is not true if Options::max_error() > 0 and |
402 | | // the Target subtype takes advantage of this by returning suboptimal |
403 | | // distances. This is because when UpdateMinDistance() is called with |
404 | | // different "min_dist" parameters (i.e., the distance to beat), the |
405 | | // implementation may return a different distance for the same edge. Since |
406 | | // the btree_set is keyed by (distance, shape_id, edge_id) this can create |
407 | | // duplicate edges in the results. |
408 | | // |
409 | | // The flag below is true when duplicates must be avoided explicitly. This |
410 | | // is achieved by maintaining a separate set keyed by (shape_id, edge_id) |
411 | | // only, and checking whether each edge is in that set before computing the |
412 | | // distance to it. |
413 | | // |
414 | | // TODO(ericv): Check whether it is faster to avoid duplicates by default |
415 | | // (even when Options::max_results() == 1), rather than just when we need to. |
416 | | bool avoid_duplicates_; |
417 | | using ShapeEdgeId = s2shapeutil::ShapeEdgeId; |
418 | | absl::flat_hash_set<ShapeEdgeId> tested_edges_; |
419 | | |
420 | | // The algorithm maintains a priority queue of unprocessed S2CellIds, sorted |
421 | | // in increasing order of distance from the target. |
422 | | struct QueueEntry { |
423 | | // A lower bound on the distance from the target to "id". This is the key |
424 | | // of the priority queue. |
425 | | Distance distance; |
426 | | |
427 | | // The cell being queued. |
428 | | S2CellId id; |
429 | | |
430 | | // If "id" belongs to the index, this field stores the corresponding |
431 | | // S2ShapeIndexCell. Otherwise "id" is a proper ancestor of one or more |
432 | | // S2ShapeIndexCells and this field stores nullptr. The purpose of this |
433 | | // field is to avoid an extra Seek() when the queue entry is processed. |
434 | | const S2ShapeIndexCell* index_cell; |
435 | | |
436 | | QueueEntry(Distance _distance, S2CellId _id, |
437 | | const S2ShapeIndexCell* _index_cell) |
438 | 0 | : distance(_distance), id(_id), index_cell(_index_cell) { |
439 | 0 | } |
440 | 0 | bool operator<(const QueueEntry& other) const { |
441 | | // The priority queue returns the largest elements first, so we want the |
442 | | // "largest" entry to have the smallest distance. |
443 | 0 | return other.distance < distance; |
444 | 0 | } |
445 | | }; |
446 | | using CellQueue = |
447 | | std::priority_queue<QueueEntry, absl::InlinedVector<QueueEntry, 16>>; |
448 | | CellQueue queue_; |
449 | | |
450 | | // Temporaries, defined here to avoid multiple allocations / initializations. |
451 | | |
452 | | S2ShapeIndex::Iterator iter_; |
453 | | std::vector<S2CellId> max_distance_covering_; |
454 | | std::vector<S2CellId> initial_cells_; |
455 | | }; |
456 | | |
457 | | |
458 | | ////////////////// Implementation details follow //////////////////// |
459 | | |
460 | | template <class Distance> |
461 | 0 | inline S2ClosestEdgeQueryBase<Distance>::Options::Options() = default; |
462 | | |
463 | | template <class Distance> |
464 | 0 | inline int S2ClosestEdgeQueryBase<Distance>::Options::max_results() const { |
465 | 0 | return max_results_; |
466 | 0 | } |
467 | | |
468 | | template <class Distance> |
469 | | inline void S2ClosestEdgeQueryBase<Distance>::Options::set_max_results( |
470 | 0 | int max_results) { |
471 | 0 | ABSL_DCHECK_GE(max_results, 1); |
472 | 0 | max_results_ = max_results; |
473 | 0 | } |
474 | | |
475 | | template <class Distance> |
476 | | inline Distance S2ClosestEdgeQueryBase<Distance>::Options::max_distance() |
477 | 0 | const { |
478 | 0 | return max_distance_; |
479 | 0 | } |
480 | | |
481 | | template <class Distance> |
482 | | inline void S2ClosestEdgeQueryBase<Distance>::Options::set_max_distance( |
483 | 0 | Distance max_distance) { |
484 | 0 | max_distance_ = max_distance; |
485 | 0 | } |
486 | | |
487 | | template <class Distance> |
488 | | inline typename Distance::Delta |
489 | 0 | S2ClosestEdgeQueryBase<Distance>::Options::max_error() const { |
490 | 0 | return max_error_; |
491 | 0 | } |
492 | | |
493 | | template <class Distance> |
494 | | inline void S2ClosestEdgeQueryBase<Distance>::Options::set_max_error( |
495 | 0 | Delta max_error) { |
496 | 0 | max_error_ = max_error; |
497 | 0 | } |
498 | | |
499 | | template <class Distance> |
500 | | inline bool S2ClosestEdgeQueryBase<Distance>::Options::include_interiors() |
501 | 0 | const { |
502 | 0 | return include_interiors_; |
503 | 0 | } |
504 | | |
505 | | template <class Distance> |
506 | | inline void S2ClosestEdgeQueryBase<Distance>::Options::set_include_interiors( |
507 | 0 | bool include_interiors) { |
508 | 0 | include_interiors_ = include_interiors; |
509 | 0 | } |
510 | | |
511 | | template <class Distance> |
512 | 0 | inline bool S2ClosestEdgeQueryBase<Distance>::Options::use_brute_force() const { |
513 | 0 | return use_brute_force_; |
514 | 0 | } |
515 | | |
516 | | template <class Distance> |
517 | | inline void S2ClosestEdgeQueryBase<Distance>::Options::set_use_brute_force( |
518 | 0 | bool use_brute_force) { |
519 | 0 | use_brute_force_ = use_brute_force; |
520 | 0 | } |
521 | | |
522 | | template <class Distance> |
523 | | S2ClosestEdgeQueryBase<Distance>::S2ClosestEdgeQueryBase() |
524 | 0 | : tested_edges_(/*bucket_count=*/1) {} |
525 | | |
526 | | template <class Distance> |
527 | 0 | S2ClosestEdgeQueryBase<Distance>::~S2ClosestEdgeQueryBase() { |
528 | | // Prevent inline destructor bloat by providing a definition. |
529 | 0 | } |
530 | | |
531 | | template <class Distance> |
532 | | inline S2ClosestEdgeQueryBase<Distance>::S2ClosestEdgeQueryBase( |
533 | | const S2ShapeIndex* index) : S2ClosestEdgeQueryBase() { |
534 | | Init(index); |
535 | | } |
536 | | |
537 | | template <class Distance> |
538 | 0 | void S2ClosestEdgeQueryBase<Distance>::Init(const S2ShapeIndex* index) { |
539 | 0 | index_ = index; |
540 | 0 | ReInit(); |
541 | 0 | } |
542 | | |
543 | | template <class Distance> |
544 | 0 | void S2ClosestEdgeQueryBase<Distance>::ReInit() { |
545 | 0 | index_num_edges_ = 0; |
546 | 0 | index_num_edges_limit_ = 0; |
547 | 0 | index_covering_.clear(); |
548 | 0 | index_cells_.clear(); |
549 | | // We don't initialize iter_ here to make queries on small indexes a bit |
550 | | // faster (i.e., where brute force is used). |
551 | 0 | } |
552 | | |
553 | | template <class Distance> |
554 | 0 | inline const S2ShapeIndex& S2ClosestEdgeQueryBase<Distance>::index() const { |
555 | 0 | return *index_; |
556 | 0 | } |
557 | | |
558 | | template <class Distance> |
559 | | inline std::vector<typename S2ClosestEdgeQueryBase<Distance>::Result> |
560 | | S2ClosestEdgeQueryBase<Distance>::FindClosestEdges(Target* target, |
561 | | const Options& options, |
562 | 0 | ShapeFilter filter) { |
563 | 0 | std::vector<Result> results; |
564 | 0 | if (filter) { |
565 | 0 | shape_filter_.emplace(*filter); |
566 | 0 | } |
567 | 0 | FindClosestEdges(target, options, &results); |
568 | 0 | shape_filter_.reset(); |
569 | 0 | return results; |
570 | 0 | } |
571 | | |
572 | | template <class Distance> |
573 | | inline void S2ClosestEdgeQueryBase<Distance>::VisitClosestEdges( |
574 | | Target* target, Options options, ResultVisitor visitor, |
575 | 0 | ShapeFilter filter) { |
576 | | // Temporarily set the visitor and shape filter, if we have one. |
577 | 0 | if (filter) { |
578 | 0 | shape_filter_.emplace(*filter); |
579 | 0 | } |
580 | |
|
581 | 0 | result_set_.clear(); |
582 | |
|
583 | 0 | visiting_ = true; |
584 | |
|
585 | 0 | int num_results_ = 0; |
586 | 0 | const int max_results = options.max_results(); |
587 | 0 | FindClosestEdgesInternal(target, options, [&](const Result& result) { |
588 | 0 | return (++num_results_ <= max_results) && visitor(result); |
589 | 0 | }); |
590 | |
|
591 | 0 | visiting_ = false; |
592 | |
|
593 | 0 | shape_filter_.reset(); |
594 | 0 | } |
595 | | |
596 | | template <class Distance> |
597 | | typename S2ClosestEdgeQueryBase<Distance>::Result |
598 | | S2ClosestEdgeQueryBase<Distance>::FindClosestEdge(Target* target, |
599 | | const Options& options, |
600 | 0 | ShapeFilter filter) { |
601 | 0 | ABSL_DCHECK_EQ(options.max_results(), 1); |
602 | 0 | if (filter) { |
603 | 0 | shape_filter_.emplace(*filter); |
604 | 0 | } |
605 | 0 | FindClosestEdgesInternal(target, options); |
606 | 0 | shape_filter_.reset(); |
607 | 0 | return result_singleton_; |
608 | 0 | } |
609 | | |
610 | | template <class Distance> |
611 | | void S2ClosestEdgeQueryBase<Distance>::FindClosestEdges( |
612 | | Target* target, const Options& options, std::vector<Result>* results, |
613 | 0 | ShapeFilter filter) { |
614 | 0 | if (filter) { |
615 | 0 | shape_filter_.emplace(*filter); |
616 | 0 | } |
617 | 0 | FindClosestEdgesInternal(target, options); |
618 | 0 | shape_filter_.reset(); |
619 | |
|
620 | 0 | results->clear(); |
621 | 0 | if (options.max_results() == 1) { |
622 | 0 | if (result_singleton_.shape_id() >= 0) { |
623 | 0 | results->push_back(result_singleton_); |
624 | 0 | } |
625 | 0 | } else if (options.max_results() == Options::kMaxMaxResults) { |
626 | 0 | std::sort(result_vector_.begin(), result_vector_.end()); |
627 | 0 | std::unique_copy(result_vector_.begin(), result_vector_.end(), |
628 | 0 | std::back_inserter(*results)); |
629 | 0 | result_vector_.clear(); |
630 | 0 | } else { |
631 | 0 | results->assign(result_set_.begin(), result_set_.end()); |
632 | 0 | result_set_.clear(); |
633 | 0 | } |
634 | 0 | } |
635 | | |
636 | | template <class Distance> |
637 | | void S2ClosestEdgeQueryBase<Distance>::FindClosestEdgesInternal( |
638 | | Target* target, const Options& options, |
639 | 0 | std::optional<ResultVisitor> visitor) { |
640 | 0 | target_ = target; |
641 | 0 | options_ = &options; |
642 | |
|
643 | 0 | tested_edges_.clear(); |
644 | 0 | distance_limit_ = options.max_distance(); |
645 | 0 | result_singleton_ = Result(); |
646 | 0 | ABSL_DCHECK(result_vector_.empty()); |
647 | 0 | ABSL_DCHECK(result_set_.empty()); |
648 | 0 | ABSL_DCHECK_GE(target->max_brute_force_index_size(), 0); |
649 | 0 | if (distance_limit_ == Distance::Zero()) return; |
650 | | |
651 | 0 | if (options.max_results() == Options::kMaxMaxResults && |
652 | 0 | options.max_distance() == Distance::Infinity()) { |
653 | 0 | ABSL_LOG(WARNING) |
654 | 0 | << "Returning all edges (max_results/max_distance not set)"; |
655 | 0 | } |
656 | |
|
657 | 0 | if (options.include_interiors()) { |
658 | 0 | absl::btree_set<int32_t> shape_ids; |
659 | |
|
660 | 0 | const size_t max_results = static_cast<size_t>(options.max_results()); |
661 | 0 | if (!shape_filter_) { |
662 | | // By default just insert shape ids into the output set. |
663 | 0 | (void)target->VisitContainingShapeIds( |
664 | 0 | *index_, [&](int id, const S2Point&) { |
665 | 0 | shape_ids.insert(id); |
666 | 0 | return shape_ids.size() < max_results; |
667 | 0 | }); |
668 | 0 | } else { |
669 | | // If we have a shape filter, then filter shape ids before storing them. |
670 | 0 | (void)target->VisitContainingShapeIds( |
671 | 0 | *index_, [&](int id, const S2Point&) { |
672 | 0 | if ((*shape_filter_)(id)) { |
673 | 0 | shape_ids.insert(id); |
674 | 0 | } |
675 | 0 | return shape_ids.size() < max_results; |
676 | 0 | }); |
677 | 0 | } |
678 | |
|
679 | 0 | const Distance kZero = Distance::Zero(); |
680 | 0 | for (int shape_id : shape_ids) { |
681 | 0 | AddResult(Result(kZero, shape_id, -1)); |
682 | 0 | } |
683 | 0 | if (distance_limit_ == kZero) return; |
684 | 0 | } |
685 | | |
686 | | // If max_error() > 0 and the target takes advantage of this, then we may |
687 | | // need to adjust the distance estimates to the priority queue cells to |
688 | | // ensure that they are always a lower bound on the true distance. For |
689 | | // example, suppose max_distance == 100, max_error == 30, and we compute the |
690 | | // distance to the target from some cell C0 as d(C0) == 80. Then because |
691 | | // the target takes advantage of max_error(), the true distance could be as |
692 | | // low as 50. In order not to miss edges contained by such cells, we need |
693 | | // to subtract max_error() from the distance estimates. This behavior is |
694 | | // controlled by the use_conservative_cell_distance_ flag. |
695 | | // |
696 | | // However there is one important case where this adjustment is not |
697 | | // necessary, namely when max_distance() < max_error(). This is because |
698 | | // max_error() only affects the algorithm once at least max_results() edges |
699 | | // have been found that satisfy the given distance limit. At that point, |
700 | | // max_error() is subtracted from distance_limit_ in order to ensure that |
701 | | // any further matches are closer by at least that amount. But when |
702 | | // max_distance() < max_error(), this reduces the distance limit to 0, |
703 | | // i.e. all remaining candidate cells and edges can safely be discarded. |
704 | | // (Note that this is how IsDistanceLess() and friends are implemented.) |
705 | | // |
706 | | // Note that Distance::Delta only supports operator==. |
707 | 0 | bool target_uses_max_error = (!(options.max_error() == Delta::Zero()) && |
708 | 0 | target_->set_max_error(options.max_error())); |
709 | | |
710 | | // Note that we can't compare max_error() and distance_limit_ directly |
711 | | // because one is a Delta and one is a Distance. Instead we subtract them. |
712 | 0 | use_conservative_cell_distance_ = target_uses_max_error && |
713 | 0 | (distance_limit_ == Distance::Infinity() || |
714 | 0 | Distance::Zero() < distance_limit_ - options.max_error()); |
715 | | |
716 | | // Use the brute force algorithm if the index is small enough. To avoid |
717 | | // spending too much time counting edges when there are many shapes, we stop |
718 | | // counting once there are too many edges. We may need to recount the edges |
719 | | // if we later see a target with a larger brute force edge threshold. |
720 | 0 | int min_optimized_edges = target_->max_brute_force_index_size() + 1; |
721 | 0 | if (min_optimized_edges > index_num_edges_limit_ && |
722 | 0 | index_num_edges_ >= index_num_edges_limit_) { |
723 | 0 | index_num_edges_ = s2shapeutil::CountEdgesUpTo(*index_, |
724 | 0 | min_optimized_edges); |
725 | 0 | index_num_edges_limit_ = min_optimized_edges; |
726 | 0 | } |
727 | |
|
728 | 0 | if (options.use_brute_force() || index_num_edges_ < min_optimized_edges) { |
729 | | // The brute force algorithm considers each edge exactly once. |
730 | 0 | avoid_duplicates_ = false; |
731 | 0 | FindClosestEdgesBruteForce(visitor); |
732 | 0 | } else { |
733 | | // If we're passing results to a visitor or the target takes advantage of |
734 | | // max_error() then we need to avoid duplicate edges explicitly. (Otherwise |
735 | | // it happens automatically.) |
736 | 0 | avoid_duplicates_ = |
737 | 0 | (visitor || (target_uses_max_error && options.max_results() > 1)); |
738 | 0 | FindClosestEdgesOptimized(visitor); |
739 | 0 | } |
740 | 0 | } |
741 | | |
742 | | template <class Distance> |
743 | | void S2ClosestEdgeQueryBase<Distance>::FindClosestEdgesBruteForce( |
744 | 0 | std::optional<ResultVisitor> visitor) { |
745 | 0 | for (int shape_id = 0; shape_id < index_->num_shape_ids(); ++shape_id) { |
746 | 0 | const S2Shape* shape = index_->shape(shape_id); |
747 | 0 | if (shape == nullptr) { |
748 | 0 | continue; |
749 | 0 | } |
750 | | |
751 | 0 | if (!shape_filter_ || (*shape_filter_)(shape_id)) { |
752 | 0 | int num_edges = shape->num_edges(); |
753 | 0 | for (int e = 0; e < num_edges; ++e) { |
754 | 0 | MaybeAddResult(*shape, shape_id, e); |
755 | 0 | } |
756 | 0 | } |
757 | 0 | } |
758 | | |
759 | | // Flush results to the visitor if we have one. |
760 | 0 | if (visitor) { |
761 | 0 | ReportResults(*visitor, Distance::Infinity()); |
762 | 0 | } |
763 | 0 | } |
764 | | |
765 | | template <class Distance> |
766 | | void S2ClosestEdgeQueryBase<Distance>::FindClosestEdgesOptimized( |
767 | 0 | std::optional<ResultVisitor> visitor) { |
768 | 0 | Distance last_cell_distance = Distance::Zero(); |
769 | |
|
770 | 0 | InitQueue(); |
771 | | // Repeatedly find the closest S2Cell to "target" and either split it into |
772 | | // its four children or process all of its edges. |
773 | 0 | while (!queue_.empty()) { |
774 | | // We need to copy the top entry before removing it, and we need to |
775 | | // remove it before adding any new entries to the queue. |
776 | 0 | QueueEntry entry = queue_.top(); |
777 | 0 | queue_.pop(); |
778 | 0 | if (!(entry.distance < distance_limit_)) { |
779 | 0 | queue_ = CellQueue(); // Clear any remaining entries. |
780 | 0 | break; |
781 | 0 | } |
782 | | |
783 | | // If the cell distance has increased since we last reported results, then |
784 | | // we can try to report more results to the visitor. |
785 | 0 | if (visitor && (last_cell_distance < entry.distance)) { |
786 | 0 | last_cell_distance = entry.distance; |
787 | 0 | if (!ReportResults(*visitor, last_cell_distance)) { |
788 | 0 | return; |
789 | 0 | } |
790 | 0 | } |
791 | | |
792 | | // If this is already known to be an index cell, just process it. |
793 | 0 | if (entry.index_cell != nullptr) { |
794 | 0 | ProcessEdges(entry); |
795 | 0 | continue; |
796 | 0 | } |
797 | | // Otherwise split the cell into its four children. Before adding a |
798 | | // child back to the queue, we first check whether it is empty. We do |
799 | | // this in two seek operations rather than four by seeking to the key |
800 | | // between children 0 and 1 and to the key between children 2 and 3. |
801 | 0 | S2CellId id = entry.id; |
802 | 0 | iter_.Seek(id.child(1).range_min()); |
803 | 0 | if (!iter_.done() && iter_.id() <= id.child(1).range_max()) { |
804 | 0 | ProcessOrEnqueue(id.child(1)); |
805 | 0 | } |
806 | 0 | if (iter_.Prev() && iter_.id() >= id.range_min()) { |
807 | 0 | ProcessOrEnqueue(id.child(0)); |
808 | 0 | } |
809 | 0 | iter_.Seek(id.child(3).range_min()); |
810 | 0 | if (!iter_.done() && iter_.id() <= id.range_max()) { |
811 | 0 | ProcessOrEnqueue(id.child(3)); |
812 | 0 | } |
813 | 0 | if (iter_.Prev() && iter_.id() >= id.child(2).range_min()) { |
814 | 0 | ProcessOrEnqueue(id.child(2)); |
815 | 0 | } |
816 | 0 | } |
817 | | |
818 | | // Flush results to the visitor if we have one. |
819 | 0 | if (visitor) { |
820 | 0 | ReportResults(*visitor, Distance::Infinity()); |
821 | 0 | } |
822 | 0 | } |
823 | | |
824 | | template <class Distance> |
825 | 0 | void S2ClosestEdgeQueryBase<Distance>::InitQueue() { |
826 | 0 | ABSL_DCHECK(queue_.empty()); |
827 | 0 | if (index_covering_.empty()) { |
828 | | // We delay iterator initialization until now to make queries on very |
829 | | // small indexes a bit faster (i.e., where brute force is used). |
830 | 0 | iter_.Init(index_, S2ShapeIndex::UNPOSITIONED); |
831 | 0 | } |
832 | | |
833 | | // Optimization: if the user is searching for just the closest edge, and the |
834 | | // center of the target's bounding cap happens to intersect an index cell, |
835 | | // then we try to limit the search region to a small disc by first |
836 | | // processing the edges in that cell. This sets distance_limit_ based on |
837 | | // the closest edge in that cell, which we can then use to limit the search |
838 | | // area. This means that the cell containing "target" will be processed |
839 | | // twice, but in general this is still faster. |
840 | | // |
841 | | // TODO(ericv): Even if the cap center is not contained, we could still |
842 | | // process one or both of the adjacent index cells in S2CellId order, |
843 | | // provided that those cells are closer than distance_limit_. |
844 | 0 | S2Cap cap = target_->GetCapBound(); |
845 | 0 | if (cap.is_empty()) return; // Empty target. |
846 | 0 | if (options().max_results() == 1 && iter_.Locate(cap.center())) { |
847 | 0 | ProcessEdges(QueueEntry(Distance::Zero(), iter_.id(), &iter_.cell())); |
848 | | // Skip the rest of the algorithm if we found an intersecting edge. |
849 | 0 | if (distance_limit_ == Distance::Zero()) return; |
850 | 0 | } |
851 | 0 | if (index_covering_.empty()) InitCovering(); |
852 | 0 | if (distance_limit_ == Distance::Infinity()) { |
853 | | // Start with the precomputed index covering. |
854 | 0 | for (size_t i = 0; i < index_covering_.size(); ++i) { |
855 | 0 | ProcessOrEnqueue(index_covering_[i], index_cells_[i]); |
856 | 0 | } |
857 | 0 | } else { |
858 | | // Compute a covering of the search disc and intersect it with the |
859 | | // precomputed index covering. |
860 | 0 | S2RegionCoverer coverer; |
861 | 0 | coverer.mutable_options()->set_max_cells(4); |
862 | 0 | S1ChordAngle radius = cap.radius() + distance_limit_.GetChordAngleBound(); |
863 | 0 | S2Cap search_cap(cap.center(), radius); |
864 | 0 | coverer.GetFastCovering(search_cap, &max_distance_covering_); |
865 | 0 | S2CellUnion::GetIntersection(index_covering_, max_distance_covering_, |
866 | 0 | &initial_cells_); |
867 | | |
868 | | // Now we need to clean up the initial cells to ensure that they all |
869 | | // contain at least one cell of the S2ShapeIndex. (Some may not intersect |
870 | | // the index at all, while other may be descendants of an index cell.) |
871 | 0 | for (size_t i = 0, j = 0; i < initial_cells_.size();) { |
872 | 0 | S2CellId id_i = initial_cells_[i]; |
873 | | // Find the top-level cell that contains this initial cell. |
874 | 0 | while (index_covering_[j].range_max() < id_i) ++j; |
875 | 0 | S2CellId id_j = index_covering_[j]; |
876 | 0 | if (id_i == id_j) { |
877 | | // This initial cell is one of the top-level cells. Use the |
878 | | // precomputed S2ShapeIndexCell pointer to avoid an index seek. |
879 | 0 | ProcessOrEnqueue(id_j, index_cells_[j]); |
880 | 0 | ++i, ++j; |
881 | 0 | } else { |
882 | | // This initial cell is a proper descendant of a top-level cell. |
883 | | // Check how it is related to the cells of the S2ShapeIndex. |
884 | 0 | S2CellRelation r = iter_.Locate(id_i); |
885 | 0 | if (r == S2CellRelation::INDEXED) { |
886 | | // This cell is a descendant of an index cell. Enqueue it and skip |
887 | | // any other initial cells that are also descendants of this cell. |
888 | 0 | ProcessOrEnqueue(iter_.id(), &iter_.cell()); |
889 | 0 | const S2CellId last_id = iter_.id().range_max(); |
890 | 0 | while (++i < initial_cells_.size() && initial_cells_[i] <= last_id) |
891 | 0 | continue; |
892 | 0 | } else { |
893 | | // Enqueue the cell only if it contains at least one index cell. |
894 | 0 | if (r == S2CellRelation::SUBDIVIDED) ProcessOrEnqueue(id_i, nullptr); |
895 | 0 | ++i; |
896 | 0 | } |
897 | 0 | } |
898 | 0 | } |
899 | 0 | } |
900 | 0 | } |
901 | | |
902 | | template <class Distance> |
903 | 0 | void S2ClosestEdgeQueryBase<Distance>::InitCovering() { |
904 | | // Find the range of S2Cells spanned by the index and choose a level such |
905 | | // that the entire index can be covered with just a few cells. These are |
906 | | // the "top-level" cells. There are two cases: |
907 | | // |
908 | | // - If the index spans more than one face, then there is one top-level cell |
909 | | // per spanned face, just big enough to cover the index cells on that face. |
910 | | // |
911 | | // - If the index spans only one face, then we find the smallest cell "C" |
912 | | // that covers the index cells on that face (just like the case above). |
913 | | // Then for each of the 4 children of "C", if the child contains any index |
914 | | // cells then we create a top-level cell that is big enough to just fit |
915 | | // those index cells (i.e., shrinking the child as much as possible to fit |
916 | | // its contents). This essentially replicates what would happen if we |
917 | | // started with "C" as the top-level cell, since "C" would immediately be |
918 | | // split, except that we take the time to prune the children further since |
919 | | // this will save work on every subsequent query. |
920 | | |
921 | | // Don't need to reserve index_cells_ since it is an InlinedVector. |
922 | 0 | index_covering_.reserve(6); |
923 | | |
924 | | // TODO(ericv): Use a single iterator (iter_) below and save position |
925 | | // information using pair<S2CellId, const S2ShapeIndexCell*> type. |
926 | 0 | S2ShapeIndex::Iterator next(index_, S2ShapeIndex::BEGIN); |
927 | 0 | S2ShapeIndex::Iterator last(index_, S2ShapeIndex::END); |
928 | 0 | last.Prev(); |
929 | 0 | if (next.id() != last.id()) { |
930 | | // The index has at least two cells. Choose a level such that the entire |
931 | | // index can be spanned with at most 6 cells (if the index spans multiple |
932 | | // faces) or 4 cells (if the index spans a single face). |
933 | 0 | int level = next.id().GetCommonAncestorLevel(last.id()) + 1; |
934 | | |
935 | | // Visit each potential top-level cell except the last (handled below). |
936 | 0 | S2CellId last_id = last.id().parent(level); |
937 | 0 | for (S2CellId id = next.id().parent(level); id != last_id; id = id.next()) { |
938 | | // Skip any top-level cells that don't contain any index cells. |
939 | 0 | if (id.range_max() < next.id()) continue; |
940 | | |
941 | | // Find the range of index cells contained by this top-level cell and |
942 | | // then shrink the cell if necessary so that it just covers them. |
943 | 0 | S2ShapeIndex::Iterator cell_first = next; |
944 | 0 | next.Seek(id.range_max().next()); |
945 | 0 | S2ShapeIndex::Iterator cell_last = next; |
946 | 0 | cell_last.Prev(); |
947 | 0 | AddInitialRange(cell_first, cell_last); |
948 | 0 | } |
949 | 0 | } |
950 | 0 | AddInitialRange(next, last); |
951 | 0 | } |
952 | | |
953 | | // Add an entry to index_covering_ and index_cells_ that covers the given |
954 | | // inclusive range of cells. |
955 | | // |
956 | | // REQUIRES: "first" and "last" have a common ancestor. |
957 | | template <class Distance> |
958 | | void S2ClosestEdgeQueryBase<Distance>::AddInitialRange( |
959 | | const S2ShapeIndex::Iterator& first, |
960 | 0 | const S2ShapeIndex::Iterator& last) { |
961 | 0 | if (first.id() == last.id()) { |
962 | | // The range consists of a single index cell. |
963 | 0 | index_covering_.push_back(first.id()); |
964 | 0 | index_cells_.push_back(&first.cell()); |
965 | 0 | } else { |
966 | | // Add the lowest common ancestor of the given range. |
967 | 0 | int level = first.id().GetCommonAncestorLevel(last.id()); |
968 | 0 | ABSL_DCHECK_GE(level, 0); |
969 | 0 | index_covering_.push_back(first.id().parent(level)); |
970 | 0 | index_cells_.push_back(nullptr); |
971 | 0 | } |
972 | 0 | } |
973 | | |
974 | | template <class Distance> |
975 | | void S2ClosestEdgeQueryBase<Distance>::MaybeAddResult(const S2Shape& shape, |
976 | | int shape_id, |
977 | 0 | int edge_id) { |
978 | 0 | if (avoid_duplicates_ && |
979 | 0 | !tested_edges_.insert(ShapeEdgeId(shape_id, edge_id)).second) { |
980 | 0 | return; |
981 | 0 | } |
982 | | |
983 | 0 | auto edge = shape.edge(edge_id); |
984 | 0 | Distance distance = distance_limit_; |
985 | 0 | if (target_->UpdateMinDistance(edge.v0, edge.v1, &distance)) { |
986 | 0 | AddResult(Result(distance, shape_id, edge_id)); |
987 | 0 | } |
988 | 0 | } |
989 | | |
990 | | template <class Distance> |
991 | 0 | void S2ClosestEdgeQueryBase<Distance>::AddResult(const Result& result) { |
992 | 0 | if (!visiting_) { |
993 | 0 | if (options().max_results() == 1) { |
994 | | // Optimization for the common case where only the closest edge is wanted. |
995 | 0 | result_singleton_ = result; |
996 | 0 | distance_limit_ = result.distance() - options().max_error(); |
997 | 0 | return; |
998 | 0 | } else if (options().max_results() == Options::kMaxMaxResults) { |
999 | 0 | result_vector_.push_back(result); // Sort/unique at end. |
1000 | 0 | return; |
1001 | 0 | } |
1002 | 0 | } |
1003 | | |
1004 | | // Add this edge to result_set_. Note that even if we already have enough |
1005 | | // edges, we can't erase an element before insertion because the "new" |
1006 | | // edge might in fact be a duplicate. |
1007 | 0 | result_set_.insert(result); |
1008 | 0 | int size = result_set_.size(); |
1009 | 0 | if (size >= options().max_results()) { |
1010 | 0 | if (size > options().max_results()) { |
1011 | 0 | result_set_.erase(--result_set_.end()); |
1012 | 0 | } |
1013 | 0 | distance_limit_ = (--result_set_.end())->distance() - options().max_error(); |
1014 | 0 | } |
1015 | 0 | } |
1016 | | |
1017 | | // Return the number of edges in the given index cell. |
1018 | 0 | inline static int CountEdges(const S2ShapeIndexCell* cell) { |
1019 | 0 | int count = 0; |
1020 | 0 | for (int s = 0; s < cell->num_clipped(); ++s) { |
1021 | 0 | count += cell->clipped(s).num_edges(); |
1022 | 0 | } |
1023 | 0 | return count; |
1024 | 0 | } Unexecuted instantiation: s2loop.cc:CountEdges(S2ShapeIndexCell const*) Unexecuted instantiation: s2min_distance_targets.cc:CountEdges(S2ShapeIndexCell const*) Unexecuted instantiation: s2polygon.cc:CountEdges(S2ShapeIndexCell const*) Unexecuted instantiation: s2builder.cc:CountEdges(S2ShapeIndexCell const*) Unexecuted instantiation: s2closest_edge_query.cc:CountEdges(S2ShapeIndexCell const*) |
1025 | | |
1026 | | // Process all the edges of the given index cell. |
1027 | | template <class Distance> |
1028 | 0 | void S2ClosestEdgeQueryBase<Distance>::ProcessEdges(const QueueEntry& entry) { |
1029 | 0 | const S2ShapeIndexCell* index_cell = entry.index_cell; |
1030 | |
|
1031 | 0 | for (int s = 0; s < index_cell->num_clipped(); ++s) { |
1032 | 0 | const S2ClippedShape& clipped = index_cell->clipped(s); |
1033 | |
|
1034 | 0 | int shape_id = clipped.shape_id(); |
1035 | 0 | if (!shape_filter_ || (*shape_filter_)(shape_id)) { |
1036 | 0 | const S2Shape* shape = index_->shape(shape_id); |
1037 | 0 | for (int j = 0; j < clipped.num_edges(); ++j) { |
1038 | 0 | MaybeAddResult(*shape, shape_id, clipped.edge(j)); |
1039 | 0 | } |
1040 | 0 | } |
1041 | 0 | } |
1042 | 0 | } |
1043 | | |
1044 | | // Enqueue the given cell id. |
1045 | | // REQUIRES: iter_ is positioned at a cell contained by "id". |
1046 | | template <class Distance> |
1047 | | inline void S2ClosestEdgeQueryBase<Distance>::ProcessOrEnqueue( |
1048 | 0 | S2CellId id) { |
1049 | 0 | ABSL_DCHECK(id.contains(iter_.id())); |
1050 | 0 | if (iter_.id() == id) { |
1051 | 0 | ProcessOrEnqueue(id, &iter_.cell()); |
1052 | 0 | } else { |
1053 | 0 | ProcessOrEnqueue(id, nullptr); |
1054 | 0 | } |
1055 | 0 | } |
1056 | | |
1057 | | // Add the given cell id to the queue. "index_cell" is the corresponding |
1058 | | // S2ShapeIndexCell, or nullptr if "id" is not an index cell. |
1059 | | // |
1060 | | // This version is called directly only by InitQueue(). |
1061 | | template <class Distance> |
1062 | | void S2ClosestEdgeQueryBase<Distance>::ProcessOrEnqueue( |
1063 | 0 | S2CellId id, const S2ShapeIndexCell* index_cell) { |
1064 | 0 | if (index_cell) { |
1065 | | // If this index cell has only a few edges, then it is faster to check |
1066 | | // them directly rather than computing the minimum distance to the S2Cell |
1067 | | // and inserting it into the queue. |
1068 | 0 | static const int kMinEdgesToEnqueue = 10; |
1069 | 0 | int num_edges = CountEdges(index_cell); |
1070 | 0 | if (num_edges == 0) return; |
1071 | 0 | if (num_edges < kMinEdgesToEnqueue) { |
1072 | | // Set "distance" to zero to avoid the expense of computing it. |
1073 | 0 | ProcessEdges(QueueEntry(Distance::Zero(), id, index_cell)); |
1074 | 0 | return; |
1075 | 0 | } |
1076 | | |
1077 | | // If we have a shape filter, we can skip cells where all the shapes would |
1078 | | // be filtered out. |
1079 | 0 | if (shape_filter_) { |
1080 | 0 | bool skip_cell = true; |
1081 | 0 | for (const S2ClippedShape& clipped : index_cell->clipped_shapes()) { |
1082 | 0 | if ((*shape_filter_)(clipped.shape_id())) { |
1083 | 0 | skip_cell = false; |
1084 | 0 | break; |
1085 | 0 | } |
1086 | 0 | } |
1087 | |
|
1088 | 0 | if (skip_cell) { |
1089 | 0 | return; |
1090 | 0 | } |
1091 | 0 | } |
1092 | 0 | } |
1093 | | |
1094 | | // Otherwise compute the minimum distance to any point in the cell and add |
1095 | | // it to the priority queue. |
1096 | 0 | S2Cell cell(id); |
1097 | 0 | Distance distance = distance_limit_; |
1098 | 0 | if (!target_->UpdateMinDistance(cell, &distance)) return; |
1099 | 0 | if (use_conservative_cell_distance_) { |
1100 | | // Ensure that "distance" is a lower bound on the true distance to the cell. |
1101 | 0 | distance = distance - options().max_error(); // operator-=() not defined. |
1102 | 0 | } |
1103 | 0 | queue_.push(QueueEntry(distance, id, index_cell)); |
1104 | 0 | } |
1105 | | |
1106 | | #endif // S2_S2CLOSEST_EDGE_QUERY_BASE_H_ |