Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 21ccc806 authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 12705048 from 222750e7 to 25Q1-release

Change-Id: Ica0d526a7094707a1b52c0721789e3f51c0c5542
parents 9bec6b47 222750e7
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -26,8 +26,8 @@ import android.location.Location;
import android.location.flags.Flags;

import com.android.internal.location.altitude.GeoidMap;
import com.android.internal.location.altitude.S2CellIdUtils;
import com.android.internal.location.altitude.nano.MapParamsProto;
import com.android.internal.location.geometry.S2CellIdUtils;
import com.android.internal.util.Preconditions;

import java.io.IOException;
+1 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.util.LruCache;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.location.altitude.nano.MapParamsProto;
import com.android.internal.location.altitude.nano.S2TileProto;
import com.android.internal.location.geometry.S2CellIdUtils;
import com.android.internal.util.Preconditions;

import java.io.IOException;
+139 −5
Original line number Diff line number Diff line
@@ -14,7 +14,7 @@
 * limitations under the License.
 */

package com.android.internal.location.altitude;
package com.android.internal.location.geometry;

import android.annotation.NonNull;

@@ -48,12 +48,22 @@ public final class S2CellIdUtils {
    private static final double UV_LIMIT = calculateUvLimit();
    private static final UvTransform[] UV_TRANSFORMS = createUvTransforms();
    private static final XyzTransform[] XYZ_TRANSFORMS = createXyzTransforms();
    private static final long MAX_SI_TI = 1L << (MAX_LEVEL + 1);

    // Used to encode (i, j, o) coordinates into primitive longs.
    private static final int I_SHIFT = 33;
    private static final int J_SHIFT = 2;
    private static final long J_MASK = (1L << 31) - 1;

    // Used to insert latitude and longitude values into arrays.
    public static final int LAT_LNG_MIN_LENGTH = 2;
    public static final int LAT_INDEX = 0;
    public static final int LNG_INDEX = 1;

    // Used to encode (si, ti) coordinates into primitive longs.
    private static final int SI_SHIFT = 32;
    private static final long TI_MASK = (1L << 32) - 1;

    static {
        initLookupCells();
    }
@@ -62,6 +72,130 @@ public final class S2CellIdUtils {
    private S2CellIdUtils() {
    }

    /**
     * Inserts into {@code latLngDegrees} the centroid latitude and longitude, in that order and
     * both measured in degrees, for the specified S2 cell ID. This array must be non-null and of
     * minimum length two. A reference to this array is returned.
     *
     * <p>Behavior is undefined for invalid S2 cell IDs.
     */
    public static double[] toLatLngDegrees(long s2CellId, double[] latLngDegrees) {
        // Used latLngDegrees as scratchpad for toLatLngRadians(long, double[]).
        final double[] latLngRadians = latLngDegrees;
        toLatLngRadians(s2CellId, latLngRadians);
        latLngDegrees[LAT_INDEX] = Math.toDegrees(latLngRadians[LAT_INDEX]);
        latLngDegrees[LNG_INDEX] = Math.toDegrees(latLngRadians[LNG_INDEX]);
        return latLngDegrees;
    }


    /**
     * Inserts into {@code latLngRadians} the centroid latitude and longitude, in that order and
     * both measured in radians, for the specified S2 cell ID. This array must be non-null and of
     * minimum length two. A reference to this array is returned.
     *
     * <p>Behavior is undefined for invalid S2 cell IDs.
     */
    public static double[] toLatLngRadians(long s2CellId, double[] latLngRadians) {
        checkNotNull(latLngRadians);
        checkLengthGreaterThanOrEqualTo(LAT_LNG_MIN_LENGTH, latLngRadians.length);

        final long siTi = toSiTi(s2CellId);
        final double u = siTiToU(siTi);
        final double v = siTiToV(siTi);

        final int face = getFace(s2CellId);
        final XyzTransform xyzTransform = faceToXyzTransform(face);
        final double x = xyzTransform.uvToX(u, v);
        final double y = xyzTransform.uvToY(u, v);
        final double z = xyzTransform.uvToZ(u, v);

        latLngRadians[LAT_INDEX] = xyzToLatRadians(x, y, z);
        latLngRadians[LNG_INDEX] = xyzToLngRadians(x, y);
        return latLngRadians;
    }

    private static long toSiTi(long s2CellId) {
        final long ijo = toIjo(s2CellId);
        final int i = ijoToI(ijo);
        final int j = ijoToJ(ijo);
        int delta = isLeaf(s2CellId) ? 1 : (((i ^ (((int) s2CellId) >>> 2)) & 1) != 0) ? 2 : 0;
        return (((long) (2 * i + delta)) << SI_SHIFT) | ((2 * j + delta) & TI_MASK);
    }

    private static int siTiToSi(long siTi) {
        return (int) (siTi >> SI_SHIFT);
    }

    private static int siTiToTi(long siTi) {
        return (int) siTi;
    }

    private static double siTiToU(long siTi) {
        final int si = siTiToSi(siTi);
        return siToU(si);
    }

    private static double siTiToV(long siTi) {
        final int ti = siTiToTi(siTi);
        return tiToV(ti);
    }

    private static double siToU(long si) {
        final double s = (1.0 / MAX_SI_TI) * si;
        if (s >= 0.5) {
            return (1 / 3.) * (4 * s * s - 1);
        }
        return (1 / 3.) * (1 - 4 * (1 - s) * (1 - s));
    }

    private static double tiToV(long ti) {
        // Same calculation as siToU.
        return siToU(ti);
    }

    private static XyzTransform faceToXyzTransform(int face) {
        // We map illegal face indices to the largest face index to preserve legacy behavior, i.e.,
        // we do not want to throw an index out of bounds exception. Note that getFace(s2CellId) is
        // guaranteed to return a non-negative face index even for invalid S2 cells, so it is
        // sufficient to just map all face indices greater than the largest face index to the
        // largest face index.
        return XYZ_TRANSFORMS[Math.min(NUM_FACES - 1, face)];
    }

    private static double xyzToLngRadians(double x, double y) {
        return Math.atan2(y, x);
    }

    private static double xyzToLatRadians(double x, double y, double z) {
        return Math.atan2(z, Math.sqrt(x * x + y * y));
    }

    private static void checkNotNull(Object object) {
        if (object == null) {
            throw new NullPointerException("Given array cannot be null.");
        }
    }

    private static void checkLengthGreaterThanOrEqualTo(int minLength, int actualLength) {
        if (actualLength < minLength) {
            throw new IllegalArgumentException(
                "Given array of length " + actualLength + " needs to be of minimum length "
                + minLength);
        }
    }

    /**
     * Returns true if the provided S2 cell contains the provided latitude/longitude, both measured
     * in degrees.
     */
    public static boolean containsLatLngDegrees(long s2CellId, double latDegrees,
            double lngDegrees) {
        int level = getLevel(s2CellId);
        long leafCellId = fromLatLngDegrees(latDegrees, lngDegrees);
        return (getParent(leafCellId, level) == s2CellId);
    }

    /**
     * Returns the leaf S2 cell ID for the specified latitude and longitude, both measured in
     * degrees.
@@ -176,7 +310,7 @@ public final class S2CellIdUtils {
     * Returns the level of the specified S2 cell. The returned level is in [0, 30] for valid
     * S2 cell IDs. Behavior is undefined for invalid S2 cell IDs.
     */
    static int getLevel(long s2CellId) {
    public static int getLevel(long s2CellId) {
        if (isLeaf(s2CellId)) {
            return MAX_LEVEL;
        }
@@ -197,12 +331,12 @@ public final class S2CellIdUtils {
     * Returns the ID of the first S2 cell in a traversal of the children S2 cells at the specified
     * level, in Hilbert curve order.
     */
    static long getTraversalStart(long s2CellId, int level) {
    public static long getTraversalStart(long s2CellId, int level) {
        return s2CellId - getLowestOnBit(s2CellId) + getLowestOnBitForLevel(level);
    }

    /** Returns the ID of the next S2 cell at the same level along the Hilbert curve. */
    static long getTraversalNext(long s2CellId) {
    public static long getTraversalNext(long s2CellId) {
        return s2CellId + (getLowestOnBit(s2CellId) << 1);
    }

@@ -211,7 +345,7 @@ public final class S2CellIdUtils {
     * lower levels (i.e., larger cells) are encoded into fewer characters.
     */
    @NonNull
    static String getToken(long s2CellId) {
    public static String getToken(long s2CellId) {
        if (s2CellId == 0) {
            return "X";
        }
+138 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.location.geometry;

import static com.google.common.truth.Truth.assertThat;

import androidx.test.ext.junit.runners.AndroidJUnit4;

import com.android.internal.location.geometry.S2CellIdUtils;

import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class S2CellIdUtilsTest {

    // S2 cell ID of a level-30 cell in Times Square.
    private static final long TIMES_SQUARE_S2_ID =
            S2CellIdUtils.fromLatLngDegrees(40.758896, -73.985130);

    // Position of the Eiffel tower (outside of any parent cell from Times Square).
    private static final double[] EIFFEL_TOWER_LATLNG = {48.858093, 2.294694};

    // Test vector around TIMES_SQUARE_S2_ID: Cell IDs and the centers for levels 0 to 30.
    // This test vector has been computed using the public S2 library in
    // external/s2-geometry-library-java
    private static final CellAndCenter[] TIMES_SQUARE_CELLS = {
            new CellAndCenter("9", -0.0, -90.0),
            new CellAndCenter("8c", 21.037511025421814, -67.38013505195958),
            new CellAndCenter("89", 34.04786296943431, -79.38034472384487),
            new CellAndCenter("89c", 38.79459515585768, -73.46516214265485),
            new CellAndCenter("89d", 41.74704688465104, -76.45630866778862),
            new CellAndCenter("89c4", 40.29416073145462, -74.96763653470293),
            new CellAndCenter("89c3", 40.827706513259564, -74.21793256064282),
            new CellAndCenter("89c24", 40.45771021423038, -73.84190634077625),
            new CellAndCenter("89c25", 40.64307662867646, -74.03001224983848),
            new CellAndCenter("89c25c", 40.708880489804564, -73.93598211433742),
            new CellAndCenter("89c259", 40.75509755935301, -73.9830029344863),
            new CellAndCenter("89c2584", 40.7781887758716, -74.00650903621303),
            new CellAndCenter("89c2585", 40.766644611813284, -73.99475634561863),
            new CellAndCenter("89c25854", 40.76087144655763, -73.98887973002674),
            new CellAndCenter("89c25855", 40.75798459318946, -73.98594135473846),
            new CellAndCenter("89c25855c", 40.75901097797799, -73.98447215023141),
            new CellAndCenter("89c25855b", 40.758497791893824, -73.98520675388987),
            new CellAndCenter("89c25855bc", 40.75875438651343, -73.98483945241185),
            new CellAndCenter("89c25855b9", 40.758934819692875, -73.98502310323867),
            new CellAndCenter("89c25855b9c", 40.75887067137071, -73.98511492858621),
            new CellAndCenter("89c25855b9d", 40.75891577956465, -73.98516084124353),
            new CellAndCenter("89c25855b9c4", 40.758893225473194, -73.98513788491626),
            new CellAndCenter("89c25855b9c7", 40.75890124402366, -73.98512640675159),
            new CellAndCenter("89c25855b9c6c", 40.758897234748815, -73.985132145834),
            new CellAndCenter("89c25855b9c6d", 40.75889441548664, -73.98512927629281),
            new CellAndCenter("89c25855b9c6c4", 40.75889582511775, -73.98513071106342),
            new CellAndCenter("89c25855b9c6c3", 40.758896326277146, -73.98512999367811),
            new CellAndCenter("89c25855b9c6c3c", 40.75889607569745, -73.98513035237076),
            new CellAndCenter("89c25855b9c6c39", 40.75889589949357, -73.98513017302443),
            new CellAndCenter("89c25855b9c6c39c", 40.75889596213849, -73.98513008335128),
            new CellAndCenter("89c25855b9c6c39f", 40.75889599346095, -73.9851300385147)};

    @Test
    public void toLatLngDegrees_matchesTestVector() {
        for (int level = 0; level <= 30; level++) {
            double[] expected = TIMES_SQUARE_CELLS[level].mCenter;
            long cellId = S2CellIdUtils.getParent(TIMES_SQUARE_S2_ID, level);

            double[] centerPoint = {0.0, 0.0};
            S2CellIdUtils.toLatLngDegrees(cellId, centerPoint);

            assertThat(approxEquals(centerPoint[0], expected[0])).isTrue();
            assertThat(approxEquals(centerPoint[1], expected[1])).isTrue();
        }
    }

    private static boolean approxEquals(double a, double b) {
        return Math.abs(a - b) <= 1e-14;
    }

    @Test
    public void containsLatLngDegrees_eachCellContainsItsCenter_works() {
        for (int level = 0; level <= 30; level++) {
            long cellId = TIMES_SQUARE_CELLS[level].toCellId();
            double[] center = TIMES_SQUARE_CELLS[level].mCenter;

            boolean isContained = S2CellIdUtils.containsLatLngDegrees(cellId, center[0], center[1]);

            assertThat(isContained).isTrue();
        }
    }

    @Test
    public void containsLatLngDegrees_testWithOutsidePoint() {
        for (int level = 0; level <= 30; level++) {
            long cellId = TIMES_SQUARE_CELLS[level].toCellId();

            assertThat(S2CellIdUtils.containsLatLngDegrees(cellId, EIFFEL_TOWER_LATLNG[0],
                  EIFFEL_TOWER_LATLNG[1])).isFalse();
        }
    }

    // A tuple with a S2 cell id, and a S2LatLng representing its center.
    private static class CellAndCenter {
        public String mToken;
        public double[] mCenter;

        CellAndCenter(String token, double latDegrees, double lngDegrees) {
            this.mToken = token;
            this.mCenter = new double[] {latDegrees, lngDegrees};
        }

        // Converts from hex representation to long format.
        long toCellId() {
            long value = 0;
            for (int pos = 0; pos < mToken.length(); pos++) {
                int digitValue = Character.digit(mToken.charAt(pos), 16);
                if (digitValue == -1) {
                    return -1;
                }
                value = value * 16 + digitValue;
            }
            value = value << (4 * (16 - mToken.length()));  // remove implicit zeros
            return value;
        }
    }
}