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;
}