.NetCore给定坐标排序搜索附近的经纬度计算距离、WGS84 GCJ02 BD09坐标转换
先过滤出大概的经纬度范围再进行计算。我们求取的虽然是个圆,但我们可以先求出该圆的外接正方形(比方说正方形的四个点),先算出该点周围的矩形的四个点的经纬度,然后使用正方形的经纬度去直接匹配数据库中的记录,然后在用方法一进行筛选(相对于只使用方法一效率高了非常多 –合理利用算法)。
话不多说,直接上代码:
public static class DistanceHelper
{
/// <summary>
/// 根据一个给定经纬度的点和距离,进行附近地点查询
/// </summary>
/// <param name="longitude">经度</param>
/// <param name="latitude">纬度</param>
/// <param name="distance">距离(单位:公里或千米)</param>
/// <returns>返回一个范围的4个点,最小纬度和纬度,最大经度和纬度</returns>
public static PositionModel FindNeighPosition(double longitude, double latitude, double distance)
{
//先计算查询点的经纬度范围
double r = 6378.137;//地球半径千米
double dis = distance;//千米距离
double dlng = 2 * Math.Asin(Math.Sin(dis / (2 * r)) / Math.Cos(latitude * Math.PI / 180));
dlng = dlng * 180 / Math.PI;//角度转为弧度
double dlat = dis / r;
dlat = dlat * 180 / Math.PI;
double minlat = latitude - dlat;
double maxlat = latitude + dlat;
double minlng = longitude - dlng;
double maxlng = longitude + dlng;
return new PositionModel
{
MinLat = minlat,
MaxLat = maxlat,
MinLng = minlng,
MaxLng = maxlng
};
}
/// <summary>
/// 计算两点位置的距离,返回两点的距离,单位:公里或千米
/// 该公式为GOOGLE提供,误差小于0.2米
/// </summary>
/// <param name="lat1">第一点纬度</param>
/// <param name="lng1">第一点经度</param>
/// <param name="lat2">第二点纬度</param>
/// <param name="lng2">第二点经度</param>
/// <returns>返回两点的距离,单位:公里或千米</returns>
public static double GetDistance(double lat1, double lng1, double lat2, double lng2)
{
//地球半径,单位米
double EARTH_RADIUS = 6378137;
double radLat1 = Rad(lat1);
double radLng1 = Rad(lng1);
double radLat2 = Rad(lat2);
double radLng2 = Rad(lng2);
double a = radLat1 - radLat2;
double b = radLng1 - radLng2;
double result = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a / 2), 2) + Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin(b / 2), 2))) * EARTH_RADIUS;
return result / 1000;
}
/// <summary>
/// 经纬度转化成弧度
/// </summary>
/// <param name="d"></param>
/// <returns></returns>
private static double Rad(double d)
{
return (double)d * Math.PI / 180d;
}
}
外卖根据中心坐标计算附近距离排序
query.OrderByDescending(d => Math.Sqrt((121.517759 - Convert.ToDouble(longitude)) * (121.517759 - Convert.ToDouble(d.longitude)) + (31.178469 - Convert.ToDouble(latitude)) * (31.178469 - Convert.ToDouble(d.latitude))));
距离计算
var dis = DistanceHelper.GetDistance(Convert.ToDouble(latitude), Convert.ToDouble(longitude), Convert.ToDouble(item.latitude), Convert.ToDouble(item.longitude));
if (dis>1)
{
item.distance = Math.Round(dis, 2).ToString() + "km";
}
else
{
item.distance = Convert.ToInt32(dis* 1000.0) + "m";
}
WGS84 GCJ02 BD09坐标转换
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace General.Core.Common.Map
{
public class TempGps
{
public double Tlng;
public double Tlat;
}
public class GPS
{
public string oLng;//经度 度分秒坐标
public string oLat;//纬度 度分秒坐标
public double lng;//经度 WGS-84
public double lat;//纬度 WGS-84
public double gLng;//经度 GCJ-02 中国坐标偏移标准 Google Map、高德、腾讯使用
public double gLat;//纬度 GCJ-02 中国坐标偏移标准 Google Map、高德、腾讯使用
public double bLng;//经度 BD-09 百度坐标偏移标准,Baidu Map使用
public double bLat;//纬度 BD-09 百度坐标偏移标准,Baidu Map使用
public double PI = Math.PI;
double xPI = Math.PI * 3000.0 / 180.0;
public TempGps delta(TempGps t)
{
var a = 6378245.0; // a: 卫星椭球坐标投影到平面地图坐标系的投影因子。
var ee = 0.00669342162296594323; // ee: 椭球的偏心率。
var dLat = this.transformLat(t.Tlng - 105.0, t.Tlat - 35.0);
var dLng = this.transformLng(t.Tlng - 105.0, t.Tlat - 35.0);
var radLat = t.Tlat / 180.0 * PI;
var magic = Math.Sin(radLat);
magic = 1 - ee * magic * magic;
var sqrtMagic = Math.Sqrt(magic);
return new TempGps() { Tlat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI), Tlng = (dLng * 180.0) / (a / sqrtMagic * Math.Cos(radLat) * PI) };
}
//WGS-84 to GCJ-02
public void gcj_encrypt()
{
if (this.outOfChina(lng, lat))
{
gLng = lng;
gLat = lat;
}
var t = this.delta(new TempGps() { Tlng = lng, Tlat = lat });
gLng = t.Tlng + lng;
gLat = t.Tlat + lat;
}
//GCJ-02 to WGS-84
public void gcj_decrypt()
{
if (this.outOfChina(gLng, gLat))
{
lng = gLng;
lat = gLat;
}
var t = this.delta(new TempGps() { Tlng = gLng, Tlat = gLat });
lng = gLng - t.Tlng;
lat = gLat - t.Tlat;
}
//GCJ-02 to BD-09
public void bd_encrypt()
{
double x = gLng;
double y = gLat;
double z = Math.Sqrt(x * x + y * y) + 0.00002 * Math.Sin(y * xPI);
double theta = Math.Atan2(y, x) + 0.000003 * Math.Cos(x * xPI);
bLng = z * Math.Cos(theta) + 0.0065;
bLat = z * Math.Sin(theta) + 0.006;
}
//BD-09 to GCJ-02
public void bd_decrypt()
{
double x = bLng - 0.0065;
double y = bLat - 0.006;
double z = Math.Sqrt(x * x + y * y) + 0.00002 * Math.Sin(y * xPI);
double theta = Math.Atan2(y, x) - 0.000003 * Math.Cos(x * xPI);
gLng = z * Math.Cos(theta);
gLat = z * Math.Sin(theta);
}
//WGS-84 to 度分秒坐标
public void wgs_decrypt()
{
oLng = TranDegreeToDMs(lng);
oLat = TranDegreeToDMs(lat);
}
//度分秒坐标 to WGS-84
public void wgs_encrypt()
{
lng = TranDMsToDegree(oLng);
lat = TranDMsToDegree(oLat);
}
public double TranDMsToDegree(string _dms)
{
string[] dms = _dms.Split('.');
if (dms.Length > 2)
return double.Parse(dms[0]) + double.Parse(dms[1]) / 60 + double.Parse(dms[2] + "." + dms[3] ?? "0") / 3600;
else
return 0d;
}
private static string TranDegreeToDMs(double d)
{
int Degree = Convert.ToInt16(Math.Truncate(d));//度
d = d - Degree;
int M = Convert.ToInt16(Math.Truncate((d) * 60));//分
int S = Convert.ToInt16(Math.Round((d * 60 - M) * 60));
if (S == 60)
{
M = M + 1;
S = 0;
}
if (M == 60)
{
M = 0;
Degree = Degree + 1;
}
string rstr = Degree.ToString() + ".";
if (M < 10)
rstr = rstr + "0" + M.ToString();
else
rstr = rstr + M.ToString();
if (S < 10)
rstr = rstr + "0" + S.ToString();
else
rstr = rstr + S.ToString();
return rstr;
}
private bool outOfChina(double _lng, double _lat)
{
if (lng < 72.004 || lng > 137.8347)
return true;
if (lat < 0.8293 || lat > 55.8271)
return true;
return false;
}
private double transformLat(double x, double y)
{
double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.Sqrt(Math.Abs(x));
ret += (20.0 * Math.Sin(6.0 * x * PI) + 20.0 * Math.Sin(2.0 * x * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.Sin(y * PI) + 40.0 * Math.Sin(y / 3.0 * PI)) * 2.0 / 3.0;
ret += (160.0 * Math.Sin(y / 12.0 * PI) + 320 * Math.Sin(y * PI / 30.0)) * 2.0 / 3.0;
return ret;
}
private double transformLng(double x, double y)
{
double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.Sqrt(Math.Abs(x));
ret += (20.0 * Math.Sin(6.0 * x * PI) + 20.0 * Math.Sin(2.0 * x * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.Sin(x * PI) + 40.0 * Math.Sin(x / 3.0 * PI)) * 2.0 / 3.0;
ret += (150.0 * Math.Sin(x / 12.0 * PI) + 300.0 * Math.Sin(x / 30.0 * PI)) * 2.0 / 3.0;
return ret;
}
}
}
public class PositionModel
{
public double MinLat { get; internal set; }
public double MaxLat { get; internal set; }
public double MinLng { get; internal set; }
public double MaxLng { get; internal set; }
}