zoukankan      html  css  js  c++  java
  • GMap.Net开发之技巧小结

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

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

    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,比如可以将一个箭头图标旋转一定角度来指向一个轨迹路线,代码如下:

    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:

            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:

    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),分辨率的大小为一个像素大小所代表的距离(单位为米)。

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

    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、保存地图为图片:

            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://github.com/luxiaoxun/MapDownloader

    参考:

    https://greatmaps.codeplex.com/

  • 相关阅读:
    [bzoj 4553][Tjoi2016&Heoi2016]序列
    [bzoj 5143][Ynoi 2018]五彩斑斓的世界
    [bzoj 4939][Ynoi 2016]掉进兔子洞
    luogu_P3674 小清新人渣的本愿
    [bzoj 2809][Apio2012]dispatching
    [bzoj 3110][zjoi 2013]K大数查询
    Entity Framework技巧系列之九
    Entity Framework技巧系列之八
    Entity Framework技巧系列之七
    Entity Framework技巧系列之六
  • 原文地址:https://www.cnblogs.com/luxiaoxun/p/3530341.html
Copyright © 2011-2022 走看看