/* * 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); } }