使用 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函數來檢查,這種檢測方法更靈活。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM