/MyMapTools

由于工作中经常和地图、GPS坐标转换、GPS轨迹查看等内容相关,经常要借助一些在线网站和工具来查看地图和位置等,在一次偶然的机会中了解到一个[GMap.NET](https://github.com/radioman/greatmaps)开源的桌面地图库和基于GMap.NET实现的[MapDownloader](https://github.com/luxiaoxun/MapDownloader)地图下载工具,于是也想实现一个自己的地图相关工具,包含以下功能: - 多种地图切换:Baidu(百度), Amap(高德), Tencent(腾讯), Tianditu(天地图), Ship, Google, Bing, OpenStreetMap, ArcGIS, Here(Nokia)等; - 坐标拾取和坐标转换:GPS(WGS84)、火星坐标(GCJ02)、百度坐标(BD09)等相互转换,地理编码和反地理编码等; - POI查询:通过百度地图、高德地图、腾讯地图等WebAPI搜索、保存POI数据; - 地图下载、拼接:通过矩形、多边形、行政区划等方式下载、缓存地图数据或者拼接成大图; - 导航路线搜索、导出:通过百度地图、高德地图、腾讯地图等WebAPI搜索、保存导航路线数据; - 历史轨迹加载、回放:加载不同格式(csv/excel/nmea)轨迹数据,并可以回放、测试等; - 实时轨迹显示:通过串口接收GPS信息显示在地图上;

Primary LanguageC#

0 前言

由于工作中经常和地图、GPS坐标转换、GPS轨迹查看等内容相关,经常要借助一些在线网站和工具来查看地图和位置等,在一次偶然的机会中了解到一个GMap.NET开源的桌面地图库和基于GMap.NET实现的MapDownloader地图下载工具,于是也想实现一个自己的地图相关工具,包含以下功能:

  • 多种地图切换:Baidu(百度), Amap(高德), Tencent(腾讯), Tianditu(天地图), Ship, Google, Bing, OpenStreetMap, ArcGIS, Here(Nokia)等;
  • 坐标拾取和坐标转换:GPS(WGS84)、火星坐标(GCJ02)、百度坐标(BD09)等相互转换,地理编码和反地理编码等;
  • POI查询:通过百度地图、高德地图、腾讯地图等WebAPI搜索、保存POI数据;
  • 地图下载、拼接:通过矩形、多边形、行政区划等方式下载、缓存地图数据或者拼接成大图;
  • 导航路线搜索、导出:通过百度地图、高德地图、腾讯地图等WebAPI搜索、保存导航路线数据;
  • 历史轨迹加载、回放:加载不同格式(csv/excel/nmea)轨迹数据,并可以回放、测试等;
  • 实时轨迹显示:通过串口接收GPS信息显示在地图上;

项目地址:https://github.com/luotengyuan/MyMapToolshttps://gitee.com/luotengyuan/MyMapTools 程序下载:https://download.csdn.net/download/loutengyuan/86507941

catch_2022-12-26_17-16-41

声明:本软件仅供个人学习与科研使用,所下载的数据版权归各个地图服务商所有,任何组织或个人因数据使用不当造成的问题,软件作者不负责任。

PS: 请替换自己的API Key!

1 功能介绍

1.1 多种在线地图切换

  • 可以根据自己的需要切换不同的地图提供商(例如:百度、高德)或者不同的地图类型(例如:普通地图、卫星地图、混合地图)

GIF_2022-12-26_17-26-08

1.2 经纬度坐标显示

  • 可以根据不同类型的经纬度坐标,将坐标标注到地图上显示 GIF_2022-12-26_17-30-22

1.3 坐标拾取功能

  • 在地图指定点右键鼠标,选择拾取该点坐标和地址菜单,可以在坐标坐标拾取标签页下查看该点的坐标和地址 GIF_2022-12-26_18-07-41
  • 根据坐标和地址也可查询在地图上的点位 GIF_2022-12-26_18-35-43

1.4 POI查询功能

  • 根据关键字查询POI,并在地图中标注显示,还可以将搜索结果导出成Excel文件 GIF_2022-12-26_17-36-04

1.5 地图下载拼接

  • 可以根据绘制矩形和多边形下载指定区域地图,可以根据行政区划下载地图 GIF_2022-12-26_17-38-08 GIF_2022-12-26_17-40-41

1.6 导航路线规划

  • 地图上右键选取导航起点、途经点、终点,点击规划路线,生成一条导航路线 GIF_2022-12-26_17-43-01
  • 可以将导航路线导出成轨迹点 GIF_2022-12-26_17-44-45
  • 还可以将轨迹导出成文件 GIF_2022-12-26_17-47-25
  • 文件格式如下 在这里插入图片描述

1.7 历史轨迹加载与回放

  • 可以通过加载csv、excel、nmea等文件将历史轨迹显示到地图上 GIF_2022-12-26_17-55-33
  • 可以将历史轨迹设置成模拟轨迹,模拟历史数据,并将模拟信息通过Pipe方式发送到其他进程作为输入,进行模拟GPS测试 GIF_2022-12-26_17-58-03

1.8 串口接收实时轨迹

  • 可以根据串口接收到的NMEA或者其他指定格式实时定位信息,并显示到地图上

GIF_2022-12-26_18-05-19

2 代码实现

2.1 GMap.NET库使用

GMap.NET是一个强大、免费、跨平台、开源的.NET控件,它在Windows Forms和WPF环境中使用Google, Yahoo!, Bing, OpenStreetMap, ArcGIS, Pergo, SigPac等路径规划、地理编码以及地图展示功能。下面介绍从下载安装到如何使用GMap.NET。

2.1.1 下载和安装

这里下载,解压后得到两个文件(GMap.NET.Core.dll 和 GMap.NET.WindowsForms.dll)。然后在项目中添加引用。 在这里插入图片描述 添加GMapControl到工具箱。在“选择项”时,选择“GMap.NET.WindowsForms.dll”文件即可添加。 在这里插入图片描述 使用NUGET安装 在这里插入图片描述

2.1.2 添加地图控件

拖拽GMapControl到Windows Form中,可看到控件中心有个小十字。查看属性,你会发现GMap.NET的一些特有属性。通过设置这些属性可以配置地图的行为,但是不能设置其内容。 在这里插入图片描述

  • Bearing - 按照指定的度数向左旋转地图
  • CanDragMap – 是否启用鼠标右键拖动(平移)地图
  • EmptyTileColor – 设置没有数据的切片所显示的颜色
  • MarkersEnabled – 是否显示定义的标记,同PolygonsEnabled和RoutesEnabled
  • ShowTileGridLines – 显示网格线
  • Zoom,MinZoom和MaxZoom - 缩放级别

GMap.NET支持多种地图源,定义地图源需要在代码中去设置。可以在GMapControl的 Load 事件中添加如下代码:

this.gMapControl1.MapProvider = OpenStreet4UMapProvider.Instance; // 设置地图源
GMaps.Instance.Mode = AccessMode.ServerAndCache; // GMap工作模式
this.gMapControl1.SetPositionByKeywords("北京"); // 地图中心位置

//使用经纬度设置地图中心
//this.gMapControl1.Position = new GMap.NET.PointLatLng(39.923518, 116.539009);

① MapProvider:设置地图源,输入GMapProviders可以看到GMap所支持的所有地图源。 在这里插入图片描述 ② GMaps.Instance.Mode: GMap可以从服务器、本地缓存、服务器或本地缓存获取数据。这适用于在应用程序中创建的所有GMap控件实例,只需要设置一次该值。 在这里插入图片描述 ③ 设置地图中心位置可以使用关键字或者经纬度。 ④ 地图显示结果。按住鼠标右键可以拖拽地图,当然也可以设置其他键来拖拽。

this.gMapControl1.DragButton = MouseButtons.Left;

在这里插入图片描述

2.1.3 添加标记

//创建一个名为“markers”的图层
GMapOverlay markers = new GMapOverlay("markers");
//创建标记,并设置位置及样式
GMapMarker marker = new GMarkerGoogle(new PointLatLng(39.923518, 116.539009), GMarkerGoogleType.blue_pushpin);
//将标记添加到图层
markers.Markers.Add(marker);
//将图层添加到地图
this.gMapControl1.Overlays.Add(markers);

① GMapOverlay:图层。添加的标记、图形、路径等都是在图层上操作的。 ② GMapMarker:GMarkerGoogle,提供标记位置(PointLatLng)和标记样式。 它有两个重载,可以使用GMarkerGoogleType和位图。GMap.NET还提供了GMarkerCross,这是一个简单的十字,不允许使用图标。

public GMarkerGoogle(PointLatLng p, GMarkerGoogleType type);
public GMarkerGoogle(PointLatLng p, Bitmap Bitmap);

GMapMarker还可以设置ToolTip。

marker.ToolTipText = "我在这里";
marker.ToolTip.Fill = new SolidBrush(Color.FromArgb(100, Color.Black));
marker.ToolTip.Foreground = Brushes.White;
marker.ToolTip.TextPadding = new Size(20, 20);

③ 地图显示结果。(发现中心的小十字没了吗,因为这是可以设置的) 在这里插入图片描述

this.gMapControl1.ShowCenter = false; //隐藏中心十字

④ 标记点击事件 标记本身没有任何事件钩子,GMapControl的OnMarkerClick事件即为标记点击事件。在下面的示例中,点击标记会弹出提示框显示ToolTip的文本内容。当然GMap.NET不只有点击事件,还有OnMarkerEnter、OnMarkerLeave。

private void gMapControl1_OnMarkerClick(GMapMarker item, MouseEventArgs e)
{
    MessageBox.Show(String.Format("Marker:{0} 被点了。", item.ToolTipText));
}

2.1.4 添加多边形

添加多边形和添加标记的原理是一样的。

GMapOverlay polygons = new GMapOverlay("polygons");
// 多边形的顶点
List<PointLatLng> points = new List<PointLatLng>();
points.Add(new PointLatLng(39.92244, 116.3922));
points.Add(new PointLatLng(39.92280, 116.4015));
points.Add(new PointLatLng(39.91378, 116.4019));
points.Add(new PointLatLng(39.91346, 116.3926));
GMapPolygon polygon = new GMapPolygon(points, "故宫");
polygon.Fill = new SolidBrush(Color.FromArgb(50, Color.Red));
polygon.Stroke = new Pen(Color.Red, 1);
polygons.Polygons.Add(polygon);
this.gMapControl1.Overlays.Add(polygons);

显示结果。 在这里插入图片描述

版权声明:以上内容参考huangli0原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 链接:https://blog.csdn.net/huangli0/article/details/80147243

2.2 坐标转换

坐标转换包含CoordType.cs、PointLatLng.cs、CoordinateTransform.cs、PointInDiffCoord.cs四个类,分别作用和代码如下:

CoordType.cs:坐标类型定义 PointLatLng.cs:经纬度封装类 CoordinateTransform.cs:不同坐标转换工具类 PointInDiffCoord.cs:不同坐标转换封装类

  • CoordType.cs 代码如下:
using System;
using System.Collections.Generic;
using System.Text;

namespace GMap.NET
{
    /// <summary>
    /// 坐标系类型
    /// </summary>
    public enum CoordType
    {
        WGS84,
        GCJ02,
        BD09,
        UNKNOW
    }
}
  • PointLatLng.cs 代码如下:
namespace GMap.NET
{
   using System;
   using System.Globalization;

   /// <summary>
   /// the point of coordinates
   /// </summary>
   [Serializable]
   public struct PointLatLng
   {
      public static readonly PointLatLng Empty = new PointLatLng();
      private double lat;
      private double lng;
      private CoordType type;


      bool NotEmpty;

      public PointLatLng(double lat, double lng)
      {
         this.lat = lat;
         this.lng = lng;
         this.type = CoordType.UNKNOW;
         NotEmpty = true;
      }

      public PointLatLng(double lat, double lng, CoordType type) : this(lat, lng)
      {
          this.type = type;
      }

      /// <summary>
      /// returns true if coordinates wasn't assigned
      /// </summary>
      public bool IsEmpty
      {
         get
         {
            return !NotEmpty;
         }
      }

      public double Lat
      {
         get
         {
            return this.lat;
         }
         set
         {
            this.lat = value;
            NotEmpty = true;
         }
      }

      public double Lng
      {
         get
         {
            return this.lng;
         }
         set
         {
            this.lng = value;
            NotEmpty = true;
         }
      }

      public CoordType Type
      {
          get
          {
              return this.type;
          }
          set
          {
              this.type = value;
          }
      }

      public static PointLatLng operator +(PointLatLng pt, SizeLatLng sz)
      {
         return Add(pt, sz);
      }

      public static PointLatLng operator -(PointLatLng pt, SizeLatLng sz)
      {
         return Subtract(pt, sz);
      }

      public static SizeLatLng operator -(PointLatLng pt1, PointLatLng pt2)
      {
          return new SizeLatLng(pt1.Lat - pt2.Lat, pt2.Lng - pt1.Lng);
      }

      public static bool operator ==(PointLatLng left, PointLatLng right)
      {
         return ((left.Lng == right.Lng) && (left.Lat == right.Lat));
      }

      public static bool operator !=(PointLatLng left, PointLatLng right)
      {
         return !(left == right);
      }

      public static PointLatLng Add(PointLatLng pt, SizeLatLng sz)
      {
         return new PointLatLng(pt.Lat - sz.HeightLat, pt.Lng + sz.WidthLng);
      }

      public static PointLatLng Subtract(PointLatLng pt, SizeLatLng sz)
      {
         return new PointLatLng(pt.Lat + sz.HeightLat, pt.Lng - sz.WidthLng);
      }

      public override bool Equals(object obj)
      {
         if(!(obj is PointLatLng))
         {
            return false;
         }
         PointLatLng tf = (PointLatLng)obj;
         return (((tf.Lng == this.Lng) && (tf.Lat == this.Lat)) && tf.GetType().Equals(base.GetType()));
      }

      public void Offset(PointLatLng pos)
      {
         this.Offset(pos.Lat, pos.Lng);
      }

      public void Offset(double lat, double lng)
      {
         this.Lng += lng;
         this.Lat -= lat;
      }

      public override int GetHashCode()
      {
         return (this.Lng.GetHashCode() ^ this.Lat.GetHashCode());
      }

      public override string ToString()
      {
         return string.Format(CultureInfo.CurrentCulture, "{{Lat={0}, Lng={1}}}", this.Lat, this.Lng);
      }
   }
}

CoordinateTransform.cs 代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GMap.NET
{
    /// <summary>
    /// 各坐标系统转换 
    /// WGS84坐标系:即地球坐标系,国际上通用的坐标系。设备一般包含GPS芯片或者北斗芯片获取的经纬度为WGS84地理坐标系, 
    /// 谷歌地图采用的是WGS84地理坐标系(**范围除外); 
    /// GCJ02坐标系:即火星坐标系,是由**国家测绘局制订的地理信息系统的坐标系统。由WGS84坐标系经加密后的坐标系。 
    /// 高德地图、腾讯地图、谷歌**地图采用的是GCJ02地理坐标系; 
    /// BD09坐标系:即百度坐标系,GCJ02坐标系经加密后的坐标系; 
    /// 搜狗坐标系、图吧坐标系等,估计也是在GCJ02基础上加密而成的。
    /// </summary>
    public static class CoordinateTransform
    {
        /// <summary>
        /// 检查坐标是否在**范围内
        /// </summary>
        /// <param name="lat">latitude 纬度</param>
        /// <param name="lng">longitude 经度</param>
        /// <returns></returns>
        private static bool OutOfChina(double lat, double lng)
        {
            if (lng < 72.004 || lng > 137.8347)
                return true;
            if (lat < 0.8293 || lat > 55.8271)
                return true;
            return false;
        }

        /// <summary>
        /// 火星坐标(GCJ02)转换为WGS坐标
        /// </summary>
        /// <param name="lngMars">火星坐标经度longitude</param>
        /// <param name="latMars">火星坐标纬度latitude</param>
        /// <param name="lngWgs">WGS经度</param>
        /// <param name="latWgs">WGS纬度</param>
        public static void ConvertGcj02ToWgs84(double lngMars, double latMars, out double lngWgs, out double latWgs)
        {
            lngWgs = lngMars;
            latWgs = latMars;
            double lngtry = lngMars;
            double lattry = latMars;
            ConvertWgs84ToGcj02(lngMars, latMars, out lngtry, out lattry);
            double dx = lngtry - lngMars;
            double dy = lattry - latMars;

            lngWgs = lngMars - dx;
            latWgs = latMars - dy;
        }

        /// <summary>
        /// WGS坐标转换为火星坐标(GCJ02)
        /// </summary>
        /// <param name="lngWgs">WGS经度</param>
        /// <param name="latWgs">WGS纬度</param>
        /// <param name="lngMars">火星坐标经度</param>
        /// <param name="latMars">火星坐标纬度</param>
        public static void ConvertWgs84ToGcj02(double lngWgs, double latWgs, out double lngMars, out double latMars)
        {
            lngMars = lngWgs;
            latMars = latWgs;

            const double pi = 3.14159265358979324;

            //
            // Krasovsky 1940
            //
            // a = 6378245.0, 1/f = 298.3
            // b = a * (1 - f)
            // ee = (a^2 - b^2) / a^2;
            const double a = 6378245.0;
            const double ee = 0.00669342162296594323;

            if (lngWgs < 72.004 || lngWgs > 137.8347)
                return;
            if (latWgs < 0.8293 || latWgs > 55.8271)
                return;

            double x = 0, y = 0;
            x = lngWgs - 105.0;
            y = latWgs - 35.0;

            double dLon = 300.0 + 1.0 * x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.Sqrt(Math.Abs(x));
            dLon += (20.0 * Math.Sin(6.0 * x * pi) + 20.0 * Math.Sin(2.0 * x * pi)) * 2.0 / 3.0;
            dLon += (20.0 * Math.Sin(x * pi) + 40.0 * Math.Sin(x / 3.0 * pi)) * 2.0 / 3.0;
            dLon += (150.0 * Math.Sin(x / 12.0 * pi) + 300.0 * Math.Sin(x / 30.0 * pi)) * 2.0 / 3.0;

            double dLat = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.Sqrt(Math.Abs(x));
            dLat += (20.0 * Math.Sin(6.0 * x * pi) + 20.0 * Math.Sin(2.0 * x * pi)) * 2.0 / 3.0;
            dLat += (20.0 * Math.Sin(y * pi) + 40.0 * Math.Sin(y / 3.0 * pi)) * 2.0 / 3.0;
            dLat += (160.0 * Math.Sin(y / 12.0 * pi) + 320.0 * Math.Sin(y * pi / 30.0)) * 2.0 / 3.0;

            double radLat = latWgs / 180.0 * pi;
            double magic = Math.Sin(radLat);
            magic = 1 - ee * magic * magic;
            double sqrtMagic = Math.Sqrt(magic);
            dLon = (dLon * 180.0) / (a / sqrtMagic * Math.Cos(radLat) * pi);
            dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
            lngMars = lngWgs + dLon;
            latMars = latWgs + dLat;
        }

        /// <summary>
        /// 火星坐标(GCJ02)转换为baidu坐标
        /// </summary>
        /// <param name="lngMars"></param>
        /// <param name="latMars"></param>
        /// <param name="lngBaidu"></param>
        /// <param name="latBaidu"></param>
        public static void ConvertGcj02ToBd09(double lngMars, double latMars, out double lngBaidu, out double latBaidu)
        {
            const double x_pi = 3.14159265358979324 * 3000.0 / 180.0;
            //const double x_pi = 3.14159265358979324;
            double x = lngMars;
            double y = latMars;
            double z = Math.Sqrt(x * x + y * y) + 0.00002 * Math.Sin(y * x_pi);
            double theta = Math.Atan2(y, x) + 0.000003 * Math.Cos(x * x_pi);
            lngBaidu = z * Math.Cos(theta) + 0.0065;
            latBaidu = z * Math.Sin(theta) + 0.006;
        }

        /// <summary>
        /// baidu坐标转换为火星坐标(GCJ02)
        /// </summary>
        /// <param name="lngBaidu"></param>
        /// <param name="latBaidu"></param>
        /// <param name="lngMars"></param>
        /// <param name="latMars"></param>
        public static void ConvertBd09ToGcj02(double lngBaidu, double latBaidu, out double lngMars, out double latMars)
        {
            const double x_pi = 3.14159265358979324 * 3000.0 / 180.0;

            double x = lngBaidu - 0.0065;
            double y = latBaidu - 0.006;
            double z = Math.Sqrt(x * x + y * y) - 0.00002 * Math.Sin(y * x_pi);
            double theta = Math.Atan2(y, x) - 0.000003 * Math.Cos(x * x_pi);
            lngMars = z * Math.Cos(theta);
            latMars = z * Math.Sin(theta);
        }

        /// <summary>
        /// WGS坐标转换为baidu坐标
        /// </summary>
        /// <param name="lngWgs"></param>
        /// <param name="latWgs"></param>
        /// <param name="lngBaidu"></param>
        /// <param name="latBaidu"></param>
        public static void ConvertWgs84ToBd09(double lngWgs, double latWgs, out double lngBaidu, out double latBaidu)
        {
            double lng;
            double lat;
            ConvertWgs84ToGcj02(lngWgs, latWgs, out lng, out lat);
            ConvertGcj02ToBd09(lng, lat, out lngBaidu, out latBaidu);
        }

        /// <summary>
        /// baidu坐标转换为WGS坐标
        /// </summary>
        /// <param name="lngBaidu"></param>
        /// <param name="latBaidu"></param>
        /// <param name="lngWgs"></param>
        /// <param name="latWgs"></param>
        public static void ConvertBd09ToWgs84(double lngBaidu, double latBaidu,out double lngWgs, out double latWgs)
        {
            double lng;
            double lat;
            ConvertBd09ToGcj02(lngBaidu, latBaidu, out lng, out lat);
            ConvertGcj02ToWgs84(lng, lat, out lngWgs, out latWgs);
        }
    }
}

PointInDiffCoord.cs 代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GMap.NET;
using GMapPositionFix;

namespace GMapCommonType
{
    public class PointInDiffCoord
    {
        private PointLatLng wgs84; 
        public PointLatLng WGS84
        {
            get
            {
                double lon, lat;
                if (CoordType == CoordType.WGS84)
                {
                    return wgs84;
                }
                else if (CoordType == CoordType.GCJ02)
                {
                    if (wgs84 == PointLatLng.Empty)
                    {
                        CoordinateTransform.ConvertGcj02ToWgs84(gcj02.Lng, gcj02.Lat, out lon, out lat);
                        wgs84.Lat = lat;
                        wgs84.Lng = lon;
                    }
                    return wgs84;
                }
                else if (CoordType == CoordType.BD09)
                {
                    if (wgs84 == PointLatLng.Empty)
                    {
                        CoordinateTransform.ConvertBd09ToWgs84(bd09.Lng, bd09.Lat, out lon, out lat);
                        wgs84.Lat = lat;
                        wgs84.Lng = lon;
                    }
                    return wgs84;
                }
                else
                {
                    return PointLatLng.Empty;
                }
            }
        }
        private PointLatLng gcj02;
        public PointLatLng GCJ02
        {
            get
            {
                double lon, lat;
                if (CoordType == CoordType.WGS84)
                {
                    if (gcj02 == PointLatLng.Empty)
                    {
                        CoordinateTransform.ConvertWgs84ToGcj02(wgs84.Lng, wgs84.Lat, out lon, out lat);
                        gcj02.Lat = lat;
                        gcj02.Lng = lon;
                    }
                    return gcj02;
                }
                else if (CoordType == CoordType.GCJ02)
                {
                    return gcj02;
                }
                else if (CoordType == CoordType.BD09)
                {
                    if (gcj02 == PointLatLng.Empty)
                    {
                        CoordinateTransform.ConvertBd09ToGcj02(bd09.Lng, bd09.Lat, out lon, out lat);
                        gcj02.Lat = lat;
                        gcj02.Lng = lon;
                    }
                    return gcj02;
                }
                else
                {
                    return PointLatLng.Empty;
                }
            }
        }
        private PointLatLng bd09;
        public PointLatLng BD09
        {
            get
            {
                double lon, lat;
                if (CoordType == CoordType.WGS84)
                {
                    if (bd09 == PointLatLng.Empty)
                    {
                        CoordinateTransform.ConvertWgs84ToBd09(wgs84.Lng, wgs84.Lat, out lon, out lat);
                        bd09.Lat = lat;
                        bd09.Lng = lon;
                    }
                    return bd09;
                }
                else if (CoordType == CoordType.GCJ02)
                {
                    if (bd09 == PointLatLng.Empty)
                    {
                        CoordinateTransform.ConvertGcj02ToBd09(gcj02.Lng, gcj02.Lat, out lon, out lat);
                        bd09.Lat = lat;
                        bd09.Lng = lon;
                    }
                    return bd09;
                }
                else if (CoordType == CoordType.BD09)
                {
                    return bd09;
                }
                else
                {
                    return PointLatLng.Empty;
                }
            }
        }
        public CoordType CoordType;
        public PointInDiffCoord()
        {
            wgs84 = PointLatLng.Empty;
            gcj02 = PointLatLng.Empty;
            bd09 = PointLatLng.Empty;
            CoordType = CoordType.UNKNOW;
        }

        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="p"></param>
        public PointInDiffCoord(PointLatLng p)
        {
            if (p.Type == CoordType.WGS84)
            {
                wgs84 = p;
                gcj02 = PointLatLng.Empty;
                bd09 = PointLatLng.Empty;
                CoordType = CoordType.WGS84;
            }
            else if (p.Type == CoordType.GCJ02)
            {
                wgs84 = PointLatLng.Empty;
                gcj02 = p;
                bd09 = PointLatLng.Empty;
                CoordType = CoordType.GCJ02;
            }
            else if (p.Type == CoordType.BD09)
            {
                wgs84 = PointLatLng.Empty;
                gcj02 = PointLatLng.Empty;
                bd09 = p;
                CoordType = CoordType.BD09;
            }
            else
            {
                wgs84 = PointLatLng.Empty;
                gcj02 = PointLatLng.Empty;
                bd09 = PointLatLng.Empty;
                CoordType = CoordType.UNKNOW;
            }
        }

        /// <summary>
        /// 构造方法
        /// </summary>
        /// <param name="lat"></param>
        /// <param name="lon"></param>
        public PointInDiffCoord(double lat, double lon)
            : this(new PointLatLng(lat, lon))
        {
            
        }

        public static PointLatLng GetWGS84Point(PointLatLng p)
        {
            double lon, lat;
            if (p != PointLatLng.Empty)
            {
                if (p.Type == CoordType.WGS84)
                {
                    return p;
                }
                else if (p.Type == CoordType.GCJ02)
                {
                    CoordinateTransform.ConvertGcj02ToWgs84(p.Lng, p.Lat, out lon, out lat);
                    return new PointLatLng(lat, lon, CoordType.WGS84);
                }
                else if (p.Type == CoordType.BD09)
                {
                    CoordinateTransform.ConvertBd09ToWgs84(p.Lng, p.Lat, out lon, out lat);
                    return new PointLatLng(lat, lon, CoordType.WGS84);
                }
                else
                {
                    return PointLatLng.Empty;
                }
            }
            else
            {
                return PointLatLng.Empty;
            }
        }

        public static PointLatLng GetGCJ02Point(PointLatLng p)
        {
            double lon, lat;
            if (p != PointLatLng.Empty)
            {
                if (p.Type == CoordType.WGS84)
                {
                    CoordinateTransform.ConvertWgs84ToGcj02(p.Lng, p.Lat, out lon, out lat);
                    return new PointLatLng(lat, lon, CoordType.GCJ02);
                }
                else if (p.Type == CoordType.GCJ02)
                {
                    return p;
                }
                else if (p.Type == CoordType.BD09)
                {
                    CoordinateTransform.ConvertBd09ToGcj02(p.Lng, p.Lat, out lon, out lat);
                    return new PointLatLng(lat, lon, CoordType.GCJ02);
                }
                else
                {
                    return PointLatLng.Empty;
                }
            }
            else
            {
                return PointLatLng.Empty;
            }
        }

        public static PointLatLng GetBD09Point(PointLatLng p)
        {
            double lon, lat;
            if (p != PointLatLng.Empty)
            {
                if (p.Type == CoordType.WGS84)
                {
                    CoordinateTransform.ConvertWgs84ToBd09(p.Lng, p.Lat, out lon, out lat);
                    return new PointLatLng(lat, lon, CoordType.BD09);
                }
                else if (p.Type == CoordType.GCJ02)
                {
                    CoordinateTransform.ConvertGcj02ToBd09(p.Lng, p.Lat, out lon, out lat);
                    return new PointLatLng(lat, lon, CoordType.BD09);
                }
                else if (p.Type == CoordType.BD09)
                {
                    return p;
                }
                else
                {
                    return PointLatLng.Empty;
                }
            }
            else
            {
                return PointLatLng.Empty;
            }
        }

        public static PointLatLng GetPointInCoordType(PointLatLng p, CoordType destType)
        {
            if (destType == CoordType.WGS84)
            {
                return GetWGS84Point(p);
            }
            else if (destType == CoordType.GCJ02)
            {
                return GetGCJ02Point(p);
            }
            else if (destType == CoordType.BD09)
            {
                return GetBD09Point(p);
            }
            else
            {
                return p;
            }
        }

        public static PointLatLng GetPointInCoordType(PointLatLng p, CoordType srcType, CoordType destType)
        {
            p.Type = srcType;
            if (destType == CoordType.WGS84)
            {
                return GetWGS84Point(p);
            }
            else if (destType == CoordType.GCJ02)
            {
                return GetGCJ02Point(p);
            }
            else if (destType == CoordType.BD09)
            {
                return GetBD09Point(p);
            }
            else
            {
                return p;
            }
        }

        public static PointLatLng GetPointInCoordType(double lat, double lon, CoordType srcType, CoordType destType)
        {
            PointLatLng p = new PointLatLng(lat, lon, srcType);
            if (destType == CoordType.WGS84)
            {
                return GetWGS84Point(p);
            }
            else if (destType == CoordType.GCJ02)
            {
                return GetGCJ02Point(p);
            }
            else if (destType == CoordType.BD09)
            {
                return GetBD09Point(p);
            }
            else
            {
                return p;
            }
        }

        public static List<PointLatLng> GetPointListInCoordType(List<PointLatLng> pList, CoordType srcType, CoordType destType)
        {
            if (srcType == destType)
            {
                return pList;
            }
            List<PointLatLng> retList = new List<PointLatLng>();
            foreach (var item in pList)
            {
                retList.Add(GetPointInCoordType(item, srcType, destType));
            }
            return retList;
        }
    }
}
  • 坐标转换示例代码:
// 创建一个GCJ02坐标
PointLatLng p = new PointLatLng(25.254845, 118.658454, CoordType.GCJ02);
// 获取指定类型坐标点
PointLatLng p_type = PointInDiffCoord.GetPointInCoordType(p, type);
// 通过GCJ02坐标点创建PointInDiffCoord对象
PointInDiffCoord pidc = new PointInDiffCoord(p);
// 获取WGS84坐标
PointLatLng p_wgs = pidc.WGS84;
// 获取GCJ02坐标
PointLatLng p_gcj = pidc.GCJ02;
// 获取BD09坐标
PointLatLng p_bd = pidc.BD09;

2.4 模拟跑车

使用历史轨迹进行模拟跑车时,程序将创建NamedPipeServer服务,将GPS信息发送出来,其他进程或程序可以通过连接该服务接收GPS信息,NamedPipeServer服务代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Pipes;
using System.Threading;

namespace MapToolsWinForm
{
    /// <summary>
    /// Windows命名管道服务类
    /// </summary>
    class NamedPipeServer
    {
        private NamedPipeServerStream serverStream;
        private string pipeName;
        private Thread connThread;
        private Thread sendThread;
        private int sleepTime = 10;

        public NamedPipeServer(string pipeName)
        {
            this.pipeName = pipeName;
            serverStream = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 254, PipeTransmissionMode.Message);
            connThread = new Thread(ConnThreadTask);
            connThread.Start();
            sendThread = new Thread(SendThreadTask);
            sendThread.Start();
        }

        private void ConnThreadTask(object obj)
        {
            while (true)
            {
                if (!serverStream.IsConnected)
                {
                    try
                    {
                        serverStream.WaitForConnection();
                        sendQueue.Clear();
                    }
                    catch (Exception)
                    {
                        serverStream.Disconnect();
                        serverStream.Dispose();
                        serverStream = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Message);
                    }
                }
                Thread.Sleep(sleepTime);
            }
        }

        Queue<string> sendQueue = new Queue<string>();
        private void SendThreadTask(object obj)
        {
            while (true)
            {
                try
                {
                    if (!serverStream.IsConnected)
                    {
                        Thread.Sleep(sleepTime);
                        continue;
                    }
                    if (sendQueue.Count <= 0)
                    {
                        Thread.Sleep(sleepTime);
                        continue;
                    }
                    string send_str = sendQueue.Dequeue();
                    byte[] byteArr = Encoding.UTF8.GetBytes(send_str);
                    serverStream.Write(byteArr, 0, byteArr.Length);
                    serverStream.Flush();
                    Thread.Sleep(sleepTime);
                }
                catch (Exception ex)
                {
                    
                }
            }
        }

        public bool sendCanData(string send_str)
        {
            if (!serverStream.IsConnected)
            {
                return false;
            }
            lock (sendQueue)
            {
                if (sendQueue.Count > 20)
                {
                    sendQueue.Clear();
                }
                sendQueue.Enqueue(send_str);
            }
            return true;
        }

        public void Close()
        {
            if (connThread != null && connThread.IsAlive)
            {
                connThread.Abort();
            }
            if (sendThread != null && sendThread.IsAlive)
            {
                sendThread.Abort();
            }
            if (sendQueue != null && sendQueue.Count > 0)
            {
                sendQueue.Clear();
            }
            try
            {
                serverStream.Disconnect();
                serverStream.Dispose();
            }
            catch (Exception)
            {
            }
        }
    }
}

服务创建和数据发送:

// 服务创建
NamedPipeServer namePipeServer = new NamedPipeServer("com.lois.pipe.gps.string");

// 数据发送,格式:lon,lat,dir,speed,alt
if (namePipeServer != null)
{
  namePipeServer.sendCanData(string.Format("{0},{1},{2},{3},{4}", wgsP.Lng, wgsP.Lat, historyGeoData.Direction, historyGeoData.Speed, historyGeoData.Altitude));
}