PHP में दो निर्देशांक के बीच की दूरी को मापना


145

नमस्ते मुझे दो बिंदुओं के बीच की दूरी की गणना करने की आवश्यकता है, जो लंबे और लंबे हैं।

मैं बाहरी एपीआई के लिए किसी भी कॉल से बचना चाहूंगा।

मैंने PHP में हैवरसाइन फॉर्मूला लागू करने की कोशिश की:

यहाँ कोड है:

class CoordDistance
 {
    public $lat_a = 0;
    public $lon_a = 0;
    public $lat_b = 0;
    public $lon_b = 0;

    public $measure_unit = 'kilometers';

    public $measure_state = false;

    public $measure = 0;

    public $error = '';



    public function DistAB()

      {
          $delta_lat = $this->lat_b - $this->lat_a ;
          $delta_lon = $this->lon_b - $this->lon_a ;

          $earth_radius = 6372.795477598;

          $alpha    = $delta_lat/2;
          $beta     = $delta_lon/2;
          $a        = sin(deg2rad($alpha)) * sin(deg2rad($alpha)) + cos(deg2rad($this->lat_a)) * cos(deg2rad($this->lat_b)) * sin(deg2rad($beta)) * sin(deg2rad($beta)) ;
          $c        = asin(min(1, sqrt($a)));
          $distance = 2*$earth_radius * $c;
          $distance = round($distance, 4);

          $this->measure = $distance;

      }
    }

कुछ दिए गए बिंदुओं के साथ इसका परीक्षण करना, जिनके पास सार्वजनिक दूरी है, मुझे एक विश्वसनीय परिणाम नहीं मिलता है।

मुझे समझ नहीं आता कि मूल सूत्र में त्रुटि है या मेरे कार्यान्वयन में


जवाबों:


273

कुछ समय पहले मैंने हैवरसाइन फॉर्मूला का एक उदाहरण लिखा था, और इसे अपनी वेबसाइट पर प्रकाशित किया था:

/**
 * Calculates the great-circle distance between two points, with
 * the Haversine formula.
 * @param float $latitudeFrom Latitude of start point in [deg decimal]
 * @param float $longitudeFrom Longitude of start point in [deg decimal]
 * @param float $latitudeTo Latitude of target point in [deg decimal]
 * @param float $longitudeTo Longitude of target point in [deg decimal]
 * @param float $earthRadius Mean earth radius in [m]
 * @return float Distance between points in [m] (same as earthRadius)
 */
function haversineGreatCircleDistance(
  $latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000)
{
  // convert from degrees to radians
  $latFrom = deg2rad($latitudeFrom);
  $lonFrom = deg2rad($longitudeFrom);
  $latTo = deg2rad($latitudeTo);
  $lonTo = deg2rad($longitudeTo);

  $latDelta = $latTo - $latFrom;
  $lonDelta = $lonTo - $lonFrom;

  $angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) +
    cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
  return $angle * $earthRadius;
}

➽ ध्यान दें कि आप पैरामीटर के साथ पास होने पर उसी इकाई में दूरी प्राप्त करते हैं $earthRadius। डिफ़ॉल्ट मान 6371000 मीटर है, इसलिए परिणाम [एम] में भी होगा। मील में परिणाम प्राप्त करने के लिए, आप उदाहरण के लिए 3959 मील पास कर सकते हैं$earthRadius और परिणाम [mi] में होगा। मेरी राय में, एसआई इकाइयों के साथ चिपकना एक अच्छी आदत है, अगर ऐसा करने का कोई विशेष कारण नहीं है।

संपादित करें:

के रूप में TreyA सही ढंग से कहा, Haversine फार्मूले के साथ कमजोरियों है प्रतिमुख अंक क्योंकि त्रुटियों गोलाई (हालांकि यह की है छोटी दूरी के लिए स्थिर)। उनके चारों ओर जाने के लिए, आप इसके बजाय विन्सेन्टी सूत्र का उपयोग कर सकते हैं ।

/**
 * Calculates the great-circle distance between two points, with
 * the Vincenty formula.
 * @param float $latitudeFrom Latitude of start point in [deg decimal]
 * @param float $longitudeFrom Longitude of start point in [deg decimal]
 * @param float $latitudeTo Latitude of target point in [deg decimal]
 * @param float $longitudeTo Longitude of target point in [deg decimal]
 * @param float $earthRadius Mean earth radius in [m]
 * @return float Distance between points in [m] (same as earthRadius)
 */
public static function vincentyGreatCircleDistance(
  $latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371000)
{
  // convert from degrees to radians
  $latFrom = deg2rad($latitudeFrom);
  $lonFrom = deg2rad($longitudeFrom);
  $latTo = deg2rad($latitudeTo);
  $lonTo = deg2rad($longitudeTo);

  $lonDelta = $lonTo - $lonFrom;
  $a = pow(cos($latTo) * sin($lonDelta), 2) +
    pow(cos($latFrom) * sin($latTo) - sin($latFrom) * cos($latTo) * cos($lonDelta), 2);
  $b = sin($latFrom) * sin($latTo) + cos($latFrom) * cos($latTo) * cos($lonDelta);

  $angle = atan2(sqrt($a), $b);
  return $angle * $earthRadius;
}

1
@ ट्रे - संभव विभिन्न संस्करण हैं, यह संस्करण विकिपीडिया में सूत्र को लागू करता है और अच्छी तरह से परखा जाता है। $ कोण का अर्थ है, रेडियन में दुनिया के मध्य का कोण, इसलिए कोई इसे पृथ्वी की त्रिज्या से गुणा कर सकता है। अगर कोई दिलचस्पी रखता है तो मैं और अधिक जटिल विंसेंटी फॉर्मूला का उदाहरण भी दे सकता हूं।
मार्टिंकिकोली

@ ट्रेया - हां मुझे पता है, मुझे यकीन नहीं है कि आप इसके साथ क्या कहना चाहते हैं। क्या आपने फ़ंक्शन का परीक्षण किया है और क्या उसने गलत परिणाम की गणना की है? और क्या आपने विकिपीडिया में सूत्र को देखा है? आपको वास्तव में अपना खुद का एक परीक्षण करना चाहिए और मुझे एक उदाहरण देना चाहिए कि आपको क्या लगता है कि गलत गणना की गई है।
मार्टिंस्टेकली

क्षमा करें, लेकिन मुझे अभी कुछ चीजें समझानी हैं। 1) प्रश्न हेवेरसिन फार्मूले के बारे में था, यदि आप किसी अन्य सूत्र का उपयोग करने का सुझाव देते हैं, तो आपको हमें बताना चाहिए। 2) Haversine सूत्र ध्रुवों के आसपास कमजोरियों है, लेकिन है छोटे (यह कोटिकोज्या सूत्र का एक समस्या है) दूरी के लिए सही। 3) आपने कहा कि गणना किए गए $ कोण के साथ एक कदम गायब है, यह केवल गलत है, यह परिणाम में सुधार नहीं कर सकता है, कृपया इसे बाहर का परीक्षण करें! 4) मैं सहमत हूं कि स्थिर विन्सेन्टी सूत्र का उपयोग करना बेहतर होगा, मैंने पहले ही एक उदाहरण देने की पेशकश की थी। शायद आप एक उत्तर भी लिख सकते हैं?
मार्टिनगॉली

@martinstoekli - आप सही हैं, आपके पास अपने हैवरसाइन फॉर्मूले में एक कदम नहीं है। मैंने अपनी टिप्पणियों को भविष्य के पाठकों को भ्रमित नहीं करने के लिए हटा दिया।
ट्रेया

1
@PratikCJoshi - अंत में विभिन्न इकाइयों का उपयोग करने के बारे में एक नोट जोड़ने का समय मिला।
martinstoeckli

63

मुझे यह कोड मिला जो मुझे विश्वसनीय परिणाम दे रहा है।

function distance($lat1, $lon1, $lat2, $lon2, $unit) {

  $theta = $lon1 - $lon2;
  $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +  cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
  $dist = acos($dist);
  $dist = rad2deg($dist);
  $miles = $dist * 60 * 1.1515;
  $unit = strtoupper($unit);

  if ($unit == "K") {
      return ($miles * 1.609344);
  } else if ($unit == "N") {
      return ($miles * 0.8684);
  } else {
      return $miles;
  }
}

परिणाम:

echo distance(32.9697, -96.80322, 29.46786, -98.53506, "M") . " Miles<br>";
echo distance(32.9697, -96.80322, 29.46786, -98.53506, "K") . " Kilometers<br>";
echo distance(32.9697, -96.80322, 29.46786, -98.53506, "N") . " Nautical Miles<br>";

2
महान सामान, मैंने यह कोशिश की और यह भी कि गूगल मैप्स दूरी को केवल यहां और वहां के दशमलव परिवर्तनों को
दिखाते हैं

क्या होगा यदि आप तीन बिंदुओं के बीच की दूरी की गणना करना चाहते हैं?
kexxcream

3
इस फ़ंक्शन को दो बार कॉल करें और उन्हें जोड़ दें, बारी-बारी से आप अपने अनुसार फ़ंक्शन बदलते हैं
जेनिथ चिंताना

कुछ परिस्थितियों में NaN रिटर्न stackoverflow.com/questions/37184259/...
Zahur श्री

23

यह @martinstoeckli और @Janith Chinthana के जवाबों के अलावा है। उन लोगों के लिए जो उत्सुक हैं कि कौन सा एल्गोरिथम सबसे तेज़ है, मैंने प्रदर्शन परीक्षण लिखा । सर्वश्रेष्ठ प्रदर्शन परिणाम codexworld.com से अनुकूलित कार्य दिखाता है :

/**
 * Optimized algorithm from http://www.codexworld.com
 *
 * @param float $latitudeFrom
 * @param float $longitudeFrom
 * @param float $latitudeTo
 * @param float $longitudeTo
 *
 * @return float [km]
 */
function codexworldGetDistanceOpt($latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo)
{
    $rad = M_PI / 180;
    //Calculate distance from latitude and longitude
    $theta = $longitudeFrom - $longitudeTo;
    $dist = sin($latitudeFrom * $rad) 
        * sin($latitudeTo * $rad) +  cos($latitudeFrom * $rad)
        * cos($latitudeTo * $rad) * cos($theta * $rad);

    return acos($dist) / $rad * 60 *  1.853;
}

यहाँ परीक्षा परिणाम है:

Test name       Repeats         Result          Performance     
codexworld-opt  10000           0.084952 sec    +0.00%
codexworld      10000           0.104127 sec    -22.57%
custom          10000           0.107419 sec    -26.45%
custom2         10000           0.111576 sec    -31.34%
custom1         10000           0.136691 sec    -60.90%
vincenty        10000           0.165881 sec    -95.26%

आपके कोड में कोडएक्सवर्ल्ड एल्गोरिदम के लिए गुणक 1.852 है, जबकि वास्तविक मूल 1.1515 है। ऐसा क्यों है? अंतर क्यों?
GotBatteries 11

@GotBatteries मूल mulitplier मील के लिए है। ऑप्टिमाइज़्ड फंक्शन रिटर्न किमी में परिणाम देता है। 1.1515 * 1.609344 = 1.853। धन्यवाद, 1.853 पर तय किया गया।
अलेक्जेंडर यानचुक 13

आप बेहतर प्रदर्शन के लिए स्थिरांक के रूप में M_PI / 180 और $ rad * 60 * 1.853 का उपयोग क्यों नहीं करते?
एवरेन युर्टसेन

@EvrenYurtesen अच्छा विचार है अगर आपकी प्राथमिकता प्रदर्शन है। लेकिन मुझे लगता है कि maintability और पठनीयता और अधिक जटिल हो जाएगा।
अलेक्जेंडर यानचुक

बस पिछली लाइन पर एक टिप्पणी डालें और कहें कि // M_PI / 180 ... आदि। मुझे नहीं पता कि इसे बनाए रखना क्यों मुश्किल होगा। यह ऐसी चीज नहीं है जिसे आप कभी बदलेंगे।
एवरेन युर्टसेन

10

यहाँ दो अक्षांश और देशांतर के बीच की दूरी की गणना के लिए सरल और सही कोड है। निम्नलिखित कोड यहाँ से पाए गए हैं - http://www.codexworld.com/distance-between-two-addresses-google-maps-ap-php/

$latitudeFrom = '22.574864';
$longitudeFrom = '88.437915';

$latitudeTo = '22.568662';
$longitudeTo = '88.431918';

//Calculate distance from latitude and longitude
$theta = $longitudeFrom - $longitudeTo;
$dist = sin(deg2rad($latitudeFrom)) * sin(deg2rad($latitudeTo)) +  cos(deg2rad($latitudeFrom)) * cos(deg2rad($latitudeTo)) * cos(deg2rad($theta));
$dist = acos($dist);
$dist = rad2deg($dist);
$miles = $dist * 60 * 1.1515;

$distance = ($miles * 1.609344).' km';

5

उन लोगों के लिए जो कम और तेज़ पसंद करते हैं (कॉल डाउन 2 ब्रैड () नहीं)।

function circle_distance($lat1, $lon1, $lat2, $lon2) {
  $rad = M_PI / 180;
  return acos(sin($lat2*$rad) * sin($lat1*$rad) + cos($lat2*$rad) * cos($lat1*$rad) * cos($lon2*$rad - $lon1*$rad)) * 6371;// Kilometers
}

2

कोशिश करो यह भयानक परिणाम देता है

function getDistance($point1_lat, $point1_long, $point2_lat, $point2_long, $unit = 'km', $decimals = 2) {
        // Calculate the distance in degrees
        $degrees = rad2deg(acos((sin(deg2rad($point1_lat))*sin(deg2rad($point2_lat))) + (cos(deg2rad($point1_lat))*cos(deg2rad($point2_lat))*cos(deg2rad($point1_long-$point2_long)))));

        // Convert the distance in degrees to the chosen unit (kilometres, miles or nautical miles)
        switch($unit) {
            case 'km':
                $distance = $degrees * 111.13384; // 1 degree = 111.13384 km, based on the average diameter of the Earth (12,735 km)
                break;
            case 'mi':
                $distance = $degrees * 69.05482; // 1 degree = 69.05482 miles, based on the average diameter of the Earth (7,913.1 miles)
                break;
            case 'nmi':
                $distance =  $degrees * 59.97662; // 1 degree = 59.97662 nautic miles, based on the average diameter of the Earth (6,876.3 nautical miles)
        }
        return round($distance, $decimals);
    }

2

पुराना सवाल, लेकिन एक PHP कोड में रुचि रखने वालों के लिए जो Google मैप्स के समान परिणाम देता है, निम्नलिखित कार्य करता है:

/**
 * Computes the distance between two coordinates.
 *
 * Implementation based on reverse engineering of
 * <code>google.maps.geometry.spherical.computeDistanceBetween()</code>.
 *
 * @param float $lat1 Latitude from the first point.
 * @param float $lng1 Longitude from the first point.
 * @param float $lat2 Latitude from the second point.
 * @param float $lng2 Longitude from the second point.
 * @param float $radius (optional) Radius in meters.
 *
 * @return float Distance in meters.
 */
function computeDistance($lat1, $lng1, $lat2, $lng2, $radius = 6378137)
{
    static $x = M_PI / 180;
    $lat1 *= $x; $lng1 *= $x;
    $lat2 *= $x; $lng2 *= $x;
    $distance = 2 * asin(sqrt(pow(sin(($lat1 - $lat2) / 2), 2) + cos($lat1) * cos($lat2) * pow(sin(($lng1 - $lng2) / 2), 2)));

    return $distance * $radius;
}

मैंने विभिन्न निर्देशांक के साथ परीक्षण किया है और यह पूरी तरह से काम करता है।

मुझे लगता है कि यह तेज होना चाहिए तो कुछ विकल्प भी। लेकिन इसका परीक्षण नहीं किया गया।

संकेत: Google मैप्स 6378137 का उपयोग पृथ्वी के दायरे के रूप में करता है। इसलिए अन्य एल्गोरिदम के साथ इसका उपयोग करना भी काम कर सकता है।


1

सटीक मानों के लिए इसे ऐसे करें:

public function DistAB()
{
      $delta_lat = $this->lat_b - $this->lat_a ;
      $delta_lon = $this->lon_b - $this->lon_a ;

      $a = pow(sin($delta_lat/2), 2);
      $a += cos(deg2rad($this->lat_a9)) * cos(deg2rad($this->lat_b9)) * pow(sin(deg2rad($delta_lon/29)), 2);
      $c = 2 * atan2(sqrt($a), sqrt(1-$a));

      $distance = 2 * $earth_radius * $c;
      $distance = round($distance, 4);

      $this->measure = $distance;
}

हम्म मुझे लगता है कि यह करना चाहिए ...

संपादित करें:

फ़ॉर्म्यूलर और कम से कम JS-कार्यान्वयन के लिए प्रयास करें: http://www.movable-type.co.uk/scripts/latlong.html

मुझे हिम्मत दो ... मैं सर्कल-फ़ंक्शंस में सभी मूल्यों को नीचा दिखाना भूल गया ...


आपके उत्तर के लिए धन्यवाद। मैं के बीच एक साधारण गणना के साथ इस कार्यान्वयन देख लिया है pointA (42,12) और pointB (43,12) $ earth_radius = ६३७२.७९५४७७५९८ जब यह 110,94 के आसपास कुछ होना चाहिए मैं परिणाम 12,745.591 के रूप में मिलता है का उपयोग कर
maxdangelo

1

नमस्ते यहाँ कोड के लिए दूरी और समय दो अलग-अलग लट और लंबी का उपयोग करें

$url ="https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=16.538048,80.613266&destinations=23.0225,72.5714";



    $ch = curl_init();
    // Disable SSL verification

    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    // Will return the response, if false it print the response
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    // Set the url
    curl_setopt($ch, CURLOPT_URL,$url);
    // Execute
    $result=curl_exec($ch);
    // Closing
    curl_close($ch);

    $result_array=json_decode($result);
print_r($result_array);

आप नीचे दिए गए उदाहरण की जाँच कर सकते हैं। लिंक ने php में अक्षांश और देशांतर का उपयोग करते हुए दो अलग-अलग स्थानों के बीच का समय प्राप्त किया


6
किसी चीज़ के लिए आपी को कॉल करना अनावश्यक हो सकता है जो कि गणित का उपयोग करके आसानी से पाया जा सकता है।
Ivotje50

1

अक्षांश और देशांतर के बिंदुओं के बीच की दूरी की गणना करने के लिए इस फ़ंक्शन को आज़माएं

function calculateDistanceBetweenTwoPoints($latitudeOne='', $longitudeOne='', $latitudeTwo='', $longitudeTwo='',$distanceUnit ='',$round=false,$decimalPoints='')
    {
        if (empty($decimalPoints)) 
        {
            $decimalPoints = '3';
        }
        if (empty($distanceUnit)) {
            $distanceUnit = 'KM';
        }
        $distanceUnit = strtolower($distanceUnit);
        $pointDifference = $longitudeOne - $longitudeTwo;
        $toSin = (sin(deg2rad($latitudeOne)) * sin(deg2rad($latitudeTwo))) + (cos(deg2rad($latitudeOne)) * cos(deg2rad($latitudeTwo)) * cos(deg2rad($pointDifference)));
        $toAcos = acos($toSin);
        $toRad2Deg = rad2deg($toAcos);

        $toMiles  =  $toRad2Deg * 60 * 1.1515;
        $toKilometers = $toMiles * 1.609344;
        $toNauticalMiles = $toMiles * 0.8684;
        $toMeters = $toKilometers * 1000;
        $toFeets = $toMiles * 5280;
        $toYards = $toFeets / 3;


              switch (strtoupper($distanceUnit)) 
              {
                  case 'ML'://miles
                         $toMiles  = ($round == true ? round($toMiles) : round($toMiles, $decimalPoints));
                         return $toMiles;
                      break;
                  case 'KM'://Kilometers
                        $toKilometers  = ($round == true ? round($toKilometers) : round($toKilometers, $decimalPoints));
                        return $toKilometers;
                      break;
                  case 'MT'://Meters
                        $toMeters  = ($round == true ? round($toMeters) : round($toMeters, $decimalPoints));
                        return $toMeters;
                      break;
                  case 'FT'://feets
                        $toFeets  = ($round == true ? round($toFeets) : round($toFeets, $decimalPoints));
                        return $toFeets;
                      break;
                  case 'YD'://yards
                        $toYards  = ($round == true ? round($toYards) : round($toYards, $decimalPoints));
                        return $toYards;
                      break;
                  case 'NM'://Nautical miles
                        $toNauticalMiles  = ($round == true ? round($toNauticalMiles) : round($toNauticalMiles, $decimalPoints));
                        return $toNauticalMiles;
                      break;
              }


    }

तब के रूप में fucntion का उपयोग करें

echo calculateDistanceBetweenTwoPoints('11.657740','77.766270','11.074820','77.002160','ML',true,5);

आशा करता हूँ की ये काम करेगा


मेरे मामले में वास्तविक परिदृश्य सही काम के साथ सत्यापित।
Daxesh Vekariya

1
इसे लिखने के लिए लगभग 5 घंटे लगे और इसे वास्तविक परिदृश्य में सत्यापित किया गया
मनोजकिरण.एक

0

गुणक को हर सर्कल पर बदल दिया जाता है क्योंकि महान सर्कल दूरी सिद्धांत के अनुसार यहां लिखा गया है:

http://en.wikipedia.org/wiki/Great-circle_distance

और आप यहाँ वर्णित इस सूत्र का उपयोग करके निकटतम मान की गणना कर सकते हैं:

http://en.wikipedia.org/wiki/Great-circle_distance#Worked_example

कुंजी प्रत्येक डिग्री - मिनट - दूसरे मूल्य को सभी डिग्री मूल्य में परिवर्तित कर रही है:

N 36°7.2', W 86°40.2'  N = (+) , W = (-), S = (-), E = (+) 
referencing the Greenwich meridian and Equator parallel

(phi)     36.12° = 36° + 7.2'/60' 

(lambda)  -86.67° = 86° + 40.2'/60'

0

सबसे आसान तरीकों में से एक है:

$my_latitude = "";
$my_longitude = "";
$her_latitude = "";
$her_longitude = "";

$distance = round((((acos(sin(($my_latitude*pi()/180)) * sin(($her_latitude*pi()/180))+cos(($my_latitude*pi()/180)) * cos(($her_latitude*pi()/180)) * cos((($my_longitude- $her_longitude)*pi()/180))))*180/pi())*60*1.1515*1.609344), 2);
echo $distance;

यह 2 दशमलव तक गोल होगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.