自己定义的两个继承GMapMarker和GMapRoute类,可以添加自定义的图标标注、和点击某点可以用红色高亮重绘
一、GMarkerGoogleExt类(第一种)
OnRender函数主要重新定义了在图标外面绘制矩形框
public class GMarkerGoogleExt:GMarkerGoogle
{
public double KiloPos { get; set; } = 0.0;
public string StationName { get; set; } = "";
public string TrackName { get; set; } = "";
public GMarkerGoogleExt(PointLatLng p,GMarkerGoogleType MarkerType,Color b,int r,bool isCircle):base(p,MarkerType)
{
this.IsHitTestVisible = true;
InRouteIndex = -1;
this.isCircle = isCircle;
InitialCircleParas(b, r);
}
public GMarkerGoogleExt(PointLatLng p, GMarkerGoogleType MarkerType) : base(p, MarkerType)
{
this.IsHitTestVisible = true;
InRouteIndex = -1;
this.isCircle = false;
InitialCircleParas(Color.Red);
}
public GMarkerGoogleExt(PointLatLng p, Bitmap Image, Color b, int r, bool isCircle) : base(p, Image)
{
this.IsHitTestVisible = true;
InRouteIndex = -1;
this.isCircle = isCircle;
InitialCircleParas(b, r);
}
public GMarkerGoogleExt(PointLatLng p, Bitmap Image, GMapRouteExt route, Color b, int r, bool isCircle) : base(p, Image)
{
this.BindRoute = route;
InRouteIndex = -1;
this.isCircle = isCircle;
InitialCircleParas(b, r);
}
private void InitialCircleParas(Color b, int r)
{
Size = new System.Drawing.Size(2 * r, 2 * r);
Offset = new System.Drawing.Point(-r, -r);
OutPen = new Pen(b, 2);
}
private void InitialCircleParas(Color b)
{
OutPen = new Pen(b, 2);
}
private bool isCircle;
public GMapRouteExt BindRoute { get; set; }
public int InRouteIndex { get; set; }
public Pen Pen
{
get;
set;
}
public Pen OutPen
{
get;
set;
}
public bool AddRect { get; set; }
public override void OnRender(Graphics g)
{
try
{
if (g == null)
return;
Rectangle rect = new Rectangle(LocalPosition.X, LocalPosition.Y, Size.Width, Size.Height);
if (!isCircle)
{
base.OnRender(g);
if (this.AddRect)
{
g.DrawRectangle(new Pen(Color.Red, this.OutPen.Width), rect);
}
}
else
{
if (OutPen != null)
{
g.DrawEllipse(OutPen, rect);
}
if (this.AddRect)
{
g.DrawRectangle(new Pen(Color.Red, this.OutPen.Width), rect);
}
}
}
catch(Exception ex)
{
}
}
public override void Dispose()
{
if (Pen != null)
{
Pen.Dispose();
Pen = null;
}
if (OutPen != null)
{
OutPen.Dispose();
OutPen = null;
}
base.Dispose();
}
}
二、GMarkerGoogleExt(第二种)
OnRender(),改变图标的位置和角度—主要为了重现轨迹动画
首先定义一个字段 private float _angle;
public override void OnRender(Graphics g)
{
try
{
if (g == null)
return;
var bitmap = RotateImage(this.Bitmap, _angle);
var offsetX = LocalPosition.X - ((LocalPosition.X - 30) + bitmap.Width / 2);
var offsetY = LocalPosition.Y - ((LocalPosition.Y - 60) + bitmap.Height / 2);
//g.DrawImageUnscaled(bitmap, LocalPosition.X + offsetX, LocalPosition.Y + offsetY);
g.DrawImageUnscaled(bitmap, LocalPosition.X , LocalPosition.Y);
}
catch (Exception ex)
{
}
}
public float _angle;
#region 旋转
private static Bitmap RotateImage(Image image, float angle)
{
if (image == null)
throw new ArgumentNullException("image");
const double pi2 = Math.PI / 2.0;
// Why can't C# allow these to be const, or at least readonly
// *sigh* I'm starting to talk like Christian Graus :omg:
double oldWidth = (double)image.Width;
double oldHeight = (double)image.Height;
// Convert degrees to radians
double theta = ((double)angle) * Math.PI / 180.0;
double locked_theta = theta;
// Ensure theta is now [0, 2pi)
while (locked_theta < 0.0)
locked_theta += 2 * Math.PI;
double newWidth, newHeight;
int nWidth, nHeight; // The newWidth/newHeight expressed as ints
#region Explaination of the calculations
/*
* The trig involved in calculating the new width and height
* is fairly simple; the hard part was remembering that when
* PI/2 <= theta <= PI and 3PI/2 <= theta < 2PI the width and
* height are switched.
*
* When you rotate a rectangle, r, the bounding box surrounding r
* contains for right-triangles of empty space. Each of the
* triangles hypotenuse's are a known length, either the width or
* the height of r. Because we know the length of the hypotenuse
* and we have a known angle of rotation, we can use the trig
* function identities to find the length of the other two sides.
*
* sine = opposite/hypotenuse
* cosine = adjacent/hypotenuse
*
* solving for the unknown we get
*
* opposite = sine * hypotenuse
* adjacent = cosine * hypotenuse
*
* Another interesting point about these triangles is that there
* are only two different triangles. The proof for which is easy
* to see, but its been too long since I've written a proof that
* I can't explain it well enough to want to publish it.
*
* Just trust me when I say the triangles formed by the lengths
* width are always the same (for a given theta) and the same
* goes for the height of r.
*
* Rather than associate the opposite/adjacent sides with the
* width and height of the original bitmap, I'll associate them
* based on their position.
*
* adjacent/oppositeTop will refer to the triangles making up the
* upper right and lower left corners
*
* adjacent/oppositeBottom will refer to the triangles making up
* the upper left and lower right corners
*
* The names are based on the right side corners, because thats
* where I did my work on paper (the right side).
*
* Now if you draw this out, you will see that the width of the
* bounding box is calculated by adding together adjacentTop and
* oppositeBottom while the height is calculate by adding
* together adjacentBottom and oppositeTop.
*/
#endregion
double adjacentTop, oppositeTop;
double adjacentBottom, oppositeBottom;
// We need to calculate the sides of the triangles based
// on how much rotation is being done to the bitmap.
// Refer to the first paragraph in the explaination above for
// reasons why.
if ((locked_theta >= 0.0 && locked_theta < pi2) ||
(locked_theta >= Math.PI && locked_theta < (Math.PI + pi2)))
{
adjacentTop = Math.Abs(Math.Cos(locked_theta)) * oldWidth;
oppositeTop = Math.Abs(Math.Sin(locked_theta)) * oldWidth;
adjacentBottom = Math.Abs(Math.Cos(locked_theta)) * oldHeight;
oppositeBottom = Math.Abs(Math.Sin(locked_theta)) * oldHeight;
}
else
{
adjacentTop = Math.Abs(Math.Sin(locked_theta)) * oldHeight;
oppositeTop = Math.Abs(Math.Cos(locked_theta)) * oldHeight;
adjacentBottom = Math.Abs(Math.Sin(locked_theta)) * oldWidth;
oppositeBottom = Math.Abs(Math.Cos(locked_theta)) * oldWidth;
}
newWidth = adjacentTop + oppositeBottom;
newHeight = adjacentBottom + oppositeTop;
nWidth = (int)Math.Ceiling(newWidth);
nHeight = (int)Math.Ceiling(newHeight);
Bitmap rotatedBmp = new Bitmap(nWidth, nHeight);
using (Graphics g = Graphics.FromImage(rotatedBmp))
{
// This array will be used to pass in the three points that
// make up the rotated image
Point[] points;
/*
* The values of opposite/adjacentTop/Bottom are referring to
* fixed locations instead of in relation to the
* rotating image so I need to change which values are used
* based on the how much the image is rotating.
*
* For each point, one of the coordinates will always be 0,
* nWidth, or nHeight. This because the Bitmap we are drawing on
* is the bounding box for the rotated bitmap. If both of the
* corrdinates for any of the given points wasn't in the set above
* then the bitmap we are drawing on WOULDN'T be the bounding box
* as required.
*/
if (locked_theta >= 0.0 && locked_theta < pi2)
{
points = new Point[] {
new Point( (int) oppositeBottom, 0 ),
new Point( nWidth, (int) oppositeTop ),
new Point( 0, (int) adjacentBottom )
};
}
else if (locked_theta >= pi2 && locked_theta < Math.PI)
{
points = new Point[] {
new Point( nWidth, (int) oppositeTop ),
new Point( (int) adjacentTop, nHeight ),
new Point( (int) oppositeBottom, 0 )
};
}
else if (locked_theta >= Math.PI && locked_theta < (Math.PI + pi2))
{
points = new Point[] {
new Point( (int) adjacentTop, nHeight ),
new Point( 0, (int) adjacentBottom ),
new Point( nWidth, (int) oppositeTop )
};
}
else
{
points = new Point[] {
new Point( 0, (int) adjacentBottom ),
new Point( (int) oppositeBottom, 0 ),
new Point( (int) adjacentTop, nHeight )
};
}
g.DrawImage(image, points);
}
return rotatedBmp;
}
三、GMapRouteExt类
/// <summary>
/// GMapRoute扩展
/// </summary>
public class GMapRouteExt:GMapRoute
{
public GMapRouteExt(string name):base(name)
{
Markers = new List<GMarkerGoogleExt>();
this.Stroke = new Pen(GetRandomColor(), 5);
PointsInterval = 1;
}
public void updateMakers(int index)
{
if(this.Overlay.Markers.Contains((this.Markers[index]))) this.Overlay.Markers.Remove(this.Markers[index]);
}
public GMapRouteExt(List<PointLatLng> points, string name) : base(points,name)
{
this.Stroke = new Pen(GetRandomColor(), 5);
Markers = new List<GMarkerGoogleExt>();
PointsInterval = 1;
}
public GMapRouteExt(PointLatLng[] points,string name) : base(points,name)
{
this.Stroke = new Pen(GetRandomColor(), 5);
initialRouteMarker();
PointsInterval = 1;
}
private void initialRouteMarker ()
{
if ( Markers == null) Markers = new List<GMarkerGoogleExt>(); ;
System.Drawing.Color color;
while ((color = GetRandomColor()) == this.Stroke.Color) ;
foreach (PointLatLng marker in this.Points)
{
GMarkerGoogleExt markerInRoute = new GMarkerGoogleExt(marker,GMarkerGoogleType.blue_dot,color,5,true);
markerInRoute.Size = new Size(8, 8);
markerInRoute.BindRoute = this;
Markers.Add(markerInRoute);
};
}
/// <summary>
/// 返回随机颜色
/// </summary>
/// <returns></returns>
public Color GetRandomColor()
{
Random RandomNum_First = new Random((int)DateTime.Now.Ticks);
// 对于C#的随机数,没什么好说的
System.Threading.Thread.Sleep(RandomNum_First.Next(50));
Random RandomNum_Sencond = new Random((int)DateTime.Now.Ticks);
// 为了在白色背景上显示,尽量生成深色
int int_Red = RandomNum_First.Next(256);
int int_Green = RandomNum_Sencond.Next(256);
int int_Blue = (int_Red + int_Green > 400) ? 0 : 400 - int_Red - int_Green;
int_Blue = (int_Blue > 255) ? 255 : int_Blue;
return Color.FromArgb(int_Red, int_Green, int_Blue);
}
/// <summary>
/// 初始化加入指定的图层中
/// </summary>
/// <param name="points"></param>
/// <param name="name"></param>
/// <param name="overlay"></param>
public GMapRouteExt(GMap.NET.PointLatLng[] points, string name,GMapOverlay overlay) : base(points, name)
{
initialRouteMarker();
overlay.Routes.Add(this);
PointsInterval = 1;
this.markersVisible = false;
}
public List<GMarkerGoogleExt> Markers
{
get;
set;
}
private bool markersVisible;
public bool MarkersVisible
{
get
{
return this.markersVisible;
}
set
{
if (value) ShowMarkers(false);
else HideMarkers();
this.markersVisible = value;
}
}
/// <summary>
/// 显示标注
/// </summary>
public void ShowMarkers(bool isForced)
{
if (this.MarkersVisible&&!isForced) return;
if (this.Markers.Count != this.Points.Count)
{
this.RemoveMarkers();
this.Markers.Clear();
initialRouteMarker();
this.ShowMarkers(true);
}
else
{
foreach (GMarkerGoogleExt marker in Markers)
{
if (!this.Overlay.Markers.Contains(marker)) this.Overlay.Markers.Add(marker);
marker.IsVisible = true;
}
}
}
/// <summary>
/// 隐藏标注
/// </summary>
public void HideMarkers()
{
if (!markersVisible) return;
foreach (GMapMarker marker in Markers)
{
marker.IsVisible = false;
}
}
/// <summary>
/// 移除标注
/// </summary>
public void RemoveMarkers()
{
foreach (GMapMarker marker in Markers)
{
if (this.Overlay.Markers.Contains(marker)) this.Overlay.Markers.Remove(marker);
}
}
/// <summary>
/// 公里标间隔
/// </summary>
public float PointsInterval { get; set; }
/// <summary>
/// 公里标开始位置
/// </summary>
public double StartKiloPos { get; set; }
/// <summary>
/// 绑定的开始标注
/// </summary>
public GMarkerGoogleExt BindeStartMarker { get; set; }
/// <summary>
/// 绑定的结束标注
/// </summary>
public GMarkerGoogleExt BindeEndMarker { get; set; }
/// <summary>
/// 获取以分隔符划分的第一部分
/// </summary>
/// <param name="splitter">分隔符</param>
/// <returns>名字的第一部分</returns>
public string GetFirstPartOfName(char splitter)
{
return this.Name.Split(splitter)[0];
}
/// <summary>
/// 编号
/// </summary>
public int RouteID { get; set; }
/// <summary>
/// 高度
/// </summary>
public List<double> Height { get; set; }
public List<double> KiloPos { get; set; }
}