1 // Copyright 2013 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 // Original Author: ericv@google.com (Eric Veach) 17 // Converted to D: madric@gmail.com (Vijay Nayar) 18 // 19 // See S2ClosestPointQuery (defined below) for an overview. 20 21 module s2.s2closest_point_query; 22 23 import s2.s1angle; 24 import s2.s1chord_angle; 25 import s2.s2cell; 26 import s2.s2closest_point_query_base; 27 import s2.s2min_distance_targets; 28 import s2.s2point; 29 import s2.s2point_index; 30 import s2.s2shape_index; 31 32 /** 33 * Options that control the set of points returned. Note that by default 34 * *all* points are returned, so you will always want to set either the 35 * max_points() option or the max_distance() option (or both). 36 * 37 * This class is also available as S2ClosestPointQuery<Data>::Options. 38 * (It is defined here to avoid depending on the "Data" template argument.) 39 */ 40 class S2ClosestPointQueryOptions : S2ClosestPointQueryBaseOptions!S2MinDistance { 41 public: 42 alias Distance = S2MinDistance; 43 alias Base = S2ClosestPointQueryBaseOptions!Distance; 44 45 // See S2ClosestPointQueryBaseOptions for the full set of options. 46 47 /** 48 * Specifies that only points whose distance to the target is less than 49 * "max_distance" should be returned. 50 * 51 * Note that points whose distance is exactly equal to "max_distance" are 52 * not returned. Normally this doesn't matter, because distances are not 53 * computed exactly in the first place, but if such points are needed then 54 * see set_inclusive_max_distance() below. 55 * 56 * DEFAULT: Distance::Infinity() 57 */ 58 void setMaxDistance(S1ChordAngle max_distance) { 59 super.setMaxDistance(Distance(max_distance)); 60 } 61 62 // Avoid hiding the base class method setMaxDistance(S1ChordAngle). 63 alias setMaxDistance = Base.setMaxDistance; 64 65 /** 66 * Like set_max_distance(), except that points whose distance is exactly 67 * equal to "max_distance" are also returned. Equivalent to calling 68 * set_max_distance(max_distance.Successor()). 69 */ 70 void setInclusiveMaxDistance(S1ChordAngle max_distance) { 71 setMaxDistance(max_distance.successor()); 72 } 73 74 /** 75 * Like set_inclusive_max_distance(), except that "max_distance" is also 76 * increased by the maximum error in the distance calculation. This ensures 77 * that all points whose true distance is less than or equal to 78 * "max_distance" will be returned (along with some points whose true 79 * distance is slightly greater). 80 * 81 * Algorithms that need to do exact distance comparisons can use this 82 * option to find a set of candidate points that can then be filtered 83 * further (e.g., using s2pred::CompareDistance). 84 */ 85 void setConservativeMaxDistance(S1ChordAngle max_distance) { 86 import s2.s2edge_distances : getUpdateMinDistanceMaxError; 87 setMaxDistance( 88 Distance(max_distance.plusError(getUpdateMinDistanceMaxError(max_distance)).successor())); 89 } 90 91 /** 92 * Versions of set_max_distance that take an S1Angle argument. (Note that 93 * these functions require a conversion, and that the S1ChordAngle versions 94 * are preferred.) 95 */ 96 void setMaxDistance(S1Angle max_distance) { 97 super.setMaxDistance(Distance(S1ChordAngle(max_distance))); 98 } 99 100 void setInclusiveMaxDistance(S1Angle max_distance) { 101 setInclusiveMaxDistance(S1ChordAngle(max_distance)); 102 } 103 104 void setConservativeMaxDistance(S1Angle max_distance) { 105 setConservativeMaxDistance(S1ChordAngle(max_distance)); 106 } 107 108 /// See S2ClosestPointQueryBaseOptions for documentation. 109 // using Base::set_max_error; // S1Chordangle version 110 void setMaxError(S1Angle max_error) { 111 super.setMaxError(S1ChordAngle(max_error)); 112 } 113 114 /// Inherited options (see s2closest_point_query_base.h for details): 115 // using Base::set_max_points; 116 // using Base::set_region; 117 // using Base::set_use_brute_force; 118 } 119 120 /** 121 * S2ClosestPointQueryTarget represents the geometry to which the distance is 122 * measured. There are subtypes for measuring the distance to a point, an 123 * edge, an S2Cell, or an S2ShapeIndex (an arbitrary collection of geometry). 124 */ 125 alias S2ClosestPointQueryTarget = S2MinDistanceTarget; 126 127 /** 128 * Target subtype that computes the closest distance to a point. 129 * 130 * This class is also available as S2ClosestPointQuery<Data>::PointTarget. 131 * (It is defined here to avoid depending on the "Data" template argument.) 132 */ 133 final class S2ClosestPointQueryPointTarget : S2MinDistancePointTarget { 134 public: 135 this(in S2Point point) { 136 super(point); 137 } 138 139 override 140 int maxBruteForceIndexSize() const { 141 // Using BM_FindClosest (which finds the single closest point), the 142 // break-even points are approximately X, Y, and Z points for grid, 143 // fractal, and regular loop geometry respectively. 144 // 145 // TODO(ericv): Adjust using benchmarks. 146 return 150; 147 } 148 } 149 150 /** 151 * Target subtype that computes the closest distance to an edge. 152 * 153 * This class is also available as S2ClosestPointQuery<Data>::EdgeTarget. 154 * (It is defined here to avoid depending on the "Data" template argument.) 155 */ 156 final class S2ClosestPointQueryEdgeTarget : S2MinDistanceEdgeTarget { 157 public: 158 this(in S2Point a, in S2Point b) { 159 super(a, b); 160 } 161 162 override 163 int maxBruteForceIndexSize() const { 164 // Using BM_FindClosestToEdge (which finds the single closest point), the 165 // break-even points are approximately X, Y, and Z points for grid, 166 // fractal, and regular loop geometry respectively. 167 // 168 // TODO(ericv): Adjust using benchmarks. 169 return 100; 170 } 171 } 172 173 /** 174 * Target subtype that computes the closest distance to an S2Cell 175 * (including the interior of the cell). 176 * 177 * This class is also available as S2ClosestPointQuery<Data>::CellTarget. 178 * (It is defined here to avoid depending on the "Data" template argument.) 179 */ 180 final class S2ClosestPointQueryCellTarget : S2MinDistanceCellTarget { 181 public: 182 this(in S2Cell cell) { 183 super(cell); 184 } 185 186 override 187 int maxBruteForceIndexSize() const { 188 // Using BM_FindClosestToCell (which finds the single closest point), the 189 // break-even points are approximately X, Y, and Z points for grid, 190 // fractal, and regular loop geometry respectively. 191 // 192 // TODO(ericv): Adjust using benchmarks. 193 return 50; 194 } 195 } 196 197 /** 198 * Target subtype that computes the closest distance to an S2ShapeIndex 199 * (an arbitrary collection of points, polylines, and/or polygons). 200 * 201 * By default, distances are measured to the boundary and interior of 202 * polygons in the S2ShapeIndex rather than to polygon boundaries only. 203 * If you wish to change this behavior, you may call 204 * 205 * target.set_include_interiors(false); 206 * 207 * (see S2MinDistanceShapeIndexTarget for details). 208 * 209 * This class is also available as S2ClosestPointQuery<Data>::ShapeIndexTarget. 210 * (It is defined here to avoid depending on the "Data" template argument.) 211 */ 212 final class S2ClosestPointQueryShapeIndexTarget : S2MinDistanceShapeIndexTarget { 213 public: 214 this(S2ShapeIndex index) { 215 super(index); 216 } 217 218 override 219 int maxBruteForceIndexSize() const { 220 // For BM_FindClosestToSameSizeAbuttingIndex (which uses a nearby 221 // S2ShapeIndex target of similar complexity), the break-even points are 222 // approximately X, Y, and Z points for grid, fractal, and regular loop 223 // geometry respectively. 224 // 225 // TODO(ericv): Adjust using benchmarks. 226 return 30; 227 } 228 } 229 230 /** 231 * Given a set of points stored in an S2PointIndex, S2ClosestPointQuery 232 * provides methods that find the closest point(s) to a given query point 233 * or query edge. Example usage: 234 * 235 * void Test(const vector<S2Point>& index_points, 236 * const vector<S2Point>& target_points) { 237 * // The template argument allows auxiliary data to be attached to each 238 * // point (in this case, the array index). 239 * S2PointIndex<int> index; 240 * for (const S2Point& point : index_points) { 241 * index.Add(point, i); 242 * } 243 * S2ClosestPointQuery<int> query(&index); 244 * query.mutable_options()->set_max_points(5); 245 * for (const S2Point& target_point : target_points) { 246 * S2ClosestPointQueryPointTarget target(target_point); 247 * for (const auto& result : query.FindClosestPoints(&target)) { 248 * // The Result class contains the following methods: 249 * // distance() is the distance to the target. 250 * // point() is the indexed point. 251 * // data() is the auxiliary data. 252 * DoSomething(target_point, result); 253 * } 254 * } 255 * } 256 * 257 * You can find either the k closest points, or all points within a given 258 * radius, or both (i.e., the k closest points up to a given maximum radius). 259 * E.g. to find all the points within 5 kilometers, call 260 * 261 * query.mutable_options()->set_max_distance( 262 * S2Earth::ToAngle(util::units::Kilometers(5))); 263 * 264 * By default *all* points are returned, so you should always specify either 265 * max_points() or max_distance() or both. There is also a FindClosestPoint() 266 * convenience method that returns only the closest point. 267 * 268 * You can restrict the results to an arbitrary S2Region, for example: 269 * 270 * S2LatLngRect rect(...); 271 * query.set_region(&rect); // Does *not* take ownership. 272 * 273 * To find the closest points to a query edge rather than a point, use: 274 * 275 * S2ClosestPointQueryEdgeTarget target(v0, v1); 276 * query.FindClosestPoints(&target); 277 * 278 * The implementation is designed to be fast for both small and large 279 * point sets. 280 */ 281 class S2ClosestPointQuery(Data) { 282 public: 283 // See S2ClosestPointQueryBase for full documentation. 284 285 alias Index = S2PointIndex!Data; 286 alias PointData = Index.PointData; 287 288 // S2MinDistance is a thin wrapper around S1ChordAngle that implements the 289 // Distance concept required by S2ClosestPointQueryBase. 290 alias Distance = S2MinDistance; 291 alias Base = S2ClosestPointQueryBase!(Distance, Data); 292 293 /** 294 * Each "Result" object represents a closest point. Here are its main 295 * methods (see S2ClosestPointQueryBase::Result for details): 296 * 297 * // The distance from the target to this point. 298 * S1ChordAngle distance() const; 299 * 300 * // The point itself. 301 * const S2Point& point() const; 302 * 303 * // The client-specified data associated with this point. 304 * const Data& data() const; 305 */ 306 alias Result = Base.Result; 307 308 alias Options = S2ClosestPointQueryOptions; 309 310 // The available target types (see definitions above). 311 alias Target = S2ClosestPointQueryTarget; 312 alias PointTarget = S2ClosestPointQueryPointTarget; 313 alias EdgeTarget = S2ClosestPointQueryEdgeTarget; 314 alias CellTarget = S2ClosestPointQueryCellTarget; 315 alias ShapeIndexTarget = S2ClosestPointQueryShapeIndexTarget; 316 317 /// Default constructor; requires initialize() to be called. 318 this() { 319 _base = new Base(); 320 } 321 322 /** 323 * Convenience constructor that calls Init(). Options may be specified here 324 * or changed at any time using the mutable_options() accessor method. 325 */ 326 this(Index index, Options options = null) { 327 this(); 328 if (options is null) 329 options = new Options(); 330 initialize(index, options); 331 } 332 333 /** 334 * Initializes the query. Options may be specified here or changed at any 335 * time using the mutable_options() accessor method. 336 * 337 * REQUIRES: "index" must persist for the lifetime of this object. 338 * REQUIRES: ReInit() must be called if "index" is modified. 339 */ 340 void initialize(Index index, Options options) { 341 if (options is null) 342 options = new Options(); 343 344 _options = options; 345 _base.initialize(index); 346 } 347 348 /** 349 * Reinitializes the query. This method must be called whenever the 350 * underlying index is modified. 351 */ 352 void reInitialize() { 353 _base.reInitialize(); 354 } 355 356 /// Returns a reference to the underlying S2PointIndex. 357 const(Index) index() const { 358 return _base.index(); 359 } 360 361 /// Returns the query options. Options can be modifed between queries. 362 const(Options) options() const { 363 return _options; 364 } 365 366 Options mutableOptions() { 367 return _options; 368 } 369 370 /** 371 * Returns the closest points to the given target that satisfy the given 372 * options. This method may be called multiple times. 373 */ 374 Result[] findClosestPoints(Target target) { 375 return _base.findClosestPoints(target, _options); 376 } 377 378 /** 379 * This version can be more efficient when this method is called many times, 380 * since it does not require allocating a new vector on each call. 381 */ 382 void findClosestPoints(Target target, ref Result[] results) { 383 _base.findClosestPoints(target, _options, results); 384 } 385 386 //////////////////////// Convenience Methods //////////////////////// 387 388 /** 389 * Returns the closest point to the target. If no point satisfies the search 390 * criteria, then a Result object with distance() == Infinity() and 391 * is_empty() == true is returned. 392 */ 393 Result findClosestPoint(Target target) { 394 static assert(__traits(classInstanceSize, Options) <= 48, "Consider not copying Options here"); 395 Options tmp_options = _options.dup; 396 tmp_options.setMaxPoints(1); 397 return _base.findClosestPoint(target, tmp_options); 398 } 399 400 /** 401 * Returns the minimum distance to the target. If the index or target is 402 * empty, returns S1ChordAngle::Infinity(). 403 * 404 * Use IsDistanceLess() if you only want to compare the distance against a 405 * threshold value, since it is often much faster. 406 */ 407 S1ChordAngle getDistance(Target target) { 408 return findClosestPoint(target).distance.s1ChordAngle; 409 } 410 411 /** 412 * Returns true if the distance to "target" is less than "limit". 413 * 414 * This method is usually much faster than GetDistance(), since it is much 415 * less work to determine whether the minimum distance is above or below a 416 * threshold than it is to calculate the actual minimum distance. 417 */ 418 bool isDistanceLess(Target target, S1ChordAngle limit) { 419 static assert(__traits(classInstanceSize, Options) <= 48, "Consider not copying Options here"); 420 Options tmp_options = _options.dup; 421 tmp_options.setMaxPoints(1); 422 tmp_options.setMaxDistance(limit); 423 tmp_options.setMaxError(S1ChordAngle.straight().toS1Angle()); 424 return !_base.findClosestPoint(target, tmp_options).isEmpty(); 425 } 426 427 // TODO: Resume here. 428 429 /** 430 * Like IsDistanceLess(), but also returns true if the distance to "target" 431 * is exactly equal to "limit". 432 */ 433 bool isDistanceLessOrEqual(Target target, S1ChordAngle limit) { 434 static assert(__traits(classInstanceSize, Options) <= 48, "Consider not copying Options here"); 435 Options tmp_options = _options.dup; 436 tmp_options.setMaxPoints(1); 437 tmp_options.setInclusiveMaxDistance(limit); 438 tmp_options.setMaxError(S1ChordAngle.straight().toS1Angle()); 439 return !_base.findClosestPoint(target, tmp_options).isEmpty(); 440 } 441 442 /** 443 * Like IsDistanceLessOrEqual(), except that "limit" is increased by the 444 * maximum error in the distance calculation. This ensures that this 445 * function returns true whenever the true, exact distance is less than 446 * or equal to "limit". 447 * 448 * For example, suppose that we want to test whether two geometries might 449 * intersect each other after they are snapped together using S2Builder 450 * (using the IdentitySnapFunction with a given "snap_radius"). Since 451 * S2Builder uses exact distance predicates (s2predicates.h), we need to 452 * measure the distance between the two geometries conservatively. If the 453 * distance is definitely greater than "snap_radius", then the geometries 454 * are guaranteed to not intersect after snapping. 455 */ 456 bool isConservativeDistanceLessOrEqual(Target target, S1ChordAngle limit) { 457 static assert(__traits(classInstanceSize, Options) <= 48, "Consider not copying Options here"); 458 Options tmp_options = _options; 459 tmp_options.setMaxPoints(1); 460 tmp_options.setConservativeMaxDistance(limit); 461 tmp_options.setMaxError(S1ChordAngle.straight().toS1Angle()); 462 return !_base.findClosestPoint(target, tmp_options).isEmpty(); 463 } 464 465 ///////////////////////// Deprecated Methods ////////////////////////// 466 467 private: 468 Options _options; 469 Base _base; 470 471 // Deprecated methods that return results using the result interface require 472 // keeping a copy of the result vector. 473 Result[] _results; 474 }