In me the tiger sniffs the rose
阿新 • • 發佈:2019-01-07
基於winform的離線地圖專案
1.下載GMap(一個國外開源控制元件,codeplex有原始碼),不瞭解的百度一下,
2.專案引入GMap.NET.Core和GMap.NET.WindowsForms
3.工具箱新增GMap控制元件,把GMapControl控制元件拖到新建窗體
4.窗體載入事件初始化該控制元件的屬性或事件,OK!
一、載入地圖
gmap.MapProvider = GMapProviders.GoogleChinaMap; 預設控制元件載入的地圖圖層是谷歌中國,要想載入國內的高德、百度等圖層,就要編寫一個圖層源的類。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){}
下載地址