spacer1
spacer2 1_1 1_2
2_1
 Subscribe
 The MP2K Update!
 
 
 
 Magazine
Front Cover
What's New
Articles
News
Sample Data
Gallery
Advertise
About
 Features
MapPoint 2013
Press Releases
MapPoint Forums
Companies
Link to MP2Kmag
Wish List
MapPoint Trial
Authors
 Earlier Content
Past News Items
Past What's New Announcements
 Sponsors
 Order

MapPoint 2013

Programming MapPoint in .NET

MapPoint Book

  Spatial Community
SVG Tutorials
MapPoint

Map Visitors

  ARTICLES  


Using the New Windows 7 Location API

Richard Marsden discusses the new “Location and Sensors” API and how to get started with development including a complete working example with source code available to download.

Probably one of the most interesting additions in Windows 7 for application developers is the addition of a “Location and Sensors” API. This provides a common interface for a wide range of sensor devices – ie. similar to a device driver for a printer. This will be very useful where such peripherals are often shackled to vendor-only applications and/or serial (RS-232) connections. Today, the only place where I actively use an RS-232 connection is in the daisy chain of cables and adapters that we have to take with us on the EcoMap Costa Rica Project (URL: http://www.ecomapcostarica.com ) in order to read data from our HOBO water temperature recorders!  Closer to home, the geospatial world is still encumbered by the NMEA-0183 standard which is based on a serial (“COM port”) connection although it is usually implemented virtually. You only need to search the forums here to see the problems caused by this virtual COM port arrangement: users have to install a USB driver and configure “non-existent” settings like COM port and baud rate. They rightly expect to be able to plug their GPS device in and it “just works” – like a printer or any other USB device. These problems should go away as more people adopt Windows 7 and vendors roll out Location API drivers for their devices. This article shows you how to use the new Location API in your own application.

The suppliers of the MapPoint and Streets & Trips devices, U-Blox, already supply a Windows 7 driver that supports the new API.  For this article I used a BU-353 device. No vendor driver is available for this yet, but I was able to use the generic GPSDirect Virtual Sensor Driver (URL: http://www.turboirc.com/gps7/ ). This maps any generic NMEA-0183 device to the new Location API. You still have to configure the COM settings and I found it could do with some polish, but it is free. It should be good enough for development, but I would hesitate to use it in a production environment.

Although most readers will be interested in using the Location API with GPS devices, the API can also support other location services such as WiFi base station setting, and cell-phone triangulation. It can also provide location reports as street addresses if the device (real or virtual) supports this information.

Using the Location API

Okay, so let’s look at the actual Location API. The API is implemented using COM. A .NET COM Interop wrapper is available, but for this sample we shall use MFC. You will also need Microsoft Visual Studio 2008 and the Windows 7 SDK. The Windows 7 SDK is a free download (URL: http://www.microsoft.com/downloads/details.aspx?FamilyID=c17ba869-9671-4330-a63e-1fd44e0e2505&displaylang=en ).

After installing your Location API driver and the Windows 7 SDK, you can configure Visual Studio. The SDK comes with a configuration tool that should be able to do this for you. If not, you will have to manually add the SDK to the standard paths for include files and library (lib) files. In a standard install, they will be something like this:

C:\Program files\Microsoft SDKs\Windows\v7.0\Include

C:\Program files\Microsoft SDKs\Windows\v7.0\Lib

 

I found I had to put these paths at the top of path listings for includes and libs to avoid an incompatible mix of different files from different versions.

Next, create a new project using the multi-threading options. For this sample I have chosen an MFC dialog box application. Add “LocationAPI.lib” to the Additional Dependencies setting in the project property pages (Linker->Input). This library provides the new API. You will also need to include LocationAPI.h which provides the interface prototypes.

The API can work in two modes: Synchronous and Asynchronous. I found the synchronous API to be quick enough for my needs. The asynchronous method is multi-threaded, and the call-back function will be called on a different thread. This will be preferable for a large application, but it adds complications which are beyond the scope of this article. An asynchronous example that uses the console for output can be found on the Microsoft website here ( URL: http://msdn.microsoft.com/en-us/library/dd317649%28VS.85%29.aspx ). A GUI interface (as used here) would require thread locking mechanisms.

So let’s start coding! Our program will request location updates every half a second, and measure the distance travelled between each update.

Our dialog box class’s include file (LocationDemoDlg.h) consists of mainly MFC boilerplate, but we do have some implementation specific definitions:

private: // our location specific private data members

LOCATION_REPORT_STATUS status;

 

CComPtr<ILocation> spLoc; // This is the main Location interface

 

      double lfPrevLat, lfPrevLng;

      double lfThisLat, lfThisLng;

      double lfDistance;

 

LOCATION_REPORT_STATUS is an enum that stores the status of the latest location report request. We simply use a class-level variable as a part of the text mapping. This could be made more efficient and stored as a location variable that is mapped as a function parameter. spLoc is the pointer to our main Location interface. The remaining variables simply keep track of the current location, previous location (ie. 0.5 seconds ago), and a cumulative distance calculated between these differences.

Next we move to the implementation of CLocationDemoDlg. Again, I shall skip the bulk of the MFC boilerplate, but the following snippet should help to explain how the dialog box GUI is hooked up. The dialog box (CLocationDemoDlg) has four text labels which are used to display information at 0.5 second intervals. These four labels are hooked up to four CString definitions:

 

void CLocationDemoDlg::DoDataExchange(CDataExchange* pDX)

{

      CDialog::DoDataExchange(pDX);

      DDX_Text(pDX, IDC_STATUS, sStatus);

      DDX_Text(pDX, IDC_PREVPOS, sPrevPos);

      DDX_Text(pDX, IDC_THISPOS, sThisPos);

      DDX_Text(pDX, IDC_DISTANCE, sDistance);

}

 

Beyond the automatically generated call backs produced by MFC, we need to add two event handlers. ON_WM_TIMER handles the timer events, and a second handler handles the OK button press (to shut things down).

 

Next we move to our OnInitDialog() definition: This starts the Location API, gets a status, and an initial location:

 

 

BOOL CLocationDemoDlg::OnInitDialog()

{

// (more MFC boiler plate)

      CDialog::OnInitDialog();

      SetIcon(m_hIcon, TRUE);

      SetIcon(m_hIcon, FALSE);

 

      // Our code starts here

      lfDistance = 0.0;

 

// Create the Location object

status = REPORT_NOT_SUPPORTED;

if (SUCCEEDED(spLoc.CoCreateInstance(CLSID_Location)))

{

      // Array of report types of interest.

      // Civic addresses/etc also supported

      IID REPORT_TYPES[] = { IID_ILatLongReport };

 

      // Request permissions for this user account to receive location

// data for all the types defined in REPORT_TYPES

// The final parameter (TRUE) indicates a synchronous request

// use FALSE to request asynchronous calls

if (FAILED(spLoc->RequestPermissions(NULL, REPORT_TYPES,

 ARRAYSIZE(REPORT_TYPES), TRUE)))

{

            AfxMessageBox("Warning: Unable to request permissions.\n");

}

     

      // Get the report status

      if (SUCCEEDED(spLoc->GetReportStatus(IID_ILatLongReport, &status)))

{

            // Map status enum to a text label for display

            SetStatusString();

 

            // Next define our report objects

            // These store the reports we request from spLoc

CComPtr<ILocationReport> spLocationReport;

CComPtr<ILatLongReport> spLatLongReport;

 

// Get the current location report (ILocationReport) and

// then get a ILatLongReport from it

// Check they are are reported okay and not null

if ( (SUCCEEDED(

spLoc->GetReport(IID_ILatLongReport, &spLocationReport))

) &&

(SUCCEEDED(

spLocationReport->QueryInterface(&spLatLongReport))

))

{

lfThisLat = 0;

lfThisLng = 0;

 

// Fetch the latitude & longitude

spLatLongReport->GetLatitude(&lfThisLat);

spLatLongReport->GetLongitude(&lfThisLng);

           

             // Format them into a label for display

sPrevPos.Format("Lat: %.6f,  Lng:%.6f", lfThisLat, lfThisLng);

 

// set some sensible initial values

lfPrevLat = lfThisLat;

             lfPrevLng = lfThisLng;

           sDistance = "0.0";

}

 

}

 

  // We have completed our call

  // but keep the spLoc pointer for future use

 

  // Start the timer (Timer #1) with a 500ms (0.5s) interval

SetTimer(1,500, 0); 

 

      }

 

      // Update labels with their new information

      UpdateData(FALSE);     

 

      return TRUE; 

}

 

 

The above is a simple example of using the location API in a synchronous manner. We only fetch the location’s longitude and latitude, but other parameters such as estimated error and altitude are available.

 

We fetched the status but left it to the function SetStatusString() to process it. This simply maps the status enum into a human-readable label. Here is the definition:

 

// Set the status label string according to the status enum

// Updatedata is not called (typically this would be called after this routine anyway)

void CLocationDemoDlg::SetStatusString()

{

switch (status) // If there is an error, print the error

{

case REPORT_RUNNING:

sStatus = "Report received okay";

break;

case REPORT_NOT_SUPPORTED:

sStatus = "No devices detected.";

break;

case REPORT_ERROR:

sStatus = "Report error.";

break;

case REPORT_ACCESS_DENIED:

sStatus = "Access denied to reports.";

break;

case REPORT_INITIALIZING:

sStatus = "Report is initializing.";

break;

}

}

 

 

The call back for the OK button stops the timer and closes the dialog box:

 

void CLocationDemoDlg::OnBnClickedOk()

{

      // Stop the timer

      KillTimer(1);

 

      // Close dialog box

      OnOK();

}

 

Next we define the actual timer call back. This is almost a complete copy of the above synchronous location code. We fetch a new report, the report’s status, and longitude,latitude coordinate. The distance between the previous coordinate and the new coordinate is calculated and added to lfDistance – a cumulative distance.

 

 

void CLocationDemoDlg::OnTimer(UINT nIDEvent)

{

// Get the report status

   if (SUCCEEDED(spLoc->GetReportStatus(IID_ILatLongReport, &status)))

{

      SetStatusString();

 

CComPtr<ILocationReport> spLocationReport;

CComPtr<ILatLongReport> spLatLongReport;

 

if ((SUCCEEDED(

spLoc->GetReport(IID_ILatLongReport, &spLocationReport))

) &&

 (SUCCEEDED(

spLocationReport->QueryInterface(&spLatLongReport))

))

{

lfPrevLat = lfThisLat;

lfPrevLng = lfThisLng;

 

// Fetch the new latitude & longitude

spLatLongReport->GetLatitude(&lfThisLat);

spLatLongReport->GetLongitude(&lfThisLng);

// Format coords into a label for display

sThisPos.Format("Lat: %.6f,  Lng:%.6f", lfThisLat, lfThisLng);

sPrevPos.Format("Lat: %.6f,  Lng:%.6f", lfPrevLat, lfPrevLng);

 

// Calculate the new cumulative distance, and update label

lfDistance += CalcDistance();

sDistance.Format("%.3f km", lfDistance);

 

// update the display

         UpdateData(FALSE);

     }

   }

   CDialog::OnTimer(nIDEvent);

}

 

 

Finally we need to implement CalcDistance(). This calculates the straight line (great circle) distance between the latest coordinate and the previous coordinate. Distances less than 1 metre are returned as 0 metres.

 

 

// Calculate distance between prev & this locations

// cf. Aviation Formulary at http://williams.best.vwh.net/avform.htm

#define DEG2RAD (3.14159265 / 180.0)

#define RAD2DEG (180.0 / 3.14159265)

#define RAD2NM  ( RAD2DEG * 60.0 )

#define RAD2KM  ( RAD2NM * 1.852 )

 

double CLocationDemoDlg::CalcDistance()

{

      double lf = sin(lfPrevLat*DEG2RAD)*sin(lfThisLat*DEG2RAD);

      lf += cos(lfPrevLat*DEG2RAD)*cos(lfThisLat*DEG2RAD) * cos( (lfPrevLng-lfThisLng)*DEG2RAD );

      double d = acos(lf) * RAD2KM; // distance in kilometers

      return (d>0.001) ? d : 0.0;  // only return distance if >1m

}

 

 

And that is it! I have left out a lot of MFC boilerplate, but the functioning location-specific content is fully covered above. The full VS2008 project including all source code can be downloaded here (URL: http://d3svfn6as6o5bl.cloudfront.net/mp/info/prog/LocationDemo.zip ). This is what the functioning program looks like:

Note that a GPS device has errors and the precise coordinate will typically “wander” by a few metres back and forth. This program picks up these “wanderings” if they are larger than 1 metre per 0.5 second interval. These can quickly accumulate if reception is particularly poor.

For further information, MSDN has extensive information including a full API reference and samples for the Location API ( URL: http://msdn.microsoft.com/en-us/library/dd464636%28VS.85%29.aspx ) and the Sensor API ( URL: http://msdn.microsoft.com/en-us/library/dd318953%28VS.85%29.aspx ). The Windows 7 SDK also includes samples for both synchronous and asynchronous location calls.

Discuss this story in the forum.

Author: Richard Marsden
Email: enquiries(AT)winwaed.com
URL: http://www.winwaed.com
Richard Marsden is the proprietor of Winwaed Software Technology, LLC which provides software consulting and development services, specializing in both MapPoint and online mapping applications. He operates the Mapping-Tools.com Website for MapPoint Tools and Utilities, and recently launched the GeoWeb Guru a community website for developers of the geospatial web. In 2008, Richard was awarded Virtual Earth MVP status by Microsoft.

Prior to Winwaed, Richard worked as a software developer working on seismic processing algorithms for the oil exploration industry. He holds geology and geophysics degrees from the University of Cambridge (Churchill College), and the University of Durham; and an interdisciplinary MBA from the University of Dallas.



Google
 
MP2Kmag Internet


 Recent Discussion
 Resources
Browse GIS books and periodicals
Find a MapPoint Partner or Consultant
Real Estate Columbia Custom Home


Want Your Site To Appear Here?

   © 1999-2012 MP2K. Questions and comments to: website@mp2kmag.com
  Microsoft and MapPoint 2002/2004/2006/2009/2010/2011/2013 are either trademarks or registered trademarks of Microsoft.