目前的工作是需要手机查找附近N米以内的商户,功能如下图
数据库中记录了商家在百度标注的经纬度(如:116.412007, 39.947545),
最初想法 以圆心点为中心点,对半径做循环,半径每增加一个像素(暂定1米)再对周长做循环,到数据库中查询对应点的商家(真是一个长时间的循环工作)
上网百度类似的文章有了点眉目
大致想法是已知一个中心点,一个半径,求圆包含于圆抛物线里所有的点,这样的话就需要知道所要求的这个圆的对角线的顶点,问题来了 经纬度是一个点,半径是一个距离,不能直接加减
终于找到想要的文章
http://digdeeply.org/archives/06152067.html
PHP,Mysql-根据一个给定经纬度的点,进行附近地点查询–合理利用算法,效率提高2125倍
参考原文章 lz改成了C#类
废话不多少直接上代码:
1 /// <summary>
2 /// 经纬度坐标
3 /// </summary>
4
5 public class Degree
6 {
7 public Degree(double x, double y)
8 {
9 X = x;
10 Y = y;
11 }
12 private double x;
13
14 public double X
15 {
16 get { return x; }
17 set { x = value; }
18 }
19 private double y;
20
21 public double Y
22 {
23 get { return y; }
24 set { y = value; }
25 }
26 }
27
28
29 public class CoordDispose
30 {
31 private const double EARTH_RADIUS = 6378137.0;//地球半径(米)
32
33 /// <summary>
34 /// 角度数转换为弧度公式
35 /// </summary>
36 /// <param name="d"></param>
37 /// <returns></returns>
38 private static double radians(double d)
39 {
40 return d * Math.PI / 180.0;
41 }
42
43 /// <summary>
44 /// 弧度转换为角度数公式
45 /// </summary>
46 /// <param name="d"></param>
47 /// <returns></returns>
48 private static double degrees(double d)
49 {
50 return d * (180 / Math.PI);
51 }
52
53 /// <summary>
54 /// 计算两个经纬度之间的直接距离
55 /// </summary>
56
57 public static double GetDistance(Degree Degree1, Degree Degree2)
58 {
59 double radLat1 = radians(Degree1.X);
60 double radLat2 = radians(Degree2.X);
61 double a = radLat1 - radLat2;
62 double b = radians(Degree1.Y) - radians(Degree2.Y);
63
64 double s = 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a / 2), 2) +
65 Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin(b / 2), 2)));
66 s = s * EARTH_RADIUS;
67 s = Math.Round(s * 10000) / 10000;
68 return s;
69 }
70
71 /// <summary>
72 /// 计算两个经纬度之间的直接距离(google 算法)
73 /// </summary>
74 public static double GetDistanceGoogle(Degree Degree1, Degree Degree2)
75 {
76 double radLat1 = radians(Degree1.X);
77 double radLng1 = radians(Degree1.Y);
78 double radLat2 = radians(Degree2.X);
79 double radLng2 = radians(Degree2.Y);
80
81 double s = Math.Acos(Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Cos(radLng1 - radLng2) + Math.Sin(radLat1) * Math.Sin(radLat2));
82 s = s * EARTH_RADIUS;
83 s = Math.Round(s * 10000) / 10000;
84 return s;
85 }
86
87 /// <summary>
88 /// 以一个经纬度为中心计算出四个顶点
89 /// </summary>
90 /// <param name="distance">半径(米)</param>
91 /// <returns></returns>
92 public static Degree[] GetDegreeCoordinates(Degree Degree1, double distance)
93 {
94 double dlng = 2 * Math.Asin(Math.Sin(distance / (2 * EARTH_RADIUS)) / Math.Cos(Degree1.X));
95 dlng = degrees(dlng);//一定转换成角度数 原PHP文章这个地方说的不清楚根本不正确 后来lz又查了很多资料终于搞定了
96
97 double dlat = distance / EARTH_RADIUS;
98 dlat = degrees(dlat);//一定转换成角度数
99
100 return new Degree[] { new Degree(Math.Round(Degree1.X + dlat,6), Math.Round(Degree1.Y - dlng,6)),//left-top
101 new Degree(Math.Round(Degree1.X - dlat,6), Math.Round(Degree1.Y - dlng,6)),//left-bottom
102 new Degree(Math.Round(Degree1.X + dlat,6), Math.Round(Degree1.Y + dlng,6)),//right-top
103 new Degree(Math.Round(Degree1.X - dlat,6), Math.Round(Degree1.Y + dlng,6)) //right-bottom
104 };
105
106 }
107 }
测试方法:
1 static void Main(string[] args)
2 {
3 double a = CoordDispose.GetDistance(new Degree(116.412007, 39.947545), new Degree(116.412924, 39.947918));//116.416984,39.944959
4 double b = CoordDispose.GetDistanceGoogle(new Degree(116.412007, 39.947545), new Degree(116.412924, 39.947918));
5 Degree[] dd = CoordDispose.GetDegreeCoordinates(new Degree(116.412007, 39.947545), 102);
6 Console.WriteLine(a+" "+b);
7 Console.WriteLine(dd[0].X + "," + dd[0].Y );
8 Console.WriteLine(dd[3].X + "," + dd[3].Y);
9 Console.ReadLine();
10 }
lz试了很多次 误差在1米左右
拿到圆的顶点就好办了
数据库要是sql 2008的可以直接进行空间索引经纬度字段,这样应该性能更好(没有试过)
lz公司数据库还老 2005的 这也没关系,关键是经纬度拆分计算,这个就不用说了 网上多的是 最后上个实现的sql语句
SELECT id,zuobiao FROM dbo.zuobiao WHERE zuobiao<>'' AND
dbo.Get_StrArrayStrOfIndex(zuobiao,',',1)>116.41021 AND
dbo.Get_StrArrayStrOfIndex(zuobiao,',',1)<116.413804 AND
dbo.Get_StrArrayStrOfIndex(zuobiao,',',2)<39.949369 AND
dbo.Get_StrArrayStrOfIndex(zuobiao,',',2)>39.945721