using DevExpress.Export.Xl; using DevExpress.Map; using DevExpress.Map.Native; using DevExpress.Utils; using DevExpress.Utils.Helpers; using DevExpress.Utils.Svg; using DevExpress.XtraBars; using DevExpress.XtraGrid; using DevExpress.XtraGrid.Columns; using DevExpress.XtraGrid.Views.Grid; using DevExpress.XtraMap; using DevExpress.XtraMap.ItemEditor; using DevExpress.XtraPrinting; using DxHelper; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Data.Common; using System.Data.SQLite; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Windows.Forms; using XdCxRhDW.Entity; public enum GoogleMapType { /// /// 卫星地图 /// Sat = 47626774, /// /// 地形图 /// Landform = 370833361, /// /// 混合地图 /// Mix = 1024577166, /// /// 普通地图 /// Normal = 1818940751 } /// /// MapControl扩展类,封装了常用的非业务基础功能. /// 内置了3个图层DrawLayer、FixedDrawLayer、PosLayer. /// 必须首先调用UseDefalutOptions函数 /// public static class MapControlEx { class InnerData { internal GeoPoint _mapMenuGeoPoint; internal Dictionary _clusterCache = new Dictionary(); internal Dictionary _dataCache = new Dictionary(); internal BarManager barM; internal PopupMenu mapMenu; internal PopupMenu posMenu; internal PopupMenu rectMenu; internal Action<(double starLon, double startLat, double centerLon, double centerLat, double endLon, double endLat, double lonRange, double latRange)> mOnRectChanged; internal Action<(double Lon, double Lat)> onClickChanged; internal MapItemStorage mMapStorage; internal MapItemStorage mMapStorageFixed; internal MapPolyline distinctPath; internal MapDot hoverPoint; internal ToolTipControllerShowEventArgs hoverTip; internal ToolTipController mapToolTip; internal MapItemStorage posStorge; internal MapItemStorage trackStorge;//航迹专用 internal MapRectangle rangeItem; internal MapDot dotItem = new MapDot() { Tag = "DrawRect" }; internal bool drawingRect = false; internal bool mouseLeftDown = false; internal (double starLon, double startLat, double centerLon, double centerLat, double endLon, double endLat, double lonRange, double latRange) CurrentRect; internal MapDot preSelectedItem; } private static GoogleMapType mMapType = GoogleMapType.Normal; private static List listMapCtrl = new List(); private const int _dotSize = 8; private const int _selectedDotSize = 18; /// /// 设置地图通用属性并创建4个Layer. /// ImageLayer:绘制地图瓦片 /// DrawLayer:地图绘制图层,右键可以擦除 /// FixedDrawLayer:地图绘制层,右键无法擦除 /// PosLayer:定位点绘制专用图层 /// /// /// public static MapControl UseDefalutOptions(this MapControl ctrl) { if (ctrl.Tag != null) return ctrl; var barM = new BarManager(); barM.BeginInit(); barM.Form = ctrl; var mapMeun = new PopupMenu() { Manager = barM }; var posMenu = new PopupMenu() { Manager = barM }; var rectMenu = new PopupMenu() { Manager = barM }; barM.EndInit(); listMapCtrl.Add(ctrl); var innerData = new InnerData(); innerData.barM = barM; innerData.mapMenu = mapMeun; innerData.posMenu = posMenu; innerData.rectMenu = rectMenu; innerData.mapToolTip = new ToolTipController(); innerData.mapToolTip.InitialDelay = 10; innerData.mapToolTip.KeepWhileHovered = true; innerData.mapToolTip.ReshowDelay = 10; innerData.mapToolTip.ShowBeak = true; innerData.mapToolTip.ToolTipAnchor = DevExpress.Utils.ToolTipAnchor.Cursor; innerData.mapToolTip.ToolTipType = ToolTipType.SuperTip; ctrl.Tag = innerData; MapOverlay overlay = new MapOverlay() { Alignment = ContentAlignment.BottomLeft, JoiningOrientation = Orientation.Horizontal, Margin = new Padding(5, 0, 0, 5), Padding = new Padding(0), }; //overlay.BackgroundStyle.Fill = Color.Transparent; ctrl.Overlays.Add(overlay); ctrl.ToolTipController = innerData.mapToolTip; ctrl.ToolTipController.BeforeShow += (sender, e) => { if (e.SelectedObject == null) return; if (e.SelectedObject is MapPolyline line) { if (line.Layer != ctrl.GetDrawLayer()) { e.SuperTip = null; e.ToolTip = null; return; } } SuperToolTip superToolTip = new SuperToolTip(); if (innerData.posMenu.Visible || innerData.mapMenu.Visible || innerData.rectMenu.Visible) { e.SuperTip = null; e.ToolTip = null; return; } if (e.SelectedObject.ToString() == ItemsEditorPanelAction.AddRectangle.ToString()) { e.ToolTip = "绘制矩形"; return; } else if (e.SelectedObject is MapDot mapDot) { if (mapDot == null) return; var posItem = mapDot.Tag as PosData; if (mapDot == null || posItem == null) return; if (posItem.ClusterCount == 1) { var props = posItem.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList(); var list = new List<(int Index, string Name, string Format, object Value)>(); foreach (var prop in props) { var attrToolTip = prop.GetCustomAttribute(); if (attrToolTip == null) continue; string toolTipFormat = attrToolTip.Format; var val = prop.GetValue(posItem); string displayName = prop.Name; var attrDisplay = prop.GetCustomAttribute(); if (attrDisplay != null && !string.IsNullOrWhiteSpace(attrDisplay.Name)) displayName = attrDisplay.Name; int index = attrToolTip.Index; list.Add((index, displayName, toolTipFormat, val)); } list = list.OrderBy(p => p.Index).ToList(); foreach ((int Index, string Name, string Format, object val) in list) { string f = null; if (val is DateTime) { f = "yyyy-MM-dd HH:mm:ss"; } else if (val is float || val is double || val is decimal) { f = "f4"; } if (!string.IsNullOrWhiteSpace(Format)) f = Format; string valStr = ""; if (val is null) valStr = "--"; else if (!string.IsNullOrWhiteSpace(f)) valStr = ((dynamic)val).ToString(f); else if (val.GetType().IsEnum) { var field = val.GetType().GetField(Enum.GetName(val.GetType(), val)); var attr = field.GetCustomAttribute(); if (attr != null && !string.IsNullOrWhiteSpace(attr.Name)) { valStr = attr.Name; } else { valStr = val.ToString(); } } else valStr = val.ToString(); ToolTipItem tipItem = new ToolTipItem(); tipItem.Text = $"{Name}:{valStr}"; superToolTip.Items.Add(tipItem); } } else { ToolTipItem tipItem = new ToolTipItem(); tipItem.Text = $"当前位置附近有{posItem.ClusterCount}个定位点,放大可查看"; superToolTip.Items.Add(tipItem); } e.SuperTip = superToolTip; } else if (e.SelectedObject is MapCustomElement ele) { if (ele.Tag.GetType() == typeof(string)) return; var find = innerData.mMapStorageFixed.Items.Where(p => (p is MapCustomElement pp) && p != ele && pp.Location.Equals(ele.Location)); StringBuilder sb = new StringBuilder(); sb.Append(((object[])ele.Tag)[1]); foreach (var item in find) { sb.Append("\r\n"); sb.Append("-------------------"); sb.Append("\r\n"); sb.Append(((object[])item.Tag).Last()); } ele.ToolTipPattern = sb.ToString(); } }; ctrl.MouseMove += (sender, e) => { overlay.Items.Clear(); var geo = (GeoPoint)ctrl.ScreenPointToCoordPoint(e.Location); string ee = "E"; string nn = "N"; if (geo.Longitude < 0) { geo.Longitude *= -1; ee = "W"; } if (geo.Latitude < 0) { geo.Latitude *= -1; nn = "S"; } MapOverlayTextItem item = new MapOverlayTextItem() { Alignment = ContentAlignment.BottomLeft, Text = $"{geo.Longitude:f2}°{ee} {geo.Latitude:f2}°{nn}" }; //item.TextStyle.TextColor = Color.FromArgb(128, 128, 128); item.TextStyle.Font = new Font("微软雅黑", 11F); overlay.Items.Add(item); }; ctrl.MouseDown += (sender, e) => { if (e.Button == MouseButtons.Right) { var hitInfo = ctrl.CalcHitInfo(e.Location); if (hitInfo.InMapRectangle && innerData.rangeItem != null) { rectMenu?.ShowPopup(Cursor.Position); } else if (hitInfo.InMapDot && hitInfo.MapDot.Tag is PosData) { var selectPos = (PosData)hitInfo.MapDot.Tag; if (!selectPos.Selected) { selectPos.Selected = true; ctrl.UpdatePosItem(selectPos); } if (selectPos.ClusterCount == 1) { posMenu?.ShowPopup(Cursor.Position);//选中了原始MapItem } else { //选中了聚合后的MapItem } } else { var geoPoint = ctrl.ScreenPointToCoordPoint(e.Location) as GeoPoint; innerData._mapMenuGeoPoint = geoPoint; innerData.mapMenu?.ShowPopup(Cursor.Position); } } else { innerData.mouseLeftDown = true; innerData.barM.CloseMenus(); var hitInfo = ctrl.CalcHitInfo(e.Location); if (hitInfo.InMapDot && hitInfo.MapDot.Tag is PosData) { var selectPos = hitInfo.MapDot.Tag as PosData; if (!selectPos.Selected) { selectPos.Selected = true; ctrl.UpdatePosItem(selectPos); } } } }; ctrl.MouseUp += (sender, e) => { innerData.mouseLeftDown = false; }; //中心点、缩放级别 ctrl.MinZoomLevel = 2; ctrl.ZoomLevel = 5; ctrl.MaxZoomLevel = 14; ctrl.CenterPoint = new GeoPoint(18, 110); //禁用多余特效 ctrl.EnableDelayedScrolling = true; ctrl.RenderMode = RenderMode.DirectX; ctrl.EnableAnimation = false; ctrl.EnableRotation = false; //ctrl.ShowSearchPanel = false; ctrl.SearchPanelOptions.Visible = false; ctrl.SelectionMode = ElementSelectionMode.Single;//只能单选图层上的元素 ((GeoMapCoordinateSystem)ctrl.CoordinateSystem).CircularScrollingMode = CircularScrollingMode.TilesAndVectorItems; //地图下方导航栏 ctrl.NavigationPanelOptions.Visible = false; //ctrl.NavigationPanelOptions.Height = 35; //ctrl.NavigationPanelOptions.BackgroundStyle.Fill = Color.Transparent; //ctrl.NavigationPanelOptions.ShowScrollButtons = false; //ctrl.NavigationPanelOptions.ShowZoomTrackbar = false; //ctrl.NavigationPanelOptions.ShowCoordinates = true; //ctrl.NavigationPanelOptions.ShowKilometersScale = false; //ctrl.NavigationPanelOptions.ShowMilesScale = false; //ctrl.NavigationPanelOptions.CoordinatesStyle.Font = new Font("微软雅黑", 12F); //ctrl.NavigationPanelOptions.CoordinatesStyle.TextColor = Color.FromArgb(80, 80, 80); //ctrl.NavigationPanelOptions.ScaleStyle.Font = new Font("微软雅黑", 10F); //地图绘制加载(标点、测距、框选等元素载体图层).用来绘制可擦除的元素 var layerDraw = new VectorItemsLayer() { Name = "DrawLayer" }; var drawDataStorage = new MapItemStorage(); layerDraw.Data = drawDataStorage; ctrl.Layers.Add(layerDraw); innerData.mMapStorage = drawDataStorage; //绘制图层,不可手动擦除,一般用来绘制卫星、参考站等固定元素 var layerDrawFixed = new VectorItemsLayer() { Name = "FixedDrawLayer" }; var drawDataStorageFixed = new MapItemStorage(); layerDrawFixed.Data = drawDataStorageFixed; ctrl.Layers.Add(layerDrawFixed); innerData.mMapStorageFixed = drawDataStorageFixed; //定位点专用PosLayer var layerPos = new VectorItemsLayer() { Name = "PosLayer" }; layerPos.ToolTipPattern = " ";//随便给一个,不然不会显示定位点的tooltip layerPos.AllowEditItems = true; layerPos.EnableHighlighting = false; layerPos.EnableSelection = false; ctrl.Layers.Add(layerPos); var posStorge = new MapItemStorage(); layerPos.Data = posStorge; innerData.posStorge = posStorge; return ctrl; } /// /// 在定位点上增加右键菜单 /// /// /// /// /// 一个回调,参数为选中的定位点 /// /// public static MapControl AddPosMenu(this MapControl ctrl, string caption, SvgImage img, Action action, Func showCondition = null) { var btnCustom = new BarButtonItem() { Caption = caption }; btnCustom.ImageOptions.SvgImage = img; btnCustom.Tag = ctrl; var innerData = ctrl.Tag as InnerData; btnCustom.ItemClick += (sender, e) => { action((T)innerData.preSelectedItem.Tag); }; innerData.barM.BeginInit(); innerData.barM.Items.Add(btnCustom); innerData.posMenu.LinksPersistInfo.Add(new LinkPersistInfo(btnCustom)); innerData.barM.EndInit(); innerData.posMenu.BeforePopup += (sender, e) => { if (showCondition == null) btnCustom.Visibility = BarItemVisibility.Always; else { bool showBtn = showCondition((T)innerData.preSelectedItem.Tag); if (showBtn) btnCustom.Visibility = BarItemVisibility.Always; else btnCustom.Visibility = BarItemVisibility.Never; } }; return ctrl; } /// /// 将GridControl单行元素右键菜单挂载到地图定位点右键菜单上(实验) /// /// /// /// /// public static GridControl MountRowMenuToMapPos(this GridControl grid, MapControl mapCtrl) where T : PosData { mapCtrl.PosSelectedChanged(t => { var view = grid.MainView as GridView; view.ClearSelection(); for (int i = 0; i < view.RowCount; i++) { if ((view.GetRow(i) as T).ID == t.ID) { view.FocusedRowHandle = i; view.SelectRow(i); break; } } }); var data = grid.Tag as GridControlEx.GridTag; data.RowButtonsItems.ForEach(t => mapCtrl.AddPosMenu(t)); return grid; } /// /// 将GridControl多行选择菜单挂载到地图框选区域右键菜单上(实验) /// /// /// /// public static GridControl MountMultRowMenuToMapRegion(this GridControl grid, MapControl mapCtrl) { return grid; } /// /// 在定位点上增加右键菜单 /// /// /// 右键菜单 /// public static MapControl AddPosMenu(this MapControl ctrl, BarButtonItem btn) { if (ctrl.Tag == null) ctrl.UseDefalutOptions(); var innerData = ctrl.Tag as InnerData; innerData.barM.BeginInit(); var btnNew = new BarButtonItem() { Tag = btn.Tag, Caption = btn.Caption, }; btnNew.ItemClick += (ItemClickEventHandler)btn.Events()[btn.Caption]; btnNew.ImageOptions.SvgImage = btn.ImageOptions.SvgImage; innerData.barM.Items.Add(btnNew); innerData.posMenu.LinksPersistInfo.Add(new LinkPersistInfo(btnNew)); innerData.barM.EndInit(); innerData.posMenu.BeforePopup += (sender, e) => { if (btnNew.Tag != null && btnNew.Tag is Func showCondition) { if (showCondition()) btnNew.Visibility = BarItemVisibility.Always; else btnNew.Visibility = BarItemVisibility.Never; } }; return ctrl; } /// /// 在地图上增加右键菜单 /// /// /// /// /// 一个回调,参数为点击位置的经纬度 /// public static MapControl AddMapMenu(this MapControl ctrl, string caption, SvgImage img, Action action) { var btnCustom = new BarButtonItem() { Caption = caption, Name = caption }; btnCustom.ImageOptions.SvgImage = img; btnCustom.Tag = ctrl; var innerData = ctrl.Tag as InnerData; btnCustom.ItemClick += (sender, e) => { action(innerData._mapMenuGeoPoint.Longitude, innerData._mapMenuGeoPoint.Latitude); }; innerData.barM.BeginInit(); innerData.barM.Items.Add(btnCustom); innerData.mapMenu.LinksPersistInfo.Add(new LinkPersistInfo(btnCustom)); innerData.barM.EndInit(); return ctrl; } /// /// 在框选矩形上增加右键菜单 /// /// /// /// /// 一个回调,参数为框选的定位点 /// /// public static MapControl AddRectMenu(this MapControl ctrl, string caption, SvgImage img, Action> action, bool resetMapEdior = true) where T : PosData, new() { var btnCustom = new BarButtonItem() { Caption = caption }; btnCustom.ImageOptions.SvgImage = img; btnCustom.Tag = ctrl; btnCustom.ItemClick += (sender, e) => { var data = ctrl.GetRectPosItem(); ctrl.ClearRect(); action(data); if (resetMapEdior) { ctrl.MapEditor.SetTransformMode(MapItemTransform.None); } }; var innerData = ctrl.Tag as InnerData; innerData.barM.BeginInit(); innerData.barM.Items.Add(btnCustom); innerData.rectMenu.LinksPersistInfo.Add(new LinkPersistInfo(btnCustom)); innerData.barM.EndInit(); return ctrl; } public static MapControl PosSelectedChanged(this MapControl ctrl, Action action) { ctrl.MapItemClick += (sender, e) => { if (e.Item.Tag is T val) { action(val); } }; return ctrl; } /// /// 重新设置定位数据 /// /// /// /// 数据源(指定null或空集合可以清空已有数据) /// 是否清除DrawLayer上临时绘制的内容 public static void SetPosDataSource(this MapControl ctrl, IEnumerable items, bool clearDrawLayer = true) where T : PosData, new() { var innerData = ctrl.Tag as InnerData; innerData._dataCache.Clear(); if (clearDrawLayer) ctrl.ClearDrawObj(); innerData.posStorge.Items.Clear(); if (items == null || !items.Any()) { ctrl.Refresh(); return; } List list = new List(); for (int i = 0; i < items.Count(); i++) { var p = items.ElementAt(i); if (p.PosLon > 180) continue; var mapItem = new MapDot() { EnableHighlighting = DefaultBoolean.False, EnableSelection = DefaultBoolean.False, CanMove = false, Visible = p.Visible, IsHitTestVisible = true, StrokeWidth = 2, Fill = ColorHelper.IsHtmlColor(p.ColorKey) ? ColorTranslator.FromHtml(p.ColorKey) : ColorHelper.GetColor(p.ColorKey), Size = _dotSize, Tag = items.ElementAt(i), Location = new GeoPoint(p.PosLat, p.PosLon) }; list.Add(mapItem); innerData._dataCache.Add(p, mapItem); } innerData.posStorge.Items.AddRange(list); } /// /// 定位图层数据源添加一个定位点(如果需要一次性添加很多个点,请使用AddPosItems) /// /// /// /// public static void AddPosItem(this MapControl ctrl, T item) where T : PosData, new() { if (item.PosLon > 180) return; var innerData = ctrl.Tag as InnerData; var mapItem = new MapDot() { EnableHighlighting = DefaultBoolean.True, EnableSelection = DefaultBoolean.False, CanMove = false, Visible = item.Visible, IsHitTestVisible = true, Fill = ColorHelper.IsHtmlColor(item.ColorKey) ? ColorTranslator.FromHtml(item.ColorKey) : ColorHelper.GetColor(item.ColorKey), Size = _dotSize, Tag = item, Location = new GeoPoint(item.PosLat, item.PosLon) }; if (!innerData._dataCache.ContainsKey(item)) { innerData._dataCache.Add(item, mapItem); innerData.posStorge.Items.Add(mapItem); } } /// /// 定位图层数据源添加多个定位点 /// /// /// /// public static void AddPosItems(this MapControl ctrl, IEnumerable items) where T : PosData, new() { var innerData = ctrl.Tag as InnerData; List list = new List(); foreach (var item in items) { if (item.PosLon > 180) return; var mapItem = new MapDot() { EnableHighlighting = DefaultBoolean.True, EnableSelection = DefaultBoolean.False, CanMove = false, Visible = item.Visible, IsHitTestVisible = true, Fill = ColorHelper.IsHtmlColor(item.ColorKey) ? ColorTranslator.FromHtml(item.ColorKey) : ColorHelper.GetColor(item.ColorKey), Size = _dotSize, Tag = item, Location = new GeoPoint(item.PosLat, item.PosLon) }; list.Add(mapItem); innerData._dataCache.Add(item, mapItem); } innerData.posStorge.Items.AddRange(list); } /// /// 定位图层数据源删除定位点 /// /// /// /// public static void DelPosItem(this MapControl ctrl, IEnumerable data) where T : PosData, new() { if (data == null || !data.Any()) return; foreach (var item in data) { ctrl.DelPosItem(item, false); } ctrl.Refresh(); } /// /// 定位图层数据源删除定位点 /// /// /// /// /// public static void DelPosItem(this MapControl ctrl, T item, bool refreshCtrl = true) where T : PosData, new() { if (item == null) return; var innerData = ctrl.Tag as InnerData; var key = innerData._dataCache.Keys.Where(p => p.ID == item.ID).FirstOrDefault(); if (key != null) { innerData.posStorge.Items.Remove(innerData._dataCache[key]); innerData._dataCache.Remove(key); } if (refreshCtrl) ctrl.Refresh(); } /// /// 定位图层数据源删除定位点 /// /// /// /// public static void DelPosItem(this MapControl ctrl, Func predicate) where T : PosData, new() { var innerData = ctrl.Tag as InnerData; List keys = new List(); foreach (var item in innerData._dataCache.Keys) { if (predicate((T)item)) keys.Add(item); } foreach (var item in keys) { ctrl.DelPosItem(item, false); } ctrl.Refresh(); } /// /// 更新定位数据(可以更新可见性、颜色、经纬度、选中状态) /// /// /// /// /// 是否将这个点设置到地图中心显示 public static void UpdatePosItem(this MapControl ctrl, T item, bool setCenter = false) where T : PosData, new() { if (item == null || item.PosLon == 999 || item.PosLat == 999) return; var innerData = ctrl.Tag as InnerData; var key = innerData._dataCache.Keys.Where(p => p.ID == item.ID).FirstOrDefault(); if (key != null) { var mapDot = innerData._dataCache[key] as MapDot; mapDot.Visible = item.Visible;//外部修改了可见性 mapDot.Fill = ColorHelper.IsHtmlColor(item.ColorKey) ? ColorTranslator.FromHtml(item.ColorKey) : ColorHelper.GetColor(item.ColorKey);//外部修改了颜色 mapDot.Location = new GeoPoint(item.PosLat, item.PosLon);//外部修改了位置 if (mapDot.Size != (item.Selected ? _selectedDotSize : _dotSize))//外部修改了选中状态 { mapDot.Size = item.Selected ? _selectedDotSize : _dotSize; mapDot.StrokeWidth = item.Selected ? 0 : 2; if (item.ClusterCount > 1) { if (innerData._clusterCache.TryGetValue(item.ClusterKey, out MapItem clusterItem)) { (clusterItem as MapDot).Size = item.Selected ? _selectedDotSize : _dotSize; } } if (mapDot.ClusteredItems.Any()) (mapDot.ClusteredItems[0] as MapDot).Size = _selectedDotSize; if (item.Selected) { //让选中的Item在上层 var idx = innerData.posStorge.Items.IndexOf(mapDot); innerData.posStorge.Items.Swap(idx, innerData.posStorge.Items.Count - 1); //需要将上次选中的点设置为未选中 if (innerData.preSelectedItem != null) { innerData.preSelectedItem.Size = _dotSize; (innerData.preSelectedItem.Tag as PosData).Selected = false; innerData.preSelectedItem.StrokeWidth = 2; if ((innerData.preSelectedItem.Tag as PosData).ClusterCount > 1) { if (innerData._clusterCache.TryGetValue((innerData.preSelectedItem.Tag as PosData).ClusterKey, out MapItem clusterItem)) { (clusterItem as MapDot).Size = _dotSize + 2; } } } innerData.preSelectedItem = mapDot; } } if (setCenter && item.Visible) ctrl.SetCenterPoint(item.PosLon, item.PosLat, false); } } /// /// 更新定位数据(可以更新可见性、颜色、经纬度) /// /// /// /// public static void UpdatePosItem(this MapControl ctrl, IEnumerable items) where T : PosData, new() { if (items == null || !items.Any()) return; var innerData = ctrl.Tag as InnerData; foreach (var item in items) { var key = innerData._dataCache.Keys.Where(p => p.ID == item.ID).FirstOrDefault(); if (key != null) { var mapDot = innerData._dataCache[key] as MapDot; mapDot.Visible = item.Visible;//外部修改了可见性 mapDot.Fill = ColorHelper.IsHtmlColor(item.ColorKey) ? ColorTranslator.FromHtml(item.ColorKey) : ColorHelper.GetColor(item.ColorKey);//外部修改了颜色 mapDot.Location = new GeoPoint(item.PosLat, item.PosLon);//外部修改了位置 } } } /// /// 设置地图中心点 /// /// /// /// /// 是否显示动画 /// public static MapControl SetCenterPoint(this MapControl ctrl, double lon, double lat, bool animated = false) { ctrl.SetCenterPoint(new GeoPoint(lat, lon), animated); return ctrl; } /// /// 地图上添加图片 /// /// /// /// /// /// /// /// public static void DrawFixedImg(this MapControl ctrl, string tag, string id, double imgLat, double imgLon, Image img, string toolTip = "") { var innerData = ctrl.Tag as InnerData; var item = new MapCustomElement() { Tag = new object[] { tag, id, toolTip } }; item.UseAnimation = false; item.BackgroundDrawingMode = ElementState.None; item.Location = new GeoPoint(imgLat, imgLon); item.Image = img; item.ToolTipPattern = toolTip; innerData.mMapStorageFixed.Items.Add(item); } /// /// 地图上添加指定大小图片 /// /// /// /// /// /// /// /// public static void DrawFixedImg(this MapControl ctrl, string tag, string id, double imgLat, double imgLon, SvgImage img, string toolTip = "") { var innerData = ctrl.Tag as InnerData; var item = new MapCustomElement() { Tag = new object[] { tag, id, toolTip } }; item.UseAnimation = false; item.BackgroundDrawingMode = ElementState.None; item.Location = new GeoPoint(imgLat, imgLon); item.SvgImage = img; item.CanMove = false; item.EnableHighlighting = DefaultBoolean.False; item.EnableSelection = DefaultBoolean.False; item.ToolTipPattern = toolTip; innerData.mMapStorageFixed.Items.Add(item); } /// /// /// /// /// /// /// /// public static bool ExistFixedImg(this MapControl ctrl, string id, double imgLat, double imgLon) { var innerData = ctrl.Tag as InnerData; return innerData.mMapStorageFixed.Items.Any(p => (p is MapCustomElement ele) && (ele.Tag as object[])[1].ToString() == id && (ele.Location as GeoPoint).Longitude == imgLon && (ele.Location as GeoPoint).Latitude == imgLat); } public static void DelFixedImg(this MapControl ctrl, string tag) { var innerData = ctrl.Tag as InnerData; var delItems = innerData.mMapStorageFixed.Items.ToList().FindAll(p => ((object[])p.Tag)[0].ToString() == tag); foreach (var item in delItems) { innerData.mMapStorageFixed.Items.Remove(item); } } /// /// 获取当前矩形框选的定位点 /// /// /// public static IEnumerable GetRectPosItem(this MapControl ctrl) where T : PosData, new() { var innerData = ctrl.Tag as InnerData; if (innerData.CurrentRect == default) return new List(); var rect = ctrl.GetCurrentRect(); double startLon = rect.starLon; double startLat = rect.startLat; double endLon = rect.endLon; double endLat = rect.endLat; var temp = innerData._dataCache.Keys.Where(p => p.InRectangle(startLon, startLat, endLon, endLat)); var res = temp.Select(p => (T)p); return res; } /// /// 使用本地DB文件图源 /// /// /// public static MapControl UseLocalDb(this MapControl ctrl) { bool localGmdbDataEnable = false;//本地Data.Gmdb是否可用,可用时读取本地瓦片,否则执行Http请求资源 var files = Directory.GetFiles(Application.StartupPath, "Data.gmdb", SearchOption.AllDirectories); if (files.Length > 0) { try { string conStr = string.Format("Data Source=\"{0}\";Page Size=32768", files[0]); var con = new SQLiteConnection(conStr); con.Open(); con.Close(); localGmdbDataEnable = true; } catch (Exception ex) { Console.Error.WriteLine(ex.Message); } } if (localGmdbDataEnable) { RemoveWmtsLyaer(ctrl); ImageLayer layer = new ImageLayer() { Name = "WMTS:Local" }; ctrl.Layers.Add(layer); var provider = new ImageTileDataProvider();//地图瓦片提供者 provider.TileSource = new ImageTileSource();//地图瓦片数据源接口实现 layer.DataProvider = provider; } return ctrl; } private static void RemoveWmtsLyaer(MapControl ctrl) { var layers = ctrl.Layers.Where(p => p.Name.StartsWith("WMTS:")).ToList(); foreach (var item in layers) { ctrl.Layers.Remove(item); } } /// /// 使用WMTS图源 /// /// /// such as http://192.168.100.63:58089 /// /// /// public static MapControl UseWMTS(this MapControl ctrl, string url, EnumWmtsSource source, EnumMapLayerType layerType) { RemoveWmtsLyaer(ctrl); if (source == EnumWmtsSource.SJZX) { if (layerType.HasFlag(EnumMapLayerType.SatMap)) { var provider = new HttpMapDataProvider(); var tileSource = provider.TileSource as HttpTileSource; tileSource.HttpServerAddr = url; tileSource.WmtsSource = source; tileSource.LayerType = layerType; tileSource.LayerName = "satellite"; ImageLayer satelliteImageLayer = new ImageLayer() { Name = "WMTS:SJZX-SatMap" }; ctrl.Layers.Add(satelliteImageLayer); satelliteImageLayer.DataProvider = provider; } if (layerType.HasFlag(EnumMapLayerType.RoadMap) || layerType.HasFlag(EnumMapLayerType.XZQH_Map)) { var provider = new HttpMapDataProvider(); var tileSource = provider.TileSource as HttpTileSource; tileSource.HttpServerAddr = url; tileSource.WmtsSource = source; tileSource.LayerType = layerType; tileSource.LayerName = "electron"; ImageLayer electronImageLayer = new ImageLayer() { Name = "WMTS:SJZX-Electron" }; ctrl.Layers.Add(electronImageLayer); electronImageLayer.DataProvider = provider; } } else { if (layerType.HasFlag(EnumMapLayerType.SatMap)) { var provider = new HttpMapDataProvider(); var tileSource = provider.TileSource as HttpTileSource; tileSource.HttpServerAddr = url; tileSource.WmtsSource = source; tileSource.LayerType = layerType; tileSource.LayerName = "NaturalEarthII"; ImageLayer naturalEarthIIImageLayer = new ImageLayer() { Name = "WMTS:ZCJ-NaturalEarthII" }; ctrl.Layers.Add(naturalEarthIIImageLayer); naturalEarthIIImageLayer.DataProvider = provider; } if (layerType.HasFlag(EnumMapLayerType.RoadMap)) { var provider = new HttpMapDataProvider(); var tileSource = provider.TileSource as HttpTileSource; tileSource.HttpServerAddr = url; tileSource.WmtsSource = source; tileSource.LayerType = layerType; tileSource.LayerName = "roadmap-final"; ImageLayer roadmapIImageLayer = new ImageLayer() { Name = "WMTS:ZCJ-Roadmap" }; ctrl.Layers.Add(roadmapIImageLayer); roadmapIImageLayer.DataProvider = provider; } if (layerType.HasFlag(EnumMapLayerType.XZQH_Map)) { var provider = new HttpMapDataProvider(); var tileSource = provider.TileSource as HttpTileSource; tileSource.HttpServerAddr = url; tileSource.WmtsSource = source; tileSource.LayerType = layerType; tileSource.LayerName = "overlay-final"; ImageLayer overlayImageLayer = new ImageLayer() { Name = "WMTS:ZCJ-Overlay" }; ctrl.Layers.Add(overlayImageLayer); overlayImageLayer.DataProvider = provider; } } ctrl.MinZoomLevel = 3; ctrl.MaxZoomLevel = 20; return ctrl; } /// /// 使用WMS图源 /// /// /// /// /// public static MapControl UseWMS(this MapControl ctrl, string url, string layerName) { if (ctrl is null) { throw new ArgumentNullException(nameof(ctrl)); } if (string.IsNullOrEmpty(url)) { throw new ArgumentException($"“{nameof(url)}”不能为 null 或空。", nameof(url)); } if (layerName is null) { throw new ArgumentNullException(nameof(layerName)); } RemoveWmtsLyaer(ctrl); var provider = new WmsDataProvider();//地图瓦片提供者 provider.ServerUri = url; if (!string.IsNullOrWhiteSpace(layerName)) provider.ActiveLayerName = layerName; provider.CustomParameters.Add("format", "image/JPEG"); // provider.CustomParameters.Add("srs", "EPSG:4326"); var cs = (GeoMapCoordinateSystem)ctrl.CoordinateSystem; cs.Projection = new EPSG4326Projection(); //provider.ResponseCapabilities += (sender, e) => //{ // if (string.IsNullOrWhiteSpace(layerName)) // provider.ActiveLayerName = e.Layers[0].Name; //}; ImageLayer layer = new ImageLayer() { Name = "WMTS:Wms" }; layer.DataProvider = provider; ctrl.Layers.Add(layer); return ctrl; } /// /// 定位点使用内置聚合器 /// /// /// public static MapControl UseCluster(this MapControl ctrl) { var innerData = ctrl.Tag as InnerData; innerData.posStorge.Clusterer = new PosClusterer(ctrl);//定位点聚合器 return ctrl; } /// /// 为地图添加右键测距功能 /// /// public static MapControl UseDistanceLine(this MapControl ctrl) { var btnDistance = new BarButtonItem() { Caption = "测距" }; btnDistance.ImageOptions.SvgImage = SvgHelper.CreateDistanceLine(); btnDistance.Tag = ctrl; btnDistance.ItemClick += DistanceLine_ItemClick; var innerData = ctrl.Tag as InnerData; innerData.barM.BeginInit(); innerData.barM.Items.Add(btnDistance); innerData.mapMenu.LinksPersistInfo.Add(new LinkPersistInfo(btnDistance)); innerData.barM.EndInit(); return ctrl; } /// /// 使用航迹 /// /// /// 点击事件返回当前经纬度 /// public static MapControl UseHJ(this MapControl ctrl, Action<(double Lon, double Lat)> onClickChanged = null) { var btnHJ = new BarButtonItem() { Caption = "航迹" }; btnHJ.ImageOptions.SvgImage = SvgHelper.CreateMarkDot(); btnHJ.Tag = ctrl; var innerData = ctrl.Tag as InnerData; innerData.onClickChanged = onClickChanged; btnHJ.ItemClick += (sender, e) => { ctrl.MapEditor.SetEditMode(); ctrl.MouseClick += AddHJPoint; ctrl.DoubleClick += (sender1, e1) => { ctrl.MouseClick -= AddHJPoint; }; }; innerData.barM.BeginInit(); innerData.barM.Items.Add(btnHJ); innerData.mapMenu.LinksPersistInfo.Add(new LinkPersistInfo(btnHJ)); innerData.barM.EndInit(); return ctrl; } /// /// 添加航迹点 /// /// /// /// public static void AddHJPosItem(this MapControl ctrl, T item) where T : PosData, new() { if (item.PosLon > 180) return; var innerData = ctrl.Tag as InnerData; var mapItem = new MapDot() { EnableHighlighting = DefaultBoolean.True, EnableSelection = DefaultBoolean.False, CanMove = false, Visible = item.Visible, IsHitTestVisible = true, Fill = ColorHelper.IsHtmlColor(item.ColorKey) ? ColorTranslator.FromHtml(item.ColorKey) : ColorHelper.GetColor(item.ColorKey), Size = _dotSize, Tag = item, Location = new GeoPoint(item.PosLat, item.PosLon), ToolTipPattern = "dd" }; innerData.mMapStorage.Items.Add(mapItem); } private static void AddHJPoint(object sender, MouseEventArgs e) { var innerData = (sender as MapControl).Tag as InnerData; var ctrl = sender as MapControl; var geoPoint = ctrl.ScreenPointToCoordPoint(e.Location) as GeoPoint; innerData.onClickChanged?.Invoke((geoPoint.Longitude, geoPoint.Latitude)); } /// /// 为地图添加右键标点功能 /// /// /// public static MapControl UseMarkDot(this MapControl ctrl) { var btnMarkDot = new BarButtonItem() { Caption = "标点" }; btnMarkDot.ImageOptions.SvgImage = SvgHelper.CreateMarkDot(); btnMarkDot.Tag = ctrl; btnMarkDot.ItemClick += MarkDot_ItemClick; var innerData = ctrl.Tag as InnerData; innerData.barM.BeginInit(); innerData.barM.Items.Add(btnMarkDot); innerData.mapMenu.LinksPersistInfo.Add(new LinkPersistInfo(btnMarkDot)); innerData.barM.EndInit(); return ctrl; } /// /// 显示目标轨迹 /// /// /// public static MapControl UseTrack(this MapControl ctrl) { var innerData = ctrl.Tag as InnerData; //航迹线图层 var trackLayer = new VectorItemsLayer() { Name = "TrackLayer" }; var trackStorage = new MapItemStorage(); trackLayer.Data = trackStorage; ctrl.Layers.Add(trackLayer); innerData.trackStorge = trackStorage; var btnMarkDot = new BarButtonItem() { Caption = "目标轨迹" }; btnMarkDot.ImageOptions.SvgImage = SvgHelper.CreateTrack(); btnMarkDot.Tag = ctrl; btnMarkDot.ItemClick += ShowTrack_ItemClick; innerData.barM.BeginInit(); innerData.barM.Items.Add(btnMarkDot); innerData.mapMenu.LinksPersistInfo.Add(new LinkPersistInfo(btnMarkDot)); innerData.barM.EndInit(); return ctrl; } /// /// 为地图添加清除功能 /// /// /// public static MapControl UseClearAll(this MapControl ctrl) { var btnClearAll = new BarButtonItem() { Caption = "清除" }; btnClearAll.ImageOptions.SvgImage = SvgHelper.CreateClear(); btnClearAll.Tag = ctrl; btnClearAll.ItemClick += (sender, e) => ctrl.ClearDrawObj(); var innerData = ctrl.Tag as InnerData; innerData.barM.BeginInit(); innerData.barM.Items.Add(btnClearAll); innerData.mapMenu.LinksPersistInfo.Add(new LinkPersistInfo(btnClearAll)); innerData.barM.EndInit(); return ctrl; } /// /// 为地图添加导出图片功能 /// /// /// public static MapControl UseExportImg(this MapControl ctrl) { var btnExportImg = new BarButtonItem() { Caption = "导出图片" }; btnExportImg.ImageOptions.SvgImage = SvgHelper.CreateExportImg(); btnExportImg.Tag = ctrl; btnExportImg.ItemClick += ExportImg_ItemClick; var innerData = ctrl.Tag as InnerData; innerData.barM.BeginInit(); innerData.barM.Items.Add(btnExportImg); innerData.mapMenu.LinksPersistInfo.Add(new LinkPersistInfo(btnExportImg)); innerData.barM.EndInit(); return ctrl; } /// /// 为地图添加导出图片功能 /// /// /// /// public static MapControl UseExportCsv(this MapControl ctrl, bool exportHeader = true) { var innerData = ctrl.Tag as InnerData; var btnExportCsv = new BarButtonItem() { Caption = "导出Csv" }; btnExportCsv.ImageOptions.SvgImage = SvgHelper.CreateExportCsv(); btnExportCsv.Tag = ctrl; btnExportCsv.ItemClick += (sender, e) => { if (!innerData._dataCache.Any()) return; bool exportSigTime = false; Dictionary cellFormats = new Dictionary();//单元格的format var props = innerData._dataCache.Keys.GetType().GetGenericArguments().First().GetProperties(BindingFlags.Public | BindingFlags.Instance); List<(int ColumnIndex, PropertyInfo Prop, string Header)> listPorps = new List<(int, PropertyInfo, string)>(); foreach (var prop in props) { ExportCellAttribute attrExport = prop.GetCustomAttribute(); if (attrExport == null) continue; if (prop.Name == nameof(PosData.SigTime)) exportSigTime = true; if (!string.IsNullOrWhiteSpace(attrExport.Format)) { cellFormats.Add(prop.Name, attrExport.Format); } if (attrExport.ColumnIndex == -1) attrExport.ColumnIndex = 10000; var attrDisplay = prop.GetCustomAttribute(); if (attrDisplay != null && !string.IsNullOrWhiteSpace(attrDisplay.Name)) listPorps.Add((attrExport.ColumnIndex, prop, attrDisplay.Name)); else listPorps.Add((attrExport.ColumnIndex, prop, prop.Name)); } listPorps = listPorps.OrderBy(p => p.ColumnIndex).ToList(); using (var dialog = new SaveFileDialog()) { dialog.Filter = "CSV文件|*.csv"; dialog.AddExtension = true; dialog.FileName = $"Pos{DateTime.Now:yyyyMMddHHmmss}.csv"; if (dialog.ShowDialog() == DialogResult.OK) { var list = innerData._dataCache.Keys as IEnumerable; if (exportSigTime) list = list.OrderByDescending(p => p.SigTime);//如果导出了SigTime,则自动按照SigTime降序排列后导出 StreamWriter sw = new StreamWriter(dialog.FileName, false, new UTF8Encoding(true));//utf8-bom if (exportHeader) { foreach (var prop in listPorps) { sw.Write($"{prop.Header},"); } sw.WriteLine(); } WaitHelper.ShowOverlayForm(ctrl); long count = list.Count() * listPorps.Count; int idx = 1; foreach (var item in list) { foreach (var prop in listPorps) { WaitHelper.UpdateOverlyText($"{idx * 100 / count}%"); var value = prop.Prop.GetValue(item); string str = null; if (cellFormats.ContainsKey(prop.Prop.Name)) { str = ((dynamic)value).ToString(cellFormats[prop.Prop.Name]); } else { if (value is DateTime) str = $"{(DateTime)value:yyyy-MM-dd HH:mm:ss}"; else if (value is float || value is double || value is decimal) { dynamic valD = value; str = ((dynamic)value).ToString("f4"); } } sw.Write($"{str},"); idx++; } sw.WriteLine(); } sw.Close(); WaitHelper.CloseOverlayForm(); } } GC.Collect(); }; innerData.barM.BeginInit(); innerData.barM.Items.Add(btnExportCsv); innerData.mapMenu.LinksPersistInfo.Add(new LinkPersistInfo(btnExportCsv)); innerData.barM.EndInit(); return ctrl; } /// /// 为地图添加导出Excel数据功能 /// /// /// 是否导出列头 /// public static MapControl UseExportXlsx(this MapControl ctrl, bool exportHeader = true) { Action action = () => { var btnExportXlsx = new BarButtonItem() { Caption = "导出Excel" }; btnExportXlsx.ImageOptions.SvgImage = SvgHelper.CreateExportXlsx(); var innerData = ctrl.Tag as InnerData; innerData.barM.BeginInit(); innerData.barM.Items.Add(btnExportXlsx); innerData.mapMenu.LinksPersistInfo.Add(new LinkPersistInfo(btnExportXlsx)); innerData.barM.EndInit(); btnExportXlsx.ItemClick += (sender, e) => { if (!innerData._dataCache.Any()) return; GridControl gc = new GridControl(); GridView view = new GridView(); view.GridControl = gc; gc.MainView = view; gc.ViewCollection.Add(view); view.OptionsPrint.ShowPrintExportProgress = false; bool exportSigTime = false; Dictionary cellFormats = new Dictionary();//单元格的format var props = innerData._dataCache.Keys.GetType().GetGenericArguments().First().GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (var prop in props) { ExportCellAttribute attrExport = prop.GetCustomAttribute(); if (attrExport == null) continue; if (prop.Name == nameof(PosData.SigTime)) exportSigTime = true; if (!string.IsNullOrWhiteSpace(attrExport.Format)) { cellFormats.Add(prop.Name, attrExport.Format); } var col = new GridColumn(); col.FieldName = prop.Name; col.Visible = true; var attr = prop.GetCustomAttribute(); if (attr != null && !string.IsNullOrWhiteSpace(attr.Name)) col.Caption = attr.Name; else col.Caption = prop.Name; if (attrExport.ColumnIndex >= 0) col.VisibleIndex = attrExport.ColumnIndex; view.Columns.Add(col); } gc.Visible = false; gc.Parent = ctrl.Parent; gc.Width = view.Columns.Count * 120; using (var dialog = new SaveFileDialog()) { dialog.Filter = "Excel文件|*.xlsx"; dialog.AddExtension = true; dialog.FileName = $"Pos{DateTime.Now:yyyyMMddHHmmss}.xlsx"; if (dialog.ShowDialog() == DialogResult.OK) { var list = innerData._dataCache.Keys.ToList(); if (exportSigTime) list = list.OrderByDescending(p => p.SigTime).ToList();//如果导出了SigTime,则自动按照SigTime降序排列后导出 gc.DataSource = list; gc.Visible = true; var exportType = DevExpress.Export.ExportSettings.DefaultExportType; var options = new XlsxExportOptionsEx(); WaitHelper.ShowOverlayForm(ctrl); options.ExportProgress += arg => { WaitHelper.UpdateOverlyText($"{arg.ProgressPercentage}%"); if (arg.ProgressPercentage >= 100) { WaitHelper.CloseOverlayForm(); } }; view.OptionsPrint.PrintHeader = exportHeader; options.CustomizeCell += arg => { arg.Handled = true; var aligmentCenter = new XlCellAlignment() { HorizontalAlignment = XlHorizontalAlignment.Center, VerticalAlignment = XlVerticalAlignment.Center }; var aligmentLeft = new XlCellAlignment() { HorizontalAlignment = XlHorizontalAlignment.Left, VerticalAlignment = XlVerticalAlignment.Center }; if (arg.Value is bool) { arg.Formatting.Alignment = aligmentCenter; arg.Value = ((bool)arg.Value) ? "☑" : "☐"; } if (arg.Value is DateTime) { arg.Formatting.Alignment = aligmentLeft; arg.Formatting.FormatType = FormatType.DateTime; arg.Formatting.FormatString = "yyyy-MM-dd HH:mm:ss"; } else if (arg.Value is float || arg.Value is double || arg.Value is decimal) { arg.Formatting.Alignment = aligmentLeft; arg.Formatting.FormatType = FormatType.Numeric; arg.Formatting.FormatString = "f4"; } if (cellFormats.ContainsKey(arg.ColumnFieldName)) { arg.Formatting.FormatString = cellFormats[arg.ColumnFieldName]; } }; view.OptionsPrint.ShowPrintExportProgress = false; DevExpress.Export.ExportSettings.DefaultExportType = DevExpress.Export.ExportType.DataAware; gc.ExportToXlsx(dialog.FileName, options); DevExpress.Export.ExportSettings.DefaultExportType = exportType; gc.Visible = false; } } gc.Parent = null; gc.Dispose(); GC.Collect(); }; }; if (ctrl.Parent == null) { ctrl.ParentChanged += (sender, e) => action(); } else { action(); } return ctrl; } /// /// 为地图添加矩形框选区域功能 /// /// public static MapControl UseDrawRect(this MapControl ctrl, Action<(double starLon, double startLat, double centerLon, double centerLat, double endLon, double endLat, double lonRange, double latRange)> onRectChanged = null) { var innerData = ctrl.Tag as InnerData; innerData.mOnRectChanged = onRectChanged; ctrl.MapEditor.ShowEditorPanel = true; ctrl.MapEditor.PanelOptions.ShowAddCalloutButton = false; ctrl.MapEditor.PanelOptions.ShowAddCustomElementButton = false; ctrl.MapEditor.PanelOptions.ShowAddDotButton = false; ctrl.MapEditor.PanelOptions.ShowAddEllipseButton = false; ctrl.MapEditor.PanelOptions.ShowAddLineButton = false; ctrl.MapEditor.PanelOptions.ShowAddPathButton = false; ctrl.MapEditor.PanelOptions.ShowAddPolylineButton = false; ctrl.MapEditor.PanelOptions.ShowAddPushpinButton = false; ctrl.MapEditor.PanelOptions.ShowAddSplineButton = false; ctrl.MapEditor.PanelOptions.ShowEditModeButton = false; ctrl.MapEditor.PanelOptions.ShowTransformModeButton = false; ctrl.MapEditor.PanelOptions.ShowUndoRedoButtons = false; ctrl.MouseMove += DrawRect_MouseMove; ctrl.MapEditor.MapItemCreating += (sender, e) => { if (!innerData.mouseLeftDown) { e.Cancel = true; return; } if (!(e.Item is MapRectangle)) return; var items = innerData.mMapStorage.Items.ToList().FindAll(p => p.Tag != null && p.Tag.ToString() == "DrawRect"); ctrl.MapEditor.RemoveItems(items); var item = e.Item as MapRectangle; item.Tag = "DrawRect"; item.CanMove = false; item.CanResize = false; item.EnableSelection = DefaultBoolean.False; item.EnableHighlighting = DefaultBoolean.True; innerData.rangeItem = item; if (innerData.rangeItem.Height == 0 && innerData.rangeItem.Width == 0) { innerData.rangeItem.Height = 5; innerData.rangeItem.Width = 5; } item.Fill = Color.FromArgb(50, Color.FromArgb(23, 107, 209)); item.Stroke = Color.FromArgb(23, 107, 209); item.StrokeWidth = 1; innerData.dotItem.Fill = Color.Red; innerData.dotItem.Size = 6; innerData.dotItem.Location = innerData.rangeItem.GetCenter(); innerData.mMapStorage.Items.Add(innerData.rangeItem); innerData.mMapStorage.Items.Add(innerData.dotItem); DrawRect_MouseMove(ctrl, null); var layer = ctrl.Layers.Find(p => p.Name == ""); if (layer != null) ctrl.Layers.Remove(layer); }; ctrl.MapEditor.MapItemEdited += (sender, e) => { innerData.drawingRect = e.Action == MapEditorAction.Create; }; return ctrl; } /// /// 绘制矩形 /// /// /// /// /// /// /// public static void DrawRect(this MapControl ctrl, double startLon, double startLat, double endLon, double endLat, bool recCallback = false) { var innerData = ctrl.Tag as InnerData; if (innerData.rangeItem != null) innerData.mMapStorage.Items.Remove(innerData.rangeItem); if (innerData.dotItem != null) innerData.mMapStorage.Items.Remove(innerData.dotItem); double centerLon = (startLon + endLon) / 2; double centerLat = (startLat + endLat) / 2; var size = BoundingRectItemHelper.CalculateRectangleSize(ctrl.CoordinateSystem, new GeoPoint(startLat, startLon), new GeoPoint(endLat, endLon)); innerData.dotItem.Location = new GeoPoint(centerLat, centerLon); innerData.dotItem.Fill = Color.Red; innerData.dotItem.Size = 8; var rect = new MapRectangle() { CanMove = false, CanResize = false, EnableSelection = DefaultBoolean.False, EnableHighlighting = DefaultBoolean.True, Tag = "DrawRect", Width = size.Width, Height = size.Height, Location = new GeoPoint(startLat, startLon), Fill = Color.FromArgb(50, Color.FromArgb(23, 107, 209)), Stroke = Color.FromArgb(23, 107, 209), StrokeWidth = 1 }; innerData.rangeItem = rect; innerData.CurrentRect = (startLon, startLat, centerLon, centerLat, endLon, endLat, (endLon - startLon) / 2, (endLat - startLat) / 2); innerData.mMapStorage.Items.Add(innerData.rangeItem); innerData.mMapStorage.Items.Add(innerData.dotItem); if (recCallback) innerData.mOnRectChanged?.Invoke(ctrl.GetCurrentRect()); } public static void DrawDtoPonit(this MapControl ctrl, string title, IEnumerable<(double lon, double lat)> lines) { string identify = "DrawPoint_"; if (lines == null) return; lines = lines.Where(p => p.lon >= -180 && p.lon <= 180); if (!lines.Any()) return; var innerData = ctrl.Tag as InnerData; int size = 4; List list = new List(); var color = ColorHelper.GetColor(title); for (int i = 0; i < lines.Count(); i++) { var p = lines.ElementAt(i); if (double.IsNaN(p.lat) || double.IsNaN(p.lon)) continue; var mapItem = new MapDot() { EnableHighlighting = DefaultBoolean.False, EnableSelection = DefaultBoolean.False, CanMove = false, CanResize = false, StrokeWidth = 2, Visible = true, IsHitTestVisible = true, Fill = color, Size = size, Tag = $"{identify}{title}{lines.ElementAt(i).lon}", Location = new GeoPoint(p.lat, p.lon), ToolTipPattern = $"{title}", }; list.Add(mapItem); } if (!list.Any()) return; innerData.mMapStorage.Items.AddRange(list); } public static MapPolyline GetLine(this MapControl ctrl, string title, IEnumerable<(double lon, double lat)> lines) { if (lines == null || !lines.Any()) return null; var polyLine = new MapPolyline() { EnableSelection = DefaultBoolean.False, EnableHighlighting = DefaultBoolean.True, Stroke = ColorHelper.GetColor(title), StrokeWidth = 2, HighlightedStrokeWidth = 4, IsGeodesic = true, CanResize = false, CanEdit = false, CanRotate = false, IsHitTestVisible = true, CanMove = false, Tag = $"DrawDtoLine_{title}", ToolTipPattern = $"{title}", }; bool isShowPattern = lines.Count() > 400; if (isShowPattern) { polyLine.TitleOptions.Pattern = title; } foreach (var item in lines) { if (double.IsNaN(item.lat) || double.IsNaN(item.lon)) continue; polyLine.Points.Add(new GeoPoint(item.lat, item.lon)); } if (!polyLine.Points.Any()) { return null; } else { return polyLine; } } public static MapPolyline GetLine(this MapControl ctrl, string title, IEnumerable<(double lon, double lat)> lines, bool isShowPattern = false) { if (lines == null || !lines.Any()) return null; var polyLine = new MapPolyline() { EnableSelection = DefaultBoolean.False, EnableHighlighting = DefaultBoolean.True, Stroke = ColorHelper.GetColor(title), StrokeWidth = 2, HighlightedStrokeWidth = 4, IsGeodesic = true, CanResize = false, CanEdit = false, CanRotate = false, IsHitTestVisible = true, CanMove = false, Tag = $"DrawDtoLine_{title}", ToolTipPattern = $"{title}", }; if (isShowPattern) { polyLine.TitleOptions.Pattern = title; } foreach (var item in lines) { if (double.IsNaN(item.lat) || double.IsNaN(item.lon)) continue; polyLine.Points.Add(new GeoPoint(item.lat, item.lon)); } if (!polyLine.Points.Any()) { return null; } else { return polyLine; } } public static void DrawDtoLine(this MapControl ctrl, List polylines) { if (polylines == null || !polylines.Any()) return; var innerData = ctrl.Tag as InnerData; innerData.mMapStorage.Items.AddRange(polylines); } public static void DrawDtoLine(this MapControl ctrl, string title, IEnumerable<(double lon, double lat)> lines) { if (lines == null || !lines.Any()) return; var innerData = ctrl.Tag as InnerData; var polyLine = new MapPolyline() { EnableSelection = DefaultBoolean.False, EnableHighlighting = DefaultBoolean.True, Stroke = ColorHelper.GetColor(title), StrokeWidth = 2, HighlightedStrokeWidth = 4, IsGeodesic = true, CanResize = false, CanEdit = false, CanRotate = false, IsHitTestVisible = true, CanMove = false, Tag = $"DrawDtoLine_{title}", ToolTipPattern = $"{title}", }; polyLine.TitleOptions.Pattern = title; foreach (var item in lines) { if (double.IsNaN(item.lat) || double.IsNaN(item.lon)) continue; polyLine.Points.Add(new GeoPoint(item.lat, item.lon)); } if (!polyLine.Points.Any()) return; innerData.mMapStorage.Items.Add(polyLine); } /// /// 绘制时差多条线 两个点绘制一个线段 1和2一个线段 3和4一个线段 /// /// /// /// public static void DrawDtoLineTwo(this MapControl ctrl, string title, IEnumerable<(double lon, double lat)> dots) { if (dots == null || !dots.Any()) return; string identify = $"DrawDtoLine_{title}"; var innerData = ctrl.Tag as InnerData; //多条线 两个点绘制一个线段 1和2一个线段 3和4一个线段 List polylines = new List(); int index = 0; int count = dots.Count(); MapPolyline polyLine = null; foreach (var item in dots) { if (index % 2 == 0) { if (polyLine != null)//添加线段 { polylines.Add(polyLine); } polyLine = new MapPolyline() { Stroke = ColorHelper.GetColor(identify), EnableHighlighting = DefaultBoolean.False, EnableSelection = DefaultBoolean.False, StrokeWidth = 2, HighlightedStrokeWidth = 4, IsGeodesic = true, CanResize = false, CanEdit = false, CanRotate = false, IsHitTestVisible = true, CanMove = false, Tag = $"{identify}", ToolTipPattern = title }; } polyLine.Points.Add(new GeoPoint(item.lat, item.lon)); index++; if (index == count)//添加最后一个线段 { polylines.Add(polyLine); } } if (!polyLine.Points.Any()) return; innerData.mMapStorage.Items.AddRange(polylines); } public static void DrawGdopLine(this MapControl ctrl, IEnumerable<(string wcKm, double lon, double lat)> lines) { if (lines == null || !lines.Any()) return; var innerData = ctrl.Tag as InnerData; var wcKm = lines.First().wcKm; var polyLine = new MapPolyline() { Stroke = ColorHelper.GetColor(wcKm), StrokeWidth = 2, IsGeodesic = true, CanResize = false, CanEdit = false, CanRotate = false, IsHitTestVisible = true, CanMove = false, Tag = $"DrawGdopLine_{wcKm}", }; polyLine.TitleOptions.Pattern = wcKm; foreach (var item in lines) { if (double.IsNaN(item.lat) || double.IsNaN(item.lon)) continue; polyLine.Points.Add(new GeoPoint(item.lat, item.lon)); } if (!polyLine.Points.Any()) return; innerData.mMapStorage.Items.Add(polyLine); } /// /// 绘制线 /// /// /// /// /// 是否清除上次绘制 public static void DrawCXLine(this MapControl ctrl, string title, IEnumerable<(double lon, double lat)> lines, bool isClear = true) { string identify = "DrawCXLine_"; if (lines == null || !lines.Any()) return; var innerData = ctrl.Tag as InnerData; if (isClear) { var mapItem = innerData.mMapStorage.Items.Find(m => m.Tag.ToString().StartsWith(identify)); if (mapItem != null) { innerData.mMapStorage.Items.Remove(mapItem); } } var polyLine = new MapPolyline() { Stroke = Color.Red, StrokeWidth = 2, CanMove = false, Visible = true, IsHitTestVisible = true, Tag = $"{identify}{title}", ToolTipPattern = title, }; foreach (var item in lines) { if (double.IsNaN(item.lat) || double.IsNaN(item.lon)) continue; polyLine.Points.Add(new GeoPoint(item.lat, item.lon)); } if (!polyLine.Points.Any()) return; innerData.mMapStorage.Items.Add(polyLine); } public static void DrawErrEllipse(this MapControl ctrl, double r1, double r2, IEnumerable<(double lon, double lat)> lines) { if (lines == null || !lines.Any()) return; var innerData = ctrl.Tag as InnerData; //innerData.mMapStorage.Items.Clear(); var polyLine = new MapPolyline() { Stroke = Color.Red, StrokeWidth = 2, IsGeodesic = true, CanResize = false, CanEdit = false, CanRotate = false, IsHitTestVisible = true, CanMove = false, Tag = $"DrawErrEllipse", }; //polyLine.ToolTipPattern = $"长轴:{r1:f1}km\r\n短轴:{r2:f1}km"; foreach (var item in lines) { if (double.IsNaN(item.lat) || double.IsNaN(item.lon)) continue; polyLine.Points.Add(new GeoPoint(item.lat, item.lon)); } if (!polyLine.Points.Any()) return; innerData.mMapStorage.Items.Add(polyLine); var callout = new DevExpress.XtraMap.MapCallout() { Text = $"R1={r1:f1}km\r\nR2={r2:f1}km", Location = polyLine.Points.First(), CanMove = false, IsHitTestVisible = false, }; innerData.mMapStorage.Items.Add(callout); } /// /// 绘制GDOP多条线 两个点绘制一个线段 1和2一个线段 3和4一个线段 /// /// /// /// /// public static void DrawGdopLineTwo(this MapControl ctrl, string wcKm, IEnumerable<(double lon, double lat)> dots, int patterncount) { if (dots == null || !dots.Any()) return; string identify = "DrawGdopLine_"; var innerData = ctrl.Tag as InnerData; //多条线 两个点绘制一个线段 1和2一个线段 3和4一个线段 List polylines = new List(); int index = 0; int count = dots.Count(); MapPolyline polyLine = null; foreach (var item in dots) { if (index % 2 == 0) { if (polyLine != null)//添加线段 { polylines.Add(polyLine); } polyLine = new MapPolyline() { Stroke = ColorHelper.GetColor(wcKm), EnableHighlighting = DefaultBoolean.False, EnableSelection = DefaultBoolean.False, StrokeWidth = 4, IsGeodesic = true, CanResize = false, CanEdit = false, CanRotate = false, IsHitTestVisible = true, CanMove = false, Tag = $"{identify}{wcKm}", ToolTipPattern = wcKm, }; } polyLine.Points.Add(new GeoPoint(item.lat, item.lon)); if (patterncount == index)//提示公里信息 { polyLine.TitleOptions.Pattern = wcKm; } index++; if (index == count)//添加最后一个线段 { polylines.Add(polyLine); } } if (!polyLine.Points.Any()) return; innerData.mMapStorage.Items.AddRange(polylines); } /// /// 绘制半径圆 /// /// /// /// /// public static void DrawRadiusRound(this MapControl ctrl, double lat, double lon, int radius) { var innerData = ctrl.Tag as InnerData; try { if (radius <= 0) return; CoordPointCollection latLngs = new CoordPointCollection(); MyLatLng centerLatLng = new MyLatLng(lon, lat); // 0 - 360度 寻找半径为radius,圆心为centerP的圆上点的经纬度 for (int i = 0; i < 360; i++) { //获取目标经纬度 MyLatLng tempLatLng = getMyLatLng(centerLatLng, radius, i); //将自定义的经纬度类 转换成 标准经纬度类 CoordPoint p = new GeoPoint(tempLatLng.m_Latitude, tempLatLng.m_Longitude); latLngs.Add(p); } //安全性检查 if (latLngs.Count < 20) { return; } //通过绘制多边形的方式绘制圆 MapPolygon gpol = new MapPolygon(); gpol.Points = latLngs; gpol.Stroke = Color.Red; gpol.Fill = Color.FromArgb(50, Color.Red); gpol.IsHitTestVisible = true; innerData.mMapStorageFixed.Items.Add(gpol); } catch (Exception) { //Dialog.Error(ex); } } /// /// 度 转换成 弧度 /// /// /// public static double DegreesToRadians(double degrees) { const double degToRadFactor = Math.PI / 180; return degrees * degToRadFactor; } /// /// 计算两个点之间的距离 /// /// 开始经度 /// 开始纬度 /// 结束经度 /// 结束纬度 /// public static double CalcLineKm(double startlon, double startlat, double endlon, double endlat) { GeoPoint startPoint = new GeoPoint() { Longitude = startlon, Latitude = startlat }; GeoPoint endPoint = new GeoPoint() { Longitude = endlon, Latitude = endlat }; double result = 0; MapSize sizeInKm = new SphericalMercatorProjection().GeoToKilometersSize(startPoint, new MapSize(Math.Abs(startPoint.Longitude - endPoint.Longitude), Math.Abs(startPoint.Latitude - endPoint.Latitude))); result = Math.Sqrt(sizeInKm.Width * sizeInKm.Width + sizeInKm.Height * sizeInKm.Height); return result; } /// /// 计算方向线坐标 /// /// 经度 /// 纬度 /// 艏向角 角度制 /// 长度 m /// public static (double, double) CalcSituation(double lon, double lat, double deg, double dis) { double rad = deg * (Math.PI / 180); //角度转弧度 double dx = dis * Math.Sin(rad); double dy = dis * Math.Cos(rad); MyLatLng A = new MyLatLng(lon, lat); double bjd = (dx / A.Ed + A.m_RadLo) * 180 / Math.PI; double bwd = (dy / A.Ec + A.m_RadLa) * 180 / Math.PI; return (bjd, bwd); } /** * 求B点经纬度 * @param A 已知点的经纬度, * @param distance AB两地的距离 单位km * @param angle AB连线与正北方向的夹角(0~360) * @return B点的经纬度 */ private static MyLatLng getMyLatLng(MyLatLng A, double distance, double angle) { double dx = distance * 1000 * Math.Sin(DegreesToRadians(angle)); double dy = distance * 1000 * Math.Cos(DegreesToRadians(angle)); double bjd = (dx / A.Ed + A.m_RadLo) * 180 / Math.PI; double bwd = (dy / A.Ec + A.m_RadLa) * 180 / Math.PI; return new MyLatLng(bjd, bwd); } public static void ClearRect(this MapControl ctrl) { var innerDate = ctrl.Tag as InnerData; if (innerDate.rangeItem != null) innerDate.mMapStorage.Items.Remove(innerDate.rangeItem); if (innerDate.dotItem != null) innerDate.mMapStorage.Items.Remove(innerDate.dotItem); } /// /// 获取地图框选的范围信息(可能为null) /// /// /// public static (double starLon, double startLat, double centerLon, double centerLat, double endLon, double endLat, double lonRange, double latRange) GetCurrentRect(this MapControl ctrl) { var innerData = ctrl.Tag as InnerData; return innerData.CurrentRect; } /// /// 获取地图瓦片图层 /// /// /// public static List GetImageLayer(this MapControl ctrl) { if (ctrl.Layers == null) return null; return ctrl.Layers.Where(p => p.Name.StartsWith("WMTS:")).Select(p => (ImageLayer)p).ToList(); } /// /// 获取地图绘制图层 /// /// /// public static VectorItemsLayer GetDrawLayer(this MapControl ctrl) { return (VectorItemsLayer)ctrl.Layers["DrawLayer"]; } /// /// 获取地图绘制图层 /// /// /// public static VectorItemsLayer GetPosLayer(this MapControl ctrl) { return (VectorItemsLayer)ctrl.Layers["PosLayer"]; } /// /// 获取地图绘制图层的数据仓库 /// /// /// public static MapItemStorage GetDrawStorage(this MapControl ctrl) { var innerData = ctrl.Tag as InnerData; return innerData.mMapStorage; } /// /// 设置本地地图类型.默认为Normal /// 该方法为所有MapControl实例设置地图类型 /// /// public static void SetLocalMapType(GoogleMapType mapType) { mMapType = mapType; listMapCtrl.ForEach(t => { var localLayer = t.GetImageLayer()?.FirstOrDefault(p => p.Name == "WMTS:Local"); if (localLayer == null) return; var provider = localLayer.DataProvider as MapTileDataProviderBase; provider?.ClearCache(); t.Refresh(); }); } #region private private static void DrawRect_MouseMove(object sender, MouseEventArgs e) { var innerData = (sender as MapControl).Tag as InnerData; if (!innerData.drawingRect) return; innerData.dotItem.Location = innerData.rangeItem.GetCenter(); var startLon = Math.Round(innerData.rangeItem.Location.GetX(), 3); var startLat = Math.Round(innerData.rangeItem.Location.GetY(), 3); if (startLon >= 180) { startLon -= 360; } if (startLon <= -180) { startLon += 360; } var center = innerData.rangeItem.GetCenter(); var centerLon = Math.Round(center.GetX(), 3); if (centerLon >= 180) { centerLon -= 360; } if (centerLon <= -180) { centerLon += 360; } var centerLat = Math.Round(center.GetY(), 3); double endLon; if (centerLon < 0 && startLon > 0) endLon = startLon + 2 * (centerLon + 360 - startLon); else endLon = startLon + 2 * (centerLon - startLon); var endLat = startLat + 2 * (centerLat - startLat); var StartLon = Math.Min(startLon, endLon); var StartLat = Math.Min(startLat, endLat); var CenterLon = centerLon; var CenterLat = centerLat; var EndLon = Math.Max(startLon, endLon); var EndLat = Math.Max(startLat, endLat); double centerLonNew = CenterLon % 360; CenterLon = centerLonNew; //double LonRange = Math.Round((EndLon - StartLon) / 2, 3); //double LatRange = Math.Round((EndLat - StartLat) / 2, 3); double LonRange = Math.Round((EndLon - StartLon), 3); double LatRange = Math.Round((EndLat - StartLat), 3); if (LonRange >= 360) { } innerData.CurrentRect = (StartLon, StartLat, CenterLon, CenterLat, EndLon, EndLat, LonRange, LatRange); innerData.mOnRectChanged?.Invoke(innerData.CurrentRect); } private static void ExportImg_ItemClick(object sender, ItemClickEventArgs e) { var ctrl = e.Item.Tag as MapControl; using (var dialog = new SaveFileDialog()) { dialog.Filter = "PNG图片|*.png"; dialog.AddExtension = true; dialog.FileName = $"Map{DateTime.Now:yyyyMMddHHmmss}.png"; if (dialog.ShowDialog() == DialogResult.OK) { ctrl.ExportToImage(dialog.FileName, ImageFormat.Png); } } } private static void ExportCsv_ItemClick(object sender, ItemClickEventArgs e) { var ctrl = e.Item.Tag as MapControl; using (var dialog = new SaveFileDialog()) { dialog.Filter = "CSV文件|*.csv"; dialog.AddExtension = true; dialog.FileName = $"Pos{DateTime.Now:yyyyMMddHHmmss}.xlsx"; if (dialog.ShowDialog() == DialogResult.OK) { ctrl.ExportToXlsx(dialog.FileName); } } } private static void ClearDrawObj(this MapControl ctrl) { ctrl.Invoke(new Action(() => { try { var innerData = ctrl.Tag as InnerData; ctrl.MapEditor.SetTransformMode(MapItemTransform.None); innerData.drawingRect = false; innerData.CurrentRect = (0, 0, 0, 0, 0, 0, 0, 0); innerData.rangeItem = null; if (innerData.mMapStorage != null && innerData.mMapStorage.Items.Any()) { innerData.mMapStorage.Items.Clear(); } if (innerData.trackStorge != null) { innerData.trackStorge.Items.Clear(); } } catch (Exception) { } })); } private static void DistanceLine_ItemClick(object sender, ItemClickEventArgs e) { var ctrl = e.Item.Tag as MapControl; var innerData = ctrl.Tag as InnerData; ctrl.MapEditor.SetEditMode(); var items = innerData.mMapStorage.Items.ToArray(); foreach (var item in items) { if (item.Tag != null && item.Tag.ToString() == "DrawDistanceLine") innerData.mMapStorage.Items.Remove(item); } if (innerData.distinctPath != null) { innerData.distinctPath.Points.Clear(); } innerData.distinctPath = new MapPolyline() { Stroke = Color.FromArgb(127, 255, 0, 199), StrokeWidth = 2, IsGeodesic = true, CanResize = false, CanEdit = false, CanRotate = false, IsHitTestVisible = false, CanMove = true, Tag = "DrawDistanceLine" }; innerData.mMapStorage.Items.Add(innerData.distinctPath); innerData.hoverPoint = new MapDot() { CanResize = false, CanMove = false, IsHitTestVisible = false, Tag = "DrawDistanceLine", Size = 8 }; innerData.hoverPoint.Fill = ColorHelper.GetColor(innerData.hoverPoint.Tag.ToString()); innerData.mMapStorage.Items.Add(innerData.hoverPoint); innerData.hoverTip = CreateHoverTip(); ctrl.MouseClick += AddDistinctPoint; ctrl.MouseMove += MovingDistinct; ctrl.MouseLeave += MapControl_MouseLeave; ctrl.MouseEnter += MapControl_MouseEnter; ctrl.DoubleClick += EndDistinct; } private static void ShowTrack_ItemClick(object sender, ItemClickEventArgs e) { var ctrl = e.Item.Tag as MapControl; var innerData = ctrl.Tag as InnerData; innerData.trackStorge.Items.Clear(); var posData = innerData._dataCache.Keys.OrderBy(p => p.SigTime); var itemList = posData.GroupBy(t => t.ColorKey); List listLine = new List(); int w = 12; int h = 20; if (ctrl.ZoomLevel < 4) { w = 2; h = 4; } else if (ctrl.ZoomLevel < 6) { w = 6; h = 12; } else { w = 10; h = 20; } foreach (var groupItems in itemList) { var targets = groupItems.ToList(); var colorKey = targets.First().ColorKey; for (int i = 0; i < targets.Count - 1; i++) { var line = new MapPolyline(); line.CanEdit = false; line.CanMove = false; line.CanResize = false; line.CanRotate = false; line.EnableSelection = DefaultBoolean.False; line.EnableHighlighting = DefaultBoolean.False; line.IsGeodesic = true; line.Stroke = ColorHelper.IsHtmlColor(colorKey) ? ColorTranslator.FromHtml(colorKey) : ColorHelper.GetColor(colorKey); line.EndLineCap.Width = w; line.EndLineCap.Length = h; line.EndLineCap.Visible = true; line.EndLineCap.IsFilled = false; line.StrokeWidth = 2; //line.EndLineCap.Length = 100; var p1 = new GeoPoint(targets[i].PosLat, targets[i].PosLon); var p2 = new GeoPoint(targets[i + 1].PosLat, targets[i + 1].PosLon); line.Points.Add(p1); line.Points.Add(p2); listLine.Add(line); } } innerData.trackStorge.Items.AddRange(listLine); } private static void MarkDot_ItemClick(object sender, ItemClickEventArgs e) { var ctrl = e.Item.Tag as MapControl; ctrl.MapEditor.SetTransformMode(MapItemTransform.None); var innerData = ctrl.Tag as InnerData; innerData.drawingRect = false; using (var frm = new MapEditPointView()) { if (frm.ShowDialog() != DialogResult.OK) return; var locPoint = new GeoPoint(frm.Lat, frm.Lon); MapCustomElement cusItem = new MapCustomElement() { Location = locPoint, UseAnimation = true, CanMove = false, EnableHighlighting = DefaultBoolean.True, EnableSelection = DefaultBoolean.False, Text = "", ToolTipPattern = $"{frm.Caption}", SvgImage = SvgHelper.CreatePentagram("#F12233", 24, 24), TextAlignment = DevExpress.XtraMap.TextAlignment.BottomCenter, Tag = "DrawMarkDot" }; innerData.mMapStorage.Items.Add(cusItem); } } private static ToolTipControllerShowEventArgs CreateHoverTip() { ToolTipTitleItem titleItem1 = new ToolTipTitleItem() { Text = $"0千米" }; titleItem1.Appearance.ForeColor = SystemColors.GrayText; titleItem1.Appearance.Font = new Font(AppearanceObject.DefaultFont.FontFamily, 12, FontStyle.Bold); SuperToolTip superToolTip = new SuperToolTip(); superToolTip.Items.Add(titleItem1); ToolTipSeparatorItem sepItem = new ToolTipSeparatorItem(); superToolTip.Items.Add(sepItem); ToolTipItem textItem = new ToolTipItem() { Text = "单击添加测距点,双击结束测距" }; superToolTip.Items.Add(textItem); ToolTipControllerShowEventArgs arg = new ToolTipControllerShowEventArgs(); arg.ShowBeak = true; arg.SuperTip = superToolTip; return arg; } private static void MapControl_MouseEnter(object sender, EventArgs e) { var innerData = (sender as MapControl).Tag as InnerData; innerData.hoverPoint.Visible = true; } private static void MapControl_MouseLeave(object sender, EventArgs e) { var innerData = (sender as MapControl).Tag as InnerData; innerData.hoverPoint.Visible = false; } private static void AddDistinctPoint(object sender, MouseEventArgs e) { var innerData = (sender as MapControl).Tag as InnerData; var ctrl = sender as MapControl; MapCallout disItem = new MapCallout { IsHitTestVisible = false, CanMove = false }; disItem.Location = ctrl.ScreenPointToCoordPoint(e.Location); disItem.Text = $"{CalcLineKm(innerData):F2}千米"; disItem.Tag = "DrawDistanceLine"; innerData.mMapStorage.Items.Add(disItem); innerData.distinctPath.Points.Add(innerData.hoverPoint.Location); innerData.hoverPoint = new MapDot() { CanResize = false, CanMove = false, IsHitTestVisible = false, Tag = "DrawDistanceLine", Size = 8 }; innerData.hoverPoint.Location = ctrl.ScreenPointToCoordPoint(e.Location); innerData.hoverPoint.Fill = ColorHelper.GetColor(innerData.hoverPoint.Tag.ToString()); innerData.mMapStorage.Items.Add(innerData.hoverPoint); innerData.distinctPath.Points.Add(innerData.hoverPoint.Location); } private static void MovingDistinct(object sender, MouseEventArgs e) { var innerData = (sender as MapControl).Tag as InnerData; var ctrl = sender as MapControl; var itemPoint = ctrl.ScreenPointToCoordPoint(e.Location); innerData.hoverPoint.Location = itemPoint; if (innerData.distinctPath.Points.Count > 0) { ctrl.MapEditor.SetEditMode(); innerData.distinctPath.Points[innerData.distinctPath.Points.Count - 1] = itemPoint; (innerData.hoverTip.SuperTip.Items[0] as ToolTipTitleItem).Text = $"{CalcLineKm(innerData):F2}千米"; innerData.mapToolTip.ShowHint(innerData.hoverTip, ctrl.PointToScreen(e.Location)); } } private static double CalcLineKm(InnerData data) { double result = 0; if (data.distinctPath.Points.Count < 2) return result; for (int i = 0; i < data.distinctPath.Points.Count - 1; i++) { var startPoint = data.distinctPath.Points[i] as GeoPoint; var endPoint = data.distinctPath.Points[i + 1] as GeoPoint; MapSize sizeInKm = new SphericalMercatorProjection().GeoToKilometersSize(startPoint, new MapSize(Math.Abs(startPoint.Longitude - endPoint.Longitude), Math.Abs(startPoint.Latitude - endPoint.Latitude))); double partlength = Math.Sqrt(sizeInKm.Width * sizeInKm.Width + sizeInKm.Height * sizeInKm.Height); result += partlength; } return result; } public static void ClearMap(this MapControl ctrl) { var innerData = ctrl.Tag as InnerData; innerData.CurrentRect = (0, 0, 0, 0, 0, 0, 0, 0); innerData.mMapStorage?.Items.Clear(); } private static void EndDistinct(object sender, EventArgs e) { var ctrl = sender as MapControl; var innerData = ctrl.Tag as InnerData; innerData.mapToolTip.HideHint(); ctrl.MouseClick -= AddDistinctPoint; ctrl.MouseMove -= MovingDistinct; ctrl.DoubleClick -= EndDistinct; ctrl.MouseLeave -= MapControl_MouseLeave; ctrl.MouseEnter -= MapControl_MouseEnter; } class MapEditPointView : DevExpress.XtraEditors.XtraForm { private System.ComponentModel.IContainer components = null; private DevExpress.XtraLayout.LayoutControl layoutControl1; private DevExpress.XtraLayout.LayoutControlGroup Root; private DevExpress.XtraLayout.LayoutControlItem layoutControlItem1; private DevExpress.XtraLayout.EmptySpaceItem emptySpaceItem1; private DevExpress.XtraLayout.LayoutControlItem layoutControlItem2; private DevExpress.XtraEditors.ButtonEdit txtLon; private DevExpress.XtraEditors.ButtonEdit txtLat; private DevExpress.XtraEditors.TextEdit txtCaption; private DevExpress.XtraLayout.LayoutControlItem layoutControlItem5; private DevExpress.XtraEditors.SimpleButton btnCancel; private DevExpress.XtraEditors.SimpleButton btnOk; private DevExpress.XtraLayout.LayoutControlItem layoutControlItem3; private DevExpress.XtraLayout.LayoutControlItem layoutControlItem6; private DevExpress.XtraEditors.DXErrorProvider.DXErrorProvider dxErrorProvider1; private DevExpress.XtraLayout.EmptySpaceItem emptySpaceItem2; private void InitializeComponent() { this.components = new System.ComponentModel.Container(); DevExpress.XtraEditors.Controls.EditorButtonImageOptions editorButtonImageOptions1 = new DevExpress.XtraEditors.Controls.EditorButtonImageOptions(); DevExpress.Utils.SerializableAppearanceObject serializableAppearanceObject1 = new DevExpress.Utils.SerializableAppearanceObject(); DevExpress.Utils.SerializableAppearanceObject serializableAppearanceObject2 = new DevExpress.Utils.SerializableAppearanceObject(); DevExpress.Utils.SerializableAppearanceObject serializableAppearanceObject3 = new DevExpress.Utils.SerializableAppearanceObject(); DevExpress.Utils.SerializableAppearanceObject serializableAppearanceObject4 = new DevExpress.Utils.SerializableAppearanceObject(); DevExpress.XtraEditors.Controls.EditorButtonImageOptions editorButtonImageOptions2 = new DevExpress.XtraEditors.Controls.EditorButtonImageOptions(); DevExpress.Utils.SerializableAppearanceObject serializableAppearanceObject5 = new DevExpress.Utils.SerializableAppearanceObject(); DevExpress.Utils.SerializableAppearanceObject serializableAppearanceObject6 = new DevExpress.Utils.SerializableAppearanceObject(); DevExpress.Utils.SerializableAppearanceObject serializableAppearanceObject7 = new DevExpress.Utils.SerializableAppearanceObject(); DevExpress.Utils.SerializableAppearanceObject serializableAppearanceObject8 = new DevExpress.Utils.SerializableAppearanceObject(); this.layoutControl1 = new DevExpress.XtraLayout.LayoutControl(); this.txtCaption = new DevExpress.XtraEditors.TextEdit(); this.txtLon = new DevExpress.XtraEditors.ButtonEdit(); this.txtLat = new DevExpress.XtraEditors.ButtonEdit(); this.Root = new DevExpress.XtraLayout.LayoutControlGroup(); this.layoutControlItem1 = new DevExpress.XtraLayout.LayoutControlItem(); this.emptySpaceItem1 = new DevExpress.XtraLayout.EmptySpaceItem(); this.layoutControlItem2 = new DevExpress.XtraLayout.LayoutControlItem(); this.layoutControlItem5 = new DevExpress.XtraLayout.LayoutControlItem(); this.btnOk = new DevExpress.XtraEditors.SimpleButton(); this.layoutControlItem3 = new DevExpress.XtraLayout.LayoutControlItem(); this.btnCancel = new DevExpress.XtraEditors.SimpleButton(); this.layoutControlItem6 = new DevExpress.XtraLayout.LayoutControlItem(); this.dxErrorProvider1 = new DevExpress.XtraEditors.DXErrorProvider.DXErrorProvider(this.components); this.emptySpaceItem2 = new DevExpress.XtraLayout.EmptySpaceItem(); ((System.ComponentModel.ISupportInitialize)(this.layoutControl1)).BeginInit(); this.layoutControl1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.txtCaption.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtLon.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.txtLat.Properties)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.Root)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.emptySpaceItem1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem2)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem5)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem3)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem6)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.dxErrorProvider1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.emptySpaceItem2)).BeginInit(); this.SuspendLayout(); // // layoutControl1 // this.layoutControl1.Controls.Add(this.btnCancel); this.layoutControl1.Controls.Add(this.btnOk); this.layoutControl1.Controls.Add(this.txtCaption); this.layoutControl1.Controls.Add(this.txtLon); this.layoutControl1.Controls.Add(this.txtLat); this.layoutControl1.Dock = System.Windows.Forms.DockStyle.Fill; this.layoutControl1.Location = new System.Drawing.Point(0, 0); this.layoutControl1.Name = "layoutControl1"; this.layoutControl1.OptionsCustomizationForm.DesignTimeCustomizationFormPositionAndSize = new System.Drawing.Rectangle(689, 0, 650, 400); this.layoutControl1.Root = this.Root; this.layoutControl1.Size = new System.Drawing.Size(272, 208); this.layoutControl1.TabIndex = 0; this.layoutControl1.Text = "layoutControl1"; // // txtCaption // this.txtCaption.Location = new System.Drawing.Point(12, 29); this.txtCaption.Name = "txtCaption"; this.txtCaption.Properties.Appearance.Options.UseTextOptions = true; this.txtCaption.Properties.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Near; this.txtCaption.Properties.AutoHeight = false; this.txtCaption.Properties.MaxLength = 30; this.txtCaption.Size = new System.Drawing.Size(248, 22); this.txtCaption.StyleController = this.layoutControl1; this.txtCaption.TabIndex = 9; // // txtLon // this.txtLon.Location = new System.Drawing.Point(12, 80); this.txtLon.Name = "txtLon"; this.txtLon.Properties.Appearance.Options.UseTextOptions = true; this.txtLon.Properties.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Near; this.txtLon.Properties.AutoHeight = false; this.txtLon.Properties.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] { new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Glyph, "° ", -1, false, true, false, editorButtonImageOptions1, new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.None), serializableAppearanceObject1, serializableAppearanceObject2, serializableAppearanceObject3, serializableAppearanceObject4, "", null, null, DevExpress.Utils.ToolTipAnchor.Default)}); this.txtLon.Size = new System.Drawing.Size(248, 22); this.txtLon.StyleController = this.layoutControl1; this.txtLon.TabIndex = 4; // // txtLat // this.txtLat.Location = new System.Drawing.Point(12, 131); this.txtLat.Name = "txtLat"; this.txtLat.Properties.Appearance.Options.UseTextOptions = true; this.txtLat.Properties.Appearance.TextOptions.HAlignment = DevExpress.Utils.HorzAlignment.Near; this.txtLat.Properties.AutoHeight = false; this.txtLat.Properties.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] { new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Glyph, "° ", -1, false, true, false, editorButtonImageOptions2, new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.None), serializableAppearanceObject5, serializableAppearanceObject6, serializableAppearanceObject7, serializableAppearanceObject8, "", null, null, DevExpress.Utils.ToolTipAnchor.Default)}); this.txtLat.Size = new System.Drawing.Size(248, 22); this.txtLat.StyleController = this.layoutControl1; this.txtLat.TabIndex = 5; // // Root // this.Root.EnableIndentsWithoutBorders = DevExpress.Utils.DefaultBoolean.True; this.Root.GroupBordersVisible = false; this.Root.Items.AddRange(new DevExpress.XtraLayout.BaseLayoutItem[] { this.layoutControlItem1, this.emptySpaceItem1, this.layoutControlItem5, this.layoutControlItem2, this.layoutControlItem3, this.layoutControlItem6, this.emptySpaceItem2}); this.Root.Name = "Root"; this.Root.Size = new System.Drawing.Size(272, 208); this.Root.TextVisible = false; // // layoutControlItem1 // this.layoutControlItem1.Control = this.txtLon; this.layoutControlItem1.Location = new System.Drawing.Point(0, 43); this.layoutControlItem1.MaxSize = new System.Drawing.Size(0, 51); this.layoutControlItem1.MinSize = new System.Drawing.Size(54, 51); this.layoutControlItem1.Name = "layoutControlItem1"; this.layoutControlItem1.Padding = new DevExpress.XtraLayout.Utils.Padding(2, 2, 10, 2); this.layoutControlItem1.Size = new System.Drawing.Size(252, 51); this.layoutControlItem1.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; this.layoutControlItem1.Text = "经度"; this.layoutControlItem1.TextLocation = DevExpress.Utils.Locations.Top; this.layoutControlItem1.TextSize = new System.Drawing.Size(24, 14); // // emptySpaceItem1 // this.emptySpaceItem1.AllowHotTrack = false; this.emptySpaceItem1.Location = new System.Drawing.Point(0, 162); this.emptySpaceItem1.MaxSize = new System.Drawing.Size(0, 26); this.emptySpaceItem1.MinSize = new System.Drawing.Size(104, 26); this.emptySpaceItem1.Name = "emptySpaceItem1"; this.emptySpaceItem1.Size = new System.Drawing.Size(113, 26); this.emptySpaceItem1.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; this.emptySpaceItem1.TextSize = new System.Drawing.Size(0, 0); // // layoutControlItem2 // this.layoutControlItem2.Control = this.txtLat; this.layoutControlItem2.Location = new System.Drawing.Point(0, 94); this.layoutControlItem2.MaxSize = new System.Drawing.Size(0, 51); this.layoutControlItem2.MinSize = new System.Drawing.Size(54, 51); this.layoutControlItem2.Name = "layoutControlItem2"; this.layoutControlItem2.Padding = new DevExpress.XtraLayout.Utils.Padding(2, 2, 10, 2); this.layoutControlItem2.Size = new System.Drawing.Size(252, 51); this.layoutControlItem2.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; this.layoutControlItem2.Text = "纬度"; this.layoutControlItem2.TextLocation = DevExpress.Utils.Locations.Top; this.layoutControlItem2.TextSize = new System.Drawing.Size(24, 14); // // layoutControlItem5 // this.layoutControlItem5.Control = this.txtCaption; this.layoutControlItem5.Location = new System.Drawing.Point(0, 0); this.layoutControlItem5.MaxSize = new System.Drawing.Size(0, 43); this.layoutControlItem5.MinSize = new System.Drawing.Size(54, 43); this.layoutControlItem5.Name = "layoutControlItem5"; this.layoutControlItem5.Size = new System.Drawing.Size(252, 43); this.layoutControlItem5.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; this.layoutControlItem5.Text = "标题"; this.layoutControlItem5.TextLocation = DevExpress.Utils.Locations.Top; this.layoutControlItem5.TextSize = new System.Drawing.Size(24, 14); // // btnOk // this.btnOk.Location = new System.Drawing.Point(125, 174); this.btnOk.Name = "btnOk"; this.btnOk.Size = new System.Drawing.Size(65, 22); this.btnOk.StyleController = this.layoutControl1; this.btnOk.TabIndex = 10; this.btnOk.Text = "确定"; this.btnOk.Click += new System.EventHandler(this.btnOk_Click); // // layoutControlItem3 // this.layoutControlItem3.Control = this.btnOk; this.layoutControlItem3.Location = new System.Drawing.Point(113, 162); this.layoutControlItem3.MaxSize = new System.Drawing.Size(69, 26); this.layoutControlItem3.MinSize = new System.Drawing.Size(69, 26); this.layoutControlItem3.Name = "layoutControlItem3"; this.layoutControlItem3.Size = new System.Drawing.Size(69, 26); this.layoutControlItem3.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; this.layoutControlItem3.TextSize = new System.Drawing.Size(0, 0); this.layoutControlItem3.TextVisible = false; // // btnCancel // this.btnCancel.Location = new System.Drawing.Point(194, 174); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(66, 22); this.btnCancel.StyleController = this.layoutControl1; this.btnCancel.TabIndex = 11; this.btnCancel.Text = "取消"; this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); // // layoutControlItem6 // this.layoutControlItem6.Control = this.btnCancel; this.layoutControlItem6.Location = new System.Drawing.Point(182, 162); this.layoutControlItem6.MaxSize = new System.Drawing.Size(70, 26); this.layoutControlItem6.MinSize = new System.Drawing.Size(70, 26); this.layoutControlItem6.Name = "layoutControlItem6"; this.layoutControlItem6.Size = new System.Drawing.Size(70, 26); this.layoutControlItem6.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; this.layoutControlItem6.TextSize = new System.Drawing.Size(0, 0); this.layoutControlItem6.TextVisible = false; // // dxErrorProvider1 // this.dxErrorProvider1.ContainerControl = this; // // emptySpaceItem2 // this.emptySpaceItem2.AllowHotTrack = false; this.emptySpaceItem2.Location = new System.Drawing.Point(0, 145); this.emptySpaceItem2.MinSize = new System.Drawing.Size(104, 1); this.emptySpaceItem2.Name = "emptySpaceItem2"; this.emptySpaceItem2.Size = new System.Drawing.Size(252, 17); this.emptySpaceItem2.SizeConstraintsType = DevExpress.XtraLayout.SizeConstraintsType.Custom; this.emptySpaceItem2.TextSize = new System.Drawing.Size(0, 0); // // MapEditPointView // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 14F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(272, 208); this.Controls.Add(this.layoutControl1); this.IconOptions.ShowIcon = false; this.MaximizeBox = false; this.MinimizeBox = false; this.Name = "MapEditPointView"; this.ShowInTaskbar = false; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "标点"; ((System.ComponentModel.ISupportInitialize)(this.layoutControl1)).EndInit(); this.layoutControl1.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.txtCaption.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtLon.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.txtLat.Properties)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.Root)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.emptySpaceItem1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem2)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem5)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem3)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.layoutControlItem6)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.dxErrorProvider1)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.emptySpaceItem2)).EndInit(); this.ResumeLayout(false); } protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } public MapEditPointView() { InitializeComponent(); } public string Caption { get { return txtCaption.Text.Trim(); } set { txtCaption.Text = value; } } public double Lon { get { return double.Parse(txtLon.Text.Trim()); } set { txtLon.Text = value.ToString(); } } public double Lat { get { return double.Parse(txtLat.Text.Trim()); } set { txtLat.Text = value.ToString(); } } private void btnOk_Click(object sender, EventArgs e) { dxErrorProvider1.ClearErrors(); double lon, lat; if (!double.TryParse(txtLon.Text, out lon)) { dxErrorProvider1.SetError(txtLon, "请设置正确的经度"); return; } if (!double.TryParse(txtLat.Text, out lat)) { dxErrorProvider1.SetError(txtLat, "请设置正确的纬度"); return; } if (lon < -180 || lon > 180) { dxErrorProvider1.SetError(txtLon, "经度范围[-180,180]"); return; } if (lat < -90 || lat > 90) { dxErrorProvider1.SetError(txtLat, "纬度范围[-90,90]"); return; } this.DialogResult = DialogResult.OK; } private void btnCancel_Click(object sender, EventArgs e) { this.DialogResult = DialogResult.Cancel; } } #endregion #region 半径圆 public class MyLatLng { public double Rc = 6378137; //赤道半径 public double Rj = 6356725; //极半径 public double m_LoDeg, m_LoMin, m_LoSec; public double m_LaDeg, m_LaMin, m_LaSec; public double m_Longitude, m_Latitude; public double m_RadLo, m_RadLa; public double Ec; public double Ed; public MyLatLng(double longitude, double latitude) { m_LoDeg = (int)longitude; m_LoMin = (int)((longitude - m_LoDeg) * 60); m_LoSec = (longitude - m_LoDeg - m_LoMin / 60) * 3600; m_LaDeg = (int)latitude; m_LaMin = (int)((latitude - m_LaDeg) * 60); m_LaSec = (latitude - m_LaDeg - m_LaMin / 60) * 3600; m_Longitude = longitude; m_Latitude = latitude; m_RadLo = longitude * Math.PI / 180; m_RadLa = latitude * Math.PI / 180; Ec = Rj + (Rc - Rj) * (90 - m_Latitude) / 90; Ed = Ec * Math.Cos(m_RadLa); } } #endregion #region ImageLayer数据源接口实现(本地和Http接口) class ImageTileSource : IImageTileSource { SQLiteConnection con; public ImageTileSource() { var files = Directory.GetFiles(Application.StartupPath, "Data.gmdb", SearchOption.AllDirectories); if (files.Length > 0) { try { conStr = string.Format(conStr, files[0]); con = new SQLiteConnection(conStr); con.Open(); } catch { conStr = null; Console.WriteLine("Data.gmdb文件读取出错!"); } } else { conStr = null; } } public string Name => "GMap地图瓦片"; public bool CanDisposeSourceImage => true; public Image GetImage(int x, int y, int level, Size size) { var data = QueryTile(x, y, level, 0); if (data == null) return null; return Image.FromStream(new MemoryStream(data)); } string QuerySql = "SELECT Tile FROM main.TilesData WHERE id = (SELECT id FROM main.Tiles WHERE X={0} AND Y={1} AND Zoom={2} AND Type={3})"; string conStr = "Data Source=\"{0}\";Page Size=32768"; byte[] QueryTile(int x, int y, int zoom, int type) { if (conStr == null) return null; type = (int)mMapType; if (type == 0) type = 1818940751; using (DbCommand com = con.CreateCommand()) { com.CommandText = string.Format(QuerySql, x, y, zoom, type); using (DbDataReader rd = com.ExecuteReader(System.Data.CommandBehavior.SequentialAccess)) { if (rd.Read()) { long length = rd.GetBytes(0, 0, null, 0, 0); byte[] tile = new byte[length]; rd.GetBytes(0, 0, tile, 0, tile.Length); return tile; } } } return null; } } class HttpMapDataProvider : MapDataProviderBase { public HttpMapDataProvider() { TileSource = new HttpTileSource(this); } public override MapSize GetMapSizeInPixels(double zoomLevel) { double imageSize; imageSize = HttpTileSource.CalculateTotalImageSize(zoomLevel); return new MapSize(imageSize, imageSize); } protected override Size BaseSizeInPixels { get { return new Size(Convert.ToInt32(HttpTileSource.tileSize * 2), Convert.ToInt32(HttpTileSource.tileSize * 2)); } } } class HttpTileSource : MapTileSourceBase { public const int tileSize = 256; public const int maxZoomLevel = 14; public string HttpServerAddr { get; set; } public EnumWmtsSource WmtsSource { get; set; } public EnumMapLayerType LayerType { get; set; } public string LayerName { get; set; } public GoogleMapType MapType { get; set; } = GoogleMapType.Normal; internal static double CalculateTotalImageSize(double zoomLevel) { if (zoomLevel < 1.0) return zoomLevel * tileSize * 2; return Math.Pow(2.0, zoomLevel) * tileSize; } public HttpTileSource(ICacheOptionsProvider cacheOptionsProvider) : base((int)CalculateTotalImageSize(maxZoomLevel), (int)CalculateTotalImageSize(maxZoomLevel), tileSize, tileSize, cacheOptionsProvider) { } public override Uri GetTileByZoomLevel(int zoomLevel, int tilePositionX, int tilePositionY) { if (string.IsNullOrWhiteSpace(HttpServerAddr)) return null; try { if (zoomLevel <= maxZoomLevel) { string imgUrl = string.Empty; if (WmtsSource == EnumWmtsSource.SJZX) { imgUrl = $"{HttpServerAddr}?lyr={LayerName}&x={tilePositionX}&y={tilePositionY}&z={zoomLevel}"; } else { string suffix = ".png"; if (LayerType == EnumMapLayerType.SatMap) { suffix = ".jpg"; } int maxY = (int)Math.Pow(2, zoomLevel); imgUrl = $"{HttpServerAddr}/{LayerName}/{zoomLevel}/{tilePositionX}/{maxY - tilePositionY}{suffix}"; } //string imgUrl = string.Format("http://192.168.0.214:58089/{0}/{1}/{2}/{3}", (int)GoogleMapType.Normal, zoomLevel, tilePositionX, tilePositionY); Uri u = new Uri(imgUrl); return u; } return null; } catch { Console.WriteLine($"加载地图数据出错,无法连接到{HttpServerAddr}"); return null; } } } #endregion //定位点聚合器 class PosClusterer : IClusterer { private MapControl ctrl; private IMapDataAdapter owner; private MapViewport preViewPoint; private DebounceDispatcher dispatcher = new DebounceDispatcher(); public bool IsBusy { get; private set; } public MapItemCollection Items { get; private set; } public PosClusterer(MapControl ctrl) { this.ctrl = ctrl; } public void SetOwner(IMapDataAdapter owner) { this.owner = owner; Items = new MapItemCollection(owner); } public void Clusterize(IEnumerable sourceItems, MapViewport viewport, bool sourceChanged) { IsBusy = true; if (sourceChanged) DoClusterize(sourceItems, viewport, sourceChanged); else dispatcher.Debounce(100, () => DoClusterize(sourceItems, viewport, sourceChanged)); } private void DoClusterize(IEnumerable sourceItems, MapViewport viewport, bool sourceChanged) { if (preViewPoint == null) { preViewPoint = viewport; return; } if (!sourceChanged && preViewPoint.ZoomLevel == viewport.ZoomLevel)//地图移动或者MapControl大小发生了改变时直接操作Items避免闪烁 { preViewPoint = viewport; } else//地图进行了缩放 { Items.Clear(); var cache = new Dictionary>(); foreach (MapDot item in sourceItems) { var point = ctrl.CoordPointToScreenPoint(item.Location); int pointX = (int)point.X; int pointY = (int)point.Y; bool visible = true; if (pointX <= _dotSize || pointX > ctrl.Width - _dotSize) visible = false; else if (pointY <= _dotSize || pointY > ctrl.Height - _dotSize) visible = false; pointX /= (_dotSize * 2); pointY /= (_dotSize * 2); var key = pointX << 16 | pointY; if (!visible) key = -key; if (!cache.ContainsKey(key)) cache.Add(key, new List()); cache[key].Add(item); } var innerData = ctrl.Tag as InnerData; innerData._clusterCache.Clear(); LinkedList temp = new LinkedList(); foreach (var kv in cache) { var firstDot = kv.Value[0] as MapDot; var firstObj = firstDot.Tag as PosData; firstDot.Size = kv.Value.Count > 1 ? _dotSize + 2 : _dotSize; firstObj.ClusterCount = kv.Value.Count; firstObj.ClusterKey = kv.Key; innerData._clusterCache.Add(kv.Key, firstDot); temp.AddLast(firstDot); } Items.AddRange(temp); cache.Clear(); preViewPoint = viewport; owner.OnClustered(); } this.IsBusy = false; } //计算多个坐标的中心位置 public GeoPoint GetCenterPointFromListOfCoordinates(List geoCoordinateList) { int total = geoCoordinateList.Count; double lat = 0, lon = 0; foreach (MapDot g in geoCoordinateList) { lat += (g.Location as GeoPoint).Latitude * Math.PI / 180; lon += (g.Location as GeoPoint).Longitude * Math.PI / 180; } lat /= total; lon /= total; return new GeoPoint(lat * 180 / Math.PI, lon * 180 / Math.PI); } } } class MyLatLng { static double Rc = 6378137; static double Rj = 6356725; double m_LoDeg, m_LoMin, m_LoSec; double m_LaDeg, m_LaMin, m_LaSec; double m_Longitude, m_Latitude; double m_RadLo, m_RadLa; double Ec; double Ed; public MyLatLng(double longitude, double latitude) { m_LoDeg = (int)longitude; m_LoMin = (int)((longitude - m_LoDeg) * 60); m_LoSec = (longitude - m_LoDeg - m_LoMin / 60) * 3600; m_LaDeg = (int)latitude; m_LaMin = (int)((latitude - m_LaDeg) * 60); m_LaSec = (latitude - m_LaDeg - m_LaMin / 60) * 3600; m_Longitude = longitude; m_Latitude = latitude; m_RadLo = longitude * Math.PI / 180; m_RadLa = latitude * Math.PI / 180; Ec = Rj + (Rc - Rj) * (90 - m_Latitude) / 90; Ed = Ec * Math.Cos(m_RadLa); } }