c# 基于GMap.NET實(shí)現(xiàn)電子圍欄功能(WPF版)
前言
GMap.NET是一個(gè)強(qiáng)大、免費(fèi)、跨平臺(tái)、開源的.NET控件。分為WPF和winform版。GMap.NET的基本知識(shí)不做過多介紹,本文主要介紹如何使用該控件實(shí)現(xiàn)電子圍欄功能。
電子圍欄主要有兩個(gè)功能模塊:界面展示圍欄區(qū)域,判斷人員出入圍欄的邏輯。GMap.NET的WPF版本功能并不強(qiáng)大,實(shí)現(xiàn)一些復(fù)雜的功能就只能發(fā)掘WPF的潛力了。GMap.NET給我們提供了一個(gè)基本的平臺(tái),必須熟練掌握WPF才能開發(fā)出復(fù)雜gis產(chǎn)品。
圍欄區(qū)域界面顯示
1 認(rèn)識(shí) GMapMarker
GMapControl是地圖的主容器;地圖就是多個(gè)圖片拼接而來,這個(gè)圖片組成GMapControl的底圖。底圖之上點(diǎn)綴用戶自定義的控件。用戶自定義控件必須通過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); }
一個(gè)GMapMarker關(guān)聯(lián)一個(gè)gps坐標(biāo),同時(shí)可以顯示一個(gè)控件(Shape );為什么在Shape外面包含一個(gè)marker?maker主要功能就是將控件釘?shù)紾MapControl的一個(gè)點(diǎn)。當(dāng)?shù)貓D移動(dòng)時(shí),maker會(huì)做相應(yīng)的移動(dòng),maker移動(dòng)會(huì)帶動(dòng)shape移動(dòng)。所以,我們只管把shape內(nèi)部處理好就行了,不用管地圖移動(dòng)。maker的作用不大,并不能幫我們實(shí)現(xiàn)復(fù)雜的功能;Shape才是我們施展拳腳的地方。
2 用戶控件實(shí)現(xiàn)畫圖
在控件中UserControlFence實(shí)現(xiàn)電子圍欄的繪制,該控件會(huì)關(guān)聯(lián)到maker的shape。UserControlFence控件以Grid(name為gridRoot)布局;WPF的Path可以實(shí)現(xiàn)任意圖像的繪畫,首先要將Path加入到Grid。我們的輸入是多個(gè)gps點(diǎn)坐標(biāo),怎么能轉(zhuǎn)換成Path上各個(gè)點(diǎn)坐標(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)換成 控件相對(duì)于直接父面板的坐標(biāo) Point ptToMapCtrl2 = new Point(ptOfMapCtrl.X, ptOfMapCtrl.Y); //轉(zhuǎn)成屏幕坐標(biāo) Point ptOfScreen = MapCtrl.PointToScreen(ptToMapCtrl2); //轉(zhuǎn)換成相對(duì)于gridRoot的坐標(biāo) Point ptOfParentPanel = gridRoot.PointFromScreen(ptOfScreen); return ptOfParentPanel; }
轉(zhuǎn)換過程就是:相對(duì)于Map控件坐標(biāo)-->屏幕坐標(biāo)-->相對(duì)于Grid的坐標(biāo)。因?yàn)镻ath是Grid的Child,最后的坐標(biāo)也是相對(duì)于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]; //起始點(diǎn) 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ū)域繪制。還有一點(diǎn)要注意:當(dāng)?shù)貓D縮放時(shí),必須重新繪制。地圖縮放比例不同,繪制區(qū)域大小也會(huì)改變(形狀不會(huì)變)。只需要監(jiān)視地圖控件的事件 public event MapZoomChanged OnMapZoomChanged;就行。
出入電子圍欄區(qū)域判斷
該判斷邏輯有多種實(shí)現(xiàn)方法,下面逐一介紹;
1 利用WPF的輔助函數(shù) VisualTreeHelper.HitTest
通過判斷gps點(diǎn)坐標(biāo)是否在控件內(nèi)來判斷。gps坐標(biāo)先要轉(zhuǎn)成控件點(diǎn)坐標(biāo)(轉(zhuǎn)換函數(shù)見前文)。函數(shù)實(shí)現(xiàn)比較簡(jiǎ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實(shí)現(xiàn)
這是System.Drawing下的一組類,屬于微軟早期的類庫(kù);該類的點(diǎn)坐標(biāo)還是float型,精度不高。對(duì)于gps坐標(biāo)我先做了放大處理,如果不做處理誤差會(huì)很大。
private bool IsInFence2(PointLatLng gpsPoint) { double rate = 100000; //由于float精度問題。對(duì)坐標(biāo)放大處理,否則誤差會(huì)很大。 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ù)點(diǎn)坐標(biāo)計(jì)算
理論上這種方式效率是最高的,并且不依賴界面控件。但是這種方法不是微軟提供的,準(zhǔn)確性還需要驗(yàn)證。下面的函數(shù)是從網(wǎng)上找的,我對(duì)此計(jì)算結(jié)果做了驗(yàn)證,與前兩種計(jì)算方法的結(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ū)域繪制方法與軌跡回放、測(cè)距等處理有類似之處;GMap.Net為我們做的工作并不多,關(guān)鍵是要掌握處理這一類問題的精髓,做到舉一反三,許多問題就會(huì)迎刃而解。
以上就是c# 基于GMap.NET實(shí)現(xiàn)電子圍欄功能(WPF版)的詳細(xì)內(nèi)容,更多關(guān)于c# GMap.NET實(shí)現(xiàn)電子圍欄的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#實(shí)現(xiàn)將Doc文檔轉(zhuǎn)換成rtf格式的方法示例
這篇文章主要介紹了C#實(shí)現(xiàn)將Doc文檔轉(zhuǎn)換成rtf格式的方法,結(jié)合實(shí)例形式分析了C#針對(duì)word文件的讀取及文檔格式轉(zhuǎn)換相關(guān)操作技巧,需要的朋友可以參考下2017-07-07C# IDE VS2005中的Hosting Process (vshost.exe)作用介紹
這篇文章主要介紹了C# IDE VS2005中的Hosting Process (vshost.exe)作用介紹,vshost.exe是一個(gè)宿主進(jìn)程,主要用來提高調(diào)試效率,需要的朋友可以參考下2015-01-01在c#中把字符串轉(zhuǎn)為變量名并獲取變量值的小例子
這篇文章介紹了在c#中把字符串轉(zhuǎn)為變量名并獲取變量值的小例子,有需要的朋友可以參考一下2013-09-09C#實(shí)現(xiàn)兩個(gè)exe程序之間通信詳解
這篇文章主要為大家詳細(xì)介紹了C#如何使用SendMessage實(shí)現(xiàn)兩個(gè)程序之間的通信功能,文中的示例代碼簡(jiǎn)潔易懂,需要的小伙伴可以參考下2023-07-07C#中的Linq Intersect與Except方法使用實(shí)例
這篇文章主要介紹了C#中的Linq Intersect與Except方法使用實(shí)例,本文直接給出示例代碼,需要的朋友可以參考下2015-06-06C# 16 進(jìn)制字符串轉(zhuǎn) int的方法
這篇文章主要介紹了C# 16 進(jìn)制字符串轉(zhuǎn) int的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-04-04Winform實(shí)現(xiàn)抓取web頁(yè)面內(nèi)容的方法
這篇文章主要介紹了Winform實(shí)現(xiàn)抓取web頁(yè)面內(nèi)容的方法,代碼只有短短幾行,但是功能很實(shí)用,需要的朋友可以參考下2014-09-09