地图下载工具初步完成后,有网友提出需要加入经纬网络,看了文档,没有找到相关内容。那就自己动手,丰衣足食吧。最终效果如下图所示:
具体思路是,根据地图界面显示的经纬网络范围,确定一个合理的参数,结合地图层级,将经纬网络分别生成并显示出来,同时在周围显示具体数据,当鼠标拖动地图、地图放大缩小时,自动计算和更新经纬网络。比如,第3层,经度范围-180到180,可以平均为8份制定经线,纬度范围-90至90(实则为85°),可以平分为6份。
经过测算,不同层级内,经纬度平分参数如下:
代码为:
public static int[] lngsplit = {
2,
6,
8,
12,
24,
60,
120,
240,
360,
720,
1440,
2160,
4320,
9600,
21600,
43200,
64800,
129600,
259200,
648000,
1296000
};
public static int[] latsplit = {
2,
4,
6,
10,
15,
45,
90,
180,
270,
540,
1080,
2160,
3600,
7200,
21600,
43200,
64800,
129600,
324000,
648000,
1296000
};
具体实现步骤为,新建一个类LngLatGrid,构造函数传入参数GMapControl control,同时,动态添加一个层,用来放置经纬线和刻度值文本。具体代码如下
public LngLatGrid(GMapControl control)
{
this.control = control;
InitOverlay();
InitEvent();
}
///
/// 初始化经纬网层
///
private void InitOverlay()
{
bool haslnglatgrid = false;
foreach (GMapOverlay overlay in control.Overlays)
{
if (overlay.Id.Equals("lnglatgrid"))
{
haslnglatgrid = true;
this.gridOverlay = overlay;
continue;
}
}
if (!haslnglatgrid)
{
gridOverlay = new GMapOverlay("lnglatgrid");
control.Overlays.Add(gridOverlay);
}
}
///
/// 初始化事件
///
private void InitEvent()
{
control.MouseDown += control_MouseDown;
control.MouseUp += control_MouseUp;
control.MouseMove += control_MouseMove;
control.OnMapZoomChanged += control_OnMapZoomChanged;
}
void control_OnMapZoomChanged()
{
UpdateLngLat();
}
void control_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (isDrag)
{
UpdateLngLat();
}
}
void control_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
isDrag = false; UpdateLngLat();
}
void control_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
isDrag = true;
}
internal void ClearLngLat()
{
gridOverlay.Clear();
}
经纬网计算函数为:
这里要强调一下,过细的拆分可能出现浮点数而变得不准确,需要先将范围扩大,所以我们可以把经度×3600,相当于把度换算成秒,这样平均计算时就不会有太大误差。
public void UpdateLngLat()
{
if (GlobalConfig.isshowLnglatGird)
{
int zoom = (int)control.Zoom;
if (zoom > GlobalConfig.lngsplit.Length)
return;
gridOverlay.Clear();
Rectangle m = control.ClientRectangle;
PointLatLng p1 = control.FromLocalToLatLng(m.X, m.Y);
PointLatLng p2 = control.FromLocalToLatLng(m.Width, m.Height);
double latn = p1.Lat < 90 ? p1.Lat : 90;
double lats = p2.Lat > -90 ? p2.Lat : -90;
double lnge = p2.Lng < 180 ? p2.Lng : 180;
double lngw = p1.Lng > -180 ? p1.Lng : -180;
double lngsplit = 360d * 3600 / GlobalConfig.lngsplit[zoom - 1] ;
double latsplit = 180d * 3600 / GlobalConfig.latsplit[zoom - 1] ;
double mx = Convert.ToInt32(lngw * 3600 / lngsplit) * lngsplit;
double nx = Convert.ToInt32(lats * 3600 / latsplit) * latsplit;
for (double i = mx; i < lnge*3600; )
{
double cdeg = i / 3600;
GMapPolygon p = new GMapPolygon(new List()
{
new PointLatLng(lats,cdeg),
new PointLatLng(latn,cdeg)
}, "lng" + cdeg.ToString().PadLeft(3, '0'));
i += lngsplit;
p.Stroke = new Pen(new SolidBrush(Color.Black), 2);
gridOverlay.Polygons.Add(p);
GMapMarker markern = new LabelMarker(new PointLatLng(latn, cdeg), Tools.ToDegreeStr(cdeg,"lng"), Color.Black);
gridOverlay.Markers.Add(markern);
GPoint slatp = control.FromLatLngToLocal(new PointLatLng(lats, cdeg));
PointLatLng slat = control.FromLocalToLatLng((int)slatp.X, (int)(slatp.Y - 40));
GMapMarker markerw = new LabelMarker(slat, Tools.ToDegreeStr(cdeg, "lng"), Color.Black);
gridOverlay.Markers.Add(markerw);
}
for (double i = nx; i <= latn * 3600; )
{
double cdeg = i / 3600;
GMapPolygon p = new GMapPolygon(new List()
{
new PointLatLng(cdeg,lngw),
new PointLatLng(cdeg,lnge)
}, "lat" + cdeg.ToString().PadLeft(3, '0'));
i += latsplit;
p.Stroke = new Pen(new SolidBrush(Color.Red), 2);
gridOverlay.Polygons.Add(p);
GPoint elngp = control.FromLatLngToLocal(new PointLatLng(cdeg, lnge));
PointLatLng elng = control.FromLocalToLatLng((int)elngp.X - 140, (int)(elngp.Y));
GMapMarker markere = new LabelMarker(elng, Tools.ToDegreeStr(cdeg,"lat"), Color.Red);
gridOverlay.Markers.Add(markere);
GMapMarker markerw = new LabelMarker(new PointLatLng(cdeg, lngw), Tools.ToDegreeStr(cdeg, "lat"), Color.Red);
gridOverlay.Markers.Add(markerw);
}
control.Invalidate();
}
else
{
gridOverlay.Clear();
}
}
因为GMap.net中并未提供直接标注文字的功能,所以我们要改造一个marker,用Graphics进行文本绘制,
using GMap.NET.WindowsForms;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
namespace Mapmerger.Utils
{
class LabelMarker:GMapMarker
{
private string text;
public string Text
{
get { return text; }
set { text = value; }
}
private Color defaultColor;
public LabelMarker(GMap.NET.PointLatLng p, string text,Color color)
: base(p)
{
this.text = text;
this.defaultColor = color;
}
public override void OnRender(Graphics g)
{
RectangleF rect = new RectangleF(LocalPosition.X, LocalPosition.Y, Size.Width, Size.Height);
Font font = new Font("宋体", 18);
StringFormat format = StringFormat.GenericTypographic;
float dpi = g.DpiY;
using (GraphicsPath path = GetStringPath(text, dpi, rect, font, format))
{
g.SmoothingMode = SmoothingMode.AntiAlias;//设置字体质量
g.DrawPath(Pens.White, path);//绘制轮廓(描边)
g.FillPath(new SolidBrush(defaultColor), path);//填充轮廓(填充)
}
}
GraphicsPath GetStringPath(string s, float dpi, RectangleF rect, Font font, StringFormat format)
{
GraphicsPath path = new GraphicsPath();
float emSize = dpi * font.SizeInPoints / 72;
path.AddString(s, font.FontFamily, (int)font.Style, emSize, rect, format);
return path;
}
public override void Dispose()
{
base.Dispose();
}
}
}
相关功能测试效果较好,效率也很好,因为动态更新且经纬网和注记数量较少,运行速度很快。完工。