c# 基于GMap.NET實現(xiàn)電子圍欄功能(WPF版)
前言
GMap.NET是一個強大、免費、跨平臺、開源的.NET控件。分為WPF和winform版。GMap.NET的基本知識不做過多介紹,本文主要介紹如何使用該控件實現(xiàn)電子圍欄功能。
電子圍欄主要有兩個功能模塊:界面展示圍欄區(qū)域,判斷人員出入圍欄的邏輯。GMap.NET的WPF版本功能并不強大,實現(xiàn)一些復(fù)雜的功能就只能發(fā)掘WPF的潛力了。GMap.NET給我們提供了一個基本的平臺,必須熟練掌握WPF才能開發(fā)出復(fù)雜gis產(chǎn)品。
圍欄區(qū)域界面顯示
1 認(rèn)識 GMapMarker
GMapControl是地圖的主容器;地圖就是多個圖片拼接而來,這個圖片組成GMapControl的底圖。底圖之上點綴用戶自定義的控件。用戶自定義控件必須通過GMapMarker間接添加進(jìn)來,看下面代碼:
GMapMarker maker = new GMapMarker(ptLatLng); //UserControlFence用戶自定控件 _ctrlCurrentFence = new UserControlFence() { Marker = maker, MapCtrl = MainMap }; _ctrlCurrentFence.FenceInfo = CreateFenceInfoModel(); maker.Shape = _ctrlCurrentFence; this.MainMap.Markers.Add(maker);
GMapMarker 的定義也不復(fù)雜:
public class GMapMarker : INotifyPropertyChanged { public object Tag; public GMapMarker(PointLatLng pos); public UIElement Shape { get; set; } public PointLatLng Position { get; set; } public GMapControl Map { get; } public Point Offset { get; set; } public int LocalPositionX { get; } public int LocalPositionY { get; } public int ZIndex { get; set; } public event PropertyChangedEventHandler PropertyChanged; public virtual void Clear(); protected void OnPropertyChanged(string name); protected void OnPropertyChanged(PropertyChangedEventArgs name); }
一個GMapMarker關(guān)聯(lián)一個gps坐標(biāo),同時可以顯示一個控件(Shape );為什么在Shape外面包含一個marker?maker主要功能就是將控件釘?shù)紾MapControl的一個點。當(dāng)?shù)貓D移動時,maker會做相應(yīng)的移動,maker移動會帶動shape移動。所以,我們只管把shape內(nèi)部處理好就行了,不用管地圖移動。maker的作用不大,并不能幫我們實現(xiàn)復(fù)雜的功能;Shape才是我們施展拳腳的地方。
2 用戶控件實現(xiàn)畫圖
在控件中UserControlFence實現(xiàn)電子圍欄的繪制,該控件會關(guān)聯(lián)到maker的shape。UserControlFence控件以Grid(name為gridRoot)布局;WPF的Path可以實現(xiàn)任意圖像的繪畫,首先要將Path加入到Grid。我們的輸入是多個gps點坐標(biāo),怎么能轉(zhuǎn)換成Path上各個點坐標(biāo)? 這需要經(jīng)過多次轉(zhuǎn)換;
Point ToCtrlPoint(PointLatLng gpsPoint) { //轉(zhuǎn)換成GMap.NET控件坐標(biāo) GPoint ptOfMapCtrl = MapCtrl.FromLatLngToLocal(gpsPoint); //GMap.NET控件坐標(biāo)要轉(zhuǎn)換成 控件相對于直接父面板的坐標(biāo) Point ptToMapCtrl2 = new Point(ptOfMapCtrl.X, ptOfMapCtrl.Y); //轉(zhuǎn)成屏幕坐標(biāo) Point ptOfScreen = MapCtrl.PointToScreen(ptToMapCtrl2); //轉(zhuǎn)換成相對于gridRoot的坐標(biāo) Point ptOfParentPanel = gridRoot.PointFromScreen(ptOfScreen); return ptOfParentPanel; }
轉(zhuǎn)換過程就是:相對于Map控件坐標(biāo)-->屏幕坐標(biāo)-->相對于Grid的坐標(biāo)。因為Path是Grid的Child,最后的坐標(biāo)也是相對于Grid的坐標(biāo)。用該坐標(biāo)繪制Path,就是電子圍欄的區(qū)域;
Path的Data是Geometry,生成Geometry函數(shù)如下:
private PathGeometry CreatPath() { if (_listPoints.Count <= 1) { PathRouteLine.Data = null; return null; } List<Point> listPt = ListWndPoint; PathFigure pathFigure = new PathFigure(); pathFigure.StartPoint = listPt[0]; //起始點 pathFigure.IsClosed = true; for (int i = 1; i < listPt.Count; i++) { //加入線段 LineSegment line = new LineSegment() { Point = listPt[i] }; pathFigure.Segments.Add(line); } PathGeometry geometry = new PathGeometry(); geometry.Figures.Add(pathFigure); return geometry; }
這樣就完成電子圍欄的區(qū)域繪制。還有一點要注意:當(dāng)?shù)貓D縮放時,必須重新繪制。地圖縮放比例不同,繪制區(qū)域大小也會改變(形狀不會變)。只需要監(jiān)視地圖控件的事件 public event MapZoomChanged OnMapZoomChanged;就行。
出入電子圍欄區(qū)域判斷
該判斷邏輯有多種實現(xiàn)方法,下面逐一介紹;
1 利用WPF的輔助函數(shù) VisualTreeHelper.HitTest
通過判斷gps點坐標(biāo)是否在控件內(nèi)來判斷。gps坐標(biāo)先要轉(zhuǎn)成控件點坐標(biāo)(轉(zhuǎn)換函數(shù)見前文)。函數(shù)實現(xiàn)比較簡單;
private bool IsInFence(PointLatLng gpsPoint) { if (_listPoints.Count <= 2) return false; Point ptWnd = ToCtrlPoint(gpsPoint); HitTestResult result = VisualTreeHelper.HitTest(gridRoot, ptWnd); if (result == null || result.VisualHit==null) return false; bool hit = result.VisualHit == PathRouteLineInner; return hit; }
2 通過GraphicsPath、Region實現(xiàn)
這是System.Drawing下的一組類,屬于微軟早期的類庫;該類的點坐標(biāo)還是float型,精度不高。對于gps坐標(biāo)我先做了放大處理,如果不做處理誤差會很大。
private bool IsInFence2(PointLatLng gpsPoint) { double rate = 100000; //由于float精度問題。對坐標(biāo)放大處理,否則誤差會很大。 System.Drawing.Drawing2D.GraphicsPath pointPath = new System.Drawing.Drawing2D.GraphicsPath(); System.Drawing.PointF[] points = _listPoints.Select(o => new System.Drawing.PointF((float)(o.Lng * rate), (float)(o.Lat * rate))).ToArray(); pointPath.AddLines(points); pointPath.CloseFigure(); System.Drawing.Region region = new System.Drawing.Region(pointPath); System.Drawing.PointF ptHit = new System.Drawing.PointF((float)(gpsPoint.Lng * rate), (float)(gpsPoint.Lat * rate)); bool visible = region.IsVisible(ptHit); return visible; }
3 直接根據(jù)點坐標(biāo)計算
理論上這種方式效率是最高的,并且不依賴界面控件。但是這種方法不是微軟提供的,準(zhǔn)確性還需要驗證。下面的函數(shù)是從網(wǎng)上找的,我對此計算結(jié)果做了驗證,與前兩種計算方法的結(jié)果一致的。
private bool IsInFence3(PointLatLng gpsPoint) { int count = _listPoints.Count; if (count < 3) { return false; } bool result = false; for (int i = 0, j = count-1; i < count; i++) { var p1 = _listPoints[i]; var p2 = _listPoints[j]; if (p1.Lat < gpsPoint.Lat && p2.Lat >= gpsPoint.Lat || p2.Lat < gpsPoint.Lat && p1.Lat >= gpsPoint.Lat) { if (p1.Lng + (gpsPoint.Lat - p1.Lat) / (p2.Lat - p1.Lat) * (p2.Lng - p1.Lng) < gpsPoint.Lng) { result = !result; } } j = i; } return result; }
后記
電子圍欄區(qū)域繪制方法與軌跡回放、測距等處理有類似之處;GMap.Net為我們做的工作并不多,關(guān)鍵是要掌握處理這一類問題的精髓,做到舉一反三,許多問題就會迎刃而解。
以上就是c# 基于GMap.NET實現(xiàn)電子圍欄功能(WPF版)的詳細(xì)內(nèi)容,更多關(guān)于c# GMap.NET實現(xiàn)電子圍欄的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#實現(xiàn)將Doc文檔轉(zhuǎn)換成rtf格式的方法示例
這篇文章主要介紹了C#實現(xiàn)將Doc文檔轉(zhuǎn)換成rtf格式的方法,結(jié)合實例形式分析了C#針對word文件的讀取及文檔格式轉(zhuǎn)換相關(guān)操作技巧,需要的朋友可以參考下2017-07-07C# IDE VS2005中的Hosting Process (vshost.exe)作用介紹
這篇文章主要介紹了C# IDE VS2005中的Hosting Process (vshost.exe)作用介紹,vshost.exe是一個宿主進(jìn)程,主要用來提高調(diào)試效率,需要的朋友可以參考下2015-01-01在c#中把字符串轉(zhuǎn)為變量名并獲取變量值的小例子
這篇文章介紹了在c#中把字符串轉(zhuǎn)為變量名并獲取變量值的小例子,有需要的朋友可以參考一下2013-09-09C#中的Linq Intersect與Except方法使用實例
這篇文章主要介紹了C#中的Linq Intersect與Except方法使用實例,本文直接給出示例代碼,需要的朋友可以參考下2015-06-06C# 16 進(jìn)制字符串轉(zhuǎn) int的方法
這篇文章主要介紹了C# 16 進(jìn)制字符串轉(zhuǎn) int的方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2018-04-04Winform實現(xiàn)抓取web頁面內(nèi)容的方法
這篇文章主要介紹了Winform實現(xiàn)抓取web頁面內(nèi)容的方法,代碼只有短短幾行,但是功能很實用,需要的朋友可以參考下2014-09-09