
How it works. BackGround
As you can see in the screenshot the map generated from this application is very simple.
It's only your the path, and it's able to:
Read data from any NMEA GPS device.
Read your position and print it to screen.
Load and save your path.
Zoom in/out function.
Panning on your path.
Center on the map
Run in demo mode with random generated data.
So You can save and load it but for now your can't edit or add some text inside that.
About application setup, it's very simple, you have only to setup your COM port.
This port must be that same where is attached your NMEA device, via bluetooth, irda or integrate.
(Personally I have an Asus MyPal 636N device, so it's have the GPS device built-in on COM5.)
Using the code
Basically it's compesed of three main actors (it's similar to a simple MVC pattern).
Form (Control):
It's the main form of the application so it's contain the Windows UI (menu,controls...)
Reader (Model):
It's the class that work with the GPS device, it's allow to read from serial with a threaded method.
Mapper (View):
The Mapper is the actor that parse the GPS NMEA phrases and draw it on screen. It's also allow the user to zoom and pan the map that contain your path.
Form
It's initialize the application, as you can see it's able to run on 240px X 320px devices.
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);
}
Form
It's the event registered method, it's called on DataReceived event.
As you can see with the m_isDemoMode== True the application will generate randoom coordinate.
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);
}
}
Form
It's the method that start and stop the Readed thread.
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;
}
Form
It's the region that solve problem to pan the map on your touchscreen device.
You can enable the m_mapper.clearAndDraw(), it will be able to clear the screen before redraw the moved map.
#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
Event exposed to Form on data received
public delegate void DataReceivedEventHandler(string data);
public event DataReceivedEventHandler dataReceived;
Reader
The thread method that read on serial port.
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);
}
}
}
Reader
These methods allow the Form to start and stop the reader thread.
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;
}
}
And now follow me in that Mapper section.
This is the simple method that parse the data readed from Reader it's called by the Form on an 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;
}
}
Mapper
The drawLatLong() method allow to convert the latitude and longitude data.
In example the latitude:43 38.5810 will be converted to 162710 using that formula 43*360+34*60+5810. At the end of parsing and conversion it will add the data to the private List<POINT> m_points; and later it will call the method draw()
This method will draw all the lines that is not drawed before.Obviously on pan or application start this method will draw all points.
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++;
}
Mapper
At the end you can see the loadPath(..) and savePath methods.
These methods are called by the form that ask to user the filename/location windows form dialog
m_mapper.loatPath(openFileDialog1.FileName); m_mapper.savePath(saveFileDialog1.FileName);. So this simply load/save the private List<POINT> m_points; from/to a file.
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();
}
Points of Interest
I know that is a simple project and that I'm a skilled developer but I want to dimostrate that the device development is very simply with VisualStudio2005 and CompactFramework 2.0
It's my first project on the great CodeProject website so please contact me if you have any doubts or proposal.
I'm always available for collaboration.
Leonardo Salvatore
Msn:leonardosalvatore@hotmail.com
Icq: 165495518
Skype: leonardosalvatore
Google: leonardosalvatore@gmail.com
History
Start of development: 8/7/2006
First release: 8/9/2006
Second release : 8/10/2006
Added Center function.
Now demo mode run without Gps or any serial port.
Some and various fix.
· Download demo project - 7 Kb To try it you need only to unpack that Zip and launch the exe on your device.
· Download source - 80 Kb
http://www.codeproject.com/useritems/GpsTracer.asp