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