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下的一組類,屬于微軟早期的類庫;該類的點(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#中把Datatable轉(zhuǎn)換為Json的5個(gè)代碼實(shí)例
這篇文章主要介紹了C#中把Datatable轉(zhuǎn)換為Json的5個(gè)代碼實(shí)例,需要的朋友可以參考下2014-04-04
C#解決訪問API顯示基礎(chǔ)連接已經(jīng)關(guān)閉的問題
最近在 Web 部署百度 AI 圖像識(shí)別 AipSdk.dll 封裝庫的時(shí)候,在調(diào)用OCR圖像識(shí)別 API 的時(shí)候,顯示為 “ 基礎(chǔ)連接已經(jīng)關(guān)閉: 接收時(shí)發(fā)生錯(cuò)誤,” ,并且運(yùn)行后直接崩潰,所以本文給大家介紹了C#解決訪問API顯示基礎(chǔ)連接已經(jīng)關(guān)閉的問題,需要的朋友可以參考下2024-12-12
C#實(shí)現(xiàn)Bitmap類型與Byte[]類型相互轉(zhuǎn)化的示例詳解
在C#編程中,Bitmap類型和Byte[]類型之間的相互轉(zhuǎn)化是圖像處理和數(shù)據(jù)傳輸中常見的需求,Bitmap類型表示一個(gè)位圖圖像,而Byte[]類型則是一個(gè)字節(jié)數(shù)組,本文將詳細(xì)介紹如何在這兩種類型之間進(jìn)行相互轉(zhuǎn)化,需要的朋友可以參考下2024-07-07
C#?Winform消息通知之系統(tǒng)本地通知local?toast?notification
這篇文章主要為大家介紹了C#?Winform消息通知之系統(tǒng)本地通知local?toast?notification使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08

