自己定义的两个继承GMapMarker和GMapRoute类,可以添加自定义的图标标注、和点击某点可以用红色高亮重绘

一、GMarkerGoogleExt类(第一种)

OnRender函数主要重新定义了在图标外面绘制矩形框

  1. public class GMarkerGoogleExt:GMarkerGoogle
  2. {
  3. public double KiloPos { get; set; } = 0.0;
  4. public string StationName { get; set; } = "";
  5. public string TrackName { get; set; } = "";
  6. public GMarkerGoogleExt(PointLatLng p,GMarkerGoogleType MarkerType,Color b,int r,bool isCircle):base(p,MarkerType)
  7. {
  8. this.IsHitTestVisible = true;
  9. InRouteIndex = -1;
  10. this.isCircle = isCircle;
  11. InitialCircleParas(b, r);
  12. }
  13. public GMarkerGoogleExt(PointLatLng p, GMarkerGoogleType MarkerType) : base(p, MarkerType)
  14. {
  15. this.IsHitTestVisible = true;
  16. InRouteIndex = -1;
  17. this.isCircle = false;
  18. InitialCircleParas(Color.Red);
  19. }
  20. public GMarkerGoogleExt(PointLatLng p, Bitmap Image, Color b, int r, bool isCircle) : base(p, Image)
  21. {
  22. this.IsHitTestVisible = true;
  23. InRouteIndex = -1;
  24. this.isCircle = isCircle;
  25. InitialCircleParas(b, r);
  26. }
  27. public GMarkerGoogleExt(PointLatLng p, Bitmap Image, GMapRouteExt route, Color b, int r, bool isCircle) : base(p, Image)
  28. {
  29. this.BindRoute = route;
  30. InRouteIndex = -1;
  31. this.isCircle = isCircle;
  32. InitialCircleParas(b, r);
  33. }
  34. private void InitialCircleParas(Color b, int r)
  35. {
  36. Size = new System.Drawing.Size(2 * r, 2 * r);
  37. Offset = new System.Drawing.Point(-r, -r);
  38. OutPen = new Pen(b, 2);
  39. }
  40. private void InitialCircleParas(Color b)
  41. {
  42. OutPen = new Pen(b, 2);
  43. }
  44. private bool isCircle;
  45. public GMapRouteExt BindRoute { get; set; }
  46. public int InRouteIndex { get; set; }
  47. public Pen Pen
  48. {
  49. get;
  50. set;
  51. }
  52. public Pen OutPen
  53. {
  54. get;
  55. set;
  56. }
  57. public bool AddRect { get; set; }
  58. public override void OnRender(Graphics g)
  59. {
  60. try
  61. {
  62. if (g == null)
  63. return;
  64. Rectangle rect = new Rectangle(LocalPosition.X, LocalPosition.Y, Size.Width, Size.Height);
  65. if (!isCircle)
  66. {
  67. base.OnRender(g);
  68. if (this.AddRect)
  69. {
  70. g.DrawRectangle(new Pen(Color.Red, this.OutPen.Width), rect);
  71. }
  72. }
  73. else
  74. {
  75. if (OutPen != null)
  76. {
  77. g.DrawEllipse(OutPen, rect);
  78. }
  79. if (this.AddRect)
  80. {
  81. g.DrawRectangle(new Pen(Color.Red, this.OutPen.Width), rect);
  82. }
  83. }
  84. }
  85. catch(Exception ex)
  86. {
  87. }
  88. }
  89. public override void Dispose()
  90. {
  91. if (Pen != null)
  92. {
  93. Pen.Dispose();
  94. Pen = null;
  95. }
  96. if (OutPen != null)
  97. {
  98. OutPen.Dispose();
  99. OutPen = null;
  100. }
  101. base.Dispose();
  102. }
  103. }

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