Calculate distance between 2 GPS coordinates

前端 未结 29 3494
青春惊慌失措
青春惊慌失措 2020-11-21 23:34

How do I calculate distance between two GPS coordinates (using latitude and longitude)?

相关标签:
29条回答
  • 2020-11-22 00:06

    Here's a Kotlin variation:

    import kotlin.math.*
    
    class HaversineAlgorithm {
    
        companion object {
            private const val MEAN_EARTH_RADIUS = 6371.0
            private const val D2R = Math.PI / 180.0
        }
    
        private fun haversineInKm(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
            val lonDiff = (lon2 - lon1) * D2R
            val latDiff = (lat2 - lat1) * D2R
            val latSin = sin(latDiff / 2.0)
            val lonSin = sin(lonDiff / 2.0)
            val a = latSin * latSin + (cos(lat1 * D2R) * cos(lat2 * D2R) * lonSin * lonSin)
            val c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a))
            return EQATORIAL_EARTH_RADIUS * c
        }
    }
    
    0 讨论(0)
  • 2020-11-22 00:07

    I. Regarding "Breadcrumbs" method

    1. Earth radius is different on different Lat. This must be taken into consideration in Haversine algorithm.
    2. Consider Bearing change, which turns straight lines to arches (which are longer)
    3. Taking Speed change into account will turn arches to spirals (which are longer or shorter than arches)
    4. Altitude change will turn flat spirals to 3D spirals (which are longer again). This is very important for hilly areas.

    Below see the function in C which takes #1 and #2 into account:

    double   calcDistanceByHaversine(double rLat1, double rLon1, double rHeading1,
           double rLat2, double rLon2, double rHeading2){
      double rDLatRad = 0.0;
      double rDLonRad = 0.0;
      double rLat1Rad = 0.0;
      double rLat2Rad = 0.0;
      double a = 0.0;
      double c = 0.0;
      double rResult = 0.0;
      double rEarthRadius = 0.0;
      double rDHeading = 0.0;
      double rDHeadingRad = 0.0;
    
      if ((rLat1 < -90.0) || (rLat1 > 90.0) || (rLat2 < -90.0) || (rLat2 > 90.0)
                  || (rLon1 < -180.0) || (rLon1 > 180.0) || (rLon2 < -180.0)
                  || (rLon2 > 180.0)) {
            return -1;
      };
    
      rDLatRad = (rLat2 - rLat1) * DEGREE_TO_RADIANS;
      rDLonRad = (rLon2 - rLon1) * DEGREE_TO_RADIANS;
      rLat1Rad = rLat1 * DEGREE_TO_RADIANS;
      rLat2Rad = rLat2 * DEGREE_TO_RADIANS;
    
      a = sin(rDLatRad / 2) * sin(rDLatRad / 2) + sin(rDLonRad / 2) * sin(
                  rDLonRad / 2) * cos(rLat1Rad) * cos(rLat2Rad);
    
      if (a == 0.0) {
            return 0.0;
      }
    
      c = 2 * atan2(sqrt(a), sqrt(1 - a));
      rEarthRadius = 6378.1370 - (21.3847 * 90.0 / ((fabs(rLat1) + fabs(rLat2))
                  / 2.0));
      rResult = rEarthRadius * c;
    
      // Chord to Arc Correction based on Heading changes. Important for routes with many turns and U-turns
    
      if ((rHeading1 >= 0.0) && (rHeading1 < 360.0) && (rHeading2 >= 0.0)
                  && (rHeading2 < 360.0)) {
            rDHeading = fabs(rHeading1 - rHeading2);
            if (rDHeading > 180.0) {
                  rDHeading -= 180.0;
            }
            rDHeadingRad = rDHeading * DEGREE_TO_RADIANS;
            if (rDHeading > 5.0) {
                  rResult = rResult * (rDHeadingRad / (2.0 * sin(rDHeadingRad / 2)));
            } else {
                  rResult = rResult / cos(rDHeadingRad);
            }
      }
      return rResult;
    }
    

    II. There is an easier way which gives pretty good results.

    By Average Speed.

    Trip_distance = Trip_average_speed * Trip_time

    Since GPS Speed is detected by Doppler effect and is not directly related to [Lon,Lat] it can be at least considered as secondary (backup or correction) if not as main distance calculation method.

    0 讨论(0)
  • 2020-11-22 00:08

    Here's a Haversine function in Python that I use:

    from math import pi,sqrt,sin,cos,atan2
    
    def haversine(pos1, pos2):
        lat1 = float(pos1['lat'])
        long1 = float(pos1['long'])
        lat2 = float(pos2['lat'])
        long2 = float(pos2['long'])
    
        degree_to_rad = float(pi / 180.0)
    
        d_lat = (lat2 - lat1) * degree_to_rad
        d_long = (long2 - long1) * degree_to_rad
    
        a = pow(sin(d_lat / 2), 2) + cos(lat1 * degree_to_rad) * cos(lat2 * degree_to_rad) * pow(sin(d_long / 2), 2)
        c = 2 * atan2(sqrt(a), sqrt(1 - a))
        km = 6367 * c
        mi = 3956 * c
    
        return {"km":km, "miles":mi}
    
    0 讨论(0)
  • 2020-11-22 00:08
        private double deg2rad(double deg)
        {
            return (deg * Math.PI / 180.0);
        }
    
        private double rad2deg(double rad)
        {
            return (rad / Math.PI * 180.0);
        }
    
        private double GetDistance(double lat1, double lon1, double lat2, double lon2)
        {
            //code for Distance in Kilo Meter
            double theta = lon1 - lon2;
            double dist = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) + Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(deg2rad(theta));
            dist = Math.Abs(Math.Round(rad2deg(Math.Acos(dist)) * 60 * 1.1515 * 1.609344 * 1000, 0));
            return (dist);
        }
    
        private double GetDirection(double lat1, double lon1, double lat2, double lon2)
        {
            //code for Direction in Degrees
            double dlat = deg2rad(lat1) - deg2rad(lat2);
            double dlon = deg2rad(lon1) - deg2rad(lon2);
            double y = Math.Sin(dlon) * Math.Cos(lat2);
            double x = Math.Cos(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) - Math.Sin(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(dlon);
            double direct = Math.Round(rad2deg(Math.Atan2(y, x)), 0);
            if (direct < 0)
                direct = direct + 360;
            return (direct);
        }
    
        private double GetSpeed(double lat1, double lon1, double lat2, double lon2, DateTime CurTime, DateTime PrevTime)
        {
            //code for speed in Kilo Meter/Hour
            TimeSpan TimeDifference = CurTime.Subtract(PrevTime);
            double TimeDifferenceInSeconds = Math.Round(TimeDifference.TotalSeconds, 0);
            double theta = lon1 - lon2;
            double dist = Math.Sin(deg2rad(lat1)) * Math.Sin(deg2rad(lat2)) + Math.Cos(deg2rad(lat1)) * Math.Cos(deg2rad(lat2)) * Math.Cos(deg2rad(theta));
            dist = rad2deg(Math.Acos(dist)) * 60 * 1.1515 * 1.609344;
            double Speed = Math.Abs(Math.Round((dist / Math.Abs(TimeDifferenceInSeconds)) * 60 * 60, 0));
            return (Speed);
        }
    
        private double GetDuration(DateTime CurTime, DateTime PrevTime)
        {
            //code for speed in Kilo Meter/Hour
            TimeSpan TimeDifference = CurTime.Subtract(PrevTime);
            double TimeDifferenceInSeconds = Math.Abs(Math.Round(TimeDifference.TotalSeconds, 0));
            return (TimeDifferenceInSeconds);
        }
    
    0 讨论(0)
  • 2020-11-22 00:09

    This is very easy to do with geography type in SQL Server 2008.

    SELECT geography::Point(lat1, lon1, 4326).STDistance(geography::Point(lat2, lon2, 4326))
    -- computes distance in meters using eliptical model, accurate to the mm
    

    4326 is SRID for WGS84 elipsoidal Earth model

    0 讨论(0)
  • 2020-11-22 00:09

    A T-SQL function, that I use to select records by distance for a center

    Create Function  [dbo].[DistanceInMiles] 
     (  @fromLatitude float ,
        @fromLongitude float ,
        @toLatitude float, 
        @toLongitude float
      )
       returns float
    AS 
    BEGIN
    declare @distance float
    
    select @distance = cast((3963 * ACOS(round(COS(RADIANS(90-@fromLatitude))*COS(RADIANS(90-@toLatitude))+ 
    SIN(RADIANS(90-@fromLatitude))*SIN(RADIANS(90-@toLatitude))*COS(RADIANS(@fromLongitude-@toLongitude)),15)) 
    )as float) 
      return  round(@distance,1)
    END
    
    0 讨论(0)
提交回复
热议问题