dcsimg
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Take Your Apps Far and Wide with a GPS Tracking System  : Page 3

You already know that GPS is used extensively in a wide variety of mobile devices in order to track delivery and service vehicles—or any other mobile fleet. This kind of application is not as difficult as you may at first think. Find out how to use Visual Studio to create a GPS tracking app, with maps, that runs on Windows Mobile Pocket PC devices.


advertisement
Creating the Server Application
With the Pocket PC application completed, it is now time to build the server application. Using Visual Studio 2005, create a new Windows application and name it C:\MapServer. Populate the default Form1 with the following controls (see also Figure 7):
  • GroupBox controls
  • WebBrowser controls
  • Label controls
  • TextBox controls
  • Button controls

Figure 7. Populate the default Form1 with the various controls as shown.

Using Virtual Earth to Display Maps
To display a map of locations returned by the Pocket PC clients, I will use Microsoft's Virtual Earth, a map and search system comprising maps, aerial images, business directories, etc. Using VE, you can search for businesses, addresses, as well as ask for directions. You can access VE at http://local.live.com/.

Microsoft provides a map control that allows developers to embed VE maps into their own applications. You can use it to build a custom solution using VE mapping services. The VE Map control is made up of a JavaScript page and a cascading style sheet. The VE Map control is hosted at http://dev.virtualearth.net/mapcontrol/v3/mapcontrol.js. The CSS is located at http://local.live.com/css/MapControl.css.

The first step to using Virtual Earth is to create an HTML file to contain all the necessary JavaScript functions to interact with the VE map.

Tip: To get detailed help and the references to the various methods in the Virtual Map control, download the Virtual Earth SDK. As of this writing, Microsoft Virtual Earth is in version 3.0.
Add an HTML page to the project (right-click on project name in Solution Explorer and then select Add —> New Item… Select HTML Page). Name the HTML page VirtualEarth.html.

Populate the HTML page with the following:


<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>My Virtual Earth</title>
    <link href="http://local.live.com/css/MapControl.css" type="text/css" rel="stylesheet" />
    <script type="text/javascript" src="http://dev.virtualearth.net/mapcontrol/v3/mapcontrol.js"></script>
    <script type="text/javascript">
    
    var map = null;
      
    //---Go to a particular location on the map---
    function goto_map_position(lat, lng)
    {
       map.PanToLatLong(new VELatLong(lat,lng));
    }
            
            //---Load the Map---
    function loadMap()
    {
       container = document.getElementById("VirtualEarthMap");
       container.style.width = 488;
       container.style.height = 469;           	
       
       //---instantiate the VE map---
       map = new VEMap("VirtualEarthMap")     	
       map.LoadMap(new VELatLong(38.898748, -77.037684), 12 ,'r' , false);
    }
    </script>

</head>
<body onload="loadMap()" style="margin: 0px">
    <div id="VirtualEarthMap">
    </div>
</body>
</html>
The VirtualEarth.html page contains a reference to the VE Map control (.js) as well as the CSS file (.css). It also contains two JavaScript functions:
  • goto_map_position()—Goes to a particular location on the map based on the latitude and longitude specified.
  • loadMap()—Loads the map with the various parameters such as size of the map, zoom level, latitude and longitude, etc. It also attaches event handlers to the various events so that when certain events happen, the appropriate event handler is fired.
You would also need to set the Copy to Output Directory property of VirtualEarth.html to "Copy if newer" (via the Property window) so that the HTML page is deployed during runtime.

Using Sockets for Communication
While you can use Web services to pass the positional information from the Pocket PC to the server, doing so will take up a significant amount of bandwidth. This is because Web services uses XML SOAP packets for information exchange and this XML padding consumes considerable bandwidth consider that all we want to send is latitude and longitude (usually less then 20 bytes) information. Clearly, using Web services over a GPRS connection is not a feasible option (and an expensive one at that). For this reason, I have decided to program sockets directly to pass data to the server.

In Solution Explorer, add a new Class item to the project (right-click on project name and select Add —> New Item…. Select the Class item) and name it as WMClient.vb. Populate the file as follows:


Imports System.Net.Sockets

Public Class WMClient
    Public Event LatLng( _
       ByVal ID As Integer, _
       ByVal lat As Double, _
       ByVal lng As Double)

    Private _client As TcpClient

    '---used for sending/receiving data---
    Private data() As Byte

    Public Sub New(ByVal client As TcpClient)
        _client = client

        '---start reading data from the client in a separate thread---
        ReDim data(_client.ReceiveBufferSize)
        _client.GetStream.BeginRead(data, 0, _
           CInt(_client.ReceiveBufferSize), _
           AddressOf ReceiveMessage, Nothing)
    End Sub

    Public Sub ReceiveMessage(ByVal ar As IAsyncResult)
        '---read from client---
        Dim bytesRead As Integer
        Try
            SyncLock _client.GetStream
                bytesRead = _client.GetStream.EndRead(ar)
            End SyncLock

            '---client has disconnected---
            If bytesRead < 1 Then
                Exit Sub
            Else
                '---get the message sent---
                Dim messageReceived As String = _
                    System.Text.Encoding.ASCII. _
                    GetString(data, 0, bytesRead)
                Dim field() As String = messageReceived.Split(":")

                '---raise an event to pass back the lat and lng---
                RaiseEvent LatLng( _
                   field(0), _
                   Convert.ToDouble(field(1)), _
                   Convert.ToDouble(field(2)))
            End If

            '---continue reading from client---
            SyncLock _client.GetStream
                _client.GetStream.BeginRead(data, 0, _
                   CInt(_client.ReceiveBufferSize), _
                   AddressOf ReceiveMessage, Nothing)
            End SyncLock
        Catch ex As Exception
            Console.WriteLine(ex.ToString)
        End Try
    End Sub
End Class
Basically, the WMClient class does the following:
  • Contains a public event named LatLng. This event is raised when it receives data sent from Windows Mobile clients.
  • Creates a constructor that begins to read incoming data from the connected client
  • Processes incoming data from the ReceiveMessage() subroutine. The LatLng event is raised so that the data can be passed to the calling function (Form1) to update the map.
Coding the Server
Back to Form1, and we are now ready to code the main logic for the server. Switching to the code-behind of Form1, first import the following namespaces:

Imports System.Net.Sockets
Imports System.Threading
To programmatically interact with the map displayed within the WebBrowser control, you need to mark the Form1 class as COM-visible using the ComVisibleAttribute class:

<System.Runtime.InteropServices.ComVisibleAttribute(True)> _
Public Class Form1
Next, declare a member variable WMClient to store incoming connections:

<System.Runtime.InteropServices.ComVisibleAttribute(True)> _
Public Class Form1
    Dim WithEvents user As WMClient
Next, define the ListenForConnections() subroutine so that the server can continually listen for incoming connections:

    Private Sub ListenForConnections ()
        Const portNo As Integer = 3456
        Dim localAdd As System.Net.IPAddress = _
           System.Net.IPAddress.Parse("10.0.1.4")
        Dim listener As New TcpListener(localAdd, portNo)
        listener.Start()
        While True
            user = New WMClient(listener.AcceptTcpClient)
        End While
    End Sub
Author's Note: I am assuming the IP address of the server is 10.0.1.4. Replace this with the IP address of your server. Also, I have chosen the port 3456 to use for the communication.

In the Form1_Load event, you will read the content of the VirtualEarth.html file and load it into the two WebBrowser controls:


    Private Sub Form1_Load( _
       ByVal sender As System.Object, _
       ByVal e As System.EventArgs) Handles MyBase.Load

        Dim fileContents As String
        fileContents = My.Computer.FileSystem.ReadAllText( _
            Application.StartupPath & "\VirtualEarth.html")

        WebBrowser1.DocumentText = fileContents
        WebBrowser1.ObjectForScripting = Me

        WebBrowser2.DocumentText = fileContents
        WebBrowser2.ObjectForScripting = Me

        Dim t1 As New Thread(AddressOf ListenForConnections)
        t1.Start()
    End Sub
Also, note that I am using a separate thread to invoke the ListenForConnections() subroutine. This is because the ListenForConnections() subroutine is an infinite loop and using a separate thread to invoke it will prevent the UI of the application from freezing.

To handle events raised by the WMClient class, define the LatLng subroutine as the event handler. In this event, you will receive the ID, latitude, and longitude that were sent by the client. This information will be used to update the map, which is serviced by a delegate (you cannot directly update Windows controls in a separate thread):


    Private Sub LatLng( _
       ByVal ID As Integer, _
       ByVal lat As Double, _
       ByVal lng As Double) _
       Handles user.LatLng

        '---display map at specific location---
        Me.BeginInvoke(New _
           mydelegate(AddressOf GotoLocation), _
           New Object() {ID, lat, lng})
    End Sub
The GotoLocation() subroutine updates the appropriate TextBox as well as the WebBrowser controls. To update the map to display the specified location, you need to invoke the "goto_map_position" JavaScript function that you defined earlier in the HTML file:
Figure 8. This figure shows what the server will look like when it is started during testing.

    Private Delegate Sub mydelegate( _
       ByVal ID As Integer, _
       ByVal lat As Double, _
       ByVal lng As Double)
    Private Sub GotoLocation( _
       ByVal ID As Integer, _
       ByVal lat As Double, _
       ByVal lng As Double)
        Console.WriteLine(lat & ":" & lng)
        Dim param() As Object = New Object() {lat, lng}
        If ID = 1 Then
            txtLatitude_1.Text = lat
            txtLongitude_1.Text = lng
            WebBrowser1.Document.InvokeScript( _
               "goto_map_position", param)
        ElseIf ID = 2 Then
            txtLatitude_2.Text = lat
            txtLongitude_2.Text = lng
            WebBrowser2.Document.InvokeScript( _
               "goto_map_position", param)
        End If
    End Sub
That's it! You can now test your server by pressing F5. Figure 8 shows what the server will look like when it is started.

But that's not the end of the story. You now need to modify the Pocket PC client application so that it will send the latitude and longitude information to the server.



Thanks for your registration, follow us on our social networks to keep up-to-date