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.s2coords; 19 20 // This file contains documentation of the various coordinate systems used 21 // throughout the library. Most importantly, S2 defines a framework for 22 // decomposing the unit sphere into a hierarchy of "cells". Each cell is a 23 // quadrilateral bounded by four geodesics. The top level of the hierarchy is 24 // obtained by projecting the six faces of a cube onto the unit sphere, and 25 // lower levels are obtained by subdividing each cell into four children 26 // recursively. Cells are numbered such that sequentially increasing cells 27 // follow a continuous space-filling curve over the entire sphere. The 28 // transformation is designed to make the cells at each level fairly uniform 29 // in size. 30 // 31 // 32 ////////////////////////// S2Cell Decomposition ///////////////////////// 33 // 34 // The following methods define the cube-to-sphere projection used by 35 // the S2Cell decomposition. 36 // 37 // In the process of converting a latitude-longitude pair to a 64-bit cell 38 // id, the following coordinate systems are used: 39 // 40 // (id) 41 // An S2CellId is a 64-bit encoding of a face and a Hilbert curve position 42 // on that face. The Hilbert curve position implicitly encodes both the 43 // position of a cell and its subdivision level (see s2cell_id.h). 44 // 45 // (face, i, j) 46 // Leaf-cell coordinates. "i" and "j" are integers in the range 47 // [0,(2**30)-1] that identify a particular leaf cell on the given face. 48 // The (i, j) coordinate system is right-handed on each face, and the 49 // faces are oriented such that Hilbert curves connect continuously from 50 // one face to the next. 51 // 52 // (face, s, t) 53 // Cell-space coordinates. "s" and "t" are real numbers in the range 54 // [0,1] that identify a point on the given face. For example, the point 55 // (s, t) = (0.5, 0.5) corresponds to the center of the top-level face 56 // cell. This point is also a vertex of exactly four cells at each 57 // subdivision level greater than zero. 58 // 59 // (face, si, ti) 60 // Discrete cell-space coordinates. These are obtained by multiplying 61 // "s" and "t" by 2**31 and rounding to the nearest unsigned integer. 62 // Discrete coordinates lie in the range [0,2**31]. This coordinate 63 // system can represent the edge and center positions of all cells with 64 // no loss of precision (including non-leaf cells). In binary, each 65 // coordinate of a level-k cell center ends with a 1 followed by 66 // (30 - k) 0s. The coordinates of its edges end with (at least) 67 // (31 - k) 0s. 68 // 69 // (face, u, v) 70 // Cube-space coordinates in the range [-1,1]. To make the cells at each 71 // level more uniform in size after they are projected onto the sphere, 72 // we apply a nonlinear transformation of the form u=f(s), v=f(t). 73 // The (u, v) coordinates after this transformation give the actual 74 // coordinates on the cube face (modulo some 90 degree rotations) before 75 // it is projected onto the unit sphere. 76 // 77 // (face, u, v, w) 78 // Per-face coordinate frame. This is an extension of the (face, u, v) 79 // cube-space coordinates that adds a third axis "w" in the direction of 80 // the face normal. It is always a right-handed 3D coordinate system. 81 // Cube-space coordinates can be converted to this frame by setting w=1, 82 // while (u,v,w) coordinates can be projected onto the cube face by 83 // dividing by w, i.e. (face, u/w, v/w). 84 // 85 // (x, y, z) 86 // Direction vector (S2Point). Direction vectors are not necessarily unit 87 // length, and are often chosen to be points on the biunit cube 88 // [-1,+1]x[-1,+1]x[-1,+1]. They can be be normalized to obtain the 89 // corresponding point on the unit sphere. 90 // 91 // (lat, lng) 92 // Latitude and longitude (S2LatLng). Latitudes must be between -90 and 93 // 90 degrees inclusive, and longitudes must be between -180 and 180 94 // degrees inclusive. 95 // 96 // Note that the (i, j), (s, t), (si, ti), and (u, v) coordinate systems are 97 // right-handed on all six faces. 98 99 import s2.r2point; 100 import s2.s2point; 101 import s2.util.math.mathutil; 102 import bitop = core.bitop; 103 import algorithm = std.algorithm; 104 import math = std.math; 105 106 // This is the number of levels needed to specify a leaf cell. This 107 // constant is defined here so that the S2::Metric class and the conversion 108 // functions below can be implemented without including s2cell_id.h. Please 109 // see s2cell_id.h for other useful constants and conversion functions. 110 immutable int MAX_CELL_LEVEL = 30; 111 112 // The maximum index of a valid leaf cell plus one. The range of valid leaf 113 // cell indices is [0..kLimitIJ-1]. 114 immutable int LIMIT_IJ = 1 << MAX_CELL_LEVEL; // == S2CellId::kMaxSize 115 116 // The maximum value of an si- or ti-coordinate. The range of valid (si,ti) 117 // values is [0..MAX_SI_TI]. 118 immutable uint MAX_SI_TI = 1U << (MAX_CELL_LEVEL + 1); 119 120 // Convert the i- or j-index of a leaf cell to the minimum corresponding s- 121 // or t-value contained by that cell. The argument must be in the range 122 // [0..2**30], i.e. up to one position beyond the normal range of valid leaf 123 // cell indices. 124 double IJtoSTMin(int i) 125 in { 126 assert(i >= 0 && i <= LIMIT_IJ); 127 } do { 128 return (1.0 / LIMIT_IJ) * i; 129 } 130 131 // Return the i- or j-index of the leaf cell containing the given 132 // s- or t-value. If the argument is outside the range spanned by valid 133 // leaf cell indices, return the index of the closest valid leaf cell (i.e., 134 // return values are clamped to the range of valid leaf cell indices). 135 int STtoIJ(double s) { 136 return algorithm.max( 137 0, algorithm.min(LIMIT_IJ - 1, cast(int) math.lround(LIMIT_IJ * s - 0.5))); 138 } 139 140 // Convert an si- or ti-value to the corresponding s- or t-value. 141 double SiTitoST(uint si) 142 in { 143 assert(si >= 0 && si <= MAX_SI_TI); 144 } do { 145 return (1.0 / MAX_SI_TI) * si; 146 } 147 148 // Return the si- or ti-coordinate that is nearest to the given s- or 149 // t-value. The result may be outside the range of valid (si,ti)-values. 150 uint STtoSiTi(double s) { 151 // MAX_SI_TI == 2^31, so the result doesn't fit in an int32 when s == 1. 152 return cast(uint) math.lround(s * MAX_SI_TI); 153 } 154 155 // Convert (face, u, v) coordinates to a direction vector (not 156 // necessarily unit length). 157 S2Point FaceUVtoXYZ(int face, double u, double v) { 158 switch (face) { 159 case 0: return S2Point([ 1, u, v]); 160 case 1: return S2Point([-u, 1, v]); 161 case 2: return S2Point([-u, -v, 1]); 162 case 3: return S2Point([-1, -v, -u]); 163 case 4: return S2Point([ v, -1, -u]); 164 default: return S2Point([ v, u, -1]); 165 } 166 } 167 168 S2Point FaceUVtoXYZ(int face, in R2Point uv) { 169 return FaceUVtoXYZ(face, uv.x, uv.y); 170 } 171 172 // If the dot product of p with the given face normal is positive, 173 // set the corresponding u and v values (which may lie outside the range 174 // [-1,1]) and return true. Otherwise return false. 175 bool FaceXYZtoUV(int face, in S2Point p, out double pu, out double pv) { 176 if (face < 3) { 177 if (p[face] <= 0) return false; 178 } else { 179 if (p[face-3] >= 0) return false; 180 } 181 ValidFaceXYZtoUV(face, p, pu, pv); 182 return true; 183 } 184 185 186 bool FaceXYZtoUV(int face, in S2Point p, out R2Point puv) { 187 return FaceXYZtoUV(face, p, puv.data[0], puv.data[1]); 188 } 189 190 // Given a *valid* face for the given point p (meaning that dot product 191 // of p with the face normal is positive), return the corresponding 192 // u and v values (which may lie outside the range [-1,1]). 193 void ValidFaceXYZtoUV(int face, in S2Point p, out double pu, out double pv) 194 in { 195 assert(p.dotProd(GetNorm(face)) > 0); 196 } do { 197 switch (face) { 198 case 0: pu = p[1] / p[0]; pv = p[2] / p[0]; break; 199 case 1: pu = -p[0] / p[1]; pv = p[2] / p[1]; break; 200 case 2: pu = -p[0] / p[2]; pv = -p[1] / p[2]; break; 201 case 3: pu = p[2] / p[0]; pv = p[1] / p[0]; break; 202 case 4: pu = p[2] / p[1]; pv = -p[0] / p[1]; break; 203 default: pu = -p[1] / p[2]; pv = -p[0] / p[2]; break; 204 } 205 } 206 207 void ValidFaceXYZtoUV(int face, in S2Point p, out R2Point puv) { 208 ValidFaceXYZtoUV(face, p, puv.data[0], puv.data[1]); 209 } 210 211 // Transform the given point P to the (u,v,w) coordinate frame of the given 212 // face (where the w-axis represents the face normal). 213 S2Point FaceXYZtoUVW(int face, in S2Point p) { 214 // The result coordinates are simply the dot products of P with the (u,v,w) 215 // axes for the given face (see kFaceUVWAxes). 216 switch (face) { 217 case 0: return S2Point([ p.y(), p.z(), p.x()]); 218 case 1: return S2Point([-p.x(), p.z(), p.y()]); 219 case 2: return S2Point([-p.x(), -p.y(), p.z()]); 220 case 3: return S2Point([-p.z(), -p.y(), -p.x()]); 221 case 4: return S2Point([-p.z(), p.x(), -p.y()]); 222 default: return S2Point([ p.y(), p.x(), -p.z()]); 223 } 224 } 225 226 /** 227 * Return the face containing the given direction vector. (For points on 228 * the boundary between faces, the result is arbitrary but repeatable.) 229 */ 230 int GetFace(in S2Point p) { 231 int face = p.largestAbsComponent(); 232 if (p[face] < 0) { 233 face += 3; 234 } 235 return face; 236 } 237 238 /** 239 * Convert a direction vector (not necessarily unit length) to 240 * (face, u, v) coordinates. 241 */ 242 int XYZtoFaceUV(in S2Point p, out double pu, out double pv) { 243 int face = GetFace(p); 244 ValidFaceXYZtoUV(face, p, pu, pv); 245 return face; 246 } 247 248 int XYZtoFaceUV(in S2Point p, out R2Point puv) { 249 return XYZtoFaceUV(p, puv.data[0], puv.data[1]); 250 } 251 252 /** 253 * Convert a direction vector (not necessarily unit length) to 254 * (face, si, ti) coordinates and, if p is exactly equal to the center of a 255 * cell, return the level of this cell (-1 otherwise). 256 */ 257 int XYZtoFaceSiTi(in S2Point p, out int face, out uint si, out uint ti) { 258 double u, v; 259 260 face = XYZtoFaceUV(p, u, v); 261 si = STtoSiTi(UVtoST(u)); 262 ti = STtoSiTi(UVtoST(v)); 263 // If the levels corresponding to si,ti are not equal, then p is not a cell 264 // center. The si,ti values 0 and kMaxSiTi need to be handled specially 265 // because they do not correspond to cell centers at any valid level; they 266 // are mapped to level -1 by the code below. 267 int level = MAX_CELL_LEVEL - bitop.bsf(si | MAX_SI_TI); 268 if (level < 0 || 269 level != MAX_CELL_LEVEL - bitop.bsf(ti | MAX_SI_TI)) { 270 return -1; 271 } 272 assert(level <= MAX_CELL_LEVEL); 273 // In infinite precision, this test could be changed to ST == SiTi. However, 274 // due to rounding errors, UVtoST(XYZtoFaceUV(FaceUVtoXYZ(STtoUV(...)))) is 275 // not idempotent. On the other hand, center_raw is computed exactly the same 276 // way p was originally computed (if it is indeed the center of an S2Cell): 277 // the comparison can be exact. 278 S2Point center = FaceSiTitoXYZ(face, si, ti).normalize(); 279 return p == center ? level : -1; 280 } 281 282 // Convert (face, si, ti) coordinates to a direction vector (not necessarily 283 // unit length). 284 S2Point FaceSiTitoXYZ(int face, uint si, uint ti) { 285 double u = STtoUV(SiTitoST(si)); 286 double v = STtoUV(SiTitoST(ti)); 287 return FaceUVtoXYZ(face, u, v); 288 } 289 290 // Return the right-handed normal (not necessarily unit length) for an 291 // edge in the direction of the positive v-axis at the given u-value on 292 // the given face. (This vector is perpendicular to the plane through 293 // the sphere origin that contains the given edge.) 294 S2Point GetUNorm(int face, double u) { 295 switch (face) { 296 case 0: return S2Point([ u, -1, 0]); 297 case 1: return S2Point([ 1, u, 0]); 298 case 2: return S2Point([ 1, 0, u]); 299 case 3: return S2Point([-u, 0, 1]); 300 case 4: return S2Point([ 0, -u, 1]); 301 default: return S2Point([ 0, -1, -u]); 302 } 303 } 304 305 306 // Return the right-handed normal (not necessarily unit length) for an 307 // edge in the direction of the positive u-axis at the given v-value on 308 // the given face. 309 S2Point GetVNorm(int face, double v) { 310 switch (face) { 311 case 0: return S2Point([-v, 0, 1]); 312 case 1: return S2Point([ 0, -v, 1]); 313 case 2: return S2Point([ 0, -1, -v]); 314 case 3: return S2Point([ v, -1, 0]); 315 case 4: return S2Point([ 1, v, 0]); 316 default: return S2Point([ 1, 0, v]); 317 } 318 } 319 320 // Return the unit-length normal, u-axis, or v-axis for the given face. 321 S2Point GetNorm(int face) { 322 return GetUVWAxis(face, 2); 323 } 324 325 S2Point GetUAxis(int face) { 326 return GetUVWAxis(face, 0); 327 } 328 329 S2Point GetVAxis(int face) { 330 return GetUVWAxis(face, 1); 331 } 332 333 // Return the given axis of the given face (u=0, v=1, w=2). 334 S2Point GetUVWAxis(int face, int axis) { 335 double[3] p = FACE_UVW_AXES[face][axis]; 336 return S2Point([p[0], p[1], p[2]]); 337 } 338 339 // With respect to the (u,v,w) coordinate system of a given face, return the 340 // face that lies in the given direction (negative=0, positive=1) of the 341 // given axis (u=0, v=1, w=2). For example, GetUVWFace(4, 0, 1) returns the 342 // face that is adjacent to face 4 in the positive u-axis direction. 343 int GetUVWFace(int face, int axis, int direction) 344 in { 345 assert(face >= 0 && face <= 5); 346 assert(axis >= 0 && axis <= 2); 347 assert(direction >= 0 && direction <= 1); 348 } do { 349 return FACE_UVW_FACES[face][axis][direction]; 350 } 351 352 353 ////////////////// Implementation details follow //////////////////// 354 355 356 // We have implemented three different projections from cell-space (s,t) to 357 // cube-space (u,v): linear, quadratic, and tangent. They have the following 358 // tradeoffs: 359 // 360 // Linear - This is the fastest transformation, but also produces the least 361 // uniform cell sizes. Cell areas vary by a factor of about 5.2, with the 362 // largest cells at the center of each face and the smallest cells in 363 // the corners. 364 // 365 // Tangent - Transforming the coordinates via atan() makes the cell sizes 366 // more uniform. The areas vary by a maximum ratio of 1.4 as opposed to a 367 // maximum ratio of 5.2. However, each call to atan() is about as expensive 368 // as all of the other calculations combined when converting from points to 369 // cell ids, i.e. it reduces performance by a factor of 3. 370 // 371 // Quadratic - This is an approximation of the tangent projection that 372 // is much faster and produces cells that are almost as uniform in size. 373 // It is about 3 times faster than the tangent projection for converting 374 // cell ids to points or vice versa. Cell areas vary by a maximum ratio of 375 // about 2.1. 376 // 377 // Here is a table comparing the cell uniformity using each projection. "Area 378 // ratio" is the maximum ratio over all subdivision levels of the largest cell 379 // area to the smallest cell area at that level, "edge ratio" is the maximum 380 // ratio of the longest edge of any cell to the shortest edge of any cell at 381 // the same level, and "diag ratio" is the ratio of the longest diagonal of 382 // any cell to the shortest diagonal of any cell at the same level. "ToPoint" 383 // and "FromPoint" are the times in microseconds required to convert cell ids 384 // to and from points (unit vectors) respectively. "ToPointRaw" is the time 385 // to convert to a non-unit-length vector, which is all that is needed for 386 // some purposes. 387 // 388 // Area Edge Diag ToPointRaw ToPoint FromPoint 389 // Ratio Ratio Ratio (microseconds) 390 // ------------------------------------------------------------------- 391 // Linear: 5.200 2.117 2.959 0.020 0.087 0.085 392 // Tangent: 1.414 1.414 1.704 0.237 0.299 0.258 393 // Quadratic: 2.082 1.802 1.932 0.033 0.096 0.108 394 // 395 // The worst-case cell aspect ratios are about the same with all three 396 // projections. The maximum ratio of the longest edge to the shortest edge 397 // within the same cell is about 1.4 and the maximum ratio of the diagonals 398 // within the same cell is about 1.7. 399 // 400 // This data was produced using s2cell_test and s2cell_id_test. 401 402 version = S2_QUADRATIC_PROJECTION; 403 404 version (S2_LINEAR_PROJECTION) { 405 double STtoUV(double s) { 406 return 2 * s - 1; 407 } 408 409 double UVtoST(double u) { 410 return 0.5 * (u + 1); 411 } 412 } 413 414 version (S2_TAN_PROJECTION) { 415 double STtoUV(double s) { 416 // Unfortunately, tan(M_PI_4) is slightly less than 1.0. This isn't due to 417 // a flaw in the implementation of tan(), it's because the derivative of 418 // tan(x) at x=pi/4 is 2, and it happens that the two adjacent floating 419 // point numbers on either side of the infinite-precision value of pi/4 have 420 // tangents that are slightly below and slightly above 1.0 when rounded to 421 // the nearest double-precision result. 422 423 s = math.tan(math.PI_2 * s - math.PI_4); 424 return s + (1.0 / (1L << 53)) * s; 425 } 426 427 double UVtoST(double u) { 428 double a = math.atan(u); 429 return (2 * math.M_1_PI) * (a + math.PI_4); 430 } 431 } 432 433 version (S2_QUADRATIC_PROJECTION) { 434 // Convert an s- or t-value to the corresponding u- or v-value. This is 435 // a non-linear transformation from [-1,1] to [-1,1] that attempts to 436 // make the cell sizes more uniform. 437 double STtoUV(double s) { 438 if (s >= 0.5) return (1/3.) * (4*s*s - 1); 439 else return (1/3.) * (1 - 4*(1-s)*(1-s)); 440 } 441 442 // The inverse of the STtoUV transformation. Note that it is not always 443 // true that UVtoST(STtoUV(x)) == x due to numerical errors. 444 double UVtoST(double u) { 445 if (u >= 0) return 0.5 * math.sqrt(1 + 3*u); 446 else return 1 - 0.5 * math.sqrt(1 - 3*u); 447 } 448 } 449 450 /** 451 * The canonical Hilbert traversal order looks like an inverted 'U': 452 * the subcells are visited in the order (0,0), (0,1), (1,1), (1,0). 453 * The following tables encode the traversal order for various 454 * orientations of the Hilbert curve (axes swapped and/or directions 455 * of the axes reversed). 456 */ 457 458 /** 459 * Together these flags define a cell orientation. If 'kSwapMask' 460 * is true, then canonical traversal order is flipped around the 461 * diagonal (i.e. i and j are swapped with each other). If 462 * 'kInvertMask' is true, then the traversal order is rotated by 180 463 * degrees (i.e. the bits of i and j are inverted, or equivalently, 464 * the axis directions are reversed). 465 */ 466 package immutable int SWAP_MASK = 0x01; 467 package immutable int INVERT_MASK = 0x02; 468 469 /** 470 * Given a cell orientation and the (i,j)-index of a subcell (0=(0,0), 471 * 1=(0,1), 2=(1,0), 3=(1,1)), return the order in which this subcell is 472 * visited by the Hilbert curve (a position in the range [0..3]). 473 * 474 * IJ_TO_POS[orientation][ij] -> pos 475 */ 476 package immutable int[4][4] IJ_TO_POS = [ 477 // (0,0) (0,1) (1,0) (1,1) 478 [ 0, 1, 3, 2 ], // canonical order 479 [ 0, 3, 1, 2 ], // axes swapped 480 [ 2, 3, 1, 0 ], // bits inverted 481 [ 2, 1, 3, 0 ], // swapped & inverted 482 ]; 483 484 /** 485 * Return the (i,j) index of the subcell at the given position 'pos' in the 486 * Hilbert curve traversal order with the given orientation. This is the 487 * inverse of the previous table: 488 * 489 * POS_TO_IJ[orientation][pos] -> ij 490 */ 491 package immutable int[4][4] POS_TO_IJ = [ 492 // 0 1 2 3 493 [ 0, 1, 3, 2 ], // canonical order: (0,0), (0,1), (1,1), (1,0) 494 [ 0, 2, 3, 1 ], // axes swapped: (0,0), (1,0), (1,1), (0,1) 495 [ 3, 2, 0, 1 ], // bits inverted: (1,1), (1,0), (0,0), (0,1) 496 [ 3, 1, 0, 2 ], // swapped & inverted: (1,1), (0,1), (0,0), (1,0) 497 ]; 498 499 /** 500 * Return a modifier indicating how the orientation of the child subcell 501 * with the given traversal position [0..3] is related to the orientation 502 * of the parent cell. The modifier should be XOR-ed with the parent 503 * orientation to obtain the curve orientation in the child. 504 * 505 * POS_TO_ORIENTATION[pos] -> orientation_modifier 506 */ 507 package immutable int[4] POS_TO_ORIENTATION = [ 508 SWAP_MASK, 509 0, 510 0, 511 INVERT_MASK + SWAP_MASK, 512 ]; 513 514 /** The U,V,W axes for each face. */ 515 package immutable double[3][3][6] FACE_UVW_AXES = [ 516 [ 517 [ 0, 1, 0 ], 518 [ 0, 0, 1 ], 519 [ 1, 0, 0 ] 520 ], 521 [ 522 [-1, 0, 0 ], 523 [ 0, 0, 1 ], 524 [ 0, 1, 0 ] 525 ], 526 [ 527 [-1, 0, 0 ], 528 [ 0, -1, 0 ], 529 [ 0, 0, 1 ] 530 ], 531 [ 532 [ 0, 0, -1 ], 533 [ 0, -1, 0 ], 534 [-1, 0, 0 ] 535 ], 536 [ 537 [ 0, 0, -1 ], 538 [ 1, 0, 0 ], 539 [ 0, -1, 0 ] 540 ], 541 [ 542 [ 0, 1, 0 ], 543 [ 1, 0, 0 ], 544 [ 0, 0, -1 ] 545 ] 546 ]; 547 548 /** The precomputed neighbors of each face (see GetUVWFace). */ 549 package immutable int[2][3][6] FACE_UVW_FACES = [ 550 [ [ 4, 1 ], [ 5, 2 ], [ 3, 0 ] ], 551 [ [ 0, 3 ], [ 5, 2 ], [ 4, 1 ] ], 552 [ [ 0, 3 ], [ 1, 4 ], [ 5, 2 ] ], 553 [ [ 2, 5 ], [ 1, 4 ], [ 0, 3 ] ], 554 [ [ 2, 5 ], [ 3, 0 ], [ 1, 4 ] ], 555 [ [ 4, 1 ], [ 3, 0 ], [ 2, 5 ] ] 556 ];