前言
在WPF嵌入地图,有两种方式: 浏览器方式;控件方式。
1)浏览器方式就是使用浏览器控件WebBrowser,设置好网址就行了。这种方式与地图的交互不太直接,需要懂html、javascript。对于不懂web编程的开发者来说,有点困难。
2)控件方式就是使用第三方控件;不需要处了解web相关知识,使用起来比较直接,易于理解。GMap.net 类库就实现了这种控件。
GMap.net 简介
GMap.NET 是一个强大、免费、跨平台、开源的.NET控件,它在Windows Forms 和WPF环境中能够通过Google, Yahoo!, Bing, OpenStreetMap, ArcGIS, Pergo, SigPac等实现寻找路径、地理编码以及地图展示功能,并支持缓存和运行在Mobile环境中。
GMap.NET多年前已经存在,最初主要支持WinForm。WPF出现的较晚;但是,现在这个控件也可用于WPF开发。不过,网上相关WPF开发的例子较少。因为工作需要,最近使用这个控件开发了gis相关项目,把开发过程中的使用技巧写出来,以供参考!
其中部分代码参考了别人的文章,稍作修改!
程序界面:
将GMap.net加入项目
使用NuGet,搜索GMap.net就可以找到该控件:
添加地图
GMap.net是国外开发的,不过也能很好的支持国内地图。这个控件是开放的,只要按照要求完成相关设置,就可以把各类地图加进来。
要理解这些设置,就需要先理解地图的基本知识。我在这里就不多述。简单一句话句话就是:地图其实就多个图片拼接而来的;你需要告诉控件,如何根据地理坐标和缩放级别获取对应的图片就行。
以高德地图为例,看看如何设置:
需要重写GMapProvider这个类,代码如下:
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;
}
}
}
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;
}
}
private AMapProvider()
{
}
static AMapProvider()
{
Instance = new AMapProvider();
}
//根据坐标和缩放,获取对应的图片。
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)
{
//http://webrd04.is.autonavi.com/appmaptile?x=5&y=2&z=3&lang=zh_cn&size=1&scale=1&style=7
string url = string.Format(UrlFormat, pos.X, pos.Y, zoom);
Console.WriteLine("url:" + url);
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";
}
最重要的函数就是 public override PureImage GetTileImage(GPoint pos, int zoom),地图就是同一缩放比例的图片堆砌而来。
使用控件
在窗口中添加控件:主窗口代码如下
标注可移动
添加标注
使用设置RenderOptions.BitmapScalingMode="NearestNeighbor",可使图片显示较为清晰。
添加标注
标注称之为Marker。控件有一个属性 public ObservableCollection
BitmapImage _pinSrcImage;
Image CreatePinImage(GMapMarker marker)
{
Image img = new Image();
img.Tag = marker;
img.Width = 32;
img.Height = 32;
if (_pinSrcImage == null)
{
//多个标注共用一个图像源,节省内存。
_pinSrcImage = new BitmapImage(new Uri("pack://application:,,,/AMap/red-dot.png", UriKind.Absolute));
_pinSrcImage.Freeze();
}
img.Source = _pinSrcImage;
//鼠标热点位置
marker.Offset = new Point(-img.Width / 2, -img.Height / 2);
return img;
}
private void AddMaker(PointLatLng pt)
{
GMapMarker marker = new GMapMarker(pt);
marker.Shape = CreatePinImage(marker);
//将图层添加到地图
this.MainMap.Markers.Add(marker);
}
移动标注
首先需要检测鼠标是否点击了标注部分。需要在MouseDown事件中,通过WPF视觉树辅助函数来判断(VisualTreeHelper.HitTest)。其次在MouseMove函数中,将标注移动到新的坐标点。这里是通过鼠标左键移动;要实现此操作,设置控件拖动方式为 MainMap.DragButton = MouseButton.Right; 暨设置地图拖动方式为鼠标右键,防止与标注移动相冲突。
关联控件事件:
MainMap.MouseMove += MainMap_MouseMove;
MainMap.MouseDown += MainMap_MouseDown;
MainMap.MouseLeftButtonUp += MainMap_MouseLeftButtonUp;
判断鼠标是否点击了标注部分
GMapMarker _currentElement;
private void MainMap_MouseDown(object sender, MouseButtonEventArgs e)
{
if (checkMoveFlag.IsChecked == false)
{
return;
}
//判断是否点击了标注
if (_currentElement == null)
{
Point pt = e.GetPosition(MainMap);
PointLatLng point = MainMap.FromLocalToLatLng((int)pt.X, (int)pt.Y);
PointHitTestParameters parameters = new PointHitTestParameters(pt);
VisualTreeHelper.HitTest(MainMap, null, HitTestCallback, parameters);
}
}
//右键弹起,设置标注变量为空
private void MainMap_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_currentElement = null;
}
private HitTestResultBehavior HitTestCallback(HitTestResult result)
{
Image image = result.VisualHit as Image;
if (image != null)
{
_currentElement = image.Tag as GMapMarker;
return HitTestResultBehavior.Stop;
}
return HitTestResultBehavior.Continue;
}
MouseMove事件中,移动标注
private void MainMap_MouseMove(object sender, MouseEventArgs e)
{
if (checkMoveFlag.IsChecked == true &&
e.LeftButton == MouseButtonState.Pressed
&& _currentElement != null)
{
//获取坐标
Point pt = e.GetPosition(MainMap);
//转换成地理坐标
PointLatLng point = MainMap.FromLocalToLatLng((int)pt.X, (int)pt.Y);
_currentElement.Position = point;
}
}
后记:
winform和WPF是开发桌面程序的两大框架。其中WPF是最新框架,具有很多颠覆性的概念。好多人感觉WPF概念难以理解,同感觉到GMap.net对WPF的封装也不够好,使用起来不如winform版好用。WPF版的GMap.net相比与winform版,确实省略了一些功能。这是因为WPF本身就很强大灵活,GMap.net再加上这些功能好像多此一举。“”标注检测”就是一例,winform版有直接检测标注的回调函数,WPF版就省略了。WPF是可以通过视觉树HitTest函数来检查,这种检测方法更灵活。
源码下载地址 https://download.csdn.net/download/qq_29939347/10797027;