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 / , -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开发之技巧小结的更多相关文章
- iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式
iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式 说明: 1)该文简短介绍在iOS开发中遍历字典.数组和集合的几种常见方式. 2)该文对应的代码可以在下面的地址获得:https:// ...
- iOS开发实用技巧—在手机浏览器头部弹出app应用下载提示
iOS开发实用技巧—在手机浏览器头部弹出app应用下载提示 本文介绍其简单使用: 第一步:在本地建立一个访问的服务端. 打开本地终端,在本地新建一个文件夹,在该文件夹中存放测试的html页面. ...
- iOS开发实用技巧—项目新特性页面的处理
iOS开发实用技巧篇—项目新特性页面的处理 说明:本文主要说明在项目开发中会涉及到的最最简单的新特性界面(实用UIScrollView展示多张图片的轮播)的处理. 代码示例: 新建一个专门的处理新特性 ...
- Windows统一平台: 开发小技巧
Windows统一平台: 开发小技巧 技巧一: 在手机端拓展你应用的显示区域.(WP8.1中也适用) 对于Windows Phone系统的手机, 手机屏幕最上方为系统状态栏(System Tray), ...
- flex开发小技巧集锦
关于flex开发网上有非常多的相关信息介绍,因此我们要想学习关于flex开发的知识信息技能是一件非常简单和方便的事情.而针对于flex开发小编要告诉大家的是一些flex开发小技巧.利用这些小技巧能够有 ...
- PowerDesigner实用技巧小结(3)
PowerDesigner实用技巧小结(3) PowerDesigner 技巧小结 sqlserver数据库databasevbscriptsqldomain 1.PowerDesigner 使用 M ...
- 10个Visual Studio原生开发调试技巧
10个Visual Studio原生开发调试技巧(1) 2013-05-29 13:30 佚名 开源中国 我要评论(1) 字号:T | T 以下的列表中你可以看到写原生开发的调试技巧(接着以前的文章来 ...
- 移动平台3G手机网站前端开发布局技巧
本文转载至:移动平台3G手机网站前端开发布局技巧汇总 - 前端开发-武方博 您或许正在或准备参与一个WepApp项目,您或许正在Google搜索mobile development相 关的文章,您或许 ...
- PowerDesigner实用技巧小结(2)
PowerDesigner实用技巧小结 1.ORACLE数据库建模时,由于ORACLE的表名.字段名如果是小写会有一定的麻烦,需要将小写转化为大写? (1)在打开pdm的情况下,进入Tools-Mod ...
随机推荐
- BZOJ 3364: [Usaco2004 Feb]Distance Queries 距离咨询
Description 一棵树,询问两点间距离. Sol 倍增. 方向没用. 没有然后了. Code /************************************************ ...
- linux下编译qt5.6.0静态库——configure配置
linux下编译qt5.6.0静态库 linux下编译qt5.6.0静态库 configure生成makefile 安装选项 Configure选项 第三方库: 附加选项: QNX/Blackberr ...
- 算法题解之math类题
Bulb Switcher 灯泡开关 思路:除了平方数以外,其他所有位置的灯泡最终都被开关了偶数次,因此最终都为0.问题等价于求1~n中平方数的个数. public class Solution { ...
- 浏览器js自动查表脚本
javascript: void((function() {$.get("", {wen: "880350384879600241",action: " ...
- php过滤ascii控制字符
还记得以前在工作中,将爬来的其它网站的数据导到xml.但是会遇到一个问题:即网页会有ascII的控制字符. 一开始以为是别人为了防止采集而加入的,然后发现一个就往过滤表里加一个.直到慢慢发现,他们都是 ...
- oracle 存储过程中调用存储过程
create procedure sp_name() begin ……… end 比如: create procedure pro_showdbs() show datebase; end //用ex ...
- CSS3 text-overflow 属性
1. <!DOCTYPE html> <html> <head> <style> div.test { white-space:nowrap; widt ...
- 《oracle每日一练》免安装Oracle客户端使用PL/SQL
免安装Oracle客户端使用PL/SQL Oracle客户端挺招人烦的,部署连接它的应用通常需要先安装它的客户端,安装程序要求在目标机器上写注册表,假设你没有洁癖的话,你仍可能被下面的事情绊住:当你的 ...
- centos查找未挂载磁盘格式化并挂载
查看当前linux服务器分区 df -h 查看当前linux服务器硬盘: fdisk -l /dev/sda 第一块硬盘 /dev/sdb 第二块硬盘 依此类推 以/dev/sdb为新增硬盘为 ...
- 【转载】通过JDBC对MySQL数据库的增删改查
通过JDBC进行简单的增删改查(以MySQL为例) 目录 前言:什么是JDBC 一.准备工作(一):MySQL安装配置和基础学习 二.准备工作(二):下载数据库对应的jar包并导入 三.JDBC基本操 ...