blob: 6e2a97aff72782b2ae56dbb7692fd6e87e46dccd [file] [log] [blame]
/*
* Copyright (C) 2020 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.cellbroadcastservice.tests;
import static com.google.common.truth.Truth.assertThat;
import android.annotation.NonNull;
import android.telephony.CbGeoUtils.Circle;
import android.telephony.CbGeoUtils.Geometry;
import android.telephony.CbGeoUtils.LatLng;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import com.android.cellbroadcastservice.CbGeoUtils;
import junit.framework.AssertionFailedError;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class CbGeoUtilsTest extends CellBroadcastServiceTestBase {
private static final double ASSERT_EQUALS_PRECISION = .005;
LatLng mGooglePlex = new LatLng(37.423640, -122.088310);
@Before
public void setUp() throws Exception {
super.setUp();
}
@After
public void tearDown() throws Exception {
super.tearDown();
}
@Test
public void testEncodeAndParseString() {
// create list of geometries
List<Geometry> geometries = new ArrayList<>();
geometries.add(new Circle(new LatLng(10, 10), 3000));
geometries.add(new Circle(new LatLng(12, 10), 3000));
geometries.add(new Circle(new LatLng(40, 40), 3000));
// encode to string
String geoString = CbGeoUtils.encodeGeometriesToString(geometries);
// parse from string
List<Geometry> parsedGeometries = CbGeoUtils.parseGeometriesFromString(geoString);
// assert equality
assertEquals(geometries, parsedGeometries);
}
@Test
public void testAddEast() {
LatLng east100m = addEast(mGooglePlex, 100);
double distance = mGooglePlex.distance(east100m);
assertThat(mGooglePlex.lat).isEqualTo(east100m.lat);
assertEqualsWithinPrecision(100, distance);
}
@Test
public void testAddNorth() {
LatLng north120m = addSouth(mGooglePlex, -120);
double distance = mGooglePlex.distance(north120m);
assertThat(mGooglePlex.lng).isEqualTo(north120m.lng);
assertEqualsWithinPrecision(120, distance);
}
@Test
public void testExistingLatLngConversionToPoint() {
{
Circle circle = new Circle(new LatLng(37.42331, -122.08636), 500);
// ~ 622 meters according to mapping utility
LatLng ll622 =
new LatLng(37.41807, -122.08389);
assertThat(circle.contains(ll622)).isFalse();
assertEqualsWithinPrecision(622.0, ll622.distance(circle.getCenter()));
LatLng origin = circle.getCenter();
CbGeoUtils.Point ptCenter = convert(origin, circle.getCenter());
CbGeoUtils.Point pt622 = convert(origin, ll622);
assertEqualsWithinPrecision(622.0, ptCenter.distance(pt622));
}
{
LatLng alaska = new LatLng(61.219005, -149.899929);
LatLng llSouth100 = addSouth(alaska, 100);
assertEqualsWithinPrecision(100,
convert(alaska, llSouth100).distance(convert(alaska, alaska)));
LatLng llEast100 = addSouth(alaska, 100);
assertEqualsWithinPrecision(100,
convert(alaska, llEast100).distance(convert(alaska, alaska)));
LatLng llSouthEast100 = addSouth(addEast(alaska, 100), 100);
assertEqualsWithinPrecision(141.42,
convert(alaska, llSouthEast100).distance(convert(alaska, alaska)));
}
}
@Test
public void testDistanceFromSegmentToPerpendicularPoint() {
CbGeoUtils.LineSegment seg =
new CbGeoUtils.LineSegment(
newPoint(0.0, 0.0), newPoint(0.0, 100.0));
double dX = seg.distance(newPoint(50.0, 50));
assertEqualsWithinPrecision(50.0, dX);
double dY = seg.distance(newPoint(0.0, 200.0));
assertEqualsWithinPrecision(100.0, dY);
}
@Test
public void testDistanceFromSegmentToAngledPoint() {
{
CbGeoUtils.LineSegment seg =
new CbGeoUtils.LineSegment(newPoint(0.0, 0.0),
newPoint(0.0, 100.0));
double hypTriangleOf50 = Math.sqrt(Math.pow(50, 2) + Math.pow(50, 2));
double upperLeftDistanceSegment = seg.distance(newPoint(-50.0, -50.0));
assertEqualsWithinPrecision(hypTriangleOf50, upperLeftDistanceSegment);
double bottomRightDistanceSegment = seg.distance(newPoint(50.0, 150));
assertEqualsWithinPrecision(hypTriangleOf50, bottomRightDistanceSegment);
}
{
//Test segment starting with latlng
LatLng llWestNorth = mGooglePlex;
LatLng llWestSouth = addSouth(mGooglePlex, 500);
LatLng origin = llWestNorth;
CbGeoUtils.Point ptWestNorth = convert(origin, llWestNorth);
CbGeoUtils.Point ptWestSouth = convert(origin, llWestSouth);
double distancePoints = ptWestNorth.distance(ptWestSouth);
assertEqualsWithinPrecision(500, distancePoints);
}
}
@Test
public void testDistanceWithSquareToPoint() {
LatLng llWestNorth = addSouth(addEast(mGooglePlex, -100), -100);
LatLng llWestSouth = addSouth(addEast(mGooglePlex, -100), 100);
LatLng llEastSouth = addSouth(addEast(mGooglePlex, 100), 100);
LatLng llEastNorth = addSouth(addEast(mGooglePlex, 100), -100);
CbGeoUtils.DistancePolygon square = createPolygon(
llWestNorth, llWestSouth, llEastSouth, llEastNorth);
{
LatLng llDueEast = addEast(mGooglePlex, 200);
double distance = square.distance(llDueEast);
assertEqualsWithinPrecision(100, distance);
}
{
LatLng llDueEastNorth = addSouth(addEast(mGooglePlex, 200), -350);
double llDistance = square.distance(llDueEastNorth);
assertEqualsWithinPrecision(269, llDistance);
square.distance(llDueEastNorth);
}
}
private CbGeoUtils.DistancePolygon createPolygon(LatLng... latLngs) {
return new CbGeoUtils.DistancePolygon(
new android.telephony.CbGeoUtils.Polygon(
Arrays.stream(latLngs).collect(Collectors.toList())));
}
@NonNull
LatLng addEast(LatLng latLng, double meters) {
double offset = scaleMeters(meters)
/ Math.cos(Math.toRadians(latLng.lat));
return new LatLng(latLng.lat, latLng.lng + offset);
}
@NonNull
LatLng addSouth(LatLng latLng, double meters) {
return new LatLng(latLng.lat + scaleMeters(meters),
latLng.lng);
}
private static final int EARTH_RADIUS_METER = 6371 * 1000;
static double scaleMeters(double meters) {
return (meters / EARTH_RADIUS_METER) * (180 / Math.PI);
}
private CbGeoUtils.Point newPoint(double x, double y) {
return new CbGeoUtils.Point(x, y);
}
public void assertEqualsWithinPrecision(double expected, double actual) {
double precision = expected * ASSERT_EQUALS_PRECISION;
if (Math.abs(expected - actual) >= precision) {
String message = "assertEqualsWithinPrecision FAILED!\n"
+ "Expected: " + expected + ", but Actual is: " + actual + "\n";
throw new AssertionFailedError(message);
}
}
private CbGeoUtils.Point convert(LatLng origin, LatLng latLng) {
return CbGeoUtils.convertToDistanceFromOrigin(origin, latLng);
}
}