O2O地圖應用之判斷使用者訂單位址是否在服務範圍内
需求分析
在o2o項目中,經常要用到在使用者下單時判斷使用者所填位址的坐标點是否在服務範圍内的情況,這裡參考網上的實作方式,用C#來實作,經測試後有效,特此記錄。
代碼
public class MapHelper
{
/// <summary>
/// 判斷一個坐标點在多邊形坐标點的内部還是外部
/// </summary>
/// <param name="point">要判斷的坐标點</param>
/// <param name="pts">多邊形坐标點集合</param>
/// <returns></returns>
public static bool IsPointInPolygon(Point point, List<Point> pts)
{
int N = pts.Count;
//如果點位于多邊形的頂點或邊上,也算做點在多邊形内,直接傳回true
bool boundOrVertex = true;
//經過點的次數
int intersectCount = 0;
double precision = 2e-10;
Point p1, p2;
Point p = point;//目前點
p1 = pts[0];
for (int i = 1; i <= N; i++)
{
//如果點在多邊形上
if (p.Equals(p1))
{
return boundOrVertex;
}
p2 = pts[(i % N)];
if (p.Lng<Math.Min(p1.Lng,p2.Lng)||p.Lng>Math.Max(p1.Lng,p2.Lng))
{
p1 = p2;
continue;
}
if (p.Lng>Math.Min(p1.Lng,p2.Lng)&&p.Lng<Math.Max(p1.Lng,p2.Lng))
{
if (p.Lat<=Math.Max(p1.Lat,p2.Lat))
{
if (p1.Lng==p2.Lng&&p.Lat>=Math.Min(p1.Lat,p2.Lat))
{
return boundOrVertex;
}
if (p1.Lat==p2.Lat)
{
if (p1.Lat==p.Lat)
{
return boundOrVertex;
}
else
{
intersectCount++;
}
}
else
{
double xinters = (p.Lng - p1.Lng) * (p2.Lat - p1.Lat) / (p2.Lng - p1.Lng) + p1.Lat;
if (Math.Abs(p.Lat-xinters)<precision)
{
return boundOrVertex;
}
if (p.Lat<xinters)
{
intersectCount++;
}
}
}
}
else
{
if (p.Lng==p2.Lng&&p.Lat<=p2.Lat)
{
Point p3 = pts[(i+1)%N];
if (p.Lng>=Math.Min(p1.Lng,p3.Lng)&&p.Lng<=Math.Max(p1.Lng,p3.Lng))
{
intersectCount++;
}
else
{
intersectCount += 2;
}
}
}
p1 = p2;
}
if (intersectCount%2==0)
{
//偶數在多邊形外
return false;
}
else
{
//奇數在多邊形内
return true;
}
}
}
public class Point
{
/// <summary>
/// 經度
/// </summary>
public double Lng { get; set; }
/// <summary>
/// 緯度
/// </summary>
public double Lat { get; set; }
}
測試
這裡我用高德地圖示出了北京五環範圍的坐标點集合,然後随意選擇一個坐标點來進行判斷:
坐标點可以用這個工具來擷取:高德地圖API
五環範圍:
- 香泉橋 116.222208,39.992436
- 箭亭橋 116.327147,40.02046
- 上清橋 116.353948,40.02299
- 顧家莊橋 116.44128,40.020526
- 東北五環 116.48441,40.013624
- 平房橋 116.541101,39.942393
- 東南五環 116.549202,39.851595
- 舊宮新橋 116.43082,39.785968
- 狼垈東橋 116.296044,39.777442
- 宛平橋 116.225062,39.845517
- 衙門口橋 116.211308,39.894396
- 西五環 116.212595,39.944705
随機坐标:
- 林萃橋地鐵站 116.37297,40.021857
- 望京西園四區 116.47086,39.99648
- 觀音禅寺 116.533811,39.880533
- 俏狐國際 116.299713,39.772619
- 芳園裡小區 116.416336,39.78394
- 潤楓錦尚小區 116.429039,39.790535
class Program
{
static void Main(string[] args)
{
var Plist = new List<Point> {
new Point {Lng=116.222208,Lat= 39.992436},
new Point {Lng=116.327147,Lat= 40.02046},
new Point {Lng=116.353948,Lat= 40.02299},
new Point {Lng=116.44128,Lat= 40.020526},
new Point {Lng=116.48441,Lat=40.013624 },
new Point {Lng=116.541101,Lat= 39.942393},
new Point {Lng=116.549202,Lat= 39.851595},
new Point {Lng=116.43082,Lat=39.785968},
new Point {Lng=116.296044,Lat=39.777442 },
new Point {Lng=116.225062,Lat=39.845517 },
new Point {Lng=116.211308,Lat= 39.894396},
new Point {Lng=116.212595,Lat=39.944705}
};
//var p = new Point { Lng = 116.37297, Lat = 40.021857 };
//林萃橋地鐵站 内
//var p = new Point { Lng = 116.47086, Lat = 39.99648 };
//望京西園四區 内
//var p = new Point { Lng = 116.533811, Lat = 39.880533 };
//觀音禅寺 内
//var p = new Point { Lng = 116.299713, Lat = 39.772619 };
//俏狐國際 外
//var p = new Point { Lng = 116.416336, Lat = 39.78394 };
//芳園裡小區 外
var p = new Point { Lng = 116.429039, Lat = 39.790535 };
//潤楓錦尚小區 内
bool isin = MapHelper.IsPointInPolygon(p, Plist);
if (isin)
{
Console.WriteLine("随機點在五環範圍内,可以派單");
}
else
{
Console.WriteLine("随機點不在五環範圍内");
}
Console.ReadKey();
}
}
總結
- 北京的五環範圍畢竟不是一個規則的多邊形,可以盡量選擇有标志性的坐标點來規範多邊形
- 參考自:百度地圖——判斷使用者是否在配送範圍内解決方案 - aheizi - 部落格園

作者:酷小孩
出處:http://www.cnblogs.com/babycool/
本文首發部落格園,版權歸作者跟部落格園共有。
轉載必須保留本段聲明,并在頁面顯著位置給出本文連結,否則保留追究法律責任的權利。