001 /*
002 * Copyright 2006 Marc Wick, geonames.org
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 */
017 package org.geonames.utils;
018
019 /**
020 * Distance calculations.
021 *
022 * @author marc@geonames
023 *
024 */
025 public class Distance {
026
027 /**
028 * mean radius = 6372.0
029 *
030 * The Earth's equatorial radius = 6335.437 km.
031 *
032 * The Earth's polar radius = 6399.592 km.
033 *
034 *
035 */
036 public static final double EARTH_RADIUS_KM = 6372.0;
037
038 /**
039 * statute miles
040 */
041 public static final double EARTH_RADIUS_MILES = 3963.0;
042
043 /**
044 * http://mathworld.wolfram.com/GreatCircle.html
045 *
046 * and
047 *
048 * http://www.mathforum.com/library/drmath/view/51711.html
049 *
050 * @return
051 */
052 public static double distance(double lat1, double lng1, double lat2,
053 double lng2, char unit, int numberOfDigits) {
054 double a1 = Math.toRadians(lat1);
055 double b1 = Math.toRadians(lng1);
056 double a2 = Math.toRadians(lat2);
057 double b2 = Math.toRadians(lng2);
058 double d = Math.acos(Math.cos(a1) * Math.cos(b1) * Math.cos(a2)
059 * Math.cos(b2) + Math.cos(a1) * Math.sin(b1) * Math.cos(a2)
060 * Math.sin(b2) + Math.sin(a1) * Math.sin(a2));
061
062 double dist = 0;
063 if (unit == 'M') {
064 dist = d * EARTH_RADIUS_MILES;
065 } else {
066 dist = d * EARTH_RADIUS_KM;
067 }
068
069 if (Double.isNaN(dist)) {
070 // use pytagoras for very small distances,
071 dist = Math.sqrt(Math.pow(Math.abs(lat1 - lat2), 2)
072 + Math.pow(Math.abs(lng1 - lng2), 2));
073 // as rule of thumb multiply with 110km =1 degree
074 if (unit == 'M') {
075 dist *= 69;
076 } else {
077 dist *= 110;
078 }
079 }
080
081 if (numberOfDigits == 0) {
082 dist = (int) dist;
083 } else if (numberOfDigits > 0) {
084 double factor = Math.pow(10, numberOfDigits);
085 dist = Math.floor(dist * factor) / factor;
086 }
087 return dist;
088 }
089
090 public static double distanceKM(double lat1, double lng1, double lat2,
091 double lng2) {
092 return distance(lat1, lng1, lat2, lng2, 'K', 3);
093 }
094
095 public static double distanceMiles(double lat1, double lng1, double lat2,
096 double lng2) {
097 return distance(lat1, lng1, lat2, lng2, 'M', 3);
098 }
099 }