You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于串口实时经纬度,在gMap.net中绘制每秒更新的路线?

我之前也折腾过GMap.NET的实时轨迹绘制,刚好能解决你的问题!核心就是要搞定异步串口数据接收和UI线程安全更新这两个点,给你一步步拆解:

1. 先搞定串口数据的异步接收

串口接收是阻塞操作,绝对不能在UI线程里跑,不然地图会直接卡成PPT。我一般用后台线程监听串口,解析出经纬度后存到线程安全的列表里:

private SerialPort _gpsSerialPort;
private List<PointLatLng> _liveRoutePoints = new List<PointLatLng>();
private readonly object _pointLock = new object(); // 用来避免多线程冲突的锁

// 初始化串口,根据你的实际设备配置调整参数
private void SetupSerialPort()
{
    _gpsSerialPort = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);
    _gpsSerialPort.DataReceived += OnSerialDataReceived;
    try
    {
        _gpsSerialPort.Open();
    }
    catch (Exception ex)
    {
        MessageBox.Show($"串口打开失败:{ex.Message}");
    }
}

// 串口数据接收事件(这个是在后台线程触发的,不能直接操作UI)
private void OnSerialDataReceived(object sender, SerialDataReceivedEventArgs e)
{
    string rawData = _gpsSerialPort.ReadLine().Trim();
    // 这里要根据你的串口输出格式来解析,比如如果是NMEA协议的$GPGGA语句,要专门解析
    // 假设你的数据格式是 "Lat:39.9042,Lng:116.4074",自行调整解析逻辑
    var dataParts = rawData.Split(',');
    if (dataParts.Length == 2 &&
        double.TryParse(dataParts[0].Split(':')[1], out double latitude) &&
        double.TryParse(dataParts[1].Split(':')[1], out double longitude))
    {
        lock (_pointLock)
        {
            _liveRoutePoints.Add(new PointLatLng(latitude, longitude));
        }
    }
}
2. 每秒定时更新地图路线

用Windows Forms的Timer(它在UI线程触发,安全)来每秒拉取一次最新的点位,更新GMap.NET的路线:

private Timer _mapUpdateTimer;

// 初始化定时器,设置1秒间隔
private void SetupUpdateTimer()
{
    _mapUpdateTimer = new Timer
    {
        Interval = 1000 // 1000毫秒=1秒
    };
    _mapUpdateTimer.Tick += OnMapUpdateTick;
    _mapUpdateTimer.Start();
}

// 定时器触发的UI更新逻辑
private void OnMapUpdateTick(object sender, EventArgs e)
{
    if (_liveRoutePoints.Count == 0) return;

    // 线程安全地复制当前的点位列表,避免在更新时被后台线程修改
    List<PointLatLng> currentPoints;
    lock (_pointLock)
    {
        currentPoints = new List<PointLatLng>(_liveRoutePoints);
        // 可选:如果不需要保留全部历史轨迹,可以在这里清空列表,只显示最新的一段
        // _liveRoutePoints.Clear();
    }

    // 更新地图上的路线
    gMapControl1.Overlays.RemoveAll(o => o.Id == "liveRoute"); // 先移除旧的路线图层
    var routeOverlay = new GMapOverlay("liveRoute");
    var liveRoute = new GMapRoute(currentPoints, "实时轨迹")
    {
        Stroke = new Pen(Color.DarkRed, 3), // 设置路线颜色和粗细
        IsHitTestVisible = false
    };
    routeOverlay.Routes.Add(liveRoute);
    gMapControl1.Overlays.Add(routeOverlay);

    // 可选:自动将地图中心定位到最新的点位
    if (currentPoints.Count > 0)
    {
        gMapControl1.Position = currentPoints.Last();
    }
}
3. 初始化和资源清理

在窗体加载时完成所有初始化,关闭时记得释放资源:

private void MainForm_Load(object sender, EventArgs e)
{
    // 初始化GMap.NET基础配置
    gMapControl1.MapProvider = GMapProviders.GoogleMap;
    gMapControl1.MinZoom = 5;
    gMapControl1.MaxZoom = 20;
    gMapControl1.Zoom = 15;
    gMapControl1.ShowCenter = false; // 可以关掉默认的中心点标记

    // 启动串口监听和定时器
    SetupSerialPort();
    SetupUpdateTimer();
}

private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
    // 释放资源
    _mapUpdateTimer?.Stop();
    _mapUpdateTimer?.Dispose();
    _gpsSerialPort?.Close();
    _gpsSerialPort?.Dispose();
}
几个关键注意点
  • 线程安全第一:后台线程存点位,UI线程读点位,一定要用锁保护列表,不然会出现奇怪的崩溃或者数据错乱。
  • 适配你的串口格式:如果是标准GPS模块,输出的是NMEA协议(比如$GPGGA),那你需要专门解析这类语句,提取经纬度(注意NMEA的经纬度格式是度分格式,要转成十进制)。
  • 性能优化:如果运行时间很长,点位会越来越多,建议定时清理旧点位,比如只保留最近500个点,避免内存占用过高。
  • 异常处理:串口操作很容易出问题(比如设备断开),一定要加try-catch捕获异常,避免程序直接崩溃。

内容的提问来源于stack exchange,提问作者Okenite

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:43:57