GMap自定义绘图
gmap自带的绘图只难绘制路径,多边形,固定大小的圆.在实际开发中可能这些并不能满足自己所需.这里就需要自定义绘图
原理:继承GMapRoute或者GMapMarker类,重写里面的OnRender函数.在OnRender函数里重新绘制所需的图形即可,OnRender函数里传参的是(Graphics g) 这是微软自己的类,可以在msdn上找到完整的绘图说明.相信用过c#绘图的同学一定很熟悉
GMapRoute和GMapMarker和区别在于,GMapRoute的构造函数是可以传一个PointLatLng链表进去,它的基类有自动将链表里甩的GPS坐标点全部转换为屏幕坐标系下的坐标点.以便使用Graphics进行绘图.而GMapMarker每次只能传参一个坐点进去,因为它目地只是为了操作一个点
下面帖上两段代码,继承GMapRoute的类是实现了在线上加了箭头,指明了路径的方向
继承GMapMarker的类是实现画圆,同时也实现在圆上指明了方向
说明:由于获取三角形三个点坐标使用到的向量,因此需要添加引用 System.Numerics.Vectors
建议使用NuGet安装
在NuGet中搜索 System.Numerics.Vectors ,下载第一个安装即可
-
- namespace gMapActiveX.CustomMarkers
- {
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.Runtime.Serialization;
- using System.Windows.Forms;
- using GMap.NET;
- using GMap.NET.WindowsForms;
-
- /// <summary>
- /// GMap.NET route
- /// </summary>
- [Serializable]
- class MyGMapRoute : GMapRoute, ISerializable
- {
-
- /// <summary>
- /// specifies how the outline is painted
- /// </summary>
- [NonSerialized]
- public Pen MyStroke = DefaultStroke;//使用默认线属性
- public Brush Fill = new SolidBrush(Color.Yellow);//填充属性,用于箭头
- public int R = 14;//圆点半径
- public int Length = 20;//箭头三角形的宽度
- public bool TriangleIsVisible = true;//是否带箭头
- public bool DotIsVisible = true;//是否启用圆点
-
- static MyGMapRoute()
- {
- DefaultStroke.LineJoin = LineJoin.Round;
- DefaultStroke.Width = 5;
- }
-
- public MyGMapRoute(string name)
- : base(name)
- {
-
- }
-
- public MyGMapRoute(IEnumerable<PointLatLng> points, string name)
- : base(points, name)
- {
-
- }
-
- public MyGMapRoute(MapRoute oRoute)
- : base(oRoute)
- {
-
- }
-
-
- public override void OnRender(Graphics g)
- {
- if (IsVisible)
- {
- List<Point[]> pointsList = new List<Point[]>();
- Point[] pnts = new Point[LocalPoints.Count];
- for (int i = 0; i < LocalPoints.Count; i++)
- {
- Point p2 = new Point((int)LocalPoints[i].X, (int)LocalPoints[i].Y );
- pnts[pnts.Length - 1 - i] = p2;
- }
-
- if (pnts.Length > 1)
- {
- g.DrawLines(Stroke, pnts);
- for (int i = 1; i < pnts.Length; i++)
- {
- if(TriangleIsVisible)
- {
- Transfrom.GetTriangle(pnts[pnts.Length - 1 - i + 1], pnts[pnts.Length - 1 - i], Length, out PointF[] points);
- g.FillPolygon(Fill, points);//画箭头
- }
- if(DotIsVisible)
- {
- g.FillEllipse(Fill, new Rectangle(pnts[pnts.Length - 1 - i].X - R / 2, pnts[pnts.Length - 1 - i].Y - R / 2, R, R));
- }
- }
- if(DotIsVisible)
- {
- g.FillEllipse(Fill, new Rectangle(pnts[pnts.Length - 1].X - R / 2, pnts[pnts.Length - 1].Y - R / 2, R, R));
- }
- }
- }
- }
-
-
- /// <summary>
- /// 释放颜色及资源
- /// </summary>
- #region IDisposable Members
-
- bool disposed = false;
-
- public override void Dispose()
- {
- if (Stroke != null)
- {
- Stroke.Dispose();
- Stroke = null;
- }
-
- if (Fill != null)
- {
- Fill.Dispose();
- Fill = null;
- }
-
- if (!disposed)
- {
- disposed = true;
-
- LocalPoints.Clear();
- base.Clear();
- }
- }
- #endregion
-
- #region ISerializable Members
-
- // Temp store for de-serialization.
- private GPoint[] deserializedLocalPoints;
-
- /// <summary>
- /// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the target object.
- /// </summary>
- /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data.</param>
- /// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization.</param>
- /// <exception cref="T:System.Security.SecurityException">
- /// The caller does not have the required permission.
- /// </exception>
- public override void GetObjectData(SerializationInfo info, StreamingContext context)
- {
- base.GetObjectData(info, context);
-
- info.AddValue("Visible", this.IsVisible);
- info.AddValue("LocalPoints", this.LocalPoints.ToArray());
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="GMapRoute"/> class.
- /// </summary>
- /// <param name="info">The info.</param>
- /// <param name="context">The context.</param>
- protected MyGMapRoute(SerializationInfo info, StreamingContext context) : base(info, context)
- {
- //this.Stroke = Extensions.GetValue<Pen>(info, "Stroke", new Pen(Color.FromArgb(144, Color.MidnightBlue)));
- this.IsVisible = Extensions.GetStruct<bool>(info, "Visible", true);
- this.deserializedLocalPoints = Extensions.GetValue<GPoint[]>(info, "LocalPoints");
- }
-
- #endregion
- }
-
- }
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Drawing;
- using System.Runtime.Serialization;
- using GMap.NET;
- using GMap.NET.WindowsForms;
-
- namespace gMapActiveX.CustomMarkersCircle
- {
- #if !PocketPC
- [Serializable]
- public class MyGMapMarkerCircle : GMapMarker, ISerializable
- #else
- public class GMapMarkerCircle : GMapMarker
- #endif
- {
- /// <summary>
- /// In Meters 用米数确定圆
- /// </summary>
- public int Radius;
-
-
- /// <summary>
- /// 圆上是否要用箭头
- /// </summary>
- public enum Arrow {Empty,RightArrow,LeftArrow};
- public Arrow ArrowIsVisible = Arrow.Empty;
-
- /// <summary>
- /// 是否用米数确定圆
- /// </summary>
- public bool IsMeter = true;
-
-
- /// <summary>
- /// specifies how the outline is painted
- /// </summary>
- [NonSerialized]
- #if !PocketPC
- public Pen Stroke = new Pen(Color.FromArgb(155, Color.MidnightBlue));
- #else
- public Pen Stroke = new Pen(Color.MidnightBlue);
- #endif
-
- /// <summary>
- /// 圆内填充颜色
- /// </summary>
- [NonSerialized]
- #if !PocketPC
- public Brush Fill = new SolidBrush(Color.FromArgb(155, Color.AliceBlue));
-
- [NonSerialized]
- public Brush ArrowFill = new SolidBrush(Color.Yellow);
- public int Length = 20;//箭头三角形的宽度
- #else
- public Brush Fill = new System.Drawing.SolidBrush(Color.AliceBlue);
- #endif
-
- /// <summary>
- /// is filled
- /// </summary>
- public bool IsFilled = true;
-
- public MyGMapMarkerCircle(PointLatLng p,int r)
- : base(p)
- {
- Radius = r; // 0m
- IsHitTestVisible = false;
- }
-
- public override void OnRender(Graphics g)
- {
- int R = (int)((Radius) / Overlay.Control.MapProvider.Projection.GetGroundResolution((int)Overlay.Control.Zoom, Position.Lat)) * 2;
-
- if (IsFilled)
- {
- g.FillEllipse(Fill, new System.Drawing.Rectangle(LocalPosition.X - R / 2, LocalPosition.Y - R / 2, R, R));
- }
- g.DrawEllipse(Stroke, new System.Drawing.Rectangle(LocalPosition.X - R / 2, LocalPosition.Y - R / 2, R, R));
- if(ArrowIsVisible == Arrow.LeftArrow)
- {
- Transfrom.GetTriangle(new Point(LocalPosition.X + 10, LocalPosition.Y - R / 2), new Point(LocalPosition.X - 30, LocalPosition.Y - R / 2), Length, out PointF[] points);
- g.FillPolygon(ArrowFill, points);
- }
- if(ArrowIsVisible == Arrow.RightArrow)
- {
- Transfrom.GetTriangle(new Point(LocalPosition.X - 10, LocalPosition.Y - R / 2), new Point(LocalPosition.X + 30, LocalPosition.Y - R / 2), Length, out PointF[] points);
- g.FillPolygon(ArrowFill, points);
- }
- }
-
- public override void Dispose()
- {
- if (Stroke != null)
- {
- Stroke.Dispose();
- Stroke = null;
- }
-
- if (Fill != null)
- {
- Fill.Dispose();
- Fill = null;
- }
-
- base.Dispose();
- }
-
- public bool IsInside(PointLatLng p)
- {
- return (int)Overlay.Control.MapProvider.Projection.GetDistance(Position, p) * 1000 < Radius;
- }
-
- #if !PocketPC
-
- #region ISerializable Members
-
- void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
- {
- base.GetObjectData(info, context);
-
- // TODO: Radius, IsFilled
- }
-
- protected MyGMapMarkerCircle(SerializationInfo info, StreamingContext context)
- : base(info, context)
- {
- // TODO: Radius, IsFilled
- }
-
- #endregion
-
- #endif
- }
- }
绘制三角形,确定三个点的函数 代码参考 https://www.cnblogs.com/oY-CCTR/p/3755742.html
- /// <summary>
- /// 返回一条线的终点位置的一个三角形的三个点坐标,三角形指向线的终点
- /// 三角形为一个正三角形
- /// </summary>
- /// <param name="start"></param>线起始点
- /// <param name="end"></param>线终点
- /// <param name="length"></param>三角形边长
- /// <param name="triangle"></param>输出的三角形的三个点数组
- public static void GetTriangle(Point start,Point end, int length, out PointF[] triangle)
- {
- triangle = new PointF[3];
- if (start == end)
- {
- return;
- }
-
- //箭头夹角
- double angle = 60.0 / 180 * Math.PI;
-
- //求BE长度
- double widthBE = length / 2 / (Math.Tan(angle / 2));
- //计算箭头的尖部位置点,这里取整条线段的1/4处
- PointF midPoint = new PointF((3*start.X + end.X) / 4, (3*start.Y + end.Y) / 4);
-
- //起点到箭头尖的向量
- Vector2 lineVector = new Vector2(midPoint.X - start.X, midPoint.Y - start.Y);
-
- //箭头尖到 三角形和直线垂直点 的向量
- Vector2 beVector = (float)widthBE * -Vector2.Normalize(lineVector);//需用到单位向量
-
- //三角形和直线和垂直点坐标
- PointF ePt = new PointF();
- //ePt - endPt = bcVector
- ePt.X = midPoint.X + beVector.X;
- ePt.Y = midPoint.Y + beVector.Y;
-
- //三角形和直线垂直线的向量
- Vector2 cdVector = new Vector2(-lineVector.Y, lineVector.X);
-
-
- //求CE向量
- Vector2 ceVector = length / 2 * Vector2.Normalize(cdVector);//需用到单位向量
- //求C点坐标,ePt - cPt = ceVector;
- PointF cPt = new PointF();
- cPt.X = ePt.X - ceVector.X;
- cPt.Y = ePt.Y - ceVector.Y;
-
- //求DE向量
- Vector2 deVector = length / 2 * -Vector2.Normalize(cdVector);//需用到单位向量
- //求D点,ePt-dPt = deVector;
- PointF dPt = new PointF();
- dPt.X = ePt.X - deVector.X;
- dPt.Y = ePt.Y - deVector.Y;
-
- triangle[0] = midPoint;
- triangle[1] = dPt;
- triangle[2] = cPt;
- }


