时间:2021-05-20
GMap.NET是一个强大、免费、跨平台、开源的.NET控件。分为WPF和winform版。GMap.NET的基本知识不做过多介绍,本文主要介绍如何使用该控件实现电子围栏功能。
电子围栏主要有两个功能模块:界面展示围栏区域,判断人员出入围栏的逻辑。GMap.NET的WPF版本功能并不强大,实现一些复杂的功能就只能发掘WPF的潜力了。GMap.NET给我们提供了一个基本的平台,必须熟练掌握WPF才能开发出复杂gis产品。
GMapControl是地图的主容器;地图就是多个图片拼接而来,这个图片组成GMapControl的底图。底图之上点缀用户自定义的控件。用户自定义控件必须通过GMapMarker间接添加进来,看下面代码:
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 的定义也不复杂:
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关联一个gps坐标,同时可以显示一个控件(Shape);为什么在Shape外面包含一个marker?maker主要功能就是将控件钉到GMapControl的一个点。当地图移动时,maker会做相应的移动,maker移动会带动shape移动。所以,我们只管把shape内部处理好就行了,不用管地图移动。maker的作用不大,并不能帮我们实现复杂的功能;Shape才是我们施展拳脚的地方。
在控件中UserControlFence实现电子围栏的绘制,该控件会关联到maker的shape。UserControlFence控件以Grid(name为gridRoot)布局;WPF的Path可以实现任意图像的绘画,首先要将Path加入到Grid。我们的输入是多个gps点坐标,怎么能转换成Path上各个点坐标? 这需要经过多次转换;
Point ToCtrlPoint(PointLatLng gpsPoint) { //转换成GMap.NET控件坐标 GPoint ptOfMapCtrl = MapCtrl.FromLatLngToLocal(gpsPoint); //GMap.NET控件坐标要转换成 控件相对于直接父面板的坐标 Point ptToMapCtrl2 = new Point(ptOfMapCtrl.X, ptOfMapCtrl.Y); //转成屏幕坐标 Point ptOfScreen = MapCtrl.PointToScreen(ptToMapCtrl2); //转换成相对于gridRoot的坐标 Point ptOfParentPanel = gridRoot.PointFromScreen(ptOfScreen); return ptOfParentPanel; }转换过程就是:相对于Map控件坐标-->屏幕坐标-->相对于Grid的坐标。因为Path是Grid的Child,最后的坐标也是相对于Grid的坐标。用该坐标绘制Path,就是电子围栏的区域;
Path的Data是Geometry,生成Geometry函数如下:
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; }这样就完成电子围栏的区域绘制。还有一点要注意:当地图缩放时,必须重新绘制。地图缩放比例不同,绘制区域大小也会改变(形状不会变)。只需要监视地图控件的事件public event MapZoomChanged OnMapZoomChanged;就行。
该判断逻辑有多种实现方法,下面逐一介绍;
通过判断gps点坐标是否在控件内来判断。gps坐标先要转成控件点坐标(转换函数见前文)。函数实现比较简单;
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; }这是System.Drawing下的一组类,属于微软早期的类库;该类的点坐标还是float型,精度不高。对于gps坐标我先做了放大处理,如果不做处理误差会很大。
private bool IsInFence2(PointLatLng gpsPoint) { double rate = 100000; //由于float精度问题。对坐标放大处理,否则误差会很大。 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; }理论上这种方式效率是最高的,并且不依赖界面控件。但是这种方法不是微软提供的,准确性还需要验证。下面的函数是从网上找的,我对此计算结果做了验证,与前两种计算方法的结果一致的。
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; }电子围栏区域绘制方法与轨迹回放、测距等处理有类似之处;GMap.Net为我们做的工作并不多,关键是要掌握处理这一类问题的精髓,做到举一反三,许多问题就会迎刃而解。
以上就是c# 基于GMap.NET实现电子围栏功能(WPF版)的详细内容,更多关于c# GMap.NET实现电子围栏的资料请关注其它相关文章!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
如何在WinForm中使用GMap.Net项目主页:https://greatmaps.codeplex.com/下载GMap.Net,我下载的版本:great
前言实现轨迹回放,GMap.NET有对应的类GMapRoute。这个类函数很少,功能有限,只能实现简单的轨迹回放。要实现更复杂的轨迹回放,就需要自己动手了。本文
有的时候用C#写图像处理方法,比较费事,不如Matlab简单,但是Matlab又做不出WPF那样的好看界面,怎么办呢。今天正好我要实现这个功能,就顺便写个小例子
HttpTest4Net是一款基于C#实现的和HTTP压力测试工具,通过工具可以简单地对HTTP服务进行一个压力测试.虽然VS.NET也集成了压力测试项目,但由
本文所述实例主要实现WPF项目中C#改变DataGrid某一行和单元格颜色的功能。分享给大家供大家参考。具体方法如下:如果要改变DataGrid某一行的颜色、高