![]() |
Platforms, Frameworks & Libraries »
Mobile Development »
Applications
Intermediate
Flight Tracker - An Application For Mapping Flight RoutesBy Jacob SenecalAn entertaining exploration of .NET Compact Framework Graphics and XML features |
VB, Windows, .NET CF, .NET, MobileVS.NET2003, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
![]()
This program is more an exploration of the Compact Framework than an actual useful application. My first goal was to have some fun while getting away from the everyday "Hello World" type apps that are ordinarily used to illustrate features and techniques. When I was a teenager, I had a friend who was obsessed with airline flight schedules. He would spend countless hours drawing maps of airline routes by hand. They were not terribly useful (unless you were an airline industry analyst), but they were pretty cool to look at.
I decided to create a PocketPC solution for drawing airline maps. The "Flight
Tracker", as I dubbed it, would included the ability to enter and maintain data
for individual airports, airlines and flights. I limited my map area to North
America, and only entered the data for a subset of the Southwest Airlines flight
schedule. A comprehensive database would have taken months for data entry or
required the purchase of commercial flight databases. This project would require
the use of basic .NET form controls, as well as the Graphics
interface and .NET XML classes for data storage.
The first task was to compile and manage the data needed to draw flight maps.
I created a class, FlightData.vb, to contain all data-related
structures, arrays and methods. I prefer to use structures and arrays of
structures to hold program data because it makes it so easy to see what you're
doing in the code. This arrangement is also very efficient in terms of memory
and processor usage. I wanted to store the data in an open, cross-platform
format, so I decided to try out the XML class in .NET.
The System.XML.XmlDocument class makes it easy to load and save
the contents of XML files. You simply instantiate the XmlDocument
object and then call its Load or Save method with the desired filename. The data
is stored in XmlNode and XmlAttribute objects within
the XmlDocument. Each XmlNode contains a collection of
child XmlNodes and a collection of XmlAttributes. I included in each of my
structure definitions a method to load a new structure's member values from an
XmlNode object and a method to return a new XmlNode
containing the member values. Here I've included the simplest of my structures
to illustrate this configuration:
Public Structure AirlineStruct
Dim Airline As String
Public Sub New(ByRef XmlNode As Xml.XmlNode)
Dim xAttribute As Xml.XmlAttribute
For Each xAttribute In XmlNode.Attributes
Select Case xAttribute.Name
Case "Airline"
Airline = xAttribute.Value
End Select
Next
End Sub
Public Function GetXmlNode(ByRef Doc As Xml.XmlDocument) _
As Xml.XmlNode
Dim Node As Xml.XmlNode = Doc.CreateNode( _
Xml.XmlNodeType.Element, "AIRLINE", "")
Node.Attributes.Append(GetXmlAttribute(Doc, "Airline", Airline))
Return Node
End Function
End Structure
One interesting limitation to the XmlNode and
XmlAttribute classes is that they can only be instantiated by an
XmlDocument object. This was a little annoying in my case since I
had to pass a pointer to the XmlDocument to my structure's
GetXmlNode method just for this purpose.
With these methods in place, saving the class's data consists of creating an
XmlDocument and adding an XmlNode for each structure
stored in the class's arrays. When XmlNodes have all been added, simply call the
XmlDocument's Save method.
Public Sub Save()
Dim xDoc As New Xml.XmlDocument
Try
xDoc.AppendChild(xDoc.CreateElement("FlightData"))
Dim Flight As FlightStruct
For Each Flight In Flights
xDoc.FirstChild.AppendChild(Flight.GetXmlNode(xDoc))
Next
Dim Airline As AirlineStruct
For Each Airline In Airlines
xDoc.FirstChild.AppendChild(Airline.GetXmlNode(xDoc))
Next
Dim Airport As AirportStruct
For Each Airport In Airports
xDoc.FirstChild.AppendChild(Airport.GetXmlNode(xDoc))
Next
xDoc.Save("\Program Files\Flight Tracker\FlightData.xml")
HasUnsavedChanges = False
Catch ex As Exception
MsgBox("Error saving XML data file.", _
MsgBoxStyle.Exclamation, "File Error")
End Try
End Sub
Note that the all the XmlDocument's other nodes are typically
located within the document's first child node, which you must create and add
yourself if when assembling the new file's contents.
Loading is easily accomplished by loading the XmlDocument from
file and then sequencing through the collection of child nodes, loading each one
into the appropriate structure type and adding the structure its corresponding
array.
Public Sub Load()
Dim xDoc As New Xml.XmlDocument
Try
Dim Flight As FlightStruct
Dim Airline As AirlineStruct
Dim Airport As AirportStruct
ReDim Flights(-1)
ReDim Airlines(-1)
ReDim Airports(-1)
xDoc.Load("\Program Files\Flight Tracker\FlightData.xml")
Dim Root As Xml.XmlNode = xDoc.FirstChild
Dim Child As Xml.XmlNode
For Each Child In Root.ChildNodes
Select Case Child.Name
Case "FLIGHT"
Flight = New FlightStruct(Child)
ReDim Preserve Flights(UBound(Flights) + 1)
Flights(UBound(Flights)) = Flight
Case "AIRLINE"
Airline = New AirlineStruct(Child)
ReDim Preserve Airlines(UBound(Airlines) + 1)
Airlines(UBound(Airlines)) = Airline
Case "AIRPORT"
Airport = New AirportStruct(Child)
ReDim Preserve Airports(UBound(Airports) + 1)
Airports(UBound(Airports)) = Airport
End Select
Next
HasUnsavedChanges = False
Catch ex As Exception
MsgBox("Error loading XML data file.", _
MsgBoxStyle.Exclamation, "File Error")
End Try
End Sub
The rest of the data management portion of the program consisted of a tabbed
dialog with ListView controls for Flights, Airlines and Airports,
as well as buttons and menus for creating, editing, deleting and sorting the
list elements.
The first thing I needed was a background image for my map. I found a simple
outline of the North American continent on a CorelDraw 8 clip art CD (I assume
that it is royalty-free, but I couldn't find any documentation to verify this).
I changed the color scheme of the map and converted it to a 240 x 274 JPG.
Everything was going fine until I tried to run the program on my iPAQ 3765 (what
I use for development purposes at work). The program started to load...and then
exited with an ArgumentException! I tried to load the image
dynamically at runtime but still ended up with the same error message. After a
couple of hours scouring the internet for an answer, I finally found the
following post on the OpenNetCF.org forums: 16bpp Bitmap
allways an error - Terry Mohre. The posting stated that, "16bpp ALWAYS
raises an 'ArgumentException' and fails to instantiate the Bitmap class". I
checked my JPG and ,sure enough, it was in 16 bits. I converted it to 24 bit and
it loaded without a hitch.
Next, as I began to fill in the Paint event handler for my map, I realized
that all the familiar .NET desktop Graphics features had been pared
down to a tiny subset in the Compact Framework. I set out in my web browser once
again to see what solutions were available in place of the GDI+ double-buffering
and anti-aliasing features. I quickly found a solid example for a
double-buffering technique on MSDN: How
to Create a Microsoft .NET Compact Framework-based Image Button - Alex
Yakhnin. The first part of this technique was to override the
OnPaintBackground event handler in the .NET Control
class. Because this event handler is a protected class member, you must create a
derived class to perform the override. This new class is used wherever a double
buffered drawing surface is needed throughout the program.
Public Class MapCanvas
Inherits System.Windows.Forms.Control
Protected Overrides Sub OnPaintBackground(ByVal e As _
System.Windows.Forms.PaintEventArgs)
'do nothing
End Sub
End Class
Although this class worked fine in the compiled program, I must say that it
wreaked havoc in the development environment. Maybe this is somehow related to
the fact that the Compact Framework does not support the
UserControl class that is normally used for this sort of thing? As
a result, I was constantly having to delete and re-create the canvas objects in
my forms. Sometimes I had to restart Visual Studio to get it to recognize that
the MapCanvases were valid objects.
The second part of the double-buffering technique was to draw everything in
an off-screen bitmap first, and then copy the bitmap onto the
MapCanvas object. This is illustrated by the Paint handler from the
airport location picking dialog:
Private Sub Canvas_Paint(ByVal sender As Object, ByVal e _
As System.Windows.Forms.PaintEventArgs) Handles Canvas.Paint
Dim bg As Graphics = Graphics.FromImage(bmBuffer)
bg.DrawImage(pbMap.Image, 0, 0)
bg.DrawLine(Pen, 0, YPos, 240, YPos)
bg.DrawLine(Pen, XPos, 0, XPos, 272)
bg.Dispose()
e.Graphics.DrawImage(bmBuffer, 0, 0)
End Sub
You can make the Paint handler run faster by maintaining graphics objects at the form-level...
Private Brush As New SolidBrush(Color.Blue)
Private Pen As New Pen(Color.Blue)
Private bmBuffer As Bitmap
As for anti-aliasing, there doesn't appear to be any support for it in the PocketPC's GDI. I'm sure there are probably third-party libraries that could provided features equivalent to the desktop's GDI+, but I didn't spend any time looking for them.
Once my double-buffering code was in place, I processed the contents of the
flight data class to fill an array of RectangleF objects, each of
which represented a single flight path. A corresponding array of integers was
used to store heading information which determined which corners of the
rectangle were the origination point and destination point. In order to animate
the flight paths on the map, a Timer was used to increment the
rectangle sizes from 0 to 100% in a succession of screen refreshes. The timer
and rectangles were a quick and dirty approach, but they worked rather well.
The program is easy to use. Just copy the .NET CF executable to your PocketPC (You must put it in "\Program Files\Flight Tracker". Create if necessary), along with the FlightData.xml file. The program will initially show a map of airport locations. Click Show/Flights to see the animated map of airline routes. Clicking Tools/Edit Flight Data will open the tabbed dialog where you can add the flight info for your favorite airline!
| You must Sign In to use this message board. | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 30 May 2004 Editor: Nishant Sivakumar |
Copyright 2004 by Jacob Senecal Everything else Copyright © CodeProject, 1999-2009 Web22 | Advertise on the Code Project |