Great Circle Distance question

前端 未结 4 2158
误落风尘
误落风尘 2020-12-14 12:18

I am familiar with the formula to calculate the Great Circle Distance between two points.

i.e.



        
相关标签:
4条回答
  • 2020-12-14 12:47

    See the section Destination point given distance and bearing from start point at this website: http://www.movable-type.co.uk/scripts/latlong.html

    0 讨论(0)
  • 2020-12-14 12:54

    It would be harder to back out the Haversine fomula, then generate your own, I would think.

    First the angle generated from the Earth core by traveling a "straight" line on the surface (you think it is straight, but it is curving).

    Angle in Radians = Arc Length / radius. Angle = ArcLen/6371 km

    Latitude should be easy, just the "vertical" (north/south) component of your angle.

    Lat1 + Cos(bearing) * Angle

    Longitude divisions vary by latitude. So that becomes harder. You would use:

    Sin(bearing) * Angle (with East defined as negative) to find the angle in longitude direction, but converting back to actual longitude at that latitude would be more difficult.

    0 讨论(0)
  • 2020-12-14 12:58

    To answer my own question just so it's here for anyone curious, a PHP class as converted from the C function provided by Chad Birch:

    class GreatCircle 
    {
    
        /*
         * Find a point a certain distance and vector away from an initial point
         * converted from c function found at: http://sam.ucsd.edu/sio210/propseawater/ppsw_c/gcdist.c
         * 
         * @param int distance in meters
         * @param double direction in degrees i.e. 0 = North, 90 = East, etc.
         * @param double lon starting longitude
         * @param double lat starting latitude
         * @return array ('lon' => $lon, 'lat' => $lat)
         */
        public static function getPositionByDistance($distance, $direction, $lon, $lat)
        {
            $metersPerDegree = 111120.00071117;
            $degreesPerMeter = 1.0 / $metersPerDegree;
            $radiansPerDegree = pi() / 180.0;
            $degreesPerRadian = 180.0 / pi();
    
            if ($distance > $metersPerDegree*180)
            {
                $direction -= 180.0;
                if ($direction < 0.0)
                {
                    $direction += 360.0;
                }
                $distance = $metersPerDegree * 360.0 - $distance;
            }
    
            if ($direction > 180.0)
            {
                $direction -= 360.0;
            }
    
            $c = $direction * $radiansPerDegree;
            $d = $distance * $degreesPerMeter * $radiansPerDegree;
            $L1 = $lat * $radiansPerDegree;
            $lon *= $radiansPerDegree;
            $coL1 = (90.0 - $lat) * $radiansPerDegree;
            $coL2 = self::ahav(self::hav($c) / (self::sec($L1) * self::csc($d)) + self::hav($d - $coL1));
            $L2   = (pi() / 2) - $coL2;
            $l    = $L2 - $L1;
    
            $dLo = (cos($L1) * cos($L2));
            if ($dLo != 0.0)
            {
                $dLo  = self::ahav((self::hav($d) - self::hav($l)) / $dLo);
            }
    
            if ($c < 0.0) 
            {
                $dLo = -$dLo;
            }
    
            $lon += $dLo;
            if ($lon < -pi())
            {
                $lon += 2 * pi();
            }
            elseif ($lon > pi())
            {
                $lon -= 2 * pi();
            }
    
            $xlat = $L2 * $degreesPerRadian;
            $xlon = $lon * $degreesPerRadian;
    
            return array('lon' => $xlon, 'lat' => $xlat);
        }
    
    
        /*
         * copy the sign
         */
        private static function copysign($x, $y)
        {
            return ((($y) < 0.0) ? - abs($x) : abs($x));
        }   
    
        /*
         * not greater than 1
         */
        private static function ngt1($x)
        {
            return (abs($x) > 1.0 ? self::copysign(1.0 , $x) : ($x));
        }   
    
        /*
         * haversine
         */
        private static function hav($x)
        {
            return ((1.0 - cos($x)) * 0.5);
        }
    
        /*
         * arc haversine
         */
        private static function ahav($x)
        {
            return acos(self::ngt1(1.0 - ($x * 2.0)));
        }
    
        /*
         * secant
         */
        private static function sec($x)
        {
            return (1.0 / cos($x));
        }
    
        /*
         * cosecant
         */
        private static function csc($x)
        {
            return (1.0 / sin($x));
        }
    
    }
    
    0 讨论(0)
  • 2020-12-14 12:59

    Here's a C implementation that I found, should be fairly straightforward to translate to PHP:

    #define KmPerDegree         111.12000071117
    #define DegreesPerKm        (1.0/KmPerDegree)
    #define PI                  M_PI
    #define TwoPI               (M_PI+M_PI)
    #define HalfPI              M_PI_2
    #define RadiansPerDegree    (PI/180.0)
    #define DegreesPerRadian    (180.0/PI)
    #define copysign(x,y)       (((y)<0.0)?-fabs(x):fabs(x))
    #define NGT1(x)             (fabs(x)>1.0?copysign(1.0,x):(x))
    #define ArcCos(x)           (fabs(x)>1?quiet_nan():acos(x))
    #define hav(x)              ((1.0-cos(x))*0.5)              /* haversine */
    #define ahav(x)             (ArcCos(NGT1(1.0-((x)*2.0))))   /* arc haversine */
    #define sec(x)              (1.0/cos(x))                    /* secant */
    #define csc(x)              (1.0/sin(x))                    /* cosecant */
    
    /*
    **  GreatCirclePos() --
    **
    **  Compute ending position from course and great-circle distance.
    **
    **  Given a starting latitude (decimal), the initial great-circle
    **  course and a distance along the course track, compute the ending
    **  position (decimal latitude and longitude).
    **  This is the inverse function to GreatCircleDist).
    */
    void
    GreatCirclePos(dist, course, slt, slg, xlt, xlg)
        double  dist;   /* -> great-circle distance (km) */
        double  course; /* -> initial great-circle course (degrees) */
        double  slt;    /* -> starting decimal latitude (-S) */
        double  slg;    /* -> starting decimal longitude(-W) */
        double  *xlt;   /* <- ending decimal latitude (-S) */
        double  *xlg;   /* <- ending decimal longitude(-W) */
    {
        double  c, d, dLo, L1, L2, coL1, coL2, l;
    
        if (dist > KmPerDegree*180.0) {
            course -= 180.0;
            if (course < 0.0) course += 360.0;
            dist    = KmPerDegree*360.0-dist;
        }
        if (course > 180.0) course -= 360.0;
        c    = course*RadiansPerDegree;
        d    = dist*DegreesPerKm*RadiansPerDegree;
        L1   = slt*RadiansPerDegree;
        slg *= RadiansPerDegree;
        coL1 = (90.0-slt)*RadiansPerDegree;
        coL2 = ahav(hav(c)/(sec(L1)*csc(d))+hav(d-coL1));
        L2   = HalfPI-coL2;
        l    = L2-L1;
        if ((dLo=(cos(L1)*cos(L2))) != 0.0)
            dLo  = ahav((hav(d)-hav(l))/dLo);
        if (c < 0.0) dLo = -dLo;
        slg += dLo;
        if (slg < -PI)
            slg += TwoPI;
        else if (slg > PI)
            slg -= TwoPI;
    
        *xlt = L2*DegreesPerRadian;
        *xlg = slg*DegreesPerRadian;
    
    } /* GreatCirclePos() */
    

    Source: http://sam.ucsd.edu/sio210/propseawater/ppsw_c/gcdist.c

    0 讨论(0)
提交回复
热议问题