需要手機查找附近N米以內的商戶,致想法是已知一個中心點,一個半徑,求圓包含於圓拋物線里所有的點,經緯度是一個點,半徑是一個距離,不能直接加減,下面提供C#的解決方法
數據庫中記錄了商家在百度標注的經緯度(如:116.412007, 39.947545)
最初想法,以圓心點為中心點,對半徑做循環,半徑每增加一個像素(暫定1米)再對周長做循環,到數據庫中查詢對應點的商家(真是一個長時間的循環工作),上網百度類似的文章有了點眉目
大致想法是已知一個中心點,一個半徑,求圓包含於圓拋物線里所有的點,這樣的話就需要知道所要求的這個圓的對角線的頂點,問題來了 經緯度是一個點,半徑是一個距離,不能直接加減
/// <summary> /// 經緯度坐標 /// </summary> public class Degree { public Degree(double x, double y) { X = x; Y = y; } private double x; public double X { get { return x; } set { x = value; } } private double y; public double Y { get { return y; } set { y = value; } } } public class CoordDispose { private const double EARTH_RADIUS = 6378137.0;//地球半徑(米) /// <summary> /// 角度數轉換為弧度公式 /// </summary> /// <param name="d"></param> /// <returns></returns> private static double radians(double d) { return d * Math.PI / 180.0; } /// <summary> /// 弧度轉換為角度數公式 /// </summary> /// <param name="d"></param> /// <returns></returns> private static double degrees(double d) { return d * (180 / Math.PI); } /// <summary> /// 計算兩個經緯度之間的直接距離 /// </summary> public static double GetDistance(Degree Degree1, Degree Degree2) { double radLat1 = radians(Degree1.X); double radLat2 = radians(Degree2.X); double a = radLat1 - radLat2; double b = radians(Degree1.Y) - radians(Degree2.Y); double s = 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))); s = s * EARTH_RADIUS; s = Math.Round(s * 10000) / 10000; return s; } /// <summary> /// 計算兩個經緯度之間的直接距離(google 算法) /// </summary> public static double GetDistanceGoogle(Degree Degree1, Degree Degree2) { double radLat1 = radians(Degree1.X); double radLng1 = radians(Degree1.Y); double radLat2 = radians(Degree2.X); double radLng2 = radians(Degree2.Y); double s = Math.Acos(Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Cos(radLng1 - radLng2) + Math.Sin(radLat1) * Math.Sin(radLat2)); s = s * EARTH_RADIUS; s = Math.Round(s * 10000) / 10000; return s; } /// <summary> /// 以一個經緯度為中心計算出四個頂點 /// </summary> /// <param name="distance">半徑(米)</param> /// <returns></returns> public static Degree[] GetDegreeCoordinates(Degree Degree1, double distance) { double dlng = 2 * Math.Asin(Math.Sin(distance / (2 * EARTH_RADIUS)) / Math.Cos(Degree1.X)); dlng = degrees(dlng);//一定轉換成角度數 原PHP文章這個地方說的不清楚根本不正確 后來lz又查了很多資料終於搞定了 double dlat = distance / EARTH_RADIUS; dlat = degrees(dlat);//一定轉換成角度數 return new Degree[] { new Degree(Math.Round(Degree1.X + dlat,6), Math.Round(Degree1.Y - dlng,6)),//left-top new Degree(Math.Round(Degree1.X - dlat,6), Math.Round(Degree1.Y - dlng,6)),//left-bottom new Degree(Math.Round(Degree1.X + dlat,6), Math.Round(Degree1.Y + dlng,6)),//right-top new Degree(Math.Round(Degree1.X - dlat,6), Math.Round(Degree1.Y + dlng,6)) //right-bottom }; } }
測試方法:
static void Main(string[] args) { double a = CoordDispose.GetDistance(new Degree(116.412007, 39.947545), new Degree(116.412924, 39.947918));//116.416984,39.944959 double b = CoordDispose.GetDistanceGoogle(new Degree(116.412007, 39.947545), new Degree(116.412924, 39.947918)); Degree[] dd = CoordDispose.GetDegreeCoordinates(new Degree(116.412007, 39.947545), 102); Console.WriteLine(a+" "+b); Console.WriteLine(dd[0].X + "," + dd[0].Y ); Console.WriteLine(dd[3].X + "," + dd[3].Y); Console.ReadLine(); }
試了很多次 誤差在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
來自:jb51