zoukankan      html  css  js  c++  java
  • 使用 GMap.NET 实现添加标注、移动标注功能。(WPF版)

    前言

    在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),地图就是同一缩放比例的图片堆砌而来。

    使用控件

     在窗口中添加控件:主窗口代码如下

    <Window x:Class="GMapTest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:gmap="clr-namespace:GMap.NET.WindowsPresentation;assembly=GMap.NET.WindowsPresentation"
            xmlns:local="clr-namespace:GMapTest" Loaded="Window_Loaded"
            mc:Ignorable="d"  Background="#5A9EA5"  
            Title="MainWindow" Height="450" Width="800">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="31*"/>
                <ColumnDefinition Width="167*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
    
            <StackPanel Margin="5" Orientation="Horizontal" Grid.ColumnSpan="2">
                <CheckBox x:Name="checkMoveFlag" Margin="5,2,2,2" Click="CheckMoveFlag_Click">标注可移动</CheckBox>
                <CheckBox x:Name="checkAddFlag" Margin="10,2,2,2">添加标注</CheckBox>
            </StackPanel>
    
            <GroupBox Grid.Row="1" Margin="0" Grid.ColumnSpan="2">
                <gmap:GMapControl x:Name="MainMap" MaxZoom="24" MinZoom="1" 
                              RenderOptions.BitmapScalingMode="NearestNeighbor" 
                                  UseLayoutRounding="True" SnapsToDevicePixels="True">
                </gmap:GMapControl>
            </GroupBox>
        </Grid>
    </Window>

    使用设置RenderOptions.BitmapScalingMode="NearestNeighbor",可使图片显示较为清晰。

    添加标注

    标注称之为Marker。控件有一个属性 public ObservableCollection<GMapMarker> Markers { get; }用于存放标注。添加标注就是设置好GMapMarker相关属性就行。代码如下:

          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函数来检查,这种检测方法更灵活。

  • 相关阅读:
    NIO中几个非常重要的技术点
    NIO的epoll空轮询bug
    mysql支持跨表删除多条记录
    使用Fastjson生成Json字符串少字段属性(数据丢失)
    Linux系统下安装rz/sz命令及使用说明
    Slave_SQL_Running: No mysql同步故障
    二次幂权限设计
    spring容器加载完毕做一件事情(利用ContextRefreshedEvent事件)
    XStream别名;元素转属性;去除集合属性(剥皮);忽略不需要元素
    JDBC通用DAO
  • 原文地址:https://www.cnblogs.com/yuanchenhui/p/GMapTest.html
Copyright © 2011-2022 走看看