基于GMap.NET库实现的Windows桌面地图工具软件分享

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/MyMapTools、https://gitee.com/luotengyuan/MyMapTools
程序下载:https://download.csdn.net/download/loutengyuan/86507941

catch_2022-12-26_17-16-41

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

PS: 请替换自己的API Key!

  • http://lbsyun.baidu.com/index.php?title=webapi

  • https://lbs.amap.com/api/webservice/summary/

  • https://lbs.qq.com/webservice_v1/index.html

    GIF_2022-12-26_17-19-30

    最近有人使用我在工程中配置的key去请求搜索服务,我的也是个人开发账号,配额才100,导致我天天收到短信和邮件提示,所以需要使用的朋友还是自己申请一下key,谢谢!

    catch_2022-12-26_18-40-03

    catch_2022-12-26_18-43-21

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));
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/22427.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小伙用AI“复活”奶奶引争议 科技边界在哪里(图)

小伙用AI“复活”奶奶引争议 科技边界在哪里(图) 上海00后用AI“复活”奶奶引发巨大争议&#xff1a;科技的边界在哪里。(图片来源&#xff1a;视频截图) 上海00后小伙用AI“复活”奶奶&#xff0c;还原其生前的音容笑貌&#xff0c;却引发巨大争议&#xff0c;人们在思考&…

音视频技术开发周刊 | 300

每周一期&#xff0c;纵览音视频技术领域的干货。 新闻投稿&#xff1a;contributelivevideostack.com。 著名数据和AI企业Databricks&#xff0c;收购类ChatGPT开源平台MosaicML 6月27日&#xff0c;Databricks在官网宣布&#xff0c;以13亿美元&#xff08;约94亿元&#xff…

比GPT-4 Office还炸裂!阿里版GPT全家桶来袭

Datawhale干货 发布&#xff1a;阿里巴巴&#xff0c;来源&#xff1a;新智元 【导读】通义千问一出世&#xff0c;阿里版GPT全家桶立马来了。草图秒变程序&#xff0c;开会还能摸鱼&#xff0c;会议记录邮件文案全整活&#xff01;这只是开始&#xff0c;工作和生活将全面进入…

大咖齐聚CCIG论坛——文档图像智能分析的产业前沿

目录 1 文档图像智能分析技术2 大咖齐聚CCIG20233 议题介绍3.1 从模式识别到类脑研究3.2 视觉-语言预训练模型演进及应用3.3 篡改文本图像的生成和检测3.4 智能文档处理在工业界的应用与挑战 4 观看入口&议程 1 文档图像智能分析技术 文档图像智能分析是指使用计算机视觉和…

谷歌tryOnDiffusion实现了高度逼真的虚拟试穿;ChatGPT 或将推出 AI 应用商店

&#x1f989; AI新闻 &#x1f680; ChatGPT Plus 用户已支持联网和插件功能&#xff0c;OpenAI 或将推出 AI 应用商店 摘要&#xff1a;OpenAI 宣布 ChatGPT Plus 用户已支持联网和插件功能。据外媒 The Information 报道&#xff0c;OpenAI 正考虑为 AI 软件创建一个应用商…

可解释机器学习-Task01-导论

&#x1f4a1; 博客地址&#xff1a;https://1nnoh.top/3W8VFN4/ &#x1f947; 版权: 本文由【1nnoh】原创 各位大佬敬请查阅 &#x1f389; 声明: 本博文禁止转载 感谢尊重和理解 ❤️ 如果文章对你有帮助 欢迎一键三连 可解释机器学习-Task01-导论 0x00 Abstract0x01 什么是…

一、导论——可解释性机器学习(DataWhale组队学习)

目录 导言一、什么是可解释人工智能?二、学可解释机器学习有什么用?2.1学习可解释机器学习的原因2.2 Machine Teaching :人工智能教人类学习2.3 细粒度图像分类2.4前沿AI 三、本身可解释性好的机器学习模型四、传统机器学习算法的可解释性分析五、卷积神经网络的可解释性分析…

在TitanIDE中使用ChatGPT辅助科研开发

作者&#xff1a;行云创新CEO 马洪喜 命题&#xff1a;太空望远镜拍摄的照片处理 假设&#xff1a;我是图形科学家&#xff0c;但不是特别懂Python 先上传一张银河系照片&#xff0c;目的是把彩色转成灰度&#xff1a; 然后我不会啊&#xff0c; 问问chatGPT 彩色图片转灰度…

itest监考机制_iTEST管理员组织大型无纸化考试的经验分享

嘉宾介绍 林涛 集美大学外国语学院在线平台负责人,自2017年起负责学校iTEST大学外语测试与训练系统(下称iTEST系统)的管理工作,曾统筹过七个学期的全校性大学英语在线考试。林老师对于如何使用iTEST系统组织和管理无纸化考试有着丰富的实践经验与使用心得。 经验分享 Q1:请问…

考试系统分享

介绍 学之思考试系统是一款基于k12教育的考试平台&#xff0c;小学、初中、高中和不同学科均可以均可以使用&#xff0c;支持各种题型(单选、多选、判断、填空、解答)&#xff0c;里面包含了三大系统&#xff1a;学生系统、教师系统(TODO)、管理员系统、微信小程序学生端。 演…

腾讯云认证FAQ | 考试类型、考试预约等常见问题

要说2023年最火的是什么&#xff0c;云计算必须占一个名额。 为啥这么火呢&#xff1f;看市场反馈就知道了&#xff1a; 2019年以来&#xff0c;云计算领域对技术岗位的人才需求始终在40%以上&#xff0c;并逐年提高。 2022年显示&#xff0c;核心技术人才的招聘需求占比达到…

考试管理系统/在线考试系统

目录 摘要 Abstract 1 绪论 1.1课题研究背景和意义 1.2 国内外现状分析 1.3 课题研究主要内容 2 核心技术介绍 2.1 mysql技术介绍 2.2 eclipse编译器介绍 2.3 Spring框架简介 2.4 Vue.js技术简介 3 需求分析 3.1 系统的设计模式 3.2 系统的设计目标…

【亲测可用】经验分享,开发在线考试平台(类似问卷星、考试星、阿里巴巴在线考试、易考通、考试云、移动网大、移动网校、中石化网络学院、宝武微学院、iTEST等)如何用程序代码防止学生作弊?

来源&#xff1a; 宝武微学院 http://mooc.baosteel.com 移动网大、移动网校 https://wangda.chinamobile.com 1、如何避免考生考试过程切屏去搜寻答案&#xff1f; 使用JavaScript自带的全局方法屏蔽考生作弊 window.onmouseleave window.onblur window.onmouseout documen…

itest监考机制_干货 | iTEST管理员组织大型无纸化考试的经验分享

原标题&#xff1a;干货 | iTEST管理员组织大型无纸化考试的经验分享 嘉宾介绍 林涛 集美大学外国语学院在线平台负责人&#xff0c;自2017年起负责学校iTEST大学外语测试与训练系统(下称iTEST系统)的管理工作&#xff0c;曾统筹过七个学期的全校性大学英语在线考试。林老师对于…

计算机等级考试无纸化模拟软件(免费版)

分享内容 计算机等级考试【National Computer Rank Examination】&#xff0c;简称NCRE。主要考核计算机基础知识和使用一种高级计算机语言编写程序以及上机调试的基本技能。当代大学生大学期间除了自己的必修课程外&#xff0c;总会给自己找一些其他的东西让自己忙碌起来。除了…

微信订阅号发送模板消息

参考的链接&#xff1a; 微信公众平台测试号——模板消息发送Demo_a816120的博客-CSDN博客 开放接口 | 微信开放文档 微信公众平台 功能一&#xff1a;代码实现发送微信公众平台配置的模板消息 1、事先获取好appID和appsecret 2、书写发送的工具类 package com.talk915.…

微信公众号订阅消息

1、官网介绍 功能介绍 | 微信开放文档 订阅通知是一个用户主动订阅、服务号按需下发的通知能力。使用过程请遵守《微信公众平台服务协议》《微信公众平台运营规范》。 如有疑问&#xff0c;可在微信开放社区反馈。 设置订阅功能&#xff1a;服务号可以在图文消息、网页等场景…

windows11默认浏览器开启chatGPT—直接拿下

1、进入浏览器扩展设置 https://microsoftedge.microsoft.com/addons/search/Wetab?hlzh-CN 搜索此软件 2、添加到扩展后&#xff0c;打开查看显示 3、注册账号&#xff0c;然后使用即可

为你的Pycharm装一个得力助手(ChatGPT吧)

Pycharm算是我们日常工作中用到的一个非常重要的python工具&#xff0c;如果能为Pycharm安装一个小助手那么我们的办公效率一定可以事半功倍&#xff01;&#xff01; 插件安装 1、点击左上角 file——setting 2、点击Plugins——输入NexChatGpt&#xff0c;安装 即可使用&…

教你如何用Python分析出选注双色球号码

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ 数据集介绍 找从19年到现在的开奖历史数据&#xff0c;我们首先要把这个历史数据拿到&#xff0c; 拿到我们再进行做分析&#xff0c;分析每个号码出现的频率是多少&#xff0c; 哪个多&#x…