65.9K
CodeProject 正在变化。 阅读更多。
Home

适用于 Windows Mobile CE 5 的 GPS 追踪器应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (27投票s)

2006 年 8 月 9 日

CPOL

3分钟阅读

viewsIcon

301685

downloadIcon

3404

一个简单的 GPS 追踪器,专为 Windows Mobile 2005 上的 Compact Framework 2.0 SDK 开发

Screenshot

引言

这是一个简单的 GPS 追踪器,专为 Windows Mobile 2005/2003 上的 Compact Framework 2.0 SDK 开发。所以,首先,你需要 VisualStudio 2005Windows Mobile CE 5 SDK。你可以在模拟器设备或真实设备上开发它。正如你在照片中看到的,我开发的应用是在真实设备上完成的:出色的 Asus MyPal 636N。

Screenshot

工作原理:背景

上面的截图显示,这个应用程序生成的地图非常简单。它只是你的路线,而且应用程序能够

  • 从任何 NMEA GPS 设备读取数据
  • 读取你的位置并将其显示在屏幕上
  • 加载和保存你的路线
  • 放大/缩小
  • 平移你的路线
  • 居中地图
  • 以随机生成的数据运行演示模式

你可以保存和加载它,但目前你不能编辑或添加其他文本。关于应用程序设置,它非常简单:你只需要设置你的 COM 端口。这个端口必须是你的 NMEA 设备通过蓝牙、IrDA 或集成连接的同一个端口。我个人拥有一台 Asus MyPal 636N 设备,所以 GPS 设备内置在 COM5 端口上。

使用代码

它基本上由三个主要部分组成,类似于简单的 MVC 模式

Form (控件):这是应用程序的主窗体,所以它包含了 Windows UI (菜单、控件...)

Reader (模型):这是与 GPS 设备协同工作的类,所以它允许通过线程方法从串行端口读取数据

Mapper (视图):这是解析 GPS NMEA 短语并在屏幕上绘制它们的参与者;它还允许用户缩放和平移包含路线的地图

表单

它初始化了应用程序;正如你所见,它能够运行在 240px X 320px 的设备上。

        public Form1()
        {
            InitializeComponent();
            m_graphics = this.CreateGraphics();
            m_mapper = new Mapper(m_graphics, 0, 30, 240, 300);
            m_rTh = new reader(m_port);
            m_rTh.dataReceived += new reader.DataReceivedEventHandler(parse);
        }

注册的事件处理方法会在 DataReceived 事件上被调用。当 m_isDemoMode==True 时,应用程序将生成随机坐标。

        public void parse(String readed)
        {
            if (!m_isDemoMode)
            {
                m_mapper.parseAndDraw(readed);
            }
            else
            {
                Random r = new Random();
                String rSecond1 = (int)(r.NextDouble() * 10 - 1) + "" + 
                                  (int)(r.NextDouble() * 10 - 1) + "" + 
                                  (int)(r.NextDouble() * 10 - 1) + "" + 
                                  (int)(r.NextDouble() * 10 - 1);
                String rSecond2 = (int)(r.NextDouble() * 10 - 1) + "" + 
                                  (int)(r.NextDouble() * 10 - 1) + "" + 
                                  (int)(r.NextDouble() * 10 - 1) + "" + 
                                  (int)(r.NextDouble() * 10 - 1);
                String rPrime1 = (int)(r.NextDouble() * 1 - 1) + "";
                String rPrime2 = (int)(r.NextDouble() * 1 - 1) + "";

                m_mapper.drawLatLong("434" + rPrime1 + "." + rSecond1, 
                                     "0111" + rPrime2 + "." + rSecond2);
            }
        }

这是启动和停止 Readed 线程的方法。

        private void menuItemRunStop_Click(object sender, EventArgs e)
        {
            if (m_isRunning)
            {
                m_rTh.stop();
            }
            else
            {
                m_rTh.start();
            }
            menuItemRunStop.Checked = !menuItemRunStop.Checked;
            m_isRunning = !m_isRunning;
        }

这是解决在触摸屏设备上平移地图问题的区域。你可以启用 m_mapper.clearAndDraw(),它会在重绘移动的地图之前清除屏幕。

        #region Panning

        private Point touch;
        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            if (m_mapper != null)
            {
                m_mapper.moveCenter(touch.X - e.X, touch.Y - e.Y);
                m_mapper.draw();//m_mapper.clearAndDraw();
            }
            touch.X = e.X;
            touch.Y = e.Y;
        }

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            touch.X = e.X;
            touch.Y = e.Y;
        }


        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            Form1_MouseMove(sender, e);
            m_mapper.clearAndDraw();
        }

        #endregion

Reader

数据接收后暴露给 Form 的事件

        public delegate void DataReceivedEventHandler(string data);
        public event DataReceivedEventHandler dataReceived;

在串行端口上读取数据的线程方法

        private void methodTh()
        {
            m_serialPort1.Open();
            byte[] buffer= new byte[100];
            while (m_run)
            {
                Thread.Sleep(500);
                m_readed = m_serialPort1.ReadLine();
                if (m_readed.Length > 0)
                {
                    dataReceived(m_readed);
                }
            }
        }

这些方法允许 Form 启动和停止读取器线程

        public void start()
        {
            if (m_th == null)
            {
                m_th = new Thread(methodTh);
            }
            m_run = true;
            m_th.Start();
        }

        public void stop()
        {
            m_run = false;
            Thread.Sleep(500);
            m_serialPort1.Close();
            if (m_th != null)
            {
                m_th.Abort();
                m_th = null;
            }
        }

Mapper (映射器)

现在,请跟随我进入 Mapper 部分。这是一个简单的解析从 Reader 读取的数据的方法。它由 FormDataReceivedEvent 上调用。

        public void parseAndDraw(string s)
        {
            string[] Words = s.Split(',');

            m_g.FillRectangle(m_bgBrush, new Rectangle(m_clip.X, 
                                         m_clip.Y + 5, m_clip.Width, 15));
            m_g.DrawString(s, m_font,m_fontBrush, new RectangleF(m_clip.X ,
                           m_clip.Y + 5, m_clip.Width, 15));

            switch (Words[0])
            {
                case "$GPRMC":
                // $GPRMC,170111,A,4338.5810,N,07015.1010,W,000.0,
                //        360.0,060199,017.5,W*73
                    // RMC - Recommended minimum specific GPS/Transit data

                    if (Words[3].Length > 0 && Words[5].Length > 0)
                    {
                        drawLatLong(Words[3], Words[5]);
                    }
                    break;
                case "$GPGSV":
                // $GPGSV,2,1,08,03,17,171,42,06,21,047,44,14,
                //        28,251,45,16,25,292,44*71
                    // GSV - Satellites in view
                    break;
                case "$GPGSA":
                // $GPGGA,170111,4338.581,N,07015.101,W,1,
                          00,2.0,1.1,M,-31.8,M,,*71
                    //GSA - GPS dilution of precision and active satellites
                    break;
                default:
                    break;
            }
        }

drawLatLong() 方法允许转换纬度和经度数据。在示例中,latitude:43 38.5810 将使用公式 43*360+34*60+5810 转换为 162710。在解析和转换完成后,它会将数据添加到 private List<POINT> m_points;,然后调用 draw() 方法。这个方法将绘制所有之前未绘制的线条。显然,在平移或应用程序启动时,这个方法将绘制所有点。

public void drawLatLong(string latitude, string longitude)
{

    Point aPoint = new Point();

    aPoint.X = 
      (Convert.ToInt32(latitude.Substring(latitude.Length - 4, 4)));
    aPoint.Y = 
      (Convert.ToInt32(longitude.Substring(longitude.Length - 4, 4)));

    aPoint.X += 
      (Convert.ToInt32(latitude.Substring(latitude.Length - 7, 2))) * 60;
    aPoint.Y += 
      (Convert.ToInt32(longitude.Substring(longitude.Length - 7, 2))) * 60;

    aPoint.X += 
      (Convert.ToInt32(latitude.Substring(latitude.Length - 9, 2))) * 3600;
    aPoint.Y += 
      (Convert.ToInt32(longitude.Substring(longitude.Length - 9, 2))) * 3600;

    m_points.Add(aPoint);
    draw();
}

public void draw()
{
    float xTo = 0;
    float xFrom = 0;
    float yTo = 0;
    float yFrom = 0;
               
    for (int i = m_drawded; i < m_points.Count; i++)
    {
        xTo = (m_points[i].X - m_points[0].X) / m_scale + m_center.X;
        xFrom = (m_points[i - 1].X - 
                 m_points[0].X) / m_scale + m_center.X;
        yTo = (m_points[i].Y - m_points[0].Y) / m_scale + m_center.Y;
        yFrom = (m_points[i - 1].Y - m_points[0].Y) / 
                 m_scale + m_center.Y;
        m_g.DrawLine(m_linePen, (int)xTo, (int)yTo, 
                    (int)xFrom, (int)yFrom);
        m_g.DrawEllipse(m_pointPen, 
                        new Rectangle((int)xFrom - 2, 
                        (int)yFrom - 2, 4, 4));
    }

    m_g.DrawEllipse(m_lastPointPen, 
                    new Rectangle((int)xTo - 2, (int)yTo - 2, 4, 4));
    m_drawded++;
}

最后,你还可以看到 loadPath(...)savePath 方法。这些方法由窗体调用,窗体从 Windows 窗体对话框 m_mapper.loatPath(openFileDialog1.FileName); m_mapper.savePath(saveFileDialog1.FileName); 中询问用户文件名/位置。所以这只是简单地从文件加载/保存 private List<POINT> m_points;

        public void loatPath(String filename)
        {
            StreamReader sr = new StreamReader(filename);

            m_points.Clear();
            int n = 0;
            Point p = new Point();
            while (!sr.EndOfStream)
            {

                String readed = sr.ReadLine();
                p.X = Convert.ToInt32(readed);
                readed = sr.ReadLine();
                p.Y = Convert.ToInt32(readed);
                m_points.Add(p);
                n++;
            }
            m_drawded = 1;
            sr.Close();
            clearAndDraw();
        }

        public void savePath(String filename)
        {
            StreamWriter sw = new StreamWriter(filename);

            foreach (Point p in m_points)
            {
                sw.WriteLine(Convert.ToString(p.X));
                sw.WriteLine(Convert.ToString(p.Y));
            }

            sw.Flush();
            sw.Close();
        }

关注点

我知道对于技术娴熟的开发者来说,这是一个简单的项目,但我想证明使用 VisualStudio2005 和 CompactFramework 2.0 进行设备开发非常简单。这是我在伟大的 CodeProject 网站上的第一个项目,所以如果你有任何疑问或建议,请随时联系我。我随时愿意合作。

历史

  • 开发开始日期:2006年7月8日
  • 首次发布:2006年9月8日
  • 第二次发布:2006年10月8日
    • 添加了居中功能
    • 现在演示模式可以在没有 GPS 或任何串行端口的情况下运行
    • 一些各种修复
  • 文章已编辑并发布到 CodeProject.com 主文章库:2007年5月22日
© . All rights reserved.