目的
这次专题遇到了两个经纬度之间的距离计算,目前有好几间餐厅位置的资料,及很多活动场地位置资料,都有经纬度的资讯。而目的是要计算出选取某活动场地时,附近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
以上,感谢阅读。