/*
* Copyright (C) 2013 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.google.android.glass.sample.compass.util;
/**
* A utility class containing arithmetic and geometry helper methods.
*/
public class MathUtils {
/** The number of half winds for boxing the compass. */
private static final int NUMBER_OF_HALF_WINDS = 16;
/** The Earth's radius, in kilometers. */
private static final double EARTH_RADIUS_KM = 6371.0;
/**
* Calculates {@code a mod b} in a way that respects negative values (for example,
* {@code mod(-1, 5) == 4}, rather than {@code -1}).
*
* @param a the dividend
* @param b the divisor
* @return {@code a mod b}
*/
public static int mod(int a, int b) {
return (a % b + b) % b;
}
/**
* Calculates {@code a mod b} in a way that respects negative values (for example,
* {@code mod(-1, 5) == 4}, rather than {@code -1}).
*
* @param a the dividend
* @param b the divisor
* @return {@code a mod b}
*/
public static float mod(float a, float b) {
return (a % b + b) % b;
}
/**
* Converts the specified heading angle into an index between 0-15 that can be used to retrieve
* the direction name for that heading (known as "boxing the compass", down to the half-wind
* level).
*
* @param heading the heading angle
* @return the index of the direction name for the angle
*/
public static int getHalfWindIndex(float heading) {
float partitionSize = 360.0f / NUMBER_OF_HALF_WINDS;
float displacedHeading = MathUtils.mod(heading + partitionSize / 2, 360.0f);
return (int) (displacedHeading / partitionSize);
}
/**
* Gets the relative bearing from one geographical coordinate to another.
*
* @param latitude1 the latitude of the source point
* @param longitude1 the longitude of the source point
* @param latitude2 the latitude of the destination point
* @param longitude2 the longitude of the destination point
* @return the relative bearing from point 1 to point 2, in degrees. The result is guaranteed
* to fall in the range 0-360
*/
public static float getBearing(double latitude1, double longitude1, double latitude2,
double longitude2) {
latitude1 = Math.toRadians(latitude1);
longitude1 = Math.toRadians(longitude1);
latitude2 = Math.toRadians(latitude2);
longitude2 = Math.toRadians(longitude2);
double dLon = longitude2 - longitude1;
double y = Math.sin(dLon) * Math.cos(latitude2);
double x = Math.cos(latitude1) * Math.sin(latitude2) - Math.sin(latitude1)
* Math.cos(latitude2) * Math.cos(dLon);
double bearing = Math.atan2(y, x);
return mod((float) Math.toDegrees(bearing), 360.0f);
}
/**
* Gets the great circle distance in kilometers between two geographical points, using
* the <a href="http://en.wikipedia.org/wiki/Haversine_formula">haversine formula</a>.
*
* @param latitude1 the latitude of the first point
* @param longitude1 the longitude of the first point
* @param latitude2 the latitude of the second point
* @param longitude2 the longitude of the second point
* @return the distance, in kilometers, between the two points
*/
public static float getDistance(double latitude1, double longitude1, double latitude2,
double longitude2) {
double dLat = Math.toRadians(latitude2 - latitude1);
double dLon = Math.toRadians(longitude2 - longitude1);
double lat1 = Math.toRadians(latitude1);
double lat2 = Math.toRadians(latitude2);
double sqrtHaversineLat = Math.sin(dLat / 2);
double sqrtHaversineLon = Math.sin(dLon / 2);
double a = sqrtHaversineLat * sqrtHaversineLat + sqrtHaversineLon * sqrtHaversineLon
* Math.cos(lat1) * Math.cos(lat2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return (float) (EARTH_RADIUS_KM * c);
}
}