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

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

    }
}

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

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

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

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

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

 

参考:

https://greatmaps.codeplex.com/

 

作者:阿凡卢

出处:http://www.cnblogs.com/luxiaoxun/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

http://www.cnblogs.com/luxiaoxun/p/3530341.html

时间: 2024-11-03 18:23:54

GMap.Net开发之技巧小结的相关文章

JS常用函数和常用技巧小结_javascript技巧

学习和工作的过程中总结的干货,包括常用函数.常用js技巧.常用正则表达式.git笔记等.为刚接触前端的童鞋们提供一个简单的查询的途径,也以此来缅怀我的前端学习之路. Ajax请求 jquery ajax函数 我自己封装了一个ajax的函数,代码如下: var Ajax = function(url, type success, error) { $.ajax({ url: url, type: type, dataType: 'json', timeout: 10000, success: fu

Jsp应用技巧小结

js|技巧     1.Jsp页面输入内容显示时tag文字和换行处理 从输入页面的文本输入框输入两行文字: lt;br>huuhh<br>lt;b></b><BR> 确认后转入下一确认页面时,发现换行信息已被自动去除,另外,tag文字被以处理html的方式处理,没有正确显示输入信息. 解决方案 在确认页面对所要显示的信息做如下处理上面的问题就迎刃而解了:假设要显示的信息保存在一个字符串变量comment中,在显示之前对它做如下处理: lt;td bgcolo

手机开发必备技巧:javascript及CSS功能代码分享

这篇文章主要介绍了手机开发必备技巧:javascript及CSS功能代码分享,本文讲解了viewport(可视区域)操作.链接操作.javascript事件等内容,需要的朋友可以参考下     1. viewport: 也就是可视区域.对于桌面浏览器,我们都很清楚viewport是什么,就是出去了所有工具栏.状态栏.滚动条等等之后用于看网页的区域, 这是真正有效的区域.由于移动设备屏幕宽度不同于传统web,因此我们需要改变viewport; 实际上我们可以操作的属性有4 个:  代码如下: wi

php数组键名技巧小结

 这篇文章主要介绍了php数组键名技巧小结,分析了php数组键名常见的使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下     本文较为详细的总结了php数组键名的技巧.分享给大家供大家参考.具体分析如下: 1.$arr[true] 等价于 $arr[1]:$arr[false] 等价于 $arr[0]. 2.使null做为键名,相当于创建或覆盖一个$arr[null],可以使用$arr[null]或$arr[""]来访问. 3.使用带小数点的数字作为键名时,键名会自动截取整数部分

iOS开发实用技巧—在手机浏览器头部弹出app应用下载提示

iOS开发实用技巧-在手机浏览器头部弹出app应用下载提示 本文介绍其简单使用: 第一步:在本地建立一个访问的服务端. 打开本地终端,在本地新建一个文件夹,在该文件夹中存放测试的html页面. 在终端输入一行代码:python -m SimpleHTTPServer ---第二步:配置环境 打开safari,偏好设置->勾选 "在菜单栏中显示开发菜单",如下图所示. 第三步:编写html文件 在测试文件appdown.html中编写的测试代码如下: `说明`444934666为对

iOS开发实用技巧—项目新特性页面的处理

iOS开发实用技巧篇-项目新特性页面的处理 说明:本文主要说明在项目开发中会涉及到的最最简单的新特性界面(实用UIScrollView展示多张图片的轮播)的处理. 代码示例: 新建一个专门的处理新特性界面的控制器,可以实用代码也可以用xib,在这里实用纯代码方式,创建一个控制器NewfeatureViewController. 头文件代码: 1 // 2 // JMNewfeatureViewController.h 3 // 4 5 #import <UIKit/UIKit.h> 6 7 t

Linux c c++ 开发调试技巧

看到一篇介绍 linux c/c++ 开发调试技巧的文章,感觉挺使用,哪来和大家分享.  通向 UNIX 天堂的 10 个阶梯Author: Arpan Sen, 高级技术人员, Systems Documentation, Inc. (SDI) 讨论几种可以帮助 C++ 开发人员节省时间的技巧和免费工具.  C++ 开发人员在日常工作中通常要完成多个任务:开发新软件.调试其他人的代码.制订测试计划.为每个计划开发测试.管理衰退软件(regression suite)等等.在多种角色之间频繁转换

php日期操作技巧小结_php技巧

本文实例总结了php日期操作技巧.分享给大家供大家参考,具体如下: 1.php将表单里面获取的日期格式转换成统一的格式 2015-9-9 都统一转换成 2015-09-09 这样在数据库都是统一的格式,方便以后查询 $year = "2015"; $month = "9"; $day = "09"; var_dump(checkdate($month,$day, $year));//月和日带有前导0都是符合格式的 if(checkdate($mo

你值得拥有的Android Studio开发小技巧_Android

上一次发过了一个介绍Studio的,这里再发一个补充下. 我们都知道,Android Studio的功能是非常强大的,也是很智能的.如果有人告诉你学Android开发要用命令行,你可以告诉他Android Studio是谷歌提供给开发者提升效率的.试想想你脑袋里有一堆想法,但是开发编码效率太低导致想法都丢失了或者兴趣消失了多可怕对不.好了废话不多说,这里介绍几个比较好用的技巧和快捷键,提升我们的编码效率. 1.完成一句代码 如果我们调用了一个方法,并给方法传递了参数,这个时候如果我们要结束这个语