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






4.80/5 (27投票s)
一个简单的 GPS 追踪器,专为 Windows Mobile 2005 上的 Compact Framework 2.0 SDK 开发
引言
这是一个简单的 GPS 追踪器,专为 Windows Mobile 2005/2003 上的 Compact Framework 2.0 SDK 开发。所以,首先,你需要 VisualStudio 2005 和 Windows Mobile CE 5 SDK。你可以在模拟器设备或真实设备上开发它。正如你在照片中看到的,我开发的应用是在真实设备上完成的:出色的 Asus MyPal 636N。
工作原理:背景
上面的截图显示,这个应用程序生成的地图非常简单。它只是你的路线,而且应用程序能够
- 从任何 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
读取的数据的方法。它由 Form
在 DataReceivedEvent
上调用。
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日