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 // Original author: ericv@google.com (Eric Veach)
16 // Converted to D:  madric@gmail.com (Vijay Nayar)
17 
18 module s2.s2metrics;
19 
20 // The following are various constants that describe the shapes and sizes of
21 // S2Cells (see s2coords.h and s2cell_id.h).  They are useful for deciding
22 // which cell level to use in order to satisfy a given condition (e.g. that
23 // cell vertices must be no further than "x" apart).  All of the raw constants
24 // are differential quantities; you can use the GetValue(level) method to
25 // compute the corresponding length or area on the unit sphere for cells at a
26 // given level.  The minimum and maximum bounds are valid for cells at all
27 // levels, but they may be somewhat conservative for very large cells
28 // (e.g. face cells).
29 
30 import s2coords = s2.s2coords;
31 import algorithm = std.algorithm;
32 import math = std.math;
33 import s2.util.math.s2const;
34 
35 // Defines a cell metric of the given dimension (1 == length, 2 == area).
36 struct Metric(int DimV) {
37 private:
38   immutable double _deriv;
39 
40 public:
41   this(double deriv) {
42     _deriv = deriv;
43   }
44 
45   // The "deriv" value of a metric is a derivative, and must be multiplied by
46   // a length or area in (s,t)-space to get a useful value.
47   @property
48   double deriv() const {
49     return _deriv;
50   }
51 
52   // Return the value of a metric for cells at the given level. The value is
53   // either a length or an area on the unit sphere, depending on the
54   // particular metric.
55   double getValue(int level) const {
56     return math.ldexp(_deriv, -DimV * level);
57   }
58 
59   // Return the level at which the metric has approximately the given
60   // value.  For example, S2::kAvgEdge.GetClosestLevel(0.1) returns the
61   // level at which the average cell edge length is approximately 0.1.
62   // The return value is always a valid level.
63   int getClosestLevel(double value) const {
64     return getLevelForMaxValue((DimV == 1 ? math.SQRT2 : 2) * value);
65   }
66 
67   // Return the minimum level such that the metric is at most the given
68   // value, or S2CellId::kMaxLevel if there is no such level.  For example,
69   // S2::kMaxDiag.GetLevelForMaxValue(0.1) returns the minimum level such
70   // that all cell diagonal lengths are 0.1 or smaller.  The return value
71   // is always a valid level.
72   int getLevelForMaxValue(double value) const
73   out (level) {
74     assert(level == s2coords.MAX_CELL_LEVEL || getValue(level) <= value);
75     assert(level == 0 || getValue(level - 1) > value);
76   } do {
77     if (value <= 0) {
78       return s2coords.MAX_CELL_LEVEL;
79     }
80 
81     // This code is equivalent to computing a floating-point "level"
82     // value and rounding up.  ilogb() returns the exponent corresponding to a
83     // fraction in the range [1,2).
84     int level = math.ilogb(value / _deriv);
85     level = algorithm.max(0, algorithm.min(s2coords.MAX_CELL_LEVEL, -(level >> (DimV - 1))));
86     return level;
87   }
88 
89   // Return the maximum level such that the metric is at least the given
90   // value, or zero if there is no such level.  For example,
91   // S2::kMinWidth.GetLevelForMinValue(0.1) returns the maximum level such
92   // that all cells have a minimum width of 0.1 or larger.  The return value
93   // is always a valid level.
94   int getLevelForMinValue(double value) const
95   out (level) {
96     assert(level == 0 || getValue(level) >= value);
97     assert(level == s2coords.MAX_CELL_LEVEL || getValue(level + 1) < value);
98   } do {
99     if (value <= 0) {
100       return s2coords.MAX_CELL_LEVEL;
101     }
102 
103     // This code is equivalent to computing a floating-point "level"
104     // value and rounding down.
105     int level = math.ilogb(_deriv / value);
106     level = algorithm.max(0, algorithm.min(s2coords.MAX_CELL_LEVEL, level >> (DimV - 1)));
107     return level;
108   }
109 }
110 
111 alias LengthMetric = Metric!1;
112 alias AreaMetric = Metric!2;
113 
114 version = S2_QUADRATIC_PROJECTION;
115 
116 // Each cell is bounded by four planes passing through its four edges and
117 // the center of the sphere.  These metrics relate to the angle between each
118 // pair of opposite bounding planes, or equivalently, between the planes
119 // corresponding to two different s-values or two different t-values.  For
120 // example, the maximum angle between opposite bounding planes for a cell at
121 // level k is kMaxAngleSpan.GetValue(k), and the average angle span for all
122 // cells at level k is approximately kAvgAngleSpan.GetValue(k).
123 version (S2_LINEAR_PROJECTION) {
124   immutable LengthMetric MIN_ANGLE_SPAN = LengthMetric(1.0);           // 1.000
125   immutable LengthMetric MAX_ANGLE_SPAN = LengthMetric(2.0);           // 2.000
126 }
127 version (S2_TAN_PROJECTION) {
128   immutable LengthMetric MIN_ANGLE_SPAN = LengthMetric(M_PI / 2);   // 1.571
129   immutable LengthMetric MAX_ANGLE_SPAN = LengthMetric(M_PI / 2);   // 1.571
130 }
131 version (S2_QUADRATIC_PROJECTION) {
132   immutable LengthMetric MIN_ANGLE_SPAN = LengthMetric(4.0 / 3);       // 1.333
133   immutable LengthMetric MAX_ANGLE_SPAN = LengthMetric(1.704897179199218452); // 1.705
134 }
135 
136 // This is true for all projections.
137 immutable LengthMetric AVG_ANGLE_SPAN = LengthMetric(M_PI / 2);     // 1.571
138 
139 // The width of geometric figure is defined as the distance between two
140 // parallel bounding lines in a given direction.  For cells, the minimum
141 // width is always attained between two opposite edges, and the maximum
142 // width is attained between two opposite vertices.  However, for our
143 // purposes we redefine the width of a cell as the perpendicular distance
144 // between a pair of opposite edges.  A cell therefore has two widths, one
145 // in each direction.  The minimum width according to this definition agrees
146 // with the classic geometric one, but the maximum width is different.  (The
147 // maximum geometric width corresponds to kMaxDiag defined below.)
148 //
149 // For a cell at level k, the distance between opposite edges is at least
150 // kMinWidth.GetValue(k) and at most kMaxWidth.GetValue(k).  The average
151 // width in both directions for all cells at level k is approximately
152 // kAvgWidth.GetValue(k).
153 //
154 // The width is useful for bounding the minimum or maximum distance from a
155 // point on one edge of a cell to the closest point on the opposite edge.
156 // For example, this is useful when "growing" regions by a fixed distance.
157 //
158 // Note that because S2Cells are not usually rectangles, the minimum width of
159 // a cell is generally smaller than its minimum edge length.  (The interior
160 // angles of an S2Cell range from 60 to 120 degrees.)
161 version (S2_LINEAR_PROJECTION) {
162   immutable LengthMetric MIN_WIDTH = LengthMetric(math.sqrt(2.0 / 3));             // 0.816
163   immutable LengthMetric AVG_WIDTH = LengthMetric(1.411459345844456965);           // 1.411
164 }
165 
166 version (S2_TAN_PROJECTION) {
167   immutable LengthMetric MIN_WIDTH = LengthMetric(M_PI / (2 * math.sqrt(2.0))); // 1.111
168   immutable LengthMetric AVG_WIDTH = LengthMetric(1.437318638925160885);           // 1.437
169 }
170 
171 version (S2_QUADRATIC_PROJECTION) {
172   immutable LengthMetric MIN_WIDTH = LengthMetric(2 * math.sqrt(2.0) / 3);         // 0.943
173   immutable LengthMetric AVG_WIDTH = LengthMetric(1.434523672886099389);           // 1.435
174 }
175 
176 // This is true for all projections.
177 immutable LengthMetric MAX_WIDTH = LengthMetric(MAX_ANGLE_SPAN.deriv());
178 
179 // The minimum edge length of any cell at level k is at least
180 // kMinEdge.GetValue(k), and the maximum is at most kMaxEdge.GetValue(k).
181 // The average edge length is approximately kAvgEdge.GetValue(k).
182 //
183 // The edge length metrics can also be used to bound the minimum, maximum,
184 // or average distance from the center of one cell to the center of one of
185 // its edge neighbors.  In particular, it can be used to bound the distance
186 // between adjacent cell centers along the space-filling Hilbert curve for
187 // cells at any given level.
188 version (S2_LINEAR_PROJECTION) {
189   immutable LengthMetric MIN_EDGE = LengthMetric(2 * math.sqrt(2.0) / 3);        // 0.943
190   immutable LengthMetric AVG_EDGE = LengthMetric(1.440034192955603643);          // 1.440
191 }
192 version (S2_TAN_PROJECTION) {
193   immutable LengthMetric MIN_EDGE = LengthMetric(M_PI / (2 * math.sqrt(2.0)));  // 1.111
194   immutable LengthMetric AVG_EDGE = LengthMetric(1.461667032546739266);         // 1.462
195 }
196 version (S2_QUADRATIC_PROJECTION) {
197   immutable LengthMetric MIN_EDGE = LengthMetric(2 * math.sqrt(2.0) / 3);       // 0.943
198   immutable LengthMetric AVG_EDGE = LengthMetric(1.459213746386106062);         // 1.459
199 }
200 // This is true for all projections.
201 immutable LengthMetric MAX_EDGE = LengthMetric(MAX_ANGLE_SPAN.deriv());
202 
203 // The minimum diagonal length of any cell at level k is at least
204 // kMinDiag.GetValue(k), and the maximum is at most kMaxDiag.GetValue(k).
205 // The average diagonal length is approximately kAvgDiag.GetValue(k).
206 //
207 // The maximum diagonal also happens to be the maximum diameter of any cell,
208 // and also the maximum geometric width (see the discussion above).  So for
209 // example, the distance from an arbitrary point to the closest cell center
210 // at a given level is at most half the maximum diagonal length.
211 version (S2_LINEAR_PROJECTION) {
212   immutable LengthMetric MIN_DIAG = LengthMetric(2 * math.sqrt(2.0) / 3);        // 0.943
213   immutable LengthMetric MAX_DIAG = LengthMetric(2 * math.sqrt(2.0));            // 2.828
214   immutable LengthMetric AVG_DIAG = LengthMetric(2.031817866418812674);          // 2.032
215 }
216 version (S2_TAN_PROJECTION) {
217   immutable LengthMetric MIN_DIAG = LengthMetric(M_PI * math.sqrt(2.0) / 3);     // 1.481
218   immutable LengthMetric MAX_DIAG = LengthMetric(M_PI * math.sqrt(2.0 / 3));     // 2.565
219   immutable LengthMetric AVG_DIAG = LengthMetric(2.063623197195635753);          // 2.064
220 }
221 version (S2_QUADRATIC_PROJECTION) {
222   immutable LengthMetric MIN_DIAG = LengthMetric(8 * math.sqrt(2.0) / 9);        // 1.257
223   immutable LengthMetric MAX_DIAG = LengthMetric(2.438654594434021032);          // 2.439
224   immutable LengthMetric AVG_DIAG = LengthMetric(2.060422738998471683);          // 2.060
225 }
226 
227 // The minimum area of any cell at level k is at least kMinArea.GetValue(k),
228 // and the maximum is at most kMaxArea.GetValue(k).  The average area of all
229 // cells at level k is exactly kAvgArea.GetValue(k).
230 version (S2_LINEAR_PROJECTION) {
231   immutable AreaMetric MIN_AREA = AreaMetric(4 / (3 * math.sqrt(3)));        // 0.770
232   immutable AreaMetric MAX_AREA = AreaMetric(4);                             // 4.000
233 }
234 version (S2_TAN_PROJECTION) {
235   immutable AreaMetric MIN_AREA = AreaMetric((M_PI*M_PI) / (4*math.sqrt(2.0))); // 1.745
236   immutable AreaMetric MAX_AREA = AreaMetric(M_PI * M_PI / 4);               // 2.467
237 }
238 version (S2_QUADRATIC_PROJECTION) {
239   immutable AreaMetric MIN_AREA = AreaMetric(8 * math.sqrt(2.0) / 9);        // 1.257
240   immutable AreaMetric MAX_AREA = AreaMetric(2.635799256963161491);          // 2.636
241 }
242 immutable AreaMetric AVG_AREA = AreaMetric(4 * M_PI / 6);                    // 2.094
243 
244 // This is the maximum edge aspect ratio over all cells at any level, where
245 // the edge aspect ratio of a cell is defined as the ratio of its longest
246 // edge length to its shortest edge length.
247 version (S2_LINEAR_PROJECTION) {
248   immutable double MAX_EDGE_ASPECT = math.sqrt(2);                               // 1.414
249 }
250 version (S2_TAN_PROJECTION) {
251   immutable double MAX_EDGE_ASPECT = math.sqrt(2);                               // 1.414
252 }
253 version (S2_QUADRATIC_PROJECTION) {
254   immutable double MAX_EDGE_ASPECT = 1.442615274452682920;                       // 1.443
255 }
256 
257 // This is the maximum diagonal aspect ratio over all cells at any level,
258 // where the diagonal aspect ratio of a cell is defined as the ratio of its
259 // longest diagonal length to its shortest diagonal length.
260 immutable double MAX_DIAG_ASPECT = math.sqrt(3.0);                               // 1.732