1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.location.altitude;
18 
19 import android.annotation.NonNull;
20 
21 import java.util.Arrays;
22 import java.util.Locale;
23 
24 /**
25  * Provides lightweight S2 cell ID utilities without traditional geometry dependencies.
26  *
27  * <p>See <a href="https://s2geometry.io/">the S2 Geometry Library website</a> for more details.
28  */
29 public final class S2CellIdUtils {
30 
31     /** The level of all leaf S2 cells. */
32     public static final int MAX_LEVEL = 30;
33 
34     private static final int MAX_SIZE = 1 << MAX_LEVEL;
35     private static final double ONE_OVER_MAX_SIZE = 1.0 / MAX_SIZE;
36     private static final int NUM_FACES = 6;
37     private static final int POS_BITS = 2 * MAX_LEVEL + 1;
38     private static final int SWAP_MASK = 0x1;
39     private static final int LOOKUP_BITS = 4;
40     private static final int LOOKUP_MASK = (1 << LOOKUP_BITS) - 1;
41     private static final int INVERT_MASK = 0x2;
42     private static final int LEAF_MASK = 0x1;
43     private static final int[] LOOKUP_POS = new int[1 << (2 * LOOKUP_BITS + 2)];
44     private static final int[] LOOKUP_IJ = new int[1 << (2 * LOOKUP_BITS + 2)];
45     private static final int[] POS_TO_ORIENTATION = {SWAP_MASK, 0, 0, INVERT_MASK + SWAP_MASK};
46     private static final int[][] POS_TO_IJ =
47             {{0, 1, 3, 2}, {0, 2, 3, 1}, {3, 2, 0, 1}, {3, 1, 0, 2}};
48     private static final double UV_LIMIT = calculateUvLimit();
49     private static final UvTransform[] UV_TRANSFORMS = createUvTransforms();
50     private static final XyzTransform[] XYZ_TRANSFORMS = createXyzTransforms();
51 
52     // Used to encode (i, j, o) coordinates into primitive longs.
53     private static final int I_SHIFT = 33;
54     private static final int J_SHIFT = 2;
55     private static final long J_MASK = (1L << 31) - 1;
56 
57     static {
initLookupCells()58         initLookupCells();
59     }
60 
61     /** Prevents instantiation. */
S2CellIdUtils()62     private S2CellIdUtils() {
63     }
64 
65     /**
66      * Returns the leaf S2 cell ID for the specified latitude and longitude, both measured in
67      * degrees.
68      */
fromLatLngDegrees(double latDegrees, double lngDegrees)69     public static long fromLatLngDegrees(double latDegrees, double lngDegrees) {
70         return fromLatLngRadians(Math.toRadians(latDegrees), Math.toRadians(lngDegrees));
71     }
72 
73     /**
74      * Returns the ID of the parent of the specified S2 cell at the specified parent level.
75      * Behavior is undefined for invalid S2 cell IDs or parent levels not in
76      * [0, {@code getLevel(s2CellId)}[.
77      */
getParent(long s2CellId, int level)78     public static long getParent(long s2CellId, int level) {
79         long newLsb = getLowestOnBitForLevel(level);
80         return (s2CellId & -newLsb) | newLsb;
81     }
82 
83     /**
84      * Inserts into {@code neighbors} the four S2 cell IDs corresponding to the neighboring
85      * cells adjacent across the specified cell's four edges. This array must be of minimum
86      * length four, and elements at the tail end of the array not corresponding to a neighbor
87      * are set to zero. A reference to this array is returned.
88      *
89      * <p>Inserts in the order of down, right, up, and left directions, in that order. All
90      * neighbors are guaranteed to be distinct.
91      */
getEdgeNeighbors(long s2CellId, @NonNull long[] neighbors)92     public static void getEdgeNeighbors(long s2CellId, @NonNull long[] neighbors) {
93         int level = getLevel(s2CellId);
94         int size = levelToSizeIj(level);
95         int face = getFace(s2CellId);
96         long ijo = toIjo(s2CellId);
97         int i = ijoToI(ijo);
98         int j = ijoToJ(ijo);
99 
100         int iPlusSize = i + size;
101         int iMinusSize = i - size;
102         int jPlusSize = j + size;
103         int jMinusSize = j - size;
104         boolean iPlusSizeLtMax = iPlusSize < MAX_SIZE;
105         boolean iMinusSizeGteZero = iMinusSize >= 0;
106         boolean jPlusSizeLtMax = jPlusSize < MAX_SIZE;
107         boolean jMinusSizeGteZero = jMinusSize >= 0;
108 
109         int index = 0;
110         // Down direction.
111         neighbors[index++] = getParent(fromFijSame(face, i, jMinusSize, jMinusSizeGteZero),
112                 level);
113         // Right direction.
114         neighbors[index++] = getParent(fromFijSame(face, iPlusSize, j, iPlusSizeLtMax), level);
115         // Up direction.
116         neighbors[index++] = getParent(fromFijSame(face, i, jPlusSize, jPlusSizeLtMax), level);
117         // Left direction.
118         neighbors[index++] = getParent(fromFijSame(face, iMinusSize, j, iMinusSizeGteZero),
119                 level);
120 
121         // Pad end of neighbor array with zeros.
122         Arrays.fill(neighbors, index, neighbors.length, 0);
123     }
124 
125     /** Returns the "i" coordinate for the specified S2 cell. */
getI(long s2CellId)126     public static int getI(long s2CellId) {
127         return ijoToI(toIjo(s2CellId));
128     }
129 
130     /** Returns the "j" coordinate for the specified S2 cell. */
getJ(long s2CellId)131     public static int getJ(long s2CellId) {
132         return ijoToJ(toIjo(s2CellId));
133     }
134 
135     /**
136      * Returns the leaf S2 cell ID for the specified latitude and longitude, both measured in
137      * radians.
138      */
fromLatLngRadians(double latRadians, double lngRadians)139     private static long fromLatLngRadians(double latRadians, double lngRadians) {
140         double cosLat = Math.cos(latRadians);
141         double x = Math.cos(lngRadians) * cosLat;
142         double y = Math.sin(lngRadians) * cosLat;
143         double z = Math.sin(latRadians);
144         return fromXyz(x, y, z);
145     }
146 
147     /**
148      * Returns the level of the specified S2 cell. The returned level is in [0, 30] for valid
149      * S2 cell IDs. Behavior is undefined for invalid S2 cell IDs.
150      */
getLevel(long s2CellId)151     static int getLevel(long s2CellId) {
152         if (isLeaf(s2CellId)) {
153             return MAX_LEVEL;
154         }
155         return MAX_LEVEL - (Long.numberOfTrailingZeros(s2CellId) >> 1);
156     }
157 
158     /** Returns the lowest-numbered bit that is on for the specified S2 cell. */
getLowestOnBit(long s2CellId)159     static long getLowestOnBit(long s2CellId) {
160         return s2CellId & -s2CellId;
161     }
162 
163     /** Returns the lowest-numbered bit that is on for any S2 cell on the specified level. */
getLowestOnBitForLevel(int level)164     static long getLowestOnBitForLevel(int level) {
165         return 1L << (2 * (MAX_LEVEL - level));
166     }
167 
168     /**
169      * Returns the ID of the first S2 cell in a traversal of the children S2 cells at the specified
170      * level, in Hilbert curve order.
171      */
getTraversalStart(long s2CellId, int level)172     static long getTraversalStart(long s2CellId, int level) {
173         return s2CellId - getLowestOnBit(s2CellId) + getLowestOnBitForLevel(level);
174     }
175 
176     /** Returns the ID of the next S2 cell at the same level along the Hilbert curve. */
getTraversalNext(long s2CellId)177     static long getTraversalNext(long s2CellId) {
178         return s2CellId + (getLowestOnBit(s2CellId) << 1);
179     }
180 
181     /**
182      * Encodes the S2 cell id to compact text strings suitable for display or indexing. Cells at
183      * lower levels (i.e., larger cells) are encoded into fewer characters.
184      */
185     @NonNull
getToken(long s2CellId)186     static String getToken(long s2CellId) {
187         if (s2CellId == 0) {
188             return "X";
189         }
190 
191         // Convert to a hex string with as many digits as necessary.
192         String hex = Long.toHexString(s2CellId).toLowerCase(Locale.US);
193         // Prefix 0s to get a length 16 string.
194         String padded = padStart(hex);
195         // Trim zeroes off the end.
196         return padded.replaceAll("0*$", "");
197     }
198 
padStart(String string)199     private static String padStart(String string) {
200         if (string.length() >= 16) {
201             return string;
202         }
203         return "0".repeat(16 - string.length()) + string;
204     }
205 
206     /** Returns the leaf S2 cell ID of the specified (x, y, z) coordinate. */
fromXyz(double x, double y, double z)207     private static long fromXyz(double x, double y, double z) {
208         int face = xyzToFace(x, y, z);
209         UvTransform uvTransform = UV_TRANSFORMS[face];
210         double u = uvTransform.xyzToU(x, y, z);
211         double v = uvTransform.xyzToV(x, y, z);
212         return fromFuv(face, u, v);
213     }
214 
215     /** Returns the leaf S2 cell ID of the specified (face, u, v) coordinate. */
fromFuv(int face, double u, double v)216     private static long fromFuv(int face, double u, double v) {
217         int i = uToI(u);
218         int j = vToJ(v);
219         return fromFij(face, i, j);
220     }
221 
222     /** Returns the leaf S2 cell ID of the specified (face, i, j) coordinate. */
fromFij(int face, int i, int j)223     private static long fromFij(int face, int i, int j) {
224         int bits = (face & SWAP_MASK);
225         // Update most significant bits.
226         long msb = ((long) face) << (POS_BITS - 33);
227         for (int k = 7; k >= 4; --k) {
228             bits = lookupBits(i, j, k, bits);
229             msb = updateBits(msb, k, bits);
230             bits = maskBits(bits);
231         }
232         // Update least significant bits.
233         long lsb = 0;
234         for (int k = 3; k >= 0; --k) {
235             bits = lookupBits(i, j, k, bits);
236             lsb = updateBits(lsb, k, bits);
237             bits = maskBits(bits);
238         }
239         return (((msb << 32) + lsb) << 1) + 1;
240     }
241 
fromFijWrap(int face, int i, int j)242     private static long fromFijWrap(int face, int i, int j) {
243         double u = iToU(i);
244         double v = jToV(j);
245 
246         XyzTransform xyzTransform = XYZ_TRANSFORMS[face];
247         double x = xyzTransform.uvToX(u, v);
248         double y = xyzTransform.uvToY(u, v);
249         double z = xyzTransform.uvToZ(u, v);
250 
251         int newFace = xyzToFace(x, y, z);
252         UvTransform uvTransform = UV_TRANSFORMS[newFace];
253         double newU = uvTransform.xyzToU(x, y, z);
254         double newV = uvTransform.xyzToV(x, y, z);
255 
256         int newI = uShiftIntoI(newU);
257         int newJ = vShiftIntoJ(newV);
258         return fromFij(newFace, newI, newJ);
259     }
260 
fromFijSame(int face, int i, int j, boolean isSameFace)261     private static long fromFijSame(int face, int i, int j, boolean isSameFace) {
262         if (isSameFace) {
263             return fromFij(face, i, j);
264         }
265         return fromFijWrap(face, i, j);
266     }
267 
268     /**
269      * Returns the face associated with the specified (x, y, z) coordinate. For a coordinate
270      * on a face boundary, the returned face is arbitrary but repeatable.
271      */
xyzToFace(double x, double y, double z)272     private static int xyzToFace(double x, double y, double z) {
273         double absX = Math.abs(x);
274         double absY = Math.abs(y);
275         double absZ = Math.abs(z);
276         if (absX > absY) {
277             if (absX > absZ) {
278                 return (x < 0) ? 3 : 0;
279             }
280             return (z < 0) ? 5 : 2;
281         }
282         if (absY > absZ) {
283             return (y < 0) ? 4 : 1;
284         }
285         return (z < 0) ? 5 : 2;
286     }
287 
uToI(double u)288     private static int uToI(double u) {
289         double s;
290         if (u >= 0) {
291             s = 0.5 * Math.sqrt(1 + 3 * u);
292         } else {
293             s = 1 - 0.5 * Math.sqrt(1 - 3 * u);
294         }
295         return Math.max(0, Math.min(MAX_SIZE - 1, (int) Math.round(MAX_SIZE * s - 0.5)));
296     }
297 
vToJ(double v)298     private static int vToJ(double v) {
299         // Same calculation as uToI.
300         return uToI(v);
301     }
302 
lookupBits(int i, int j, int k, int bits)303     private static int lookupBits(int i, int j, int k, int bits) {
304         bits += ((i >> (k * LOOKUP_BITS)) & LOOKUP_MASK) << (LOOKUP_BITS + 2);
305         bits += ((j >> (k * LOOKUP_BITS)) & LOOKUP_MASK) << 2;
306         return LOOKUP_POS[bits];
307     }
308 
updateBits(long sb, int k, int bits)309     private static long updateBits(long sb, int k, int bits) {
310         return sb | ((((long) bits) >> 2) << ((k & 0x3) * 2 * LOOKUP_BITS));
311     }
312 
maskBits(int bits)313     private static int maskBits(int bits) {
314         return bits & (SWAP_MASK | INVERT_MASK);
315     }
316 
getFace(long s2CellId)317     private static int getFace(long s2CellId) {
318         return (int) (s2CellId >>> POS_BITS);
319     }
320 
isLeaf(long s2CellId)321     private static boolean isLeaf(long s2CellId) {
322         return ((int) s2CellId & LEAF_MASK) != 0;
323     }
324 
iToU(int i)325     private static double iToU(int i) {
326         int satI = Math.max(-1, Math.min(MAX_SIZE, i));
327         return Math.max(
328                 -UV_LIMIT,
329                 Math.min(UV_LIMIT, ONE_OVER_MAX_SIZE * ((satI << 1) + 1 - MAX_SIZE)));
330     }
331 
jToV(int j)332     private static double jToV(int j) {
333         // Same calculation as iToU.
334         return iToU(j);
335     }
336 
toIjo(long s2CellId)337     private static long toIjo(long s2CellId) {
338         int face = getFace(s2CellId);
339         int bits = face & SWAP_MASK;
340         int i = 0;
341         int j = 0;
342         for (int k = 7; k >= 0; --k) {
343             int nbits = (k == 7) ? (MAX_LEVEL - 7 * LOOKUP_BITS) : LOOKUP_BITS;
344             bits += ((int) (s2CellId >>> (k * 2 * LOOKUP_BITS + 1)) & ((1 << (2 * nbits))
345                     - 1)) << 2;
346             bits = LOOKUP_IJ[bits];
347             i += (bits >> (LOOKUP_BITS + 2)) << (k * LOOKUP_BITS);
348             j += ((bits >> 2) & ((1 << LOOKUP_BITS) - 1)) << (k * LOOKUP_BITS);
349             bits &= (SWAP_MASK | INVERT_MASK);
350         }
351         int orientation =
352                 ((getLowestOnBit(s2CellId) & 0x1111111111111110L) != 0) ? (bits ^ SWAP_MASK)
353                         : bits;
354         return (((long) i) << I_SHIFT) | (((long) j) << J_SHIFT) | orientation;
355     }
356 
ijoToI(long ijo)357     private static int ijoToI(long ijo) {
358         return (int) (ijo >>> I_SHIFT);
359     }
360 
ijoToJ(long ijo)361     private static int ijoToJ(long ijo) {
362         return (int) ((ijo >>> J_SHIFT) & J_MASK);
363     }
364 
uShiftIntoI(double u)365     private static int uShiftIntoI(double u) {
366         double s = 0.5 * (u + 1);
367         return Math.max(0, Math.min(MAX_SIZE - 1, (int) Math.round(MAX_SIZE * s - 0.5)));
368     }
369 
vShiftIntoJ(double v)370     private static int vShiftIntoJ(double v) {
371         // Same calculation as uShiftIntoI.
372         return uShiftIntoI(v);
373     }
374 
levelToSizeIj(int level)375     private static int levelToSizeIj(int level) {
376         return 1 << (MAX_LEVEL - level);
377     }
378 
initLookupCells()379     private static void initLookupCells() {
380         initLookupCell(0, 0, 0, 0, 0, 0);
381         initLookupCell(0, 0, 0, SWAP_MASK, 0, SWAP_MASK);
382         initLookupCell(0, 0, 0, INVERT_MASK, 0, INVERT_MASK);
383         initLookupCell(0, 0, 0, SWAP_MASK | INVERT_MASK, 0, SWAP_MASK | INVERT_MASK);
384     }
385 
initLookupCell( int level, int i, int j, int origOrientation, int pos, int orientation)386     private static void initLookupCell(
387             int level, int i, int j, int origOrientation, int pos, int orientation) {
388         if (level == LOOKUP_BITS) {
389             int ij = (i << LOOKUP_BITS) + j;
390             LOOKUP_POS[(ij << 2) + origOrientation] = (pos << 2) + orientation;
391             LOOKUP_IJ[(pos << 2) + origOrientation] = (ij << 2) + orientation;
392         } else {
393             level++;
394             i <<= 1;
395             j <<= 1;
396             pos <<= 2;
397             for (int subPos = 0; subPos < 4; subPos++) {
398                 int ij = POS_TO_IJ[orientation][subPos];
399                 int orientationMask = POS_TO_ORIENTATION[subPos];
400                 initLookupCell(
401                         level,
402                         i + (ij >>> 1),
403                         j + (ij & 0x1),
404                         origOrientation,
405                         pos + subPos,
406                         orientation ^ orientationMask);
407             }
408         }
409     }
410 
calculateUvLimit()411     private static double calculateUvLimit() {
412         double machEps = 1.0;
413         do {
414             machEps /= 2.0f;
415         } while ((1.0 + (machEps / 2.0)) != 1.0);
416         return 1.0 + machEps;
417     }
418 
419     @NonNull
createUvTransforms()420     private static UvTransform[] createUvTransforms() {
421         UvTransform[] uvTransforms = new UvTransform[NUM_FACES];
422         uvTransforms[0] =
423                 new UvTransform() {
424 
425                     @Override
426                     public double xyzToU(double x, double y, double z) {
427                         return y / x;
428                     }
429 
430                     @Override
431                     public double xyzToV(double x, double y, double z) {
432                         return z / x;
433                     }
434                 };
435         uvTransforms[1] =
436                 new UvTransform() {
437 
438                     @Override
439                     public double xyzToU(double x, double y, double z) {
440                         return -x / y;
441                     }
442 
443                     @Override
444                     public double xyzToV(double x, double y, double z) {
445                         return z / y;
446                     }
447                 };
448         uvTransforms[2] =
449                 new UvTransform() {
450 
451                     @Override
452                     public double xyzToU(double x, double y, double z) {
453                         return -x / z;
454                     }
455 
456                     @Override
457                     public double xyzToV(double x, double y, double z) {
458                         return -y / z;
459                     }
460                 };
461         uvTransforms[3] =
462                 new UvTransform() {
463 
464                     @Override
465                     public double xyzToU(double x, double y, double z) {
466                         return z / x;
467                     }
468 
469                     @Override
470                     public double xyzToV(double x, double y, double z) {
471                         return y / x;
472                     }
473                 };
474         uvTransforms[4] =
475                 new UvTransform() {
476 
477                     @Override
478                     public double xyzToU(double x, double y, double z) {
479                         return z / y;
480                     }
481 
482                     @Override
483                     public double xyzToV(double x, double y, double z) {
484                         return -x / y;
485                     }
486                 };
487         uvTransforms[5] =
488                 new UvTransform() {
489 
490                     @Override
491                     public double xyzToU(double x, double y, double z) {
492                         return -y / z;
493                     }
494 
495                     @Override
496                     public double xyzToV(double x, double y, double z) {
497                         return -x / z;
498                     }
499                 };
500         return uvTransforms;
501     }
502 
503     @NonNull
createXyzTransforms()504     private static XyzTransform[] createXyzTransforms() {
505         XyzTransform[] xyzTransforms = new XyzTransform[NUM_FACES];
506         xyzTransforms[0] =
507                 new XyzTransform() {
508 
509                     @Override
510                     public double uvToX(double u, double v) {
511                         return 1;
512                     }
513 
514                     @Override
515                     public double uvToY(double u, double v) {
516                         return u;
517                     }
518 
519                     @Override
520                     public double uvToZ(double u, double v) {
521                         return v;
522                     }
523                 };
524         xyzTransforms[1] =
525                 new XyzTransform() {
526 
527                     @Override
528                     public double uvToX(double u, double v) {
529                         return -u;
530                     }
531 
532                     @Override
533                     public double uvToY(double u, double v) {
534                         return 1;
535                     }
536 
537                     @Override
538                     public double uvToZ(double u, double v) {
539                         return v;
540                     }
541                 };
542         xyzTransforms[2] =
543                 new XyzTransform() {
544 
545                     @Override
546                     public double uvToX(double u, double v) {
547                         return -u;
548                     }
549 
550                     @Override
551                     public double uvToY(double u, double v) {
552                         return -v;
553                     }
554 
555                     @Override
556                     public double uvToZ(double u, double v) {
557                         return 1;
558                     }
559                 };
560         xyzTransforms[3] =
561                 new XyzTransform() {
562 
563                     @Override
564                     public double uvToX(double u, double v) {
565                         return -1;
566                     }
567 
568                     @Override
569                     public double uvToY(double u, double v) {
570                         return -v;
571                     }
572 
573                     @Override
574                     public double uvToZ(double u, double v) {
575                         return -u;
576                     }
577                 };
578         xyzTransforms[4] =
579                 new XyzTransform() {
580 
581                     @Override
582                     public double uvToX(double u, double v) {
583                         return v;
584                     }
585 
586                     @Override
587                     public double uvToY(double u, double v) {
588                         return -1;
589                     }
590 
591                     @Override
592                     public double uvToZ(double u, double v) {
593                         return -u;
594                     }
595                 };
596         xyzTransforms[5] =
597                 new XyzTransform() {
598 
599                     @Override
600                     public double uvToX(double u, double v) {
601                         return v;
602                     }
603 
604                     @Override
605                     public double uvToY(double u, double v) {
606                         return u;
607                     }
608 
609                     @Override
610                     public double uvToZ(double u, double v) {
611                         return -1;
612                     }
613                 };
614         return xyzTransforms;
615     }
616 
617     /**
618      * Transform from (x, y, z) coordinates to (u, v) coordinates, indexed by face. For a
619      * (x, y, z) coordinate within a face, each element of the resulting (u, v) coordinate
620      * should lie in the inclusive range [-1, 1], with the face center having a (u, v)
621      * coordinate equal to (0, 0).
622      */
623     private interface UvTransform {
624 
625         /**
626          * Returns for the specified (x, y, z) coordinate the corresponding u-coordinate
627          * (which may lie outside the range [-1, 1]).
628          */
xyzToU(double x, double y, double z)629         double xyzToU(double x, double y, double z);
630 
631         /**
632          * Returns for the specified (x, y, z) coordinate the corresponding v-coordinate
633          * (which may lie outside the range [-1, 1]).
634          */
xyzToV(double x, double y, double z)635         double xyzToV(double x, double y, double z);
636     }
637 
638     /**
639      * Transform from (u, v) coordinates to (x, y, z) coordinates, indexed by face. The
640      * resulting vectors are not necessarily of unit length.
641      */
642     private interface XyzTransform {
643 
644         /** Returns for the specified (u, v) coordinate the corresponding x-coordinate. */
uvToX(double u, double v)645         double uvToX(double u, double v);
646 
647         /** Returns for the specified (u, v) coordinate the corresponding y-coordinate. */
uvToY(double u, double v)648         double uvToY(double u, double v);
649 
650         /** Returns for the specified (u, v) coordinate the corresponding z-coordinate. */
uvToZ(double u, double v)651         double uvToZ(double u, double v);
652     }
653 }
654