iT邦幫忙

6

ASP.NET MVC 經緯度的距離計算 GeoCoordinate [C#]

目的

這次專題遇到了兩個經緯度之間的距離計算,目前有好幾間餐廳位置的資料,及很多活動場地位置資料,都有經緯度的資訊。而目的是要計算出選取某活動場地時,附近5公里內的餐廳有哪幾間,並且顯示彼此之間的距離是多少做成API。

上網查了一下公式,因為是計算球面兩點的最短距離,所以公式相當複雜,不過網路上有滿多別人已經寫好的演算公式的funcition可以參考,只要將兩地點的經緯度參數丟進去就可以了。

不過經過老師指點後,其實asp.net就有個參考可以計算經緯度兩點之間的最短距離。程式碼可以不用那麼長也不會讓使用者看到計算的公式。
那就是使用,也就是今天的主角 GeoCoordinate

首先要先加入參考

System.Device

using 參考

using System.Device.Location;

Controller

首先已知前端會post給我一個活動場地的經度(lng)及緯度(lat)給我。
先建立個post的function其名為PlaceNearRestaurant

[HttpPost]

  public ActionResult PlaceNearRestaurant(double lat, double lng)
    {
       
    }

GeoCoordinate

接下來在function內var一個new GeoCoordinate資料型別。

var coord = new GeoCoordinate(lat, lng);

GeoCoordinate是經緯度的資料型別,要提供緯度及經度,需引用System.Device.Location才可呼叫的出來。

緯度的範圍可從-90.0 到90.0,若不在這範圍內則會報錯。

從資料庫先篩選出需要的資料

private Activivtiest db = new Activivtiest();

Activivtiest是ADO.NET的實體資料模型,是與資料庫做連接的一個model。

var foods = db.Restaurants.Where(x => !(string.IsNullOrEmpty(x.Latitude)) || !(string.IsNullOrEmpty(x.Longitude))).ToList();

function內從資料庫取出全部的餐廳資料(Restaurants資料表),但這邊要先使用where過濾掉沒有提供經度及緯度的餐廳資料,不然到時候若某餐廳沒有提供經緯度,GeoCoordinate計算時會報錯。

顯示餐廳的欄位資料

var Newfoods = foods.Select(x => new
           {
               Id = x.Id,
               Name = x.Name,
               lat = x.Latitude,
               lng = x.Longitude,
               add = x.Address,
               optime = x.Time,
               tel = x.TEL
                   
            }
            
  return Content(JsonConvert.SerializeObject(Newfoods), "application/json");       
            

再var一個Newfood來對剛剛的foods進行篩選顯示,先測試一下是否有取得餐廳的資料,回傳的是Json的格式。

確認有取得資料後,就要來計算活動場地與餐廳之間的距離了。

計算場地與餐廳的距離

 Dis = (int)(new GeoCoordinate(double.Parse(x.Latitude),double.Parse(x.Longitude))).GetDistanceTo(coord)

在上面Newfoods的Select內需要再多加一個Dis欄位,用來存放計算後的距離資料,資料型態使用int。

計算距離的方法介紹

是使用 GeoCoordinate().GetDistanceTo()
這個方法是GeoCoordinate內建的計算球面兩點的最短距離方法,演算法都已經寫在GetDistanceTo()內了,只需要丟入需要的參數即可。回傳的距離單位是公尺(M)。

用法:

GeoCoordinate(餐廳的緯度,餐廳的經度).GetDistanceTo(活動場地的經緯度)

記得若從資料庫取得座標記得要將資料型態轉型為double。

回傳後,就可以成功的獲得計算餐廳與活動場地的距離了。

篩選出5公里內的餐廳

程式碼:

return Content(JsonConvert.SerializeObject(Newfoods.Where(x => x.Dis <= 5000).OrderBy(x => x.Dis)), "application/json");

只要在Newfoods內進行where資料篩選,取得剛剛的Dis欄位,然後只顯示小於等於5000公尺的資料就好,並使用遞增的方式呈現。
序列化成Json格式,就可以取得5公里內的餐廳資料了。

將單位變成公里的呈現方式

程式碼:

DisView = (int)(new GeoCoordinate(double.Parse(x.Latitude),double.Parse(x.Longitude))).GetDistanceTo(coord) >= 1000 
? ((double)(new GeoCoordinate(double.Parse(x.Latitude),double.Parse(x.Longitude))).GetDistanceTo(coord) / 1000.0).ToString("0.0") + "KM"
: (int)(newDisView GeoCoordinate(double.Parse(x.Latitude),double.Parse(x.Longitude))).GetDistanceTo(coord) + "M"

看起來有點複雜,就是在Newfoods建立一個用來顯示距離的欄位[DisView],使用三元運算式,將計算距離的公式套入,若大於等於1000公尺則取計算後的距離/1000(將單位轉換成公里),再加上字串KM。
不然的話小於1000就顯示計算後的距離就好,單位就不用轉換成公里了,並加上字串M。

就可以return資料了,這樣就完成了一個可以計算活動場地與餐廳的經緯度距離並篩選出5公里內的餐廳API了。

附上整個程式碼:

Controller

using

using twActivitiest.Models;
using System.Device.Location;
using Newtonsoft.Json;

function

//與資料庫的關聯
private Activivtiest db = new Activivtiest();

#region 顯示場地5公里內的美食與顯示距離

        [HttpPost]
        
        public ActionResult PlaceNearRestaurant(double lat, double lng)
        {
            
            
            //取得post的經緯度資料並存成座標型態
            var coord = new GeoCoordinate(lat, lng);
            
            //從資料庫進行篩選,不要經度或緯度有空白資料的餐廳
            var foods = db.Restaurants.Where(x => !(string.IsNullOrEmpty(x.Latitude)) || !(string.IsNullOrEmpty(x.Longitude))).ToList();
            
            
            //篩選要的欄位並計算活動場地與餐廳之間的距離
            var Newfoods = foods.Select(x => new
            {
                Id = x.Id,
                Name = x.Name,
                lat = x.Latitude,
                lng = x.Longitude,
                add = x.Address,
                optime = x.Time,
                tel = x.TEL,
                
                
                //計算活動場地與餐廳的距離方法
                
                Dis = (int)(new GeoCoordinate(double.Parse(x.Latitude), double.Parse(x.Longitude))).GetDistanceTo(coord),
                
                
                //距離顯示方式大於等於1000公尺就將單位轉成公里
                
                DisView = (int)(new GeoCoordinate(double.Parse(x.Latitude), double.Parse(x.Longitude))).GetDistanceTo(coord) >= 1000 
                ? ((double)(new GeoCoordinate(double.Parse(x.Latitude), double.Parse(x.Longitude))).GetDistanceTo(coord) / 1000.0).ToString("0.0") + "KM"
                 : (int)(new GeoCoordinate(double.Parse(x.Latitude), double.Parse(x.Longitude))).GetDistanceTo(coord) + "M"



            }


             );
            
            
            //篩選出只要取得5000公尺內的餐廳資料,並將資料型態序列化成Json格式,並回傳資料
            
            return Content(JsonConvert.SerializeObject(Newfoods.Where(x => x.Dis <= 5000).OrderBy(x => x.Dis)), "application/json");


        }


        #endregion

以上,感謝閱讀。


1 則留言

1
YoChen
iT邦研究生 5 級 ‧ 2019-12-09 09:55:45

感謝分享~
不過最後一段,您其實可以直接Return Json就好~XDDD
e.g.

return Json(Newfoods.Where(x => x.Dis <= 5000).OrderBy(x => x.Dis));
捲毛蔡 iT邦新手 5 級 ‧ 2019-12-09 23:21:53 檢舉

原來可以醬,感謝Y大><

捲毛蔡 iT邦新手 5 級 ‧ 2019-12-21 00:10:37 檢舉

後來發現若直接用return Json()方式回傳的話,datetime會自動變成date(xxxxxxxx)的格式。
使用Content(JsonConvert.SerializeObject()的方式,datetime的格式就是資料長怎樣就是怎樣。

我要留言

立即登入留言