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 */
017package org.geonames.utils;
018
019/**
020 * Distance calculations.
021 * 
022 * @author marc@geonames
023 * 
024 */
025public 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}