嚴格來說,計算兩點間的距離,應該跟 Android 開發無關,因為這只是要用什麼方式來估算兩個經緯度間的距離而已,不過通常只有手機(或平版)上,才能比較精確的定位,所以就當成是一種開發 app 的應用好了,比如業務人在公司外面,如果有一個 app 可以即時算出所在地附近一定距離內有哪些客戶,對於臨時要做一些拜訪應該是有幫助的。

在還沒開始前,想說先把上一篇的部分做些補充,上次只有提到應用 Google web api 將經緯度轉換成地址,那如果是顛倒的需求,要將地址轉換成經緯度該怎麼做呢?其實也粉簡單,Google api  都已經有提供了,不過這部分一樣受到 2,500次/天 以及呼叫間隔不能太頻繁的限制,呼叫方式如下

http://maps.google.com/maps/api/geocode/json?&sensor=true&language=zh-TW&address=台北市復興北路325號

於瀏覽器執行的話,可以得到一個 json 格式的回傳值,其中下圖標示紅框的部分,就是該地址轉換後的經緯度

這時候可以透過 Delphi 提供的 TJSONObject、TJsonArray、TJSONPair 等找出 json 中你需要的特定資料

接下來兩組經緯度計算距離的部分,網路上其實有很多資源,計算方式也各不相同,主要都是說地球不是圓的,是一個不規則的橢圓,所以各家計算出來有差異,至於那個比較準確,就看個人的想法嚕,以下列出三種計算方式(均取自於網路),因此標示來源

 

計算式一

//以下計算式參考自
//http://www.esanu.name/delphi/Algorithms/Maths/Calculate%20Distance%20using%20Latitudes%20and%20Longitudes.html
//回傳值為公尺
function DistanceBetweenPoint1(const StartLat, StartLong,
  EndLat, EndLong : Double) : Double;
var
  // Variables used within the Bearing and Distance calculations
  fPhimean: Double; // Mean latitude
  fdLambda: Double; // Longitude difference
  fdPhi: Double; // Latitude difference
  fAlpha: Double; // Bearing
  fRho: Double; // Meridional radius of curvature
  fNu: Double; // Transverse radius of curvature
  fR: Double; // Radius of spherical earth
  fz: Double; // Angular distance at centre of spheroid
  fTemp: Double; // Temporary variable used in calculations
const
  // Constants used within the Bearing and Distance calculations
  D2R: Double = 0.017453; // Degrees to Radians Conversion
  R2D: Double = 57.295781; // Radians to Degrees Conversion
  a: Double = 6378137.0; // Major semi-axis
  b: Double = 6356752.314245; // Minor semi-axis
  e2: Double = 0.006739496742337; // Square of eccentricity of ellipsoid
  f: Double = 0.003352810664747; // Flattening of ellipsoid
begin
  // Get difference between longitudes and latitudes and mean latitude
  fdLambda := (StartLong - EndLong) * D2R;
  fdPhi := (StartLat - EndLat) * D2R;
  fPhimean := ((StartLat + EndLat) / 2.0) * D2R;

  // Calculate meridional and traverse radii of curvature at mean latitude
  fTemp := 1 - e2 * (Power(Sin(fPhimean),2));
  fRho := (a * (1 - e2)) / Power(fTemp, 1.5);
  fNu := a / (Sqrt(1 - e2 * (Sin(fPhimean) * Sin(fPhimean))));

  // Calculate angular distance
  fz := Sqrt(Power(Sin(fdPhi/2.0),2)+Cos(EndLat*D2R)*Cos(StartLat*D2R)*Power(Sin(fdLambda/2.0),2)) ;
  fz := 2 * ArcSin(fz);

  // Calculate bearing
  fAlpha := Cos(EndLat * D2R) * Sin(fdLambda) * 1 / Sin(fz);
  fAlpha := ArcSin(fAlpha);

  // Use Eular's Thereom to determine radius of the earth
  fR := (fRho * fNu) / ((fRho * Power(Sin(fAlpha),2)) + (fNu * Power(Cos(fAlpha),2)));

  // Set Bearing and Distance

  Result := (fz * fR);
end;

 

計算式二

function ConvertDegreeToRadians(const Degrees : Double) : Double;
begin
  Result := (PI/180)*Degrees;
end;

//以下計算式參考自
//http://www.dotblogs.com.tw/jeff-yeh/archive/2009/02/04/7034.aspx
//回傳值為公里
function DistanceBetweenPoint2(const StartLat, StartLong,
  EndLat, EndLong : Double) : Double;
var
  Lat1r, Lat2r,
  Long1r, Long2r : Double;
  D : Double;
const
  R : Double = 6378.137; // Earth's radius (km)
begin
  Lat1r := ConvertDegreeToRadians(StartLat);
  Lat2r := ConvertDegreeToRadians(EndLat);
  Long1r := ConvertDegreeToRadians(StartLong);
  Long2r := ConvertDegreeToRadians(EndLong);

  D := Math.ArcCos(Sin(Lat1r) *
                   Sin(Lat2r) + Cos(Lat1r) *
                   Cos(Lat2r) *
                   Cos(Long2r-Long1r)) * R;
  Result := D;
end;

 

計算式三

{********* Deg2rad ***********}
function deg2rad(deg:extended):extended;
{Degrees to Radians}
begin result:=deg * PI / 180.0; end;

{*********** Rad2Deg **********}
function rad2deg(rad:extended):extended;
{radians to degrees}
begin result:=rad / PI * 180.0;  end;

//以下計算式參考自
//http://www.delphiforfun.org/Programs/Math_Topics/Lat-Long%20Distance.htm
//回傳值為公里
function DistanceBetweenPoint3(const StartLat, StartLong,
  EndLat, EndLong : Extended) : Extended;
{Distance between to points assuming spherical earth}
var
  theta:extended;
begin
  theta := StartLong - EndLong;
  result:= Sin(deg2rad(StartLat)) * Sin(deg2rad(EndLat))
  + Cos(deg2rad(StartLat)) * Cos(deg2rad(EndLat)) * Cos(deg2rad(theta));
  result := rad2deg(Arccos(result))*60*1.1515; {miles per degree (24872 mile circumference)}
  result := result * 1.609344 {miles to kilometers}
end;

比賽又結束了...

另外幫 Gordon 大哥打一下廣告...11/19~11/22 捷康(QCom)有辦一場 Delphi XE5 深入技術研討會,對於有需要進一步用 XE5 開發 app 會粉有幫助

詳見

http://embarcadero.qcomgroup.com.tw/EDM/edm_seminar_20131119.htm

 

to be continued...

    縹緲 發表在 痞客邦 留言(5) 人氣()