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 / , -Size.Height / );
Image = image;
HighlightPen = new System.Drawing.Pen(Brushes.Red,);
radius = Size.Width >= Size.Height ? Size.Width : Size.Height;
flashTimer.Interval = ;
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, );
flashRadius = radius;
}
else
{
flashRadius += radius/;
if (flashRadius >= * radius)
{
flashRadius = radius;
FlashPen.Color = Color.FromArgb(, Color.Red);
}
else
{
Random rand = new Random();
int alpha = rand.Next();
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 / + Size.Width/, LocalPosition.Y - flashRadius / +Size.Height/, 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 / , -Size.Height / );
} 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 += * 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, ),
new Point( nWidth, (int) oppositeTop ),
new Point( , (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, )
};
}
else if (locked_theta >= Math.PI && locked_theta < (Math.PI + pi2))
{
points = new Point[] {
new Point( (int) adjacentTop, nHeight ),
new Point( , (int) adjacentBottom ),
new Point( nWidth, (int) oppositeTop )
};
}
else
{
points = new Point[] {
new Point( , (int) adjacentBottom ),
new Point( (int) oppositeBottom, ),
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(, Color.MidnightBlue)); /// <summary>
/// background color
/// </summary>
public Brush Fill = new SolidBrush(Color.FromArgb(, Color.AliceBlue)); /// <summary>
/// is filled
/// </summary>
public bool IsFilled = true; public GMapMarkerCircle(PointLatLng p)
: base(p)
{
Radius = ; // 100m
IsHitTestVisible = false;
} public override void OnRender(Graphics g)
{
int R = (int)((Radius) / Overlay.Control.MapProvider.Projection.GetGroundResolution((int)Overlay.Control.Zoom, Position.Lat)) * ; if (IsFilled)
{
g.FillEllipse(Fill, new System.Drawing.Rectangle(LocalPosition.X - R / , LocalPosition.Y - R / , R, R));
}
g.DrawEllipse(Stroke, new System.Drawing.Rectangle(LocalPosition.X - R / , LocalPosition.Y - R / , 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)) * ;

通过当前的缩放比例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 = ;
double seg = * Math.PI / segments;
for (int i = ; 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, );
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://github.com/luxiaoxun/MapDownloader

参考:

https://greatmaps.codeplex.com/

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

  1. iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式

    iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式 说明: 1)该文简短介绍在iOS开发中遍历字典.数组和集合的几种常见方式. 2)该文对应的代码可以在下面的地址获得:https:// ...

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

    iOS开发实用技巧—在手机浏览器头部弹出app应用下载提示 本文介绍其简单使用: 第一步:在本地建立一个访问的服务端.  打开本地终端,在本地新建一个文件夹,在该文件夹中存放测试的html页面.   ...

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

    iOS开发实用技巧篇—项目新特性页面的处理 说明:本文主要说明在项目开发中会涉及到的最最简单的新特性界面(实用UIScrollView展示多张图片的轮播)的处理. 代码示例: 新建一个专门的处理新特性 ...

  4. Windows统一平台: 开发小技巧

    Windows统一平台: 开发小技巧 技巧一: 在手机端拓展你应用的显示区域.(WP8.1中也适用) 对于Windows Phone系统的手机, 手机屏幕最上方为系统状态栏(System Tray), ...

  5. flex开发小技巧集锦

    关于flex开发网上有非常多的相关信息介绍,因此我们要想学习关于flex开发的知识信息技能是一件非常简单和方便的事情.而针对于flex开发小编要告诉大家的是一些flex开发小技巧.利用这些小技巧能够有 ...

  6. PowerDesigner实用技巧小结(3)

    PowerDesigner实用技巧小结(3) PowerDesigner 技巧小结 sqlserver数据库databasevbscriptsqldomain 1.PowerDesigner 使用 M ...

  7. 10个Visual Studio原生开发调试技巧

    10个Visual Studio原生开发调试技巧(1) 2013-05-29 13:30 佚名 开源中国 我要评论(1) 字号:T | T 以下的列表中你可以看到写原生开发的调试技巧(接着以前的文章来 ...

  8. 移动平台3G手机网站前端开发布局技巧

    本文转载至:移动平台3G手机网站前端开发布局技巧汇总 - 前端开发-武方博 您或许正在或准备参与一个WepApp项目,您或许正在Google搜索mobile development相 关的文章,您或许 ...

  9. PowerDesigner实用技巧小结(2)

    PowerDesigner实用技巧小结 1.ORACLE数据库建模时,由于ORACLE的表名.字段名如果是小写会有一定的麻烦,需要将小写转化为大写? (1)在打开pdm的情况下,进入Tools-Mod ...

随机推荐

  1. Maven打包排除不需要的文件。

    pom.xml <!-- package打包排除掉一些配置文件 --> <plugin> <groupId>org.apache.maven.plugins< ...

  2. MySQL 编程的6个重要的技巧

    一.每一行命令都是用分号(;)作为结束 对于MySQL,第一件你必须牢记的是它的每一行命令都是用分号(;)作为结束的,但当一行MySQL被插入在PHP代码中时,最好把后面的分号省略掉,例如:   二. ...

  3. struts2 结果页面配置

    <result>标签: * 属性: * name:逻辑视图的名称 * type:结果页面类型. * dispatcher         :转发.默认值. * redirect       ...

  4. 手动fsck修复

    [转自]http://blog.chinaunix.net/uid-26719405-id-3781541.html 由于硬盘常年读写,系统会造成系统文件损坏,导致重启后无法登陆到系统, fsck不仅 ...

  5. iOS UIViewController 和 nib 相关的3个方法

    iOS UIViewController 的 awakeFromNib 以及 - (id)initWithCoder:(NSCoder *)aDecoder 和 - (instancetype)ini ...

  6. yii框架详解 之 CWebApplication 运行流程分析

    在 程序入口处,index.php 用一句 Yii::createWebApplication($config)->run();  开始了app的运行. 那么,首先查看 CWebApplicat ...

  7. cookie相关内容

    ----客户端删除cookie <script type="text/javascript">                        function DelC ...

  8. Divide and conquer:Sumsets(POJ 2549)

    数集 题目大意:给定一些数的集合,要你求出集合中满足a+b+c=d的最大的d(每个数只能用一次) 这题有两种解法, 第一种就是对分,把a+b的和先求出来,然后再枚举d-c,枚举的时候输入按照降序搜索就 ...

  9. Mathematics:Prime Path(POJ 3126)

    素数通道 题目大意:给定两个素数a,b,要你找到一种变换,使得每次变换都是素数,如果能从a变换到b,则输出最小步数,否则输出Impossible 水题,因为要求最小步数,所以我们只需要找到到每个素数的 ...

  10. codeforces 490C. Hacking Cypher 解题报告

    题目链接:http://codeforces.com/problemset/problem/490/C 题目意思:给出一个可能有10^6 位长的字符串且没有前导0的整数,问能否一分为二,使得前面的一部 ...