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    }