GMap.Net开发之技巧小结

1、在GMap地图上,如果要让添加的图标(Marker)有个高亮(highlight)的效果,可以在MouseOver到Marker的时候设置Marker外观效果。

如果要让图标有个报警闪烁的效果,可以设置一个定时器,在定时器中改变Marker的外观,或者是用GDI来画圆闪动,带报警效果的Marker如下:

GMap.Net开发之技巧小结
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using GMap.NET;

using GMap.NET.WindowsForms;

using System.Drawing;

using System.Windows.Forms;



namespace GMapWinFormDemo

{

    class GMapMarkerImage : GMapMarker

    {

        private Image image;

        public Image Image

        {

            get

            {

                return image;

            }

            set

            {

                image = value;

                if (image != null)

                {

                    this.Size = new Size(image.Width, image.Height);

                }

            }

        }



        public bool IsHighlight = true;

        public Pen HighlightPen { set; get; }



        public Pen FlashPen { set; get; }

        private Timer flashTimer = new Timer();



        private int radius;

        private int flashRadius;



        public GMapMarkerImage(GMap.NET.PointLatLng p, Image image)

            : base(p)

        {

            Size = new System.Drawing.Size(image.Width, image.Height);

            Offset = new System.Drawing.Point(-Size.Width / 2, -Size.Height / 2);

            Image = image;

            HighlightPen = new System.Drawing.Pen(Brushes.Red,2);

            radius = Size.Width >= Size.Height ? Size.Width : Size.Height;

            flashTimer.Interval = 10;

            flashTimer.Tick += new EventHandler(flashTimer_Tick);

        }



        public void StartFlash()

        {

            flashTimer.Start();

        }





        void flashTimer_Tick(object sender, EventArgs e)

        {

            if (FlashPen == null)

            {

                FlashPen = new Pen(Brushes.Red, 3);

                flashRadius = radius;

            }

            else

            {

                flashRadius += radius/4;

                if (flashRadius >= 2 * radius)

                {

                    flashRadius = radius;

                    FlashPen.Color = Color.FromArgb(255, Color.Red);

                }

                else

                {

                    Random rand = new Random();

                    int alpha = rand.Next(255);

                    FlashPen.Color = Color.FromArgb(alpha, Color.Red);

                }

            }

            this.Overlay.Control.Refresh();

        }



        public void StopFlash()

        {

            flashTimer.Stop();

            if (FlashPen != null)

            {

                FlashPen.Dispose();

                FlashPen = null;

            }

            this.Overlay.Control.Refresh();

        }



        public override void OnRender(Graphics g)

        {

            if (image == null)

                return;



            Rectangle rect = new Rectangle(LocalPosition.X, LocalPosition.Y, Size.Width, Size.Height);

            g.DrawImage(image, rect);



            if (IsMouseOver && IsHighlight)

            {

                g.DrawRectangle(HighlightPen,rect);

            }



            if (FlashPen != null)

            {

                g.DrawEllipse(FlashPen,

                    new Rectangle(LocalPosition.X - flashRadius / 2 + Size.Width/2, LocalPosition.Y - flashRadius / 2+Size.Height/2, flashRadius, flashRadius));

            }

        }



        public override void Dispose()

        {

            if (HighlightPen != null)

            {

                HighlightPen.Dispose();

                HighlightPen = null;

            }



            if (FlashPen != null)

            {

                FlashPen.Dispose();

                FlashPen = null;

            }



            base.Dispose();

        }

    }

}
View Code

2、可以旋转角度的Marker,比如可以将一个箭头图标旋转一定角度来指向一个轨迹路线,代码如下:

GMap.Net开发之技巧小结
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Drawing;

using GMap.NET;

using GMap.NET.WindowsForms;

using GMapWinFormDemo.Properties;



namespace GMapWinFormDemo

{

    class GMapMarkerDirection : GMapMarker

    {

        private float Ang;



        private Image image;

        public Image Image

        {

            get

            {

                return image;

            }

            set

            {

                image = value;

                if (image != null)

                {

                    this.Size = new Size(image.Width, image.Height);

                }

            }

        }



        public GMapMarkerDirection(PointLatLng p, Image image, float angle)

            : base(p)

        {

            Ang = angle;

            Image = image;

            Size = new System.Drawing.Size(image.Width, image.Height);

            Offset = new System.Drawing.Point(-Size.Width / 2, -Size.Height / 2);

        }



        public override void OnRender(Graphics g)

        {



            g.DrawImageUnscaled(RotateImage(Image, Ang), LocalPosition.X, LocalPosition.Y);

        }



        //http://www.codeproject.com/KB/graphics/rotateimage.aspx

        //Author : James T. Johnson

        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;

        }



    }

}
View Code

3、在点击图标Marker的时候出现ContextMenuStrip:

GMap.Net开发之技巧小结
        void mapControl_OnMarkerClick(GMapMarker item, MouseEventArgs e)

        {

            if (e.Button == System.Windows.Forms.MouseButtons.Left)

            {

                this.contextMenuStrip1.Show(Cursor.Position);

                if (item is GMapMarkerImage)

                {

                    currentMarker = item as GMapMarkerImage;

                }

            }

        }
View Code

4、随地图放大缩小的圆,代码来自官方Demo:

GMap.Net开发之技巧小结
using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Drawing;

using GMap.NET;

using GMap.NET.WindowsForms;



namespace GMapWinFormDemo

{

    public class GMapMarkerCircle : GMapMarker

    {

        /// <summary>

        /// In Meters

        /// </summary>

        public int Radius;



        /// <summary>

        /// specifies how the outline is painted

        /// </summary>

        public Pen Stroke = new Pen(Color.FromArgb(155, Color.MidnightBlue));



        /// <summary>

        /// background color

        /// </summary>

        public Brush Fill = new SolidBrush(Color.FromArgb(155, Color.AliceBlue));



        /// <summary>

        /// is filled

        /// </summary>

        public bool IsFilled = true;



        public GMapMarkerCircle(PointLatLng p)

            : base(p)

        {

            Radius = 100; // 100m

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

        }



        public override void Dispose()

        {

            if (Stroke != null)

            {

                Stroke.Dispose();

                Stroke = null;

            }



            if (Fill != null)

            {

                Fill.Dispose();

                Fill = null;

            }



            base.Dispose();

        }

    }

}
View Code

关键就是如何在放大缩小时确定圆的半径大小,半径大小为:

int R = (int)((Radius) / Overlay.Control.MapProvider.Projection.GetGroundResolution((int)Overlay.Control.Zoom, Position.Lat)) * 2;

通过当前的缩放比例zoom和圆心的纬度来得到地图在此条件下分辨率(resolution),分辨率的大小为一个像素大小所代表的距离(单位为米)。

所以当我采用画多边形的方式在地图上画圆时,实际得到的圆在小半径和地球赤道附近下是个圆,但是在纬度较大的地方画的圆就变成了椭圆,代码如下:

GMap.Net开发之技巧小结
namespace GMapWinFormDemo

{

    public static class CirclePolygon

    {

        public static GMapPolygon CreateCircle(PointLatLng center, double radius, string name)

        {

            List<PointLatLng> pList = new List<PointLatLng>();

            int segments = 100000;

            double seg = 2 * Math.PI / segments;

            for (int i = 0; i < segments; ++i)

            {

                double theta = i * seg;

                double a = center.Lat + Math.Cos(theta) * radius;

                double b = center.Lng + Math.Sin(theta) * radius;

                pList.Add(new PointLatLng(a, b));

            }

            GMapPolygon circle = new GMapPolygon(pList, name);

            circle.Stroke = new Pen(Brushes.Red, 1);

            return circle;

        }

    }

}
View Code

5、保存地图为图片:

GMap.Net开发之技巧小结
        private void buttonSaveMap_Click(object sender, EventArgs e)

        {

            try

            {

                using (SaveFileDialog dialog = new SaveFileDialog())

                {

                    dialog.Filter = "PNG (*.png)|*.png";

                    dialog.FileName = "GMap.NET image";

                    Image image = this.mapControl.ToImage();

                    if (image != null)

                    {

                        using (image)

                        {

                            if (dialog.ShowDialog() == DialogResult.OK)

                            {

                                string fileName = dialog.FileName;

                                if (!fileName.EndsWith(".png", StringComparison.OrdinalIgnoreCase))

                                {

                                    fileName += ".png";

                                }

                                image.Save(fileName);

                                MessageBox.Show("图片已保存: " + dialog.FileName, "GMap.NET", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);

                            }

                        }

                    }

                }

            }

            catch (Exception exception)

            {

                MessageBox.Show("图片保存失败: " + exception.Message, "GMap.NET", MessageBoxButtons.OK, MessageBoxIcon.Hand);

            }

        }
View Code

 

参考:

https://greatmaps.codeplex.com/

 

你可能感兴趣的:(.net)