基于winform的离线地图项目
1.下载GMap(一个国外开源控件,codeplex有源码),不了解的百度一下,
2.项目引入GMap.NET.Core和GMap.NET.WindowsForms
3.工具箱添加GMap控件,把GMapControl控件拖到新建窗体
4.窗体加载事件初始化该控件的属性或事件,OK!
一、加载地图
private void Form1_Load(object sender, EventArgs e)
{
gmap.BackColor = Color.Red;
//gmap.CacheLocation = Environment.CurrentDirectory + "\\GMapCache\\DataExp.gmdb"; //缓存位置
//设置控件的管理模式 (缓存、联网)
//gmap.Manager.Mode = AccessMode.CacheOnly;
gmap.Manager.Mode = AccessMode.ServerOnly;
//设置控件显示的地图来源
//gmap.MapProvider = AMapSateliteProvider.Instance;
//gmap.MapProvider = AMapProvider.Instance;
//gmap.MapProvider = GMapProviders.GoogleChinaMap;
gmap.MapProvider = AMapProvider.Instance;
//载入离线缓存
GMaps.Instance.ImportFromGMDB(Application.StartupPath + "\\GMapCache\\DataExp.gmdb");
//设置控件显示的当前中心位置
// WGS84坐标系,即地球坐标系(通用坐标系)
// =》 经过国家测绘局要求采用GCJ02算法加偏后即为“火星坐标”(Google中国、腾讯和高德)
// =》百度在“火星坐标”的基础上又采用了自己的加偏算法BD09加偏后,得到“百度坐标”(搜狗、图吧都是)
gmap.Position = new PointLatLng(30.606726, 114.424348);
//gmap.Position = new PointLatLng(21.5053134635657, 67.8822576999664);//百度(BD09坐标)
gmap.MaxZoom = 16;
gmap.MinZoom = 3;
gmap.Zoom = 13;
//不显示中心十字点
gmap.ShowCenter = false;
//左键拖拽地图
gmap.DragButton = MouseButtons.Left;
gmap.Overlays.Add(_markersOverlay);
gmap.Overlays.Add(_polygons);
gmap.Overlays.Add(objects);
gmap.MouseClick += mapControl_MouseClick; //右键添加标记
gmap.MouseDoubleClick += mapControl_DoubleClick; //右键添加标记
gmap.OnMarkerClick += gmap_OnMarkerClick;
gmap.OnMarkerClick2 += gmap_OnMarkerClick2;
}
高德:
AMapProviderBase
public abstract class AMapProviderBase : GMapProvider
{
public AMapProviderBase()
{
MaxZoom = null;
RefererUrl = "http://www.amap.com/";
//Copyright = string.Format("©{0} 高德 Corporation, ©{0} NAVTEQ, ©{0} Image courtesy of NASA", DateTime.Today.Year);
}
public override PureProjection Projection
{
get { return MercatorProjection.Instance; }
}
GMapProvider[] overlays;
public override GMapProvider[] Overlays
{
get
{
if (overlays == null)
{
overlays = new GMapProvider[] { this };
}
return overlays;
}
}
}
AMapProvider
public class AMapProvider : AMapProviderBase
{
public static readonly AMapProvider Instance;
readonly Guid id = new Guid("EF3DD303-3F74-4938-BF40-232D0595EE88");
public override Guid Id
{
get { return id; }
}
readonly string name = "AMap";
public override string Name
{
get
{
return name;
}
}
static AMapProvider()
{
Instance = new AMapProvider();
}
public override PureImage GetTileImage(GPoint pos, int zoom)
{
try
{
string url = MakeTileImageUrl(pos, zoom, LanguageStr);
return GetTileImageUsingHttp(url);
}
catch (Exception ex)
{
return null;
}
}
string MakeTileImageUrl(GPoint pos, int zoom, string language)
{
var num = (pos.X + pos.Y) % 4 + 1;
//string url = string.Format(UrlFormat, num, pos.X, pos.Y, zoom);
string url = string.Format(UrlFormat, pos.X, pos.Y, zoom);
return url;
}
//static readonly string UrlFormat = "http://webrd04.is.autonavi.com/appmaptile?x={0}&y={1}&z={2}&lang=zh_cn&size=1&scale=1&style=7";
//static readonly string UrlFormat = "http://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={0}&y={1}&z={2}";
static readonly string UrlFormat = "http://localhost:8081/788865972/{2}/{0}/{1}.png"; //本地部署的GIS服务
}
百度:
/// <summary>
/// 百度地图(BD09坐标)
/// 转换:http://lbsyun.baidu.com/index.php?title=webapi/guide/changeposition
/// </summary>
public class BaiduMapProvider : BaiduMapProviderBase
{
readonly string name = "BaiduMap";
public static readonly BaiduMapProvider Instance;
readonly Guid id = new Guid("47C1561B-C785-4EBF-9EC3-2CF0E416E219");
static BaiduMapProvider()
{
Instance = new BaiduMapProvider();
}
public override Guid Id
{
get { return id; }
}
public override string Name
{
get
{
return name;
}
}
public override PureImage GetTileImage(GPoint pos, int zoom)
{
string url = MakeTileImageUrl(pos, zoom, LanguageStr);
return GetTileImageUsingHttp(url);
}
string MakeTileImageUrl(GPoint pos, int zoom, string language)
{
zoom = zoom - 1;
var offsetX = Math.Pow(2, zoom);
var offsetY = offsetX - 1;
var numX = pos.X - offsetX;
var numY = -pos.Y + offsetY;
zoom = zoom + 1;
var num = (pos.X + pos.Y) % 8 + 1;
var x = numX.ToString().Replace("-", "M");
var y = numY.ToString().Replace("-", "M");
//原来:http://q3.baidu.com/it/u=x=721;y=209;z=12;v=014;type=web&fm=44
//更新:http://online1.map.bdimg.com/tile/?qt=tile&x=23144&y=6686&z=17&styles=pl
//string url = string.Format(UrlFormat, num, x, y, zoom, "014", "web", "44");
string url = string.Format(UrlFormat, x, y, zoom);
return url;
}
//static readonly string UrlFormat = "http://q{0}.baidu.com/it/u=x={1};y={2};z={3};v={4};type={5}&fm={6}";
static readonly string UrlFormat = "http://online1.map.bdimg.com/tile/?qt=tile&x={0}&y={1}&z={2}&styles=pl";
}
引用: gmap.MapProvider = AMapProvider.Instance;
二、自定义Marker
圆:
[Serializable]
public class GMapMarkerCircle : GMapMarker, ISerializable
{
/// <summary>
/// 实际半径距离,单位为米
/// </summary>
public double RawRadius;
/// <summary>
/// specifies how the outline is painted
/// </summary>
[NonSerialized]
public Pen Stroke = new Pen(Color.FromArgb(155, Color.MidnightBlue));
/// <summary>
/// background color
/// </summary>
[NonSerialized]
public Brush Fill = new SolidBrush(Color.FromArgb(155, Color.AliceBlue));
/// <summary>
/// is filled
/// </summary>
public bool IsFilled = true;
public GMapMarkerCircle(PointLatLng p, int radius)
: base(p)
{
//IsHitTestVisible = false;
}
public override void OnRender(Graphics g)
{
//将距离转换成像素长度
int R = (int)(RawRadius / Overlay.Control.MapProvider.Projection.GetGroundResolution((int)Overlay.Control.Zoom, Position.Lat)) * 2;
if (IsFilled)
{
g.FillEllipse(Fill, new Rectangle(LocalPosition.X - R / 2, LocalPosition.Y - R / 2, R, R));
}
g.DrawEllipse(Stroke, new 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();
}
#region ISerializable Members
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
// TODO: Radius, IsFilled
}
protected GMapMarkerCircle(SerializationInfo info, StreamingContext context)
: base(info, context)
{
// TODO: Radius, IsFilled
}
#endregion
}
[Serializable]
public class GMapMarkerRect : GMapMarker, ISerializable
{
[NonSerialized]
public Pen Stroke=new Pen(Brushes.Blue);
[NonSerialized]
public GMarkerGoogle InnerMarker;
[NonSerialized]
public int BorderWidth =5;
[NonSerialized]
public Size RectSize =new Size(1000, 1000);
public GMapMarkerRect(PointLatLng p)
: base(p)
{
//IsHitTestVisible = false;
Stroke.Width= BorderWidth;
// do not forget set Size of the marker
// if so, you shall have no event on it ;}
}
public override void OnRender(Graphics g)
{
//将距离转换成像素长度
int R = (int)(RectSize.Width / Overlay.Control.MapProvider.Projection.GetGroundResolution((int)Overlay.Control.Zoom, Position.Lat));
//每次重新渲染的时候,重新计算Marker的大小和偏移。OnMarkerClick事件按Size和Offset的Marker来进行触发
Offset = new Point(-R / 2, -R / 2);
Size = new Size(R, R);
g.DrawRectangle(Stroke, new Rectangle(LocalPosition.X , LocalPosition.Y , R, R));
}
public override void Dispose()
{
if (Stroke != null)
{
Stroke.Dispose();
Stroke = null;
}
if (InnerMarker != null)
{
InnerMarker.Dispose();
InnerMarker = null;
}
base.Dispose();
}
#region ISerializable Members
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
}
protected GMapMarkerRect(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
#endregion
}
扇形:
[Serializable]
public class GMapMarkerSector : GMapMarker, ISerializable
{
[NonSerialized]
public Pen Stroke = new Pen(Brushes.Blue);
[NonSerialized]
public GMarkerGoogle InnerMarker;
[NonSerialized]
public int BorderWidth = 3;
//[NonSerialized]
//public int Angle = 90;
[NonSerialized]
public Brush Fill = new SolidBrush(Color.FromArgb(155, Color.Aqua));
[NonSerialized]
public int RawRadius = 100;
public GMapMarkerSector(PointLatLng p, int radius)
: base(p)
{
//IsHitTestVisible = false;
Radius = RawRadius = radius;
//Size = new Size(RawRadius / 2, RawRadius / 2);
}
public override void OnRender(Graphics g)
{
int R = (int)(RawRadius*2 / Overlay.Control.MapProvider.Projection.GetGroundResolution((int)Overlay.Control.Zoom, Position.Lat)) * 2;
Radius = R;
g.FillPie(Fill, new Rectangle(LocalPosition.X - R / 2, LocalPosition.Y - R / 2, R, R), (float)(-90 + (FwAngle - Angle)), Angle * 2);
g.DrawPie(Stroke, new Rectangle(LocalPosition.X - R / 2, LocalPosition.Y - R / 2, R, R), (float)(-90 + (FwAngle - Angle)), Angle * 2);
}
public override void Dispose()
{
if (Stroke != null)
{
Stroke.Dispose();
Stroke = null;
}
if (Fill != null)
{
Fill.Dispose();
Fill = null;
}
if (InnerMarker != null)
{
InnerMarker.Dispose();
InnerMarker = null;
}
base.Dispose();
}
#region ISerializable Members
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
}
protected GMapMarkerSector(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
#endregion
}
箭头:
[Serializable]
public class GMarkerArrow : GMapMarker, ISerializable
{
[NonSerialized]
public Brush Fill = new SolidBrush(Color.FromArgb(155, Color.Blue));
//[NonSerialized]
//public Pen Pen = new Pen(Brushes.Blue, 5);
static readonly Point[] Arrow = { new Point(-7, 7), new Point(0, -22), new Point(7, 7), new Point(0, 2) };
public float Bearing = 0;
private float scale = 1;
public float Scale
{
get
{
return scale;
}
set
{
scale = value;
Size = new Size((int)(14 * scale), (int)(14 * scale));
Offset = new Point(-Size.Width / 2, (int)(-Size.Height / 1.4));
}
}
public GMarkerArrow(PointLatLng p)
: base(p)
{
Scale = 1;
}
public override void OnRender(Graphics g)
{
//g.DrawRectangle(Pen, new System.Drawing.Rectangle(LocalPosition.X, LocalPosition.Y, Size.Width, Size.Height));
{
g.TranslateTransform(ToolTipPosition.X, ToolTipPosition.Y);
var c = g.BeginContainer();
{
g.RotateTransform(Bearing - Overlay.Control.Bearing);
g.ScaleTransform(Scale, Scale);
g.FillPolygon(Fill, Arrow);
}
g.EndContainer(c);
g.TranslateTransform(-ToolTipPosition.X, -ToolTipPosition.Y);
}
}
public override void Dispose()
{
//if(Pen != null)
//{
// Pen.Dispose();
// Pen = null;
//}
if (Fill != null)
{
Fill.Dispose();
Fill = null;
}
base.Dispose();
}
#region ISerializable Members
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
}
protected GMarkerArrow(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
#endregion
}
调用
//画圆
void AddCircle(PointLatLng point)
{
var cc = new GMapMarkerCircle(point, 1000)
{
//背景色填充
Fill = new SolidBrush(Color.FromArgb(100, Color.CadetBlue)),
//边框颜色
Stroke = new Pen(Color.FromArgb(100, Color.Aqua)),
ToolTipText = "测试圆",
ToolTipMode = MarkerTooltipMode.OnMouseOver,
IsCircle = true
};
objects.Markers.Add(cc);
}
//画矩形
void AddRect(PointLatLng point)
{
GMapMarkerRect mBorders = new GMapMarkerRect(point)
{
ToolTipMode = MarkerTooltipMode.OnMouseOver,
ToolTipText = "123",
//滚轮放大放小地图不受影响
//IsHitTestVisible = false,
BorderWidth = 1,
Stroke = new Pen(Brushes.DarkCyan)
};
objects.Markers.Add(mBorders);
}
//画箭头
void AddArrow(PointLatLng point)
{
GMapMarker marker = new GMarkerArrow(point)
{
Fill = new SolidBrush(Color.FromArgb(255, Color.Coral)),
Scale = 2,
Bearing = 2
};
objects.Markers.Add(marker);
}
//画扇形
void AddSector(PointLatLng point)
{
GMapMarkerSector st = new GMapMarkerSector(point,1000)
{
//填充颜色
Fill = new SolidBrush(Color.FromArgb(100, Color.CadetBlue)),
//边框颜色、宽度
Stroke = new Pen(Color.FromArgb(100, Color.DodgerBlue), 1),
Angle = 60,
FwAngle = 0,
Radius = 1000,
IsSector = true,
ToolTipMode = MarkerTooltipMode.OnMouseOver,
ToolTipText = "123",
};
_markersOverlay.Markers.Add(st);
}
三、GMap机制和重写
1.GMarker底层机制默认IsHitTestVisible=true,导致鼠标在Marker上的时候拖动不了地图图层和滚轮缩放地图。个人之前开发惯B端地图项目,觉得这是一个很不好的体验。改改改,GMapControl改底层事件
2.GMarker底层区域是一个矩形,无论你自定义什么样的图形(圆、扇形)。它的底层捕捉都是基于GMarker的Size属性所画一个矩形区域,所有导致圆形、扇形这些图形的地图点击事件范围扩大成一个矩形,这个不是我想要的效果,而且当marker集中在一个区域的时候这个问题将会放大!so,重写点击事件机制
3.重写ToolTipText提示文本显示,GMapToolTip和GMapRoundedToolTip这两个类
4.修改GMarker点击事件,原来只返回一个GMarker对象,重写后返回匹配的所有GMarker列表。
gmap.OnMarkerClick2 += gmap_OnMarkerClick2;
void gmap_OnMarkerClick2(List<GMapMarker> item, MouseEventArgs e){}
下载地址