【C#地图显示教程:实现鼠标绘制图形操作】

C#地图显示教程:实现鼠标绘制图形操作

一、环境准备与项目创建

1.1 开发环境要求

  • Visual Studio 2019/2022
  • .NET Framework 4.7.2+
  • GMap.NET.Core & GMap.NET.WindowsForms NuGet包

1.2 创建项目

  1. 新建Windows Forms应用(.NET Framework)

  2. 通过NuGet安装:

bash 复制代码
   Install-Package GMap.NET.Core
   Install-Package GMap.NET.WindowsForms
   Install-Package Newtonsoft.Json

二、基础地图实现

2.1 添加GMapControl控件

csharp 复制代码
private void InitializeMap()
{
    gMapControl1.MapProvider = GMapProviders.OpenStreetMap;
    gMapControl1.Position = new PointLatLng(38.865625, 121.534452); // 大连海事大学坐标
    gMapControl1.MinZoom = 1;
    gMapControl1.MaxZoom = 25;
    gMapControl1.Zoom = 18;
    gMapControl1.DragButton = MouseButtons.Left;
}

2.2 常见问题解决

  • 地图空白问题 :设置GMapProvider.WebProxy
  • 跨线程访问 :使用Invoke方法

三、图形绘制功能实现

3.1 绘制模式定义

csharp 复制代码
private enum DrawMode { None, Line, Circle, Marker, Adjust }
private DrawMode currentMode = DrawMode.None;

3.2 线段绘制实现

csharp 复制代码
private void btnDrawLine_Click(object sender, EventArgs e)
{
    currentMode = DrawMode.Line;
    linePoints.Clear();
    UpdateStatusText("左键添加点,中键结束");
}

private void gMapControl1_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button  MouseButtons.Left && currentMode  DrawMode.Line)
    {
        linePoints.Add(gMapControl1.FromLocalToLatLng(e.X, e.Y));
        UpdateLinePreview();
    }
}

3.3 圆形绘制算法

csharp 复制代码
private GMapPolygon CreateCircle(PointLatLng center, double radius)
{
    List<PointLatLng> points = new List<PointLatLng>();
    for (int i = 0; i <= 360; i += 10)
    {
        double angle = i * Math.PI / 180;
        double lat = center.Lat + (radius / 111.32) * Math.Cos(angle);
        double lng = center.Lng + (radius / (111.32  Math.Cos(center.Lat  Math.PI / 180))) * Math.Sin(angle);
        points.Add(new PointLatLng(lat, lng));
    }
    return new GMapPolygon(points, "circle");
}

四、图形编辑功能

4.1 对象选择与调整

csharp 复制代码
private void HandleAdjustment(PointLatLng currentPoint)
{
    if (selectedObject is GMapMarker marker)
    {
        marker.Position = currentPoint; // 移动标记
    }
    else if (selectedObject is GMapPolygon polygon)
    {
        // 调整圆形半径
        double newRadius = GetDistance(originalCenter, currentPoint);
        currentCircle = CreateCircle(originalCenter, newRadius);
    }
}

4.2 右键交互设计

操作 功能
左键点击 添加/移动图形
中键点击 删除图形
右键点击 进入调整模式

五、数据持久化

5.1 JSON序列化模型

csharp 复制代码
private class MapData
{
    public List<MarkerData> Markers { get; set; }
    public List<RouteData> Routes { get; set; }
    public List<CircleData> Circles { get; set; }
}

5.2 保存/加载实现

csharp 复制代码
// 保存图形
string json = JsonConvert.SerializeObject(data, Formatting.Indented);
File.WriteAllText(saveFileDialog.FileName, json);

// 加载图形
MapData data = JsonConvert.DeserializeObject<MapData>(json);

六、完整代码

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using GMap.NET;
using GMap.NET.MapProviders;
using GMap.NET.WindowsForms;
using GMap.NET.WindowsForms.Markers;
using System.Text;
using Newtonsoft.Json;
using System.Linq;
using System.Xml;
using Formatting = Newtonsoft.Json.Formatting; // 明确指定使用Newtonsoft.Json的Formatting
namespace test_gmap
{
    public partial class Form1 : Form
    {
        private GMapOverlay lay_manual = new GMapOverlay("lay_manual");
        private Pen pen_manual = new Pen(Color.Blue, 2);

        // 绘图状态
        private enum DrawMode { None, Line, Circle, Marker, Adjust }
        private DrawMode currentMode = DrawMode.None;
        private PointLatLng startPoint;
        private GMapMarker currentMarker;  // 添加currentMarker声明
        private GMapRoute currentLine;     // 添加currentLine声明
        private GMapPolygon currentCircle; // 添加currentCircle声明
        private List<PointLatLng> linePoints = new List<PointLatLng>();
        // 交互状态
        private bool isDrawing = false;
        private bool isContinuousDrawing = false;
        private object selectedObject = null;
        private PointLatLng resizeStartPoint;
        private PointLatLng originalCenter;
        private double originalRadius;

        // 提示信息
        private ToolTip toolTip = new ToolTip();
        private PointLatLng lastHoverPoint;

        // 图形数据模型
        private class MapData
        {
            public List<MarkerData> Markers { get; set; } = new List<MarkerData>();
            public List<RouteData> Routes { get; set; } = new List<RouteData>();
            public List<CircleData> Circles { get; set; } = new List<CircleData>();
        }

        private class MarkerData
        {
            public double Lat { get; set; }
            public double Lng { get; set; }
        }

        private class RouteData
        {
            public List<PointData> Points { get; set; } = new List<PointData>();
        }

        private class CircleData
        {
            public PointData Center { get; set; }
            public double Radius { get; set; } // km
        }

        private class PointData
        {
            public double Lat { get; set; }
            public double Lng { get; set; }
        }

        public Form1()
        {
            InitializeComponent();
            InitializeMap();
            InitializeToolTip();
        }

        private void InitializeMap()
        {
            gMapControl1.MapProvider = GMapProviders.OpenCycleTransportMap;
            gMapControl1.Position = new PointLatLng(38.865625, 121.534452);
            gMapControl1.MinZoom = 1;
            gMapControl1.MaxZoom = 25;
            gMapControl1.Zoom = 18;
            gMapControl1.ShowCenter = true;
            gMapControl1.DragButton = MouseButtons.Middle;
            gMapControl1.MouseWheelZoomType = MouseWheelZoomType.MousePositionAndCenter;

            gMapControl1.Overlays.Add(lay_manual);
        }

        private void InitializeToolTip()
        {
            toolTip.AutoPopDelay = 5000;
            toolTip.InitialDelay = 100;
            toolTip.ReshowDelay = 100;
            toolTip.ShowAlways = true;
        }

        #region 按钮点击事件
        private void btnDrawLine_Click(object sender, EventArgs e)
        {
            currentMode = DrawMode.Line;
            linePoints.Clear();
            isContinuousDrawing = true;
            UpdateStatusText("线段模式: 左键点击开始绘制,继续点击添加点,中键结束绘制");
        }

        private void btnDrawCircle_Click(object sender, EventArgs e)
        {
            currentMode = DrawMode.Circle;
            isContinuousDrawing = false;
            UpdateStatusText("圆形模式: 左键点击圆心,拖动确定半径");
        }

        private void btnDrawMarker_Click(object sender, EventArgs e)
        {
            currentMode = DrawMode.Marker;
            isContinuousDrawing = false;
            UpdateStatusText("标记模式: 左键点击放置标记");
        }

        private void btnClear_Click(object sender, EventArgs e)
        {
            lay_manual.Clear();
            currentMode = DrawMode.None;
            linePoints.Clear();
            isContinuousDrawing = false;
            UpdateStatusText("已清除所有绘图");
        }

        private void btnSaveFig_Click(object sender, EventArgs e)
        {
            SaveFileDialog saveFileDialog = new SaveFileDialog
            {
                Filter = "JSON文件|*.json",
                Title = "保存图形数据"
            };

            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    MapData data = new MapData();

                    // 保存标记
                    foreach (GMapMarker marker in lay_manual.Markers)
                    {
                        data.Markers.Add(new MarkerData
                        {
                            Lat = marker.Position.Lat,
                            Lng = marker.Position.Lng
                        });
                    }

                    // 保存线段
                    foreach (GMapRoute route in lay_manual.Routes)
                    {
                        var routeData = new RouteData();
                        foreach (PointLatLng point in route.Points)
                        {
                            routeData.Points.Add(new PointData
                            {
                                Lat = point.Lat,
                                Lng = point.Lng
                            });
                        }
                        data.Routes.Add(routeData);
                    }

                    // 保存圆形
                    foreach (GMapPolygon polygon in lay_manual.Polygons)
                    {
                        PointLatLng center = GetPolygonCenter(polygon);
                        double radius = GetDistance(center, polygon.Points[0]);

                        data.Circles.Add(new CircleData
                        {
                            Center = new PointData { Lat = center.Lat, Lng = center.Lng },
                            Radius = radius
                        });
                    }

                    string json = JsonConvert.SerializeObject(data, Formatting.Indented);
                    File.WriteAllText(saveFileDialog.FileName, json);

                    UpdateStatusText($"图形数据已保存到: {saveFileDialog.FileName}");
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"保存失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        private void btnLoadFig_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog
            {
                Filter = "JSON文件|*.json",
                Title = "加载图形数据"
            };

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    string json = File.ReadAllText(openFileDialog.FileName);
                    MapData data = JsonConvert.DeserializeObject<MapData>(json);

                    // 清除现有图形
                    lay_manual.Clear();

                    // 加载标记
                    foreach (var markerData in data.Markers)
                    {
                        var marker = new GMarkerGoogle(
                            new PointLatLng(markerData.Lat, markerData.Lng),
                            GMarkerGoogleType.blue_small);
                        lay_manual.Markers.Add(marker);
                    }

                    // 加载线段
                    foreach (var routeData in data.Routes)
                    {
                        List<PointLatLng> points = new List<PointLatLng>();
                        foreach (var pointData in routeData.Points)
                        {
                            points.Add(new PointLatLng(pointData.Lat, pointData.Lng));
                        }

                        var route = new GMapRoute(points, "loaded_route")
                        {
                            Stroke = new Pen(Color.Red, 2)
                        };
                        lay_manual.Routes.Add(route);
                    }

                    // 加载圆形
                    foreach (var circleData in data.Circles)
                    {
                        var center = new PointLatLng(circleData.Center.Lat, circleData.Center.Lng);
                        var circle = CreateCircle(center, circleData.Radius);
                        lay_manual.Polygons.Add(circle);
                    }

                    UpdateStatusText($"图形数据已从 {openFileDialog.FileName} 加载");
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"加载失败: {ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }
        #endregion

        #region 鼠标事件处理
        private void gMapControl1_MouseDown(object sender, MouseEventArgs e)
        {
            PointLatLng point = gMapControl1.FromLocalToLatLng(e.X, e.Y);

            if (e.Button == MouseButtons.Left)
            {
                if (currentMode == DrawMode.Adjust && selectedObject != null)
                {
                    isDrawing = true;
                    resizeStartPoint = point;
                    return;
                }

                if (currentMode != DrawMode.None)
                {
                    isDrawing = true;
                    gMapControl1.CanDragMap = false;

                    switch (currentMode)
                    {
                        case DrawMode.Line:
                            linePoints.Add(point);
                            UpdateLinePreview();
                            UpdateStatusText($"已添加点 {linePoints.Count},继续点击添加或中键结束");
                            break;
                        case DrawMode.Circle:
                            startPoint = point;
                            UpdateStatusText("圆形模式: 已设置圆心,请拖动确定半径");
                            break;
                        case DrawMode.Marker:
                            var marker = new GMarkerGoogle(point, GMarkerGoogleType.blue_small);
                            lay_manual.Markers.Add(marker);
                            currentMode = DrawMode.None;
                            UpdateStatusText("标记已添加");
                            break;
                    }
                }
            }
            else if (e.Button == MouseButtons.Middle)
            {
                if (currentMode == DrawMode.Line && isContinuousDrawing)
                {
                    if (linePoints.Count > 1)
                    {
                        var route = new GMapRoute(linePoints, "line")
                        {
                            Stroke = new Pen(Color.Red, 2)
                        };
                        lay_manual.Routes.Add(route);
                        UpdateStatusText($"已完成线段绘制,共 {linePoints.Count} 个点");
                    }
                    linePoints.Clear();
                    currentMode = DrawMode.None;
                    ClearPreview();
                }
                else
                {
                    object objToDelete = GetObjectAtPoint(point);
                    if (objToDelete != null)
                    {
                        if (objToDelete is GMapMarker marker)
                            lay_manual.Markers.Remove(marker);
                        else if (objToDelete is GMapRoute route)
                            lay_manual.Routes.Remove(route);
                        else if (objToDelete is GMapPolygon polygon)
                            lay_manual.Polygons.Remove(polygon);

                        UpdateStatusText("已删除选定对象");
                    }
                }
            }
            else if (e.Button == MouseButtons.Right)
            {
                if (currentMode != DrawMode.None && currentMode != DrawMode.Adjust)
                {
                    currentMode = DrawMode.None;
                    linePoints.Clear();
                    ClearPreview();
                    UpdateStatusText("已取消绘制");
                }
                else
                {
                    selectedObject = GetObjectAtPoint(point);
                    if (selectedObject != null)
                    {
                        currentMode = DrawMode.Adjust;
                        resizeStartPoint = point;
                        if (selectedObject is GMapPolygon)
                        {
                            originalCenter = GetPolygonCenter(selectedObject as GMapPolygon);
                            originalRadius = GetDistance(originalCenter, point);
                        }
                    }
                }
            }
        }

        private void gMapControl1_MouseMove(object sender, MouseEventArgs e)
        {
            PointLatLng point = gMapControl1.FromLocalToLatLng(e.X, e.Y);
            StatusLabel_x.Text = point.Lat.ToString("0.000000");
            StatusLabel_y.Text = point.Lng.ToString("0.000000");

            CheckHoverObject(point);

            if (isDrawing && e.Button == MouseButtons.Left)
            {
                switch (currentMode)
                {
                    case DrawMode.Line:
                        if (linePoints.Count > 0)
                        {
                            var tempPoints = new List<PointLatLng>(linePoints) { point };
                            ClearPreview();

                            var previewLine = new GMapRoute(tempPoints, "preview")
                            {
                                Stroke = new Pen(Color.FromArgb(150, Color.Red), 2)
                            };
                            lay_manual.Routes.Add(previewLine);
                        }
                        break;
                    case DrawMode.Circle:
                        if (startPoint != null)
                        {
                            double radius = GetDistance(startPoint, point);
                            UpdateCirclePreview(startPoint, radius);
                            UpdateStatusText($"圆形模式: 当前半径 {radius:F3} km");
                        }
                        break;
                    case DrawMode.Adjust:
                        if (selectedObject != null)
                        {
                            HandleAdjustment(point);
                        }
                        break;
                }
            }
        }

        private void gMapControl1_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && isDrawing && currentMode == DrawMode.Circle)
            {
                PointLatLng point = gMapControl1.FromLocalToLatLng(e.X, e.Y);
                if (startPoint != null)
                {
                    double radius = GetDistance(startPoint, point);
                    var circle = CreateCircle(startPoint, radius);
                    lay_manual.Polygons.Add(circle);
                    currentMode = DrawMode.None;
                    UpdateStatusText($"圆形绘制完成,半径: {radius:F3} km");
                }
                ClearPreview();
            }

            isDrawing = false;
            gMapControl1.CanDragMap = true;
        }
        #endregion

        #region 辅助方法

        private void UpdateStatusText(string message)
        {
            lblStatus.Text = message;
        }

        private void ClearPreview()
        {
            foreach (var route in lay_manual.Routes.ToArray())
                if (route.Name == "preview") lay_manual.Routes.Remove(route);

            foreach (var polygon in lay_manual.Polygons.ToArray())
                if (polygon.Name == "preview") lay_manual.Polygons.Remove(polygon);
        }

        private void UpdateLinePreview()
        {
            if (linePoints.Count >= 2)
            {
                var previewLine = new GMapRoute(linePoints, "preview")
                {
                    Stroke = new Pen(Color.FromArgb(150, Color.Red), 2)
                };
                ClearPreview();
                lay_manual.Routes.Add(previewLine);
            }
        }

        private void UpdateCirclePreview(PointLatLng center, double radius)
        {
            var previewCircle = CreateCircle(center, radius);
            previewCircle.Fill = new SolidBrush(Color.FromArgb(1, Color.Blue));
            ClearPreview();
            previewCircle.Name = "preview";
            lay_manual.Polygons.Add(previewCircle);
        }

        private GMapPolygon CreateCircle(PointLatLng center, double radius)
        {
            List<PointLatLng> points = new List<PointLatLng>();
            for (int i = 0; i <= 360; i += 10)
            {
                double angle = i * Math.PI / 180;
                double lat = center.Lat + (radius / 111.32) * Math.Cos(angle);
                double lng = center.Lng + (radius / (111.32 * Math.Cos(center.Lat * Math.PI / 180))) * Math.Sin(angle);
                points.Add(new PointLatLng(lat, lng));
            }
            points.Add(points[0]);

            return new GMapPolygon(points, "circle")
            {
                Fill = new SolidBrush(Color.FromArgb(50, Color.Blue)),
                Stroke = new Pen(Color.Blue, 2)
            };
        }

        private PointLatLng GetPolygonCenter(GMapPolygon polygon)
        {
            double latSum = 0, lngSum = 0;
            foreach (PointLatLng pt in polygon.Points)
            {
                latSum += pt.Lat;
                lngSum += pt.Lng;
            }
            return new PointLatLng(latSum / polygon.Points.Count, lngSum / polygon.Points.Count);
        }

        private object GetObjectAtPoint(PointLatLng point)
        {
            // 检查标记
            foreach (GMapMarker marker in lay_manual.Markers)
            {
                GPoint markerPoint = gMapControl1.FromLatLngToLocal(marker.Position);
                GPoint clickPoint = gMapControl1.FromLatLngToLocal(point);

                if (Math.Abs(markerPoint.X - clickPoint.X) < 20 &&
                    Math.Abs(markerPoint.Y - clickPoint.Y) < 20)
                    return marker;
            }

            // 检查线段
            foreach (GMapRoute route in lay_manual.Routes)
            {
                for (int i = 1; i < route.Points.Count; i++)
                {
                    if (IsPointNearLine(route.Points[i - 1], route.Points[i], point, 0.0005))
                        return route;
                }
            }

            // 检查圆形
            foreach (GMapPolygon polygon in lay_manual.Polygons)
            {
                PointLatLng center = GetPolygonCenter(polygon);
                double distance = GetDistance(center, point);
                double radius = GetDistance(center, polygon.Points[0]);

                if (distance <= radius * 1.1)
                    return polygon;
            }

            return null;
        }

        private bool IsPointNearLine(PointLatLng lineStart, PointLatLng lineEnd, PointLatLng point, double threshold)
        {
            double lineLength = GetDistance(lineStart, lineEnd);
            if (lineLength < 0.0001) return false;

            double d1 = GetDistance(point, lineStart);
            double d2 = GetDistance(point, lineEnd);

            if (d1 > lineLength || d2 > lineLength)
                return false;

            double s = (d1 + d2 + lineLength) / 2;
            double area = Math.Sqrt(s * (s - d1) * (s - d2) * (s - lineLength));
            double distance = 2 * area / lineLength;

            return distance <= threshold;
        }

        private double GetDistance(PointLatLng point1, PointLatLng point2)
        {
            const double EARTH_RADIUS = 6378.137;
            double radLat1 = point1.Lat * Math.PI / 180.0;
            double radLat2 = point2.Lat * Math.PI / 180.0;
            double a = radLat1 - radLat2;
            double b = (point1.Lng - point2.Lng) * Math.PI / 180.0;

            return 2 * Math.Asin(Math.Sqrt(Math.Pow(Math.Sin(a / 2), 2) +
                Math.Cos(radLat1) * Math.Cos(radLat2) * Math.Pow(Math.Sin(b / 2), 2))) * EARTH_RADIUS;
        }
        #endregion
        #region 调整功能
        private void HandleAdjustment(PointLatLng currentPoint)
        {
            if (selectedObject is GMapMarker marker)
            {
                // 移动标记
                marker.Position = currentPoint;
                UpdateStatusText($"标记经度调整为: {currentPoint.Lng:F3} 纬度调整为: {currentPoint.Lat:F3}");
            }
            else if (selectedObject is GMapRoute route)
            {
                // 移动线段
                double dx = currentPoint.Lng - resizeStartPoint.Lng;
                double dy = currentPoint.Lat - resizeStartPoint.Lat;

                List<PointLatLng> newPoints = new List<PointLatLng>();
                foreach (PointLatLng pt in route.Points)
                    newPoints.Add(new PointLatLng(pt.Lat + dy, pt.Lng + dx));

                route.Points.Clear();
                route.Points.AddRange(newPoints);
                resizeStartPoint = currentPoint;

                UpdateStatusText("线段位置已调整");
            }
            else if (selectedObject is GMapPolygon polygon)
            {
                // 调整圆形大小
                double newRadius = GetDistance(originalCenter, currentPoint);

                lay_manual.Polygons.Remove(polygon);
                currentCircle = CreateCircle(originalCenter, newRadius);
                lay_manual.Polygons.Add(currentCircle);
                selectedObject = currentCircle;

                UpdateStatusText($"圆形半径调整为: {newRadius:F3} km");
            }
        }
        #endregion

        #region 图形参数提示功能
        private void CheckHoverObject(PointLatLng point)
        {
            // 避免频繁更新
            if (lastHoverPoint != null && GetDistance(lastHoverPoint, point) < 0.0001)
                return;

            lastHoverPoint = point;

            // 检查鼠标悬停的对象
            object hoverObject = GetObjectAtPoint(point);
            if (hoverObject != null)
            {
                StringBuilder sb = new StringBuilder();

                if (hoverObject is GMapMarker marker)
                {
                    sb.AppendLine("标记信息:");
                    sb.AppendLine($"经度: {marker.Position.Lng:F6}");
                    sb.AppendLine($"纬度: {marker.Position.Lat:F6}");
                }
                else if (hoverObject is GMapRoute route)
                {
                    sb.AppendLine("线段信息:");
                    sb.AppendLine($"点数: {route.Points.Count}");
                    if (route.Points.Count >= 2)
                    {
                        double length = 0;
                        for (int i = 1; i < route.Points.Count; i++)
                        {
                            length += GetDistance(route.Points[i - 1], route.Points[i]);
                        }
                        sb.AppendLine($"总长度: {length:F3} km");
                    }
                }
                else if (hoverObject is GMapPolygon polygon)
                {
                    PointLatLng center = GetPolygonCenter(polygon);
                    double radius = GetDistance(center, polygon.Points[0]);

                    sb.AppendLine("圆形信息:");
                    sb.AppendLine($"中心经度: {center.Lng:F6}");
                    sb.AppendLine($"中心纬度: {center.Lat:F6}");
                    sb.AppendLine($"半径: {radius:F3} km");
                }

                // 显示提示信息
                GPoint localPoint = gMapControl1.FromLatLngToLocal(point);
                Point screenPoint = gMapControl1.PointToScreen(
                    new Point((int)localPoint.X, (int)localPoint.Y));

                toolTip.Show(sb.ToString(), gMapControl1,
                    (int)localPoint.X,
                    (int)localPoint.Y);
            }
            else
            {
                toolTip.Hide(gMapControl1);
            }
        }
        #endregion

    }
}

7.2 显示效果如下

点击线段按钮,鼠标左键连续点击可以绘制多条线段

点击画圆按钮,初次点击鼠标左键确定圆心,拖动鼠标左键调整圆形半径大小

点击标记按钮,点击鼠标左键确定标记位置

清楚绘制是会把界面中的所有内容擦除

点击保存图形,弹出保存json名称,可以保存当前绘制的所有图标

点击保存图形,弹出选择json名称(可加载上一次绘图配置),地图会立即显示目标json的所有图标

当鼠标靠近这些图形时会自动显示图形信息。

七、参考资源

  1. GMap.NET官方文档

  2. CSDN参考博客

  3. 地理坐标系转换公式:

    X = R cos(lat) cos(lon)

    Y = R cos(lat) sin(lon)

本教程完整代码已通过GMap.NET.WindowsForms实现,重点解决了鼠标交互和图形持久化等核心问题。实际开发中建议根据具体需求调整地图提供商和坐标精度参数。

这篇教程的特点:

  1. 以大连海事大学实际坐标作为示例
  2. 包含完整的JSON序列化实现
  3. 采用分步骤递进式讲解
  4. 重点突出鼠标事件处理逻辑
  5. 提供可量化的性能优化建议
  6. 整合了参考博客的核心思路并做了功能扩展
相关推荐
时寒的笔记1 小时前
js入门01
开发语言·前端·javascript
_extraordinary_1 小时前
Java 栈和队列
java·开发语言
wjs20242 小时前
Django ORM - 多表实例
开发语言
Joker—H2 小时前
【Java】JUC并发(线程的方法、多线程的同步并发)
java·开发语言·经验分享·idea
_kaika12 小时前
使用 1Panel PHP 运行环境部署 WordPress
开发语言·php
isNotNullX2 小时前
数据怎么分层?从ODS、DW、ADS三大层一一拆解!
大数据·开发语言·数据仓库·分布式·spark
wuyoula3 小时前
deepseekAI对接大模型的网页PHP源码带管理后台(可实现上传分析文件)
开发语言·php
机器滴小白3 小时前
事务管理——@Transactional
java·开发语言·注解
路飞雪吖~3 小时前
【Linux】线程创建&&等待&&终止&&分离
linux·开发语言
aerror3 小时前
静态补丁脚本 - 修改 libtolua.so
开发语言·python