1 // Copyright 2016 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 #include "s2/s2builderutil_snap_functions.h"
19 
20 #include <algorithm>
21 #include <cfloat>
22 #include <cmath>
23 #include <memory>
24 #include "s2/base/integral_types.h"
25 #include "s2/base/logging.h"
26 #include "s2/third_party/absl/memory/memory.h"
27 #include "s2/s2cell_id.h"
28 #include "s2/s2latlng.h"
29 #include "s2/s2metrics.h"
30 
31 using absl::make_unique;
32 using std::max;
33 using std::min;
34 using std::unique_ptr;
35 
36 namespace s2builderutil {
37 
38 const int IntLatLngSnapFunction::kMinExponent;
39 const int IntLatLngSnapFunction::kMaxExponent;
40 
IdentitySnapFunction()41 IdentitySnapFunction::IdentitySnapFunction()
42     : snap_radius_(S1Angle::Zero()) {
43 }
44 
IdentitySnapFunction(S1Angle snap_radius)45 IdentitySnapFunction::IdentitySnapFunction(S1Angle snap_radius) {
46   set_snap_radius(snap_radius);
47 }
48 
set_snap_radius(S1Angle snap_radius)49 void IdentitySnapFunction::set_snap_radius(S1Angle snap_radius) {
50   S2_DCHECK_LE(snap_radius, kMaxSnapRadius());
51   snap_radius_ = snap_radius;
52 }
53 
snap_radius() const54 S1Angle IdentitySnapFunction::snap_radius() const {
55   return snap_radius_;
56 }
57 
min_vertex_separation() const58 S1Angle IdentitySnapFunction::min_vertex_separation() const {
59   // Since SnapFunction does not move the input point, output vertices are
60   // separated by the full snap_radius().
61   return snap_radius_;
62 }
63 
min_edge_vertex_separation() const64 S1Angle IdentitySnapFunction::min_edge_vertex_separation() const {
65   // In the worst case configuration, the edge separation is half of the
66   // vertex separation.
67   return 0.5 * snap_radius_;
68 }
69 
SnapPoint(const S2Point & point) const70 S2Point IdentitySnapFunction::SnapPoint(const S2Point& point) const {
71   return point;
72 }
73 
Clone() const74 unique_ptr<S2Builder::SnapFunction> IdentitySnapFunction::Clone() const {
75   return make_unique<IdentitySnapFunction>(*this);
76 }
77 
78 
S2CellIdSnapFunction()79 S2CellIdSnapFunction::S2CellIdSnapFunction() {
80   set_level(S2CellId::kMaxLevel);
81 }
82 
S2CellIdSnapFunction(int level)83 S2CellIdSnapFunction::S2CellIdSnapFunction(int level) {
84   set_level(level);
85 }
86 
set_level(int level)87 void S2CellIdSnapFunction::set_level(int level) {
88   S2_DCHECK_GE(level, 0);
89   S2_DCHECK_LE(level, S2CellId::kMaxLevel);
90   level_ = level;
91   set_snap_radius(MinSnapRadiusForLevel(level));
92 }
93 
level() const94 int S2CellIdSnapFunction::level() const {
95   return level_;
96 }
97 
set_snap_radius(S1Angle snap_radius)98 void S2CellIdSnapFunction::set_snap_radius(S1Angle snap_radius) {
99   S2_DCHECK_GE(snap_radius, MinSnapRadiusForLevel(level()));
100   S2_DCHECK_LE(snap_radius, kMaxSnapRadius());
101   snap_radius_ = snap_radius;
102 }
103 
snap_radius() const104 S1Angle S2CellIdSnapFunction::snap_radius() const {
105   return snap_radius_;
106 }
107 
MinSnapRadiusForLevel(int level)108 S1Angle S2CellIdSnapFunction::MinSnapRadiusForLevel(int level) {
109   // snap_radius() needs to be an upper bound on the true distance that a
110   // point can move when snapped, taking into account numerical errors.
111   //
112   // The maximum error when converting from an S2Point to an S2CellId is
113   // S2::kMaxDiag.deriv() * DBL_EPSILON.  The maximum error when converting an
114   // S2CellId center back to an S2Point is 1.5 * DBL_EPSILON.  These add up to
115   // just slightly less than 4 * DBL_EPSILON.
116   return S1Angle::Radians(0.5 * S2::kMaxDiag.GetValue(level) + 4 * DBL_EPSILON);
117 }
118 
LevelForMaxSnapRadius(S1Angle snap_radius)119 int S2CellIdSnapFunction::LevelForMaxSnapRadius(S1Angle snap_radius) {
120   // When choosing a level, we need to acount for the error bound of
121   // 4 * DBL_EPSILON that is added by MinSnapRadiusForLevel().
122   return S2::kMaxDiag.GetLevelForMaxValue(
123       2 * (snap_radius.radians() - 4 * DBL_EPSILON));
124 }
125 
min_vertex_separation() const126 S1Angle S2CellIdSnapFunction::min_vertex_separation() const {
127   // We have three different bounds for the minimum vertex separation: one is
128   // a constant bound, one is proportional to snap_radius, and one is equal to
129   // snap_radius minus a constant.  These bounds give the best results for
130   // small, medium, and large snap radii respectively.  We return the maximum
131   // of the three bounds.
132   //
133   // 1. Constant bound: Vertices are always separated by at least
134   //    kMinEdge(level), the minimum edge length for the chosen snap level.
135   //
136   // 2. Proportional bound: It can be shown that in the plane, the worst-case
137   //    configuration has a vertex separation of 2 / sqrt(13) * snap_radius.
138   //    This is verified in the unit test, except that on the sphere the ratio
139   //    is slightly smaller at cell level 2 (0.54849 vs. 0.55470).  We reduce
140   //    that value a bit more below to be conservative.
141   //
142   // 3. Best asymptotic bound: This bound bound is derived by observing we
143   //    only select a new site when it is at least snap_radius() away from all
144   //    existing sites, and the site can move by at most 0.5 * kMaxDiag(level)
145   //    when snapped.
146   S1Angle min_edge = S1Angle::Radians(S2::kMinEdge.GetValue(level_));
147   S1Angle max_diag = S1Angle::Radians(S2::kMaxDiag.GetValue(level_));
148   return max(min_edge,
149              max(0.548 * snap_radius_,  // 2 / sqrt(13) in the plane
150                  snap_radius_ - 0.5 * max_diag));
151 }
152 
min_edge_vertex_separation() const153 S1Angle S2CellIdSnapFunction::min_edge_vertex_separation() const {
154   // Similar to min_vertex_separation(), in this case we have four bounds: a
155   // constant bound that holds only at the minimum snap radius, a constant
156   // bound that holds for any snap radius, a bound that is proportional to
157   // snap_radius, and a bound that is equal to snap_radius minus a constant.
158   //
159   // 1. Constant bounds:
160   //
161   //    (a) At the minimum snap radius for a given level, it can be shown that
162   //    vertices are separated from edges by at least 0.5 * kMinDiag(level) in
163   //    the plane.  The unit test verifies this, except that on the sphere the
164   //    worst case is slightly better: 0.5652980068 * kMinDiag(level).
165   //
166   //    (b) Otherwise, for arbitrary snap radii the worst-case configuration
167   //    in the plane has an edge-vertex separation of sqrt(3/19) *
168   //    kMinDiag(level), where sqrt(3/19) is about 0.3973597071.  The unit
169   //    test verifies that the bound is slighty better on the sphere:
170   //    0.3973595687 * kMinDiag(level).
171   //
172   // 2. Proportional bound: In the plane, the worst-case configuration has an
173   //    edge-vertex separation of 2 * sqrt(3/247) * snap_radius, which is
174   //    about 0.2204155075.  The unit test verifies this, except that on the
175   //    sphere the bound is slightly worse for certain large S2Cells: the
176   //    minimum ratio occurs at cell level 6, and is about 0.2196666953.
177   //
178   // 3. Best asymptotic bound: If snap_radius() is large compared to the
179   //    minimum snap radius, then the best bound is achieved by 3 sites on a
180   //    circular arc of radius "snap_radius", spaced "min_vertex_separation"
181   //    apart.  An input edge passing just to one side of the center of the
182   //    circle intersects the Voronoi regions of the two end sites but not the
183   //    Voronoi region of the center site, and gives an edge separation of
184   //    (min_vertex_separation ** 2) / (2 * snap_radius).  This bound
185   //    approaches 0.5 * snap_radius for large snap radii, i.e.  the minimum
186   //    edge-vertex separation approaches half of the minimum vertex
187   //    separation as the snap radius becomes large compared to the cell size.
188 
189   S1Angle min_diag = S1Angle::Radians(S2::kMinDiag.GetValue(level_));
190   if (snap_radius() == MinSnapRadiusForLevel(level_)) {
191     // This bound only holds when the minimum snap radius is being used.
192     return 0.565 * min_diag;            // 0.500 in the plane
193   }
194   // Otherwise, these bounds hold for any snap_radius().
195   S1Angle vertex_sep = min_vertex_separation();
196   return max(0.397 * min_diag,          // sqrt(3 / 19) in the plane
197              max(0.219 * snap_radius_,  // 2 * sqrt(3 / 247) in the plane
198                  0.5 * (vertex_sep / snap_radius_) * vertex_sep));
199 }
200 
SnapPoint(const S2Point & point) const201 S2Point S2CellIdSnapFunction::SnapPoint(const S2Point& point) const {
202   return S2CellId(point).parent(level_).ToPoint();
203 }
204 
Clone() const205 unique_ptr<S2Builder::SnapFunction> S2CellIdSnapFunction::Clone() const {
206   return make_unique<S2CellIdSnapFunction>(*this);
207 }
208 
IntLatLngSnapFunction()209 IntLatLngSnapFunction::IntLatLngSnapFunction()
210     : exponent_(-1), snap_radius_(), from_degrees_(0), to_degrees_(0) {
211 }
212 
IntLatLngSnapFunction(int exponent)213 IntLatLngSnapFunction::IntLatLngSnapFunction(int exponent) {
214   set_exponent(exponent);
215 }
216 
set_exponent(int exponent)217 void IntLatLngSnapFunction::set_exponent(int exponent) {
218   S2_DCHECK_GE(exponent, kMinExponent);
219   S2_DCHECK_LE(exponent, kMaxExponent);
220   exponent_ = exponent;
221   set_snap_radius(MinSnapRadiusForExponent(exponent));
222 
223   // Precompute the scale factors needed for snapping.  Note that these
224   // calculations need to exactly match the ones in s1angle.h to ensure
225   // that the same S2Points are generated.
226   double power = 1;
227   for (int i = 0; i < exponent; ++i) power *= 10;
228   from_degrees_ = power;
229   to_degrees_ = 1 / power;
230 }
231 
exponent() const232 int IntLatLngSnapFunction::exponent() const {
233   return exponent_;
234 }
235 
set_snap_radius(S1Angle snap_radius)236 void IntLatLngSnapFunction::set_snap_radius(S1Angle snap_radius) {
237   S2_DCHECK_GE(snap_radius, MinSnapRadiusForExponent(exponent()));
238   S2_DCHECK_LE(snap_radius, kMaxSnapRadius());
239   snap_radius_ = snap_radius;
240 }
241 
snap_radius() const242 S1Angle IntLatLngSnapFunction::snap_radius() const {
243   return snap_radius_;
244 }
245 
MinSnapRadiusForExponent(int exponent)246 S1Angle IntLatLngSnapFunction::MinSnapRadiusForExponent(int exponent) {
247   // snap_radius() needs to be an upper bound on the true distance that a
248   // point can move when snapped, taking into account numerical errors.
249   //
250   // The maximum errors in latitude and longitude can be bounded as
251   // follows (as absolute errors in terms of DBL_EPSILON):
252   //
253   //                                      Latitude      Longitude
254   // Convert to S2LatLng:                    1.000          1.000
255   // Convert to degrees:                     1.032          2.063
256   // Scale by 10**exp:                       0.786          1.571
257   // Round to integer: 0.5 * S1Angle::Degrees(to_degrees_)
258   // Scale by 10**(-exp):                    1.375          2.749
259   // Convert to radians:                     1.252          1.503
260   // ------------------------------------------------------------
261   // Total (except for rounding)             5.445          8.886
262   //
263   // The maximum error when converting the S2LatLng back to an S2Point is
264   //
265   //   sqrt(2) * (maximum error in latitude or longitude) + 1.5 * DBL_EPSILON
266   //
267   // which works out to (9 * sqrt(2) + 1.5) * DBL_EPSILON radians.  Finally
268   // we need to consider the effect of rounding to integer coordinates
269   // (much larger than the errors above), which can change the position by
270   // up to (sqrt(2) * 0.5 * to_degrees_) radians.
271   double power = 1;
272   for (int i = 0; i < exponent; ++i) power *= 10;
273   return (S1Angle::Degrees(M_SQRT1_2 / power) +
274           S1Angle::Radians((9 * M_SQRT2 + 1.5) * DBL_EPSILON));
275 }
276 
ExponentForMaxSnapRadius(S1Angle snap_radius)277 int IntLatLngSnapFunction::ExponentForMaxSnapRadius(S1Angle snap_radius) {
278   // When choosing an exponent, we need to acount for the error bound of
279   // (9 * sqrt(2) + 1.5) * DBL_EPSILON added by MinSnapRadiusForExponent().
280   snap_radius -= S1Angle::Radians((9 * M_SQRT2 + 1.5) * DBL_EPSILON);
281   snap_radius = max(snap_radius, S1Angle::Radians(1e-30));
282   double exponent = log10(M_SQRT1_2 / snap_radius.degrees());
283 
284   // There can be small errors in the calculation above, so to ensure that
285   // this function is the inverse of MinSnapRadiusForExponent() we subtract a
286   // small error tolerance.
287   return max(kMinExponent,
288              min(kMaxExponent,
289                  static_cast<int>(std::ceil(exponent - 2 * DBL_EPSILON))));
290 }
291 
min_vertex_separation() const292 S1Angle IntLatLngSnapFunction::min_vertex_separation() const {
293   // We have two bounds for the minimum vertex separation: one is proportional
294   // to snap_radius, and one is equal to snap_radius minus a constant.  These
295   // bounds give the best results for small and large snap radii respectively.
296   // We return the maximum of the two bounds.
297   //
298   // 1. Proportional bound: It can be shown that in the plane, the worst-case
299   //    configuration has a vertex separation of (sqrt(2) / 3) * snap_radius.
300   //    This is verified in the unit test, except that on the sphere the ratio
301   //    is slightly smaller (0.471337 vs. 0.471404).  We reduce that value a
302   //    bit more below to be conservative.
303   //
304   // 2. Best asymptotic bound: This bound bound is derived by observing we
305   //    only select a new site when it is at least snap_radius() away from all
306   //    existing sites, and snapping a vertex can move it by up to
307   //    ((1 / sqrt(2)) * to_degrees_) degrees.
308   return max(0.471 * snap_radius_,        // sqrt(2) / 3 in the plane
309              snap_radius_ - S1Angle::Degrees(M_SQRT1_2 * to_degrees_));
310 }
311 
min_edge_vertex_separation() const312 S1Angle IntLatLngSnapFunction::min_edge_vertex_separation() const {
313   // Similar to min_vertex_separation(), in this case we have three bounds:
314   // one is a constant bound, one is proportional to snap_radius, and one is
315   // equal to snap_radius minus a constant.
316   //
317   // 1. Constant bound: In the plane, the worst-case configuration has an
318   //    edge-vertex separation of ((1 / sqrt(13)) * to_degrees_) degrees.
319   //    The unit test verifies this, except that on the sphere the ratio is
320   //    slightly lower when small exponents such as E1 are used
321   //    (0.2772589 vs 0.2773501).
322   //
323   // 2. Proportional bound: In the plane, the worst-case configuration has an
324   //    edge-vertex separation of (2 / 9) * snap_radius (0.222222222222).  The
325   //    unit test verifies this, except that on the sphere the bound can be
326   //    slightly worse with large exponents (e.g., E9) due to small numerical
327   //    errors (0.222222126756717).
328   //
329   // 3. Best asymptotic bound: If snap_radius() is large compared to the
330   //    minimum snap radius, then the best bound is achieved by 3 sites on a
331   //    circular arc of radius "snap_radius", spaced "min_vertex_separation"
332   //    apart (see S2CellIdSnapFunction::min_edge_vertex_separation).  This
333   //    bound approaches 0.5 * snap_radius as the snap radius becomes large
334   //    relative to the grid spacing.
335 
336   S1Angle vertex_sep = min_vertex_separation();
337   return max(0.277 * S1Angle::Degrees(to_degrees_),  // 1/sqrt(13) in the plane
338              max(0.222 * snap_radius_,               // 2/9 in the plane
339                  0.5 * (vertex_sep / snap_radius_) * vertex_sep));
340 }
341 
SnapPoint(const S2Point & point) const342 S2Point IntLatLngSnapFunction::SnapPoint(const S2Point& point) const {
343   S2_DCHECK_GE(exponent_, 0);  // Make sure the snap function was initialized.
344   S2LatLng input(point);
345   int64 lat = MathUtil::FastInt64Round(input.lat().degrees() * from_degrees_);
346   int64 lng = MathUtil::FastInt64Round(input.lng().degrees() * from_degrees_);
347   return S2LatLng::FromDegrees(lat * to_degrees_, lng * to_degrees_).ToPoint();
348 }
349 
Clone() const350 unique_ptr<S2Builder::SnapFunction> IntLatLngSnapFunction::Clone() const {
351   return make_unique<IntLatLngSnapFunction>(*this);
352 }
353 
354 }  // namespace s2builderutil
355