/src/s2geometry/src/s2/s2latlng_rect_bounder.cc
Line | Count | Source |
1 | | // Copyright 2005 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/s2latlng_rect_bounder.h" |
19 | | |
20 | | #include <algorithm> |
21 | | #include <cfloat> |
22 | | #include <cmath> |
23 | | |
24 | | #include "absl/log/absl_check.h" |
25 | | #include "s2/r1interval.h" |
26 | | #include "s2/s1angle.h" |
27 | | #include "s2/s1interval.h" |
28 | | #include "s2/s2latlng.h" |
29 | | #include "s2/s2latlng_rect.h" |
30 | | #include "s2/s2point.h" |
31 | | #include "s2/s2pointutil.h" |
32 | | |
33 | | using std::fabs; |
34 | | using std::max; |
35 | | using std::min; |
36 | | |
37 | 0 | void S2LatLngRectBounder::AddPoint(const S2Point& b) { |
38 | 0 | ABSL_DCHECK(S2::IsUnitLength(b)); |
39 | 0 | AddInternal(b, S2LatLng(b)); |
40 | 0 | } |
41 | | |
42 | 0 | void S2LatLngRectBounder::AddLatLng(const S2LatLng& b_latlng) { |
43 | 0 | AddInternal(b_latlng.ToPoint(), b_latlng); |
44 | 0 | } |
45 | | |
46 | | void S2LatLngRectBounder::AddInternal(const S2Point& b, |
47 | 0 | const S2LatLng& b_latlng) { |
48 | | // Simple consistency check to verify that b and b_latlng are alternate |
49 | | // representations of the same vertex. |
50 | 0 | ABSL_DCHECK(S2::ApproxEquals(b, b_latlng.ToPoint())); |
51 | |
|
52 | 0 | if (bound_.is_empty()) { |
53 | 0 | bound_.AddPoint(b_latlng); |
54 | 0 | } else { |
55 | | // First compute the cross product N = A x B robustly. This is the normal |
56 | | // to the great circle through A and B. We don't use S2::RobustCrossProd() |
57 | | // since that method returns an arbitrary vector orthogonal to A if the two |
58 | | // vectors are proportional, and we want the zero vector in that case. |
59 | 0 | Vector3_d n = (a_ - b).CrossProd(a_ + b); // N = 2 * (A x B) |
60 | | |
61 | | // The relative error in N gets large as its norm gets very small (i.e., |
62 | | // when the two points are nearly identical or antipodal). We handle this |
63 | | // by choosing a maximum allowable error, and if the error is greater than |
64 | | // this we fall back to a different technique. Since it turns out that |
65 | | // the other sources of error in converting the normal to a maximum |
66 | | // latitude add up to at most 1.16 * DBL_EPSILON (see below), and it is |
67 | | // desirable to have the total error be a multiple of DBL_EPSILON, we have |
68 | | // chosen to limit the maximum error in the normal to 3.84 * DBL_EPSILON. |
69 | | // It is possible to show that the error is less than this when |
70 | | // |
71 | | // n.Norm() >= 8 * sqrt(3) / (3.84 - 0.5 - sqrt(3)) * DBL_EPSILON |
72 | | // = 1.91346e-15 (about 8.618 * DBL_EPSILON) |
73 | 0 | double n_norm = n.Norm(); |
74 | 0 | if (n_norm < 1.91346e-15) { |
75 | | // A and B are either nearly identical or nearly antipodal (to within |
76 | | // 4.309 * DBL_EPSILON, or about 6 nanometers on the earth's surface). |
77 | 0 | if (a_.DotProd(b) < 0) { |
78 | | // The two points are nearly antipodal. The easiest solution is to |
79 | | // assume that the edge between A and B could go in any direction |
80 | | // around the sphere. |
81 | 0 | bound_ = S2LatLngRect::Full(); |
82 | 0 | } else { |
83 | | // The two points are nearly identical (to within 4.309 * DBL_EPSILON). |
84 | | // In this case we can just use the bounding rectangle of the points, |
85 | | // since after the expansion done by GetBound() this rectangle is |
86 | | // guaranteed to include the (lat,lng) values of all points along AB. |
87 | 0 | bound_ = bound_.Union(S2LatLngRect::FromPointPair(a_latlng_, b_latlng)); |
88 | 0 | } |
89 | 0 | } else { |
90 | | // Compute the longitude range spanned by AB. |
91 | 0 | S1Interval lng_ab = S1Interval::FromPointPair(a_latlng_.lng().radians(), |
92 | 0 | b_latlng.lng().radians()); |
93 | 0 | if (lng_ab.GetLength() >= M_PI - 2 * DBL_EPSILON) { |
94 | | // The points lie on nearly opposite lines of longitude to within the |
95 | | // maximum error of the calculation. (Note that this test relies on |
96 | | // the fact that M_PI is slightly less than the true value of Pi, and |
97 | | // that representable values near M_PI are 2 * DBL_EPSILON apart.) |
98 | | // The easiest solution is to assume that AB could go on either side |
99 | | // of the pole. |
100 | 0 | lng_ab = S1Interval::Full(); |
101 | 0 | } |
102 | | |
103 | | // Next we compute the latitude range spanned by the edge AB. We start |
104 | | // with the range spanning the two endpoints of the edge: |
105 | 0 | R1Interval lat_ab = R1Interval::FromPointPair(a_latlng_.lat().radians(), |
106 | 0 | b_latlng.lat().radians()); |
107 | | |
108 | | // This is the desired range unless the edge AB crosses the plane |
109 | | // through N and the Z-axis (which is where the great circle through A |
110 | | // and B attains its minimum and maximum latitudes). To test whether AB |
111 | | // crosses this plane, we compute a vector M perpendicular to this |
112 | | // plane and then project A and B onto it. |
113 | 0 | Vector3_d m = n.CrossProd(S2Point(0, 0, 1)); |
114 | 0 | double m_a = m.DotProd(a_); |
115 | 0 | double m_b = m.DotProd(b); |
116 | | |
117 | | // We want to test the signs of "m_a" and "m_b", so we need to bound |
118 | | // the error in these calculations. It is possible to show that the |
119 | | // total error is bounded by |
120 | | // |
121 | | // (1 + sqrt(3)) * DBL_EPSILON * n_norm + 8 * sqrt(3) * (DBL_EPSILON**2) |
122 | | // = 6.06638e-16 * n_norm + 6.83174e-31 |
123 | |
|
124 | 0 | double m_error = 6.06638e-16 * n_norm + 6.83174e-31; |
125 | 0 | if (m_a * m_b < 0 || fabs(m_a) <= m_error || fabs(m_b) <= m_error) { |
126 | | // Minimum/maximum latitude *may* occur in the edge interior. |
127 | | // |
128 | | // The maximum latitude is 90 degrees minus the latitude of N. We |
129 | | // compute this directly using atan2 in order to get maximum accuracy |
130 | | // near the poles. |
131 | | // |
132 | | // Our goal is compute a bound that contains the computed latitudes of |
133 | | // all S2Points P that pass the point-in-polygon containment test. |
134 | | // There are three sources of error we need to consider: |
135 | | // - the directional error in N (at most 3.84 * DBL_EPSILON) |
136 | | // - converting N to a maximum latitude |
137 | | // - computing the latitude of the test point P |
138 | | // The latter two sources of error are at most 0.955 * DBL_EPSILON |
139 | | // individually, but it is possible to show by a more complex analysis |
140 | | // that together they can add up to at most 1.16 * DBL_EPSILON, for a |
141 | | // total error of 5 * DBL_EPSILON. |
142 | | // |
143 | | // We add 3 * DBL_EPSILON to the bound here, and GetBound() will pad |
144 | | // the bound by another 2 * DBL_EPSILON. |
145 | 0 | double max_lat = min( |
146 | 0 | atan2(sqrt(n[0]*n[0] + n[1]*n[1]), fabs(n[2])) + 3 * DBL_EPSILON, |
147 | 0 | M_PI_2); |
148 | | |
149 | | // In order to get tight bounds when the two points are close together, |
150 | | // we also bound the min/max latitude relative to the latitudes of the |
151 | | // endpoints A and B. First we compute the distance between A and B, |
152 | | // and then we compute the maximum change in latitude between any two |
153 | | // points along the great circle that are separated by this distance. |
154 | | // This gives us a latitude change "budget". Some of this budget must |
155 | | // be spent getting from A to B; the remainder bounds the round-trip |
156 | | // distance (in latitude) from A or B to the min or max latitude |
157 | | // attained along the edge AB. |
158 | | // |
159 | | // There is a maximum relative error of 4.5 * DBL_EPSILON in computing |
160 | | // the squared distance (a_ - b), which means a maximum error of (4.5 |
161 | | // / 2 + 0.5) == 2.75 * DBL_EPSILON in computing Norm(). The sin() |
162 | | // and multiply each have a relative error of 0.5 * DBL_EPSILON which |
163 | | // we round up to a total of 4 * DBL_EPSILON. |
164 | 0 | double lat_budget_z = 0.5 * (a_ - b).Norm() * sin(max_lat); |
165 | 0 | double lat_budget = 2 * asin(min((1 + 4 * DBL_EPSILON) * lat_budget_z, |
166 | 0 | 1.0)); |
167 | 0 | double max_delta = 0.5 * (lat_budget - lat_ab.GetLength()) + |
168 | 0 | DBL_EPSILON; |
169 | | |
170 | | // Test whether AB passes through the point of maximum latitude or |
171 | | // minimum latitude. If the dot product(s) are small enough then the |
172 | | // result may be ambiguous. |
173 | 0 | if (m_a <= m_error && m_b >= -m_error) { |
174 | 0 | lat_ab.set_hi(min(max_lat, lat_ab.hi() + max_delta)); |
175 | 0 | } |
176 | 0 | if (m_b <= m_error && m_a >= -m_error) { |
177 | 0 | lat_ab.set_lo(max(-max_lat, lat_ab.lo() - max_delta)); |
178 | 0 | } |
179 | 0 | } |
180 | 0 | bound_ = bound_.Union(S2LatLngRect(lat_ab, lng_ab)); |
181 | 0 | } |
182 | 0 | } |
183 | 0 | a_ = b; |
184 | 0 | a_latlng_ = b_latlng; |
185 | 0 | } |
186 | | |
187 | 0 | S2LatLngRect S2LatLngRectBounder::GetBound() const { |
188 | | // To save time, we ignore numerical errors in the computed S2LatLngs while |
189 | | // accumulating the bounds and then account for them here. |
190 | | // |
191 | | // S2LatLng(S2Point) has a maximum error of 0.955 * DBL_EPSILON in latitude. |
192 | | // In the worst case, we might have rounded "inwards" when computing the |
193 | | // bound and "outwards" when computing the latitude of a contained point P, |
194 | | // therefore we expand the latitude bounds by 2 * DBL_EPSILON in each |
195 | | // direction. (A more complex analysis shows that 1.5 * DBL_EPSILON is |
196 | | // enough, but the expansion amount should be a multiple of DBL_EPSILON in |
197 | | // order to avoid rounding errors during the expansion itself.) |
198 | | // |
199 | | // S2LatLng(S2Point) has a maximum error of DBL_EPSILON in longitude, which |
200 | | // is simply the maximum rounding error for results in the range [-Pi, Pi]. |
201 | | // This is true because the Gnu implementation of atan2() comes from the IBM |
202 | | // Accurate Mathematical Library, which implements correct rounding for this |
203 | | // intrinsic (i.e., it returns the infinite precision result rounded to the |
204 | | // nearest representable value, with ties rounded to even values). This |
205 | | // implies that we don't need to expand the longitude bounds at all, since |
206 | | // we only guarantee that the bound contains the *rounded* latitudes of |
207 | | // contained points. The *true* latitudes of contained points may lie up to |
208 | | // DBL_EPSILON outside of the returned bound. |
209 | |
|
210 | 0 | const S2LatLng kExpansion = S2LatLng::FromRadians(2 * DBL_EPSILON, 0); |
211 | 0 | return bound_.Expanded(kExpansion).PolarClosure(); |
212 | 0 | } |
213 | | |
214 | | S2LatLngRect S2LatLngRectBounder::ExpandForSubregions( |
215 | 0 | const S2LatLngRect& bound) { |
216 | | // Empty bounds don't need expansion. |
217 | 0 | if (bound.is_empty()) return bound; |
218 | | |
219 | | // First we need to check whether the bound B contains any nearly-antipodal |
220 | | // points (to within 4.309 * DBL_EPSILON). If so then we need to return |
221 | | // S2LatLngRect::Full(), since the subregion might have an edge between two |
222 | | // such points, and AddPoint() returns Full() for such edges. Note that |
223 | | // this can happen even if B is not Full(); for example, consider a loop |
224 | | // that defines a 10km strip straddling the equator extending from |
225 | | // longitudes -100 to +100 degrees. |
226 | | // |
227 | | // It is easy to check whether B contains any antipodal points, but checking |
228 | | // for nearly-antipodal points is trickier. Essentially we consider the |
229 | | // original bound B and its reflection through the origin B', and then test |
230 | | // whether the minimum distance between B and B' is less than 4.309 * |
231 | | // DBL_EPSILON. |
232 | | |
233 | | // "lng_gap" is a lower bound on the longitudinal distance between B and its |
234 | | // reflection B'. (2.5 * DBL_EPSILON is the maximum combined error of the |
235 | | // endpoint longitude calculations and the GetLength() call.) |
236 | 0 | double lng_gap = max(0.0, M_PI - bound.lng().GetLength() - 2.5 * DBL_EPSILON); |
237 | | |
238 | | // "min_abs_lat" is the minimum distance from B to the equator (if zero or |
239 | | // negative, then B straddles the equator). |
240 | 0 | double min_abs_lat = max(bound.lat().lo(), -bound.lat().hi()); |
241 | | |
242 | | // "lat_gap1" and "lat_gap2" measure the minimum distance from B to the |
243 | | // south and north poles respectively. |
244 | 0 | double lat_gap1 = M_PI_2 + bound.lat().lo(); |
245 | 0 | double lat_gap2 = M_PI_2 - bound.lat().hi(); |
246 | |
|
247 | 0 | if (min_abs_lat >= 0) { |
248 | | // The bound B does not straddle the equator. In this case the minimum |
249 | | // distance is between one endpoint of the latitude edge in B closest to |
250 | | // the equator and the other endpoint of that edge in B'. The latitude |
251 | | // distance between these two points is 2*min_abs_lat, and the longitude |
252 | | // distance is lng_gap. We could compute the distance exactly using the |
253 | | // Haversine formula, but then we would need to bound the errors in that |
254 | | // calculation. Since we only need accuracy when the distance is very |
255 | | // small (close to 4.309 * DBL_EPSILON), we substitute the Euclidean |
256 | | // distance instead. This gives us a right triangle XYZ with two edges of |
257 | | // length x = 2*min_abs_lat and y ~= lng_gap. The desired distance is the |
258 | | // length of the third edge "z", and we have |
259 | | // |
260 | | // z ~= sqrt(x^2 + y^2) >= (x + y) / sqrt(2) |
261 | | // |
262 | | // Therefore the region may contain nearly antipodal points only if |
263 | | // |
264 | | // 2*min_abs_lat + lng_gap < sqrt(2) * 4.309 * DBL_EPSILON |
265 | | // ~= 1.354e-15 |
266 | | // |
267 | | // Note that because the given bound B is conservative, "min_abs_lat" and |
268 | | // "lng_gap" are both lower bounds on their true values so we do not need |
269 | | // to make any adjustments for their errors. |
270 | 0 | if (2 * min_abs_lat + lng_gap < 1.354e-15) { |
271 | 0 | return S2LatLngRect::Full(); |
272 | 0 | } |
273 | 0 | } else if (lng_gap >= M_PI_2) { |
274 | | // B spans at most Pi/2 in longitude. The minimum distance is always |
275 | | // between one corner of B and the diagonally opposite corner of B'. We |
276 | | // use the same distance approximation that we used above; in this case |
277 | | // we have an obtuse triangle XYZ with two edges of length x = lat_gap1 |
278 | | // and y = lat_gap2, and angle Z >= Pi/2 between them. We then have |
279 | | // |
280 | | // z >= sqrt(x^2 + y^2) >= (x + y) / sqrt(2) |
281 | | // |
282 | | // Unlike the case above, "lat_gap1" and "lat_gap2" are not lower bounds |
283 | | // (because of the extra addition operation, and because M_PI_2 is not |
284 | | // exactly equal to Pi/2); they can exceed their true values by up to |
285 | | // 0.75 * DBL_EPSILON. Putting this all together, the region may |
286 | | // contain nearly antipodal points only if |
287 | | // |
288 | | // lat_gap1 + lat_gap2 < (sqrt(2) * 4.309 + 1.5) * DBL_EPSILON |
289 | | // ~= 1.687e-15 |
290 | 0 | if (lat_gap1 + lat_gap2 < 1.687e-15) { |
291 | 0 | return S2LatLngRect::Full(); |
292 | 0 | } |
293 | 0 | } else { |
294 | | // Otherwise we know that (1) the bound straddles the equator and (2) its |
295 | | // width in longitude is at least Pi/2. In this case the minimum |
296 | | // distance can occur either between a corner of B and the diagonally |
297 | | // opposite corner of B' (as in the case above), or between a corner of B |
298 | | // and the opposite longitudinal edge reflected in B'. It is sufficient |
299 | | // to only consider the corner-edge case, since this distance is also a |
300 | | // lower bound on the corner-corner distance when that case applies. |
301 | | |
302 | | // Consider the spherical triangle XYZ where X is a corner of B with |
303 | | // minimum absolute latitude, Y is the closest pole to X, and Z is the |
304 | | // point closest to X on the opposite longitudinal edge of B'. This is a |
305 | | // right triangle (Z = Pi/2), and from the spherical law of sines we have |
306 | | // |
307 | | // sin(z) / sin(Z) = sin(y) / sin(Y) |
308 | | // sin(max_lat_gap) / 1 = sin(d_min) / sin(lng_gap) |
309 | | // sin(d_min) = sin(max_lat_gap) * sin(lng_gap) |
310 | | // |
311 | | // where "max_lat_gap" = max(lat_gap1, lat_gap2) and "d_min" is the |
312 | | // desired minimum distance. Now using the facts that sin(t) >= (2/Pi)*t |
313 | | // for 0 <= t <= Pi/2, that we only need an accurate approximation when |
314 | | // at least one of "max_lat_gap" or "lng_gap" is extremely small (in |
315 | | // which case sin(t) ~= t), and recalling that "max_lat_gap" has an error |
316 | | // of up to 0.75 * DBL_EPSILON, we want to test whether |
317 | | // |
318 | | // max_lat_gap * lng_gap < (4.309 + 0.75) * (Pi/2) * DBL_EPSILON |
319 | | // ~= 1.765e-15 |
320 | 0 | if (max(lat_gap1, lat_gap2) * lng_gap < 1.765e-15) { |
321 | 0 | return S2LatLngRect::Full(); |
322 | 0 | } |
323 | 0 | } |
324 | | // Next we need to check whether the subregion might contain any edges that |
325 | | // span (M_PI - 2 * DBL_EPSILON) radians or more in longitude, since AddPoint |
326 | | // sets the longitude bound to Full() in that case. This corresponds to |
327 | | // testing whether (lng_gap <= 0) in "lng_expansion" below. |
328 | | |
329 | | // Otherwise, the maximum latitude error in AddPoint is 4.8 * DBL_EPSILON. |
330 | | // In the worst case, the errors when computing the latitude bound for a |
331 | | // subregion could go in the opposite direction as the errors when computing |
332 | | // the bound for the original region, so we need to double this value. |
333 | | // (More analysis shows that it's okay to round down to a multiple of |
334 | | // DBL_EPSILON.) |
335 | | // |
336 | | // For longitude, we rely on the fact that atan2 is correctly rounded and |
337 | | // therefore no additional bounds expansion is necessary. |
338 | | |
339 | 0 | double lat_expansion = 9 * DBL_EPSILON; |
340 | 0 | double lng_expansion = (lng_gap <= 0) ? M_PI : 0; |
341 | 0 | return bound.Expanded(S2LatLng::FromRadians(lat_expansion, |
342 | 0 | lng_expansion)).PolarClosure(); |
343 | 0 | } |
344 | | |
345 | 0 | S2LatLng S2LatLngRectBounder::MaxErrorForTests() { |
346 | | // The maximum error in the latitude calculation is |
347 | | // 3.84 * DBL_EPSILON for the cross product calculation (see above) |
348 | | // 0.96 * DBL_EPSILON for the Latitude() calculation |
349 | | // 5 * DBL_EPSILON added by AddPoint/GetBound to compensate for error |
350 | | // ------------------ |
351 | | // 9.80 * DBL_EPSILON maximum error in result |
352 | | // |
353 | | // The maximum error in the longitude calculation is DBL_EPSILON. GetBound |
354 | | // does not do any expansion because this isn't necessary in order to |
355 | | // bound the *rounded* longitudes of contained points. |
356 | 0 | return S2LatLng::FromRadians(10 * DBL_EPSILON, 1 * DBL_EPSILON); |
357 | 0 | } |