ADVISOR DETAILS

RECENT BLOG POSTS

Location Data Logger Design and Implementation, Part 7: The accuracy circle user control

This is part 7 of a series of blog posts on the design and implementation of the location-aware Windows* Store app “Location Data Logger”. Download the source code to Location Data Logger here.

The Accuracy Circle User Control

Many, if not most, internet mapping applications and consumer navigation systems don’t just display the user’s position on the map, they also give the user a visual indicator of the estimated accuracy of the position information. This typically takes the form of a circle centered on the current position with a radius corresponding to the best-guess of accuracy, hence the colloquial term “accuracy circle”. The point of the accuracy circle is to tell the user “I think I’m here, but it’s possible I’m somewhere else within this circle”. The larger the circle, the less confidence there should be about the position report.

Before I go in to the specifics of how an accuracy circle was implemented in Location Data Logger, however, I want to talk a little bit about the term “accuracy”, what it means, and how it’s calculated.

An aside: What is accuracy?

People use the term “accuracy” a lot in geolocation applications but there are a lot of misconceptions about what it really represents in the physical world, and even more misconceptions about how accurate accuracy really is.

The first rule of accuracy is that accuracy is a lie.

Maybe that’s being a little harsh, but it’s a statement that I am going to stand behind. What consumer devices report as “accuracy” is not really an accuracy at all, but rather something people in the GNSS world refer to as the “estimated position error”, or EPE. The most important word in that term is “estimated”. The harsher truth sitting behind all of this is that it is just not possible for a device to determine its own accuracy because there are simply too many factors that impact it, and virtually none of them (such as atmospheric conditions, and multipath and reflection issues) are knowable to the device. The EPE reported by your device is really just a guess, and there are no standards for how that guess is made. Each device manufacturer has their own algorithm for determining EPE based on what the device itself knows about the signals it is receiving, and that value is reported to the upper layers (in our case, to the Windows Geolocation sensor) as “accuracy”.

The only figure that a device can actually know about it’s own accuracy is something called the dilution of precision, or DOP. This is a value that can be mathematically derived from the number of satellite signals that are being tracked, and their location in the sky relative to the device, sometimes referred to as the satellite geometry or configuration. The more satellites a device can see, and the more spread out the satellites are relative to the observer, the lower the DOP.

The problem with DOP is that it’s a unitless number that represents the multiplicative effects of satellite geometry on measuring position. While it has some intuitive value—lower numbers are good, and higher numbers are bad—it isn’t a number you can give to a user and have them translate to something more tangible like feet or meters.

A device manufactuer’s EPE is an attempt to take DOP and turn it in to something more useful to the user, but these methods are imperfect, they can vary wildly from device to device, and in many cases they are even nonsinsical (I once worked with a device that always reported an accuracy in 4m increments, and another that always said its accuracy was 30m no matter what). This means you shouldn’t just take the Accuracy property from the Windows Geolocation sensor with a grain of salt: you may need the whole shaker.

Building the accuracy circle

Despite the fact that Accuracy property reported by the Geolocation sensor is a reliable measurement of a device’s accuracy, it is hardly useless and there is some value in plotting it on the map with an accuracy circle. To do this, we need two things:

  1. A user control that will hold the code and XAML to draw the circle
  2. Some math to scale the circle with the scale of the map

The code for first user control is in MapAccuracyCircle.xaml and MapAccuracyCircle.cs.

The user control

For the user control, we want to display three things: the user’s position with a dot, an accuracy disc that is shaded and semi-transparent, and a border around the accuracy disc as show below.

To implement this, I went with the canvas in XAML and defined three objects:

<UserControl
    x:Class="Location_Data_Logger.MapAccuracyCircle"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
    <Canvas>      
        <Ellipse x:Name="AccuracyCircle" Fill="CornflowerBlue" Opacity="0.20"/>
        <Ellipse x:Name="AccuracyCircleBorder" Opacity="0.8" Stroke="CornflowerBlue" StrokeThickness="2"/>
        <Ellipse Height="20" Width="20" Fill="CornflowerBlue" Margin="-10,-10,0,0"/>
    </Canvas>
</UserControl>

Note that the drawing order here is important: the semi-transparent disc must be rendered first, followed by the position dot and border. To simplify the rendering of the overlay on the map, the elements should be centered at 0,0 on the canvas. A width and height of the accuracy circle itself and its border are not specified, since they will be set in the application as the accuracy changes. The size of the position dot, however, is fixed at a 10 pixel radius and the Margin property is used to center it at our origin.

The accuracy circle must be updated whenever on of the following occurs:

  1. The device’s postion changes
  2. The map view changes

The former is done via the setErrorRadius method in the MapAccuracyCircle object:

public void setErrorRadius(double errorRadius)
{
    if (errorRadius >= 0) radius = errorRadius;
    else return;

    UpdateAccuracyCircle();
}

It is called from within update_position() in the MainPage object.

The latter is done with a handler on the ViewChanged event which is set up when the object is initialized:

private Map _map;

public MapAccuracyCircle (Map map)
{
    this.InitializeComponent();

    _map = map;

    // Set an event handler to update the control when the map view changes

    _map.ViewChanged += (s, e) => { UpdateAccuracyCircle(); };
}

Both of these methods call UpdateAccuracyCircle(), which is responsible for setting the radius of the accuracy circle on the map display.

The math

To draw a circle on the map, we have to turn the accuracy measurement, which is in meters, into an appropriate number of pixels. This requires us to know how many meters are represented by a single pixel in the map display. The Bing* Maps object does not provide a method to determine this so we need to determine it ourselves. This is a rather simple calculation because Microsoft* uses the Mercator projection for Bing Maps, and they even provide the necessary formula in their online article “Bing Maps Tile System“:

meters_per_pixel = Math.Cos(_map.Center.Latitude * DEG_TO_RAD) * CIRCUMFERENCE_EARTH / (256 * Math.Pow(2, _map.ZoomLevel));

Once the meters per pixel are known, it’s trivial to determine the circle radius in pixels by dividing our current accuracy (error radius) by that value:

pixels = radius / meters_per_pixel;

    AccuracyCircle.Width = AccuracyCircleBorder.Width= AccuracyCircle.Height = AccuracyCircleBorder.Height= pixels;

The circles are centered by setting the Margin property to offset them. 

AccuracyCircle.Margin = AccuracyCircleBorder.Margin=
    new Windows.UI.Xaml.Thickness(-pixels/2, -pixels/2, 0, 0);


← Part 6: The Export Class

Read more >

Some notes on Secure Key performance and throughput

At IDF in September I led a technical session in the security track on developing applications that make use of Secure Key. In that presentation I put up the following chart:

It plots the maximum, total throughput of the RDRAND instruction in a multithreaded application for six different systems. The Y axis is the ratio to single threaded throughput, and the X axis is the number of threads executing RDRAND as rapidly as possible. What this chart says is that total RDRAND performance scales nearly linearly with the number of threads: if you have two threads executing RDRAND you get twice the throughput of one thread, if you use three threads you get three times the throughput, and so on. This scaling continues until you hit an overall hardware limit on the CPU, such as maxing out your hardware threads or saturating the bus.

After the talk was done, I had a session attendee come up to me and ask a question: Why is it that multiple threads give the DRNG (the digital random number generator) higher throughput? Why can’t one thread simply pull as much random data via RDRAND as the DRNG is capable of generating?

The answer lies in the overall architecture of the CPU and the DRNG itself. Single-threaded performance is limited by the round-trip latency between when a RDRAND instruction is executed and when the random number is returned.

Round-trip latencies

The DRNG is connected to the cores via a bus. There are multiple busses within the CPU, and which bus is used for the DRNG is a decision that is made by the product groups when the CPU is designed. Those decisions are based on specific feature requirements and design constraints, and they can vary from generation to generation (3rd generation Core to 4th generation Core) as well as from family to family (Core to Xeon to Atom). No matter which bus is used, however, the process followed by the CPU when processing a RDRAND instruction is the same:

  1. The execution unit receives the RDRAND instruction
  2. A request for a random number is placed on the bus
  3. The DRNG receives the request from the bus
  4. The DRNG places a random number back on the bus
  5. The random number is returned to the execution unit that issued the instruction
  6. The random number is placed in the destination register

The time that elapses between steps 2 and 5 is the round-trip latency for the RDRAND instruction. The hardware thread cannot execute another RDRAND until the previous RDRAND has completed, and so this latency becomes the limit for single-thread throughput. It issues a request, waits for the answer, and then moves on.

With multiple threads executing RDRAND in parallel, however, each thread may place a request on the bus regardless of what the other threads are doing. While each thread still sees the same round-trip latency, and the same per-thread throughput, the total number of requests that are in flight on the bus increases. The end result is that the total throughput across all threads is roughly equal to the single-threaded throughput times the number of active threads. Hence, the throughput of the DRNG appears to increase.

§

Read more >

Location Data Logger Design and Implementation, Part 6: The Export Class

This is part 6 of a series of blog posts on the design and implementation of the location-aware Windows Store app “Location Data Logger”. Download the source code to Location Data Logger here.

The Export Class

The early development versions of Location Data Logger only logged data points to CSV files. This was fairly easy to implement and the code responsible for the file I/O was mixed in with the MainPage class. When the logger was started the log file was created directly inside of what would later become the logger_start() method, and log points were written out inside of update_position(). While this was not great modular design, at the time it was fast and simple, more than adequate for the task, and certainly appropriate for an early phase of development. Long term, however, I needed something more scalable, and more robust.

The goal for Location Data Logger was always to be able to write to multiple file formats. This is something that is supported by nearly every other GPS data logger app that you can find for mobile devices, as well as by dedicated consumer data loggers, and I did not want to release a less capable app for this project. And, on top of that, I didn’t want Location Data Logger to be a merely academic exercise: as an instructional tool, I felt it important to have just enough complexity to require some thoughtful design.

At minimum, the code for handling the file operations would have to move out of the MainPage class and into its own module. The real question, though, was how to handle the mutliple file formats.

One solution would have been to have one large exporter that was simply responsible for everything, but that felt unwieldly. Though it would allow for sufficient code reuse, the fact that each file format had its own dependancy modules and its own quirks (I’m looking at you, KML) meant that it would be a monolithic class with everything but the kitchen sink. On top of that, I’d need to either pass flags to tell the module which file formats were actively being logged, or created individual methods for logging each independantly as needed. While a valid approach, it flies in the face of object-oriented design principles.

The approach I chose was to make use of inheritence, creating a master Exporter class with child classes for managing each file format.

The base class

There are three basic logging operations in Location Data Logger, and operations that are common to all file formats are implemented in the base class which is called Export:

  1. Start the logging. This entails opening the log file on the filesystem and getting a handle to the file.
  2. Stop the logging. Close the file.
  3. Write to the log file. Write out one or more data points.

Note that I said “common to all file formats”. The implication here is that some file formats require special handling for one or more of these operations, but at minimum they all have some basic stuff that they need to do. Specifically, open the file, close the file, and write text to it. Any customization to these three operations is handled within the child classes.

Note that, while the Export class defines a Write() method for writing to the file, this is a low-level function. Trackpoints are logged by calling the LogPoint() method defined in the child classes, which in turn call Write() as needed.

The Export class also defines two class members:

protected StorageFile file;
protected enum ExportStatus { Inactive, Initializing, Ready };
protected ExportStatus status;

The file variable is of type StorageFile and is the handle that each module uses to write to the underlying, physical file. The Start() method in the base class is responsible for opening this handle and setting file.

The status variable provides some basic bookkeeping. It’s an enumeration with three possible values:

  • Initializing. The logger is in the process of opening the file, which is an asynchronous operation.
  • Ready. The log file was successfully opened and the Export object can write to the file.
  • Inactive. The log file is not open for writing. This means it has either not yet been opened, that it has been explicitly closed because logging was stopped, or a failure occured during an I/O operation.

A child object can use this status variable to make intelligent decisions about what file operations should or should not be attempted. (Ideally, the base Export object would also have this logic as a precaution. This is something I should add in a future version.)

The ExportCSV class

The ExportCSV class is the simplest of the export modules because the file format does not have a complicated schema. Data is organized in rows, with commas separating each field or column, and the first row contains the field names. Thus, the overload for the Start() method is very short, and is used to print the header row:

public async void Start(StorageFolder folder, String basename)
{
    pid = 1;

    await base.Start(folder, basename, extension);

    if (status != Export.ExportStatus.Ready) return;
    try
    {
        await this.Write("PID,Latitude,Longitude,Accuracy,Altitude,AltitudeAccuracy,Speed,Heading,Orientation,HighPrecision,Timestamprn");
    }
    catch
    {
        status = Export.ExportStatus.Inactive;
    }
}

Note that I use exception handling to set the status property to Inactive in the event of a failure. This will prevent the logger from attempting to write to a file that is not open.

No overloading is needed for the Stop() method. The LogPoint() method merely prints a line to the CSV file every time a track point comes in.

The ExportGPX class

This module is more complicated than the CSV module because a GPX file is an XML data file with a set schema. I had two choices here: either create the XML by hand, or use one of the XML libraries. I opted for the former because the GPX format is not very complicated, particularly for recording a single track log. It also offers slightly better crash protection, since writing the XML out as track points come in means that the file will be mostly complete (missing just the closing XML tags) in the event the app quits unexpectedly. Using an XML builder would require writing the whole file out perioidically, and when the logger is stopped. That can cause data loss in the event of a crash.

Like the ExportCSV module, then, the Starrt() method overload is used to print the preamble for the file, which in this case is a large chunk of XML. The LogPoint() method is similarly used to print the XML for each track point as they come in. Unlike the CSV module, however, this one needs an override for Stop() so that the closing XML tags can be printed:

const String footer= "</trkseg>rn</trk>rn</gpx>rn";

public override async void Stop()
{
    if (status == Export.ExportStatus.Ready)
    {
        try
        {
            await this.Write(footer);
        }

        catch
        { }
    }
    base.Stop();
 }

The ExportKML class

This is the most complicated of the exporters because a KML file has an elaborate schema, and there is no practical way to build the XML as you go. For this reason, I opted to use the XML classes in the Windows* Runtime to build the data file, and only write it out when the logger is stopped. One implication of doing this is that there is no crash protection: if the app quits unexpectedly, the KML file will not be generated. It would be good to add support for periodic writes (perhaps once or twice a minute) in future versions.

The Start() method sets up the base XML document structure and defines the parent elements that must be referenced when new log points are added. The LogPoint() method creates the XML for each log point, and adds it to the appropriate parent element. The Stop() method finishes up some final XML structures and then writes the whole thing out.

Calling the Export modules

The export modules are members of the DataLogger object. Logging a data point is quite simple, and done in the log_position() method which is called from the geo_PositionChanged event handler.

if (logCSV) eCSV.LogPoint(trkpt);
if (logGPX) eGPX.LogPoint(trkpt);
if (logKML) eKML.LogPoint(trkpt);

Opening and closing the export modules is similarly easy, and handled within the Start() and Stop() methods in DataLogger:

public void Start()
{
    basename = DateTime.Now.ToString("yyyyMMdd_HHmmss");

    Resume();

    running = true;            

    if (logCSV) eCSV.Start(folder, basename);
    if (logGPX) eGPX.Start(folder, basename);
    if (logKML) eKML.Start(folder, basename);
}

public void Stop()
{
    running = false;
    if (logCSV) eCSV.Stop();
    if (logGPX) eGPX.Stop();
    if (logKML) eKML.Stop();

    Pause();
}

Read more >

Location Data Logger Design and Implementation, Part 5: The Data Grid View

This is part 5 of a series of blog posts on the design and implementation of the location-aware Windows Store app “Location Data Logger”. Download the source code to Location Data Logger here.

The Data Grid View

As useful as the map display is sometimes you just want to be able to look at your raw data, and Location Data Logger makes this possible via its data grid view. Here you can see your logged data points and scroll through their history, in addition to displaying attributes such as speed and heading that just aren’t easily visualized on the map. This blog post discusses the design of the table view, and the data bindings used to display the data in it.

Creating the table

The first step in coding this data grid view, however, was coming to terms with the fact that the Windows* Runtime does not have a widget for this purpose.

Windows 8 can display collections of items, of course, but that interface is not oriented like a traditional table: it doesn’t have data columns in discreet rows. The grid view is designed to display tiles– basically, summary content– which you click or touch to either expand or open as a new page. While it is arguably useful for rolling up multiple pieces of independant, unqiue content into a concise display, it is not at all useful for an actual grid of data like a table or spreadsheet, and it is not appropriate for displaying our logged points in Location Data Logger. At least, not if you want to be able to quickly skim the data and see what changes from track point to track point.

There are third-party libraries in the wild that actually do provide a traditional table view, but I wanted to limit the number of add-ons and libraries that were required for Location Data Logger in order to simplify the build process as well as the legalities around its distribution, and that meant rolling my own solution using Grid and ListView elements.

The XAML for the table structure is shown below:

<Grid x:Name="gridData" Grid.Row="1" Opacity="0" Visibility="Collapsed" >
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid Grid.Row="0" Margin="0,15,0,0" Background="#FF999999">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200"/>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="80"/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Column="0" Text="Timestamp (UTC)" Style="{StaticResource ColumnHeader}"/>
        <TextBlock Grid.Column="1" Text="Latitude" Style="{StaticResource ColumnHeader}"/>
        <TextBlock Grid.Column="2" Text="Longitude" Style="{StaticResource ColumnHeader}"/>
        <TextBlock Grid.Column="3" Text="Accuracy" Style="{StaticResource ColumnHeader}"/>
        <TextBlock Grid.Column="4" Text="Altitude" Style="{StaticResource ColumnHeader}"/>
        <TextBlock Grid.Column="5" Text="Altitude Accuracy" TextWrapping="Wrap" Style="{StaticResource ColumnHeader}"/>
        <TextBlock Grid.Column="6" Text="Speed" Style="{StaticResource ColumnHeader}"/>
        <TextBlock Grid.Column="7" Text="Heading" Style="{StaticResource ColumnHeader}"/>
        <TextBlock Grid.Column="8" Text="Precision" Style="{StaticResource ColumnHeader}"/>
    </Grid>
    <Border Grid.Row="1" BorderThickness="1" BorderBrush="#FF999999">
        <ListView x:Name="listPoints" ScrollViewer.VerticalScrollMode="Enabled" ScrollViewer.VerticalScrollBarVisibility="Visible" ItemContainerStyle="{StaticResource DataGridStyle}" ItemsSource="{Binding}" SelectionMode="None" IsItemClickEnabled="False" IsDoubleTapEnabled="False" >
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Grid VerticalAlignment="Top" >
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="200"/>
                            <ColumnDefinition Width="100"/>
                            <ColumnDefinition Width="100"/>
                            <ColumnDefinition Width="80"/>
                            <ColumnDefinition Width="80"/>
                            <ColumnDefinition Width="80"/>
                            <ColumnDefinition Width="80"/>
                            <ColumnDefinition Width="80"/>
                            <ColumnDefinition Width="80"/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Text="{Binding Timestamp}" Style="{StaticResource DataCell}"/>
                        <TextBlock Grid.Column="1" Text="{Binding Latitude, Converter={StaticResource fs}, ConverterParameter={0:F6}}" Style="{StaticResource DataCell}"/>
                        <TextBlock Grid.Column="2" Text="{Binding Longitude, Converter={StaticResource fs}, ConverterParameter={0:F6}}" Style="{StaticResource DataCell}"/>
                        <TextBlock Grid.Column="3" Text="{Binding Accuracy}" Style="{StaticResource DataCell}"/>
                        <TextBlock Grid.Column="4" Text="{Binding Altitude}" Style="{StaticResource DataCell}"/>
                        <TextBlock Grid.Column="5" Text="{Binding AltitudeAccuracy}" Style="{StaticResource DataCell}"/>
                        <TextBlock Grid.Column="6" Text="{Binding Speed}" Style="{StaticResource DataCell}"/>
                        <TextBlock Grid.Column="7" Text="{Binding Heading}" Style="{StaticResource DataCell}"/>
                        <TextBlock Grid.Column="8" Text="{Binding Precision}" Style="{StaticResource DataCell}"/>
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Border>
    <ToggleButton x:Name="toggleAutoscroll" Grid.Row="2" Content="Autoscroll" IsChecked="False"/>
</Grid>

That’s a lot of code, so let’s break it down into chunks.

The general approach I chose was to create the data table using the Grid element. Each data cell in the table is a cell in the GridThe table is itself inside of an enclosing grid with three rows: the header for the table goes in the first row, and the body of the table goes in the second row. There is a third row in this grid, too, which holds a toggle button for turning the autoscroll feature on and off. The height of the first and last rows are set to “Auto” so that the grid rows will automatically size to the height of their content. The middle row of the enclosing grid is set to a hieght of “*”, which means that the data rows of the table it will fill up the remaining vertical space on the screen.

By placing the table header inside of its own Grid I can fix the header at the top of the table so that it is always visible even as the user scrolls through the data. A side effect of this, however, is that the widths of each cell have to be explicitly sized: because the header is separate from the rows of data, a width of “Auto” is just not feasible. The layout engine can only autosize based on the content of the headers, and can’t account for differences in the column widths of the actual data rows.

There are two ways of solving this. The first is to dynamically size the Grid columns in the program as data points are added, and the second is to fix the cell widths using static values. The former is actually easier than it sounds, but for the purposes of simplicity I went with the latter. While it’s certainly less flexible, it’s not a huge issue since I have control over the font sizes, and what is displayed in the data cells. Thus I can choose column widths that are guaranteed to be large enough to display the data without wasting a great deal of screen space.

The body of the table is enclosed within a ListView element with vertical scrolling enabled. Use of ListView lets me bind a data object to a template via ListView.ItemTemplate and DataTemplate, but has yet another side effect: each table row has to be its own Grid element, because there can be only one child object inside of the template. Since the table header is already its own grid with fixed-size columns, though, I’ve already paid that price.

The data bindings

The use of a template inside the ListView element allows me to create static XAML that expands to a dynamic list of elements during execution. The DataTemplate is what maps variables in MainPage to the TextBlock elements inside the Grid. The first step for this sort of binding is to associate the ListView with the source object (for clarity, I have only listed the attributes necessary for the data binding below):

<ListView x:Name="listPoints"  ItemsSource="{Binding}">

This creates an object named listPoints that is visible to the MainPage object, and binds its values to the ListView items. The listPoints object has a property called DataContext which defines the data elements. For this application, DataContext is set to an object of type ObservableCollection<Trackpoint>:

points = new ObservableCollection<Trackpoint>();
listPoints.DataContext = points;

This configures listPoints to hold a collection of Trackpoint objects. When the template is expanded in the UI, each item in the ListView will have a trackpoint associated with it. The expansion of the Trackpoint object to the template is defined in the DataTemplate element. Again, for clarity, I’ll reduce that to just the TextBlock elements inside the Grid:

<TextBlock Grid.Column="0" Text="{Binding Timestamp}" Style="{StaticResource DataCell}"/>
<TextBlock Grid.Column="1" Text="{Binding Latitude, Converter={StaticResource fs}, ConverterParameter={0:F6}}" Style="{StaticResource DataCell}"/>
<TextBlock Grid.Column="2" Text="{Binding Longitude, Converter={StaticResource fs}, ConverterParameter={0:F6}}" Style="{StaticResource DataCell}"/>
<TextBlock Grid.Column="3" Text="{Binding Accuracy}" Style="{StaticResource DataCell}"/>
<TextBlock Grid.Column="4" Text="{Binding Altitude}" Style="{StaticResource DataCell}"/>
<TextBlock Grid.Column="5" Text="{Binding AltitudeAccuracy}" Style="{StaticResource DataCell}"/>
<TextBlock Grid.Column="6" Text="{Binding Speed}" Style="{StaticResource DataCell}"/>
<TextBlock Grid.Column="7" Text="{Binding Heading}" Style="{StaticResource DataCell}"/>
<TextBlock Grid.Column="8" Text="{Binding Precision}" Style="{StaticResource DataCell}"/>

For most of these items, the data binding is just a simple binding to a property of the Trackpoint object, such as the timestamp, speed, heading, and so on. For the Latitude and Longitude properties, however, I employ a converter.

The purpose of the converter is to change the defaul display behavior of the data that is printed. The Latitude and Longitude properties are both floating point values, and by default they get displayed in exponential notation which can be difficult to read at-a-glance. The converter is used to change the default formatting using the “F6″ converter to String.Format, which prints the number followed by six decimal places which is sufficient for this purpose (a degree of longitude at the equator is about 111 km, so .0000001 degrees corresponds to just over 0.1 meter which is more precision than a consumer-grade GNSS can prdouce).

The converter function is defined by linking a namespace key to a class definition:

<local:FormatString x:Key="fs"/>

And the convert function itself is in FormatString.cs.

public object Convert(object value, Type type, object param, String lang)
{
    String fmt = param as String; // use "as String" so fmt= NULL if param is not a string
    CultureInfo culture;
            

    if ( String.IsNullOrEmpty(fmt) ) return value.ToString();

    culture = new CultureInfo(lang);
            
    if (culture != null) return String.Format(culture, fmt, value);

    return String.Format(fmt, value);
}

The format string is passed as the parameter object to the function, and the function uses that to call String.Format.

All that’s left now is to add items to the listPoints object as track points are logged. This takes place inside of the update_position callback. Remember, the DataContext for listPoints is set to the points collection, so we add each trackpoint to points. I also automatically scroll to the bottom of the table if autoscrolling has been turned out.

if (logged)
{
    // Add the point to our table
    points.Add(t);

    ...

    if ((Boolean)toggleAutoscroll.IsChecked)
    {
        listPoints.UpdateLayout();
        listPoints.ScrollIntoView(listPoints.Items.Last());
    }
}

← Part 4: Bing Maps Integration Part 6: The Export Class →

Read more >

Location Data Logger Design and Implementation, Part 4: Bing Maps Integration

This is part 4 of a series of blog posts on the design and implementation of the location-aware Windows Store app “Location Data Logger”. Download the source code to Location Data Logger here.

The Bing* Maps SDK

One of Location Data Logger’s primary features is the map display which show’s the device’s current location and a so-called breadcrumb trail of its movement. This is accomplished using the Bing Maps SDK for Windows Store apps (and note that this URL does change, so the preceding link may not work forever. Search engines are your friend). Before I dive into the details of gluing the map display to Location Data Logger, however, it’s probably worth covering an important ground rule: the Bing Maps SDK has a license agreement associated with it, and to use the map control in your app you must first obtain a developer key and then agree to the terms and conditions of its use.

Design goals

The first step in integration is to figure out what you want the control to do, and how it should fit in with the overall app. For Location Data Logger I had the following requirements:

  1. The device’s current position should always be displayed on the map. Any time a position update occurs, whether or not we are actively logging the data points, the display should update.
  2. The map should auto-center to track the device’s movement.
  3. The user can override auto-centering, as well as turn it back on.
  4. The data points that have been logged should also be displayed on the map as a breadcrumb trail.
  5. The user can turn off the breadcrumb display

These will require us to set a number of event handlers for the map control, and I’ll cover those implementation details in a moment.

Adding the map control

I used XAML to add the map control to the page layout. The XAML does require you to import the Bing.Maps namespace, however, and here I have mapped Bing.Maps to the prefix bm:.

<common:LayoutAwarePage
 x:Name="pageRoot"
 x:Class="Location_Data_Logger.MainPage"
...
xmlns:bm="using:Bing.Maps"
 

The map control can now be added thusly:

 <bm:Map Grid.Row="0" x:Name="mapPosition" Credentials="INSERT_YOUR_DEVELOPER KEY_HERE"
 PointerWheelChangedOverride="mapPosition_PointerWheelChanged" PointerMovedOverride="mapPosition_PointerMoved"
 DoubleTappedOverride="mapPosition_DoubleTapped" PointerCanceledOverride="mapPosition_PointerCanceled"
 PointerReleasedOverride="mapPosition_PointerReleased" PointerPressedOverride="mapPosition_PointerPressed"
 ViewChanged="mapPosition_ViewChanged" />
 

Note the long list of event handlers which I meantioned briefly, above. Also in there is the Credentials attribute, which is where you place yoru developer key. Your key is generated for you when you create an account for yourself and register your app. Each app you create uses its own, unique key, and this key is used to unqieuly identify your app to the Bing servers. This is how Microsoft tracks your app’s usage of the service: if your app’s usage exceeds the limits set for free accounts then your app will be blocked– for anyone who uses it, whevere they are– until the next day. (If this happens frequently you will want to consider purchasing a high-volume license.)

Displaying the device position

Display position on the map is pretty straightforward, but it does require setting up some infrastructure. And to do that, I need to frist talk about the structure of the map control, itself. The map object has, among other things, two properties that hold map objects:

  1. The Children property is a MapUIElementCollection object which is used to hold UI elements directly, and MapLayer objects if you choose to create layers of controls. If you create a custom control for placement on the map, as I do in Location Data Logger, you will add it to this collection.
  2. The ShapeLayer property is a MapShapeLayerCollection object which holds MapShapeLayer objects. A MapShapeLayer is where you draw map shapes (polylines, polygons, and multipoints).

To display the device’s position, I created a user control named MapAccuracyCircle. The specifics of this control will be discussed in a future post in this blog series, but for now just accept that this is a custom control, and as a control it gets added to the map’s Children collection. Displaying the location on the map means placing the position into an object that the Bing Maps control can understand, and then updating that position as needed.

The relevant code snippets:

 public sealed partial class MainPage : Location_Data_Logger.Common.LayoutAwarePage
 {
 ...
MapAccuracyCircle accuracy_circle;
 Location current_location;
...
public MainPage()
 {
...
accuracy_circle = new MapAccuracyCircle(mapPosition);
 accuracy_circle.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
 mapPosition.Children.Add(accuracy_circle);

Note that I start with the Visibility property set to Collapsed. This is because there is no location set yet, so the map control should not be visible. Visibility is set in the update_status() method:

accuracy_circle.Visibility = (s == Windows.Devices.Geolocation.PositionStatus.Ready) ?
 Windows.UI.Xaml.Visibility.Visible : Windows.UI.Xaml.Visibility.Collapsed;

The Location class is part of the Bing Maps API and contains the coordinates of a location on the map, and it is updated within the update_position() method:

MapLayer.SetPosition(accuracy_circle, current_location);

(Using MapLayer in this manner is how you adjust the position of a control that has been directly added to a map instead of through a map layer.)

Auto-centering on the device location

Auto-centering is quite simple. Every time a PositionChange event is received, just recenter the map.

if ( toggleAutoCenter.IsChecked == true ) mapPosition.SetView(current_location, map_zoom);

If auto-centering is enabled, via a toggle button, then set the new map view. The map’s zoom level is tracked in the variable map_zoom in case it is needed elsewhere in the future (currently, this variable is redundant and not really used).

Toggling auto-center off and on

It makes sense to disable auto-centering of the map when the user takes action that implies he or she no longer wants it, such as scrolling the map view, and to turn it back on when they are done. This is accomplished in two ways. The first is with a toggle button below the map called “Autocenter”. When it’s on, the map display will auto-center and when it’s off, it won’t. The second is using event handlers on the map control.

 // Map update events

private void mapPosition_PointerPressed(object sender, RoutedEventArgs e)
{
    map_pointer_pressed = true;
}

private void mapPosition_PointerReleased(object sender, RoutedEventArgs e)
{
    map_pointer_pressed = false;
}

private void mapPosition_PointerMoved(object sender, RoutedEventArgs e)
{
    if (map_pointer_pressed == true)
    {
       toggleAutoCenter.IsChecked = false;
    }
}

private void mapPosition_PointerCanceled(object sender, RoutedEventArgs e)
{
    map_pointer_pressed = false;
}

private void mapPosition_DoubleTapped(object sender, RoutedEventArgs e)
{
    toggleAutoCenter.IsChecked = false;
}

private void mapPosition_ViewChanged(object sender, ViewChangedEventArgs e)
{
    map_zoom = mapPosition.ZoomLevel;
}

private void mapPosition_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
    toggleAutoCenter.IsChecked = false;
}

There’s a lot going on here, and it breaks down like this. Auto-centering is disabled when any of the following occur:

  1. The map view changes. ViewChanged event is generated after the map view change has completed. Meaning, if you scroll the map view, the event is generated when the scrolling stops.
  2. The map is double-clicked. The DoubleTapped event is used to zoom in on the map. This action always changes the map’s center point unless the user just happens to double click on the exact pixel in the middle of the map. (This is not too likely, and it is a case that is ignored.)
  3. The pointer is moved while the pointer is pressed. This is the classic “click and drag” motion. Note that the added complex logic here is to prevent auto-centering from being disabled just because a mouse pointer moves across the map. The user must explicitly be doing a click-and-drag. Auto-center is immediately turned off as soon as a drag operation starts. This is to prevent the map from auto-centering while the user is actively manipulating it.
  4. The pointer wheel is changed. This refers to the wheel device on a mouse. This action is mapped to zooming in and out in the map control. See #2.

The Breadcrumb Trail

The breadcrumb trail, which shows the positions recorded by the app during logging, is displayed using a shape layer. Two objects are added to the MainPage class

MapShapeLayer layerBreadcrumb;
 MapPolyline lineBreadcrumb;

and the following code in the MainPage() constructor gets the map initialized:

lineBreadcrumb = new MapPolyline();
layerBreadcrumb = new MapShapeLayer();

lineBreadcrumb.Color = Windows.UI.Colors.CornflowerBlue;
lineBreadcrumb.Width = 3;

layerBreadcrumb.Shapes.Add(lineBreadcrumb);
mapPosition.ShapeLayers.Add(layerBreadcrumb);

The breadcrumb trail is incrementally built inside of the update_position() delegate. When update_position() is called, the DataLogger object also includes the boolean parameter logged. If this value is true, then we add the point to the lineBreadcrumb polyline.

current_location = new Location(c.Latitude, c.Longitude);
if (logged)
{

    ...

    // Add the point to our breadcrumb trail
    if (lineBreadcrumb != null) lineBreadcrumb.Locations.Add(current_location);
}

Since the breadcrumb display only shows the currently logged points, it also has to be cleared whenever the user starts a new logging session. This is accomplished in the logger_start() method.

lineBreadcrumb.Locations.Clear();

Turning the breadcrumb display on and off

This is done with a toggle button under the map.

private void toggleBreadcrumb_Click(object sender, RoutedEventArgs e)
{
    Boolean onoff = (Boolean)toggleBreadcrumb.IsChecked;

    lineBreadcrumb.Visible = onoff;
}

Note that I’m not just changing the Visible property here: I am also saving the display preference for future sessions by writing it to roamingSettings.

← Part 3: The DataLogger Class Part 5: The Data Grid View →

Read more >

Location Data Logger Design and Implementation, Part 3: Geolocation and the DataLogger class

This is part 2 of a series of blog posts on the design and implementation of the location-aware Windows Store app “Location Data Logger”. Download the source code to Location Data Logger here.

The DataLogger Class

At the heart of Location Data Logger is the DataLogger object which is responsible for obtaining location reports from the Windows 8 Geolocation sensor and sending that information to the various other components in the application. All of this is implemented within the DataLogger class.

Location Data Logger is a relatively simple application and it only has one display page, and I could easily have implemented the geolocation funcitonality into the MainPage class. I chose to go with a separate class for two reasons:

  1. For anything more than trivial applications, it is good application design to compartmentalize your objects. Rather than have MainPage be some mega-class that implements everything from geolocation to writing the data files, I broke the application out into functional components.
  2. Future-proofing. If I decide to add a second display page or state to the application, the code is already capable of supporting that.

Since this object coordinates all of the activities within the application, it needs to be able to communicate with the relevant Windows 8 sensors as well as the objects that are responsible for writing the data logs. Some of its private class members include:


Geolocator geo;
SimpleOrientationSensor sorient;
ExportCSV eCSV;
ExportGPX eGPX;
ExportKML eKML;

Initialization

When the DataLogger object is created, considerable initialization takes place.

public DataLogger()
{
       lastupdate = new DateTime(1900, 1, 1);
       hp_source = hp_tracking = running= false;
       position_delegate = null;
       status_delegate = null;
       logCSV = logGPX = false;
       logKML = true;

       geo = null
 
       sorient = SimpleOrientationSensor.GetDefault();

       folder = null;
       eGPX= new ExportGPX();
       eCSV = new ExportCSV();
       eKML = new ExportKML();
}

The Geolocator object is initialized inside of the Resume() method, which is called from MainPage when the application is ready to start or resume tracking the device’s position (though not necessarily logging).


public void Resume()
{
       geo = new Geolocator();
       geo.DesiredAccuracy = PositionAccuracy.High;
       geo.MovementThreshold = 0;

       geo.StatusChanged += new TypedEventHandler<Geolocator, StatusChangedEventArgs>(geo_StatusChanged);
       geo.PositionChanged += new TypedEventHandler<Geolocator, PositionChangedEventArgs>(geo_PositionChanged);
}

Though all of this work takes place in two separate places, I’ll discuss them as a whole.

The geolocator sensor is initialized, and immediately configured with a MovementThreshold of 0 and a DesiredAccuracy of High. Most, if not all, GPS receivers calculate their position once per second, and the goal of the application is to record every position report received even when the position has not changed. These settings ensure we receive reports from the location device as the are reported, and prevent the Windows Sensor API from filtering some out.

Event handlers for the Geolocator‘s PositionChanged and StatusChanged events are also installed, a topic that I cover in detail below.

While I initialize a SimpleOrientation sensor, I do not create an event handler for it. This is because the data logger records the device’s orientation at the time a position update comes in, not when the orientation changes. This means an event handler is not only unnecessary, but unwanted.

Why include the SimpleOrientation sensor at all, though? It’s certainly not necessary for geolocation. The answer is because this information might be useful to a device manufacturer. A device’s antenna design can have a significant affect on the reception quality of radio signals, and reception can be orientation-sensitive.

Also note that I set two variables, hp_source and hp_tracking, to false, and initialize lastupdate to a time in the distant past (Jan 1st, 1900). These variables are used to internally determine and track 1) whether or not we have a high precision data source, and 2) if the user has asked to log only high precision data. Essentially what is happening here is that I assume the location data is not high-precision until proved otherwise.

The call from MainPage.xaml.cs that gets everything started looks like this:


public MainPage()
{
      this.InitializeComponent();

      …

      logger = new DataLogger();
      logger.SetCallbackStatusChanged(update_status);
      logger.SetCallbackPositionChanged(update_position);
      logger.Resume();

      …
}

(The SetCallback* functions are explained below.)

Identifying high-precision geolocation data

The Location API in the Windows 8 Runtime abstracts the location source from the developer (and, in turn, the user). As explained in my blog “The WinRT Location API: Where did my location data come from?”, the geolocation sensor is actually a merging of multiple inputs, some specialized hardware devices such as GPS/GNSS receivers (if present), and some software sources such as WiFi triangulation. The API does not provide the developer with a means of explicitly determining where a location report originated. The best you can do is make an educated guess based on the reported accuracy and other characteristics of the position reports.

The DataLogger class looks at a combination of two factors: the update rate, and the Accuracy reported in the Geocoordinate object. This is done inside the log_position() method, which is called from the geo_PositionChanged() event handler:


TimeSpan deltat;
 
deltat = c.Timestamp - lastupdate;
 
// Do we have high-precision location data?
 
if (deltat.TotalSeconds <= 3 && c.Accuracy <= 30) hp_source = true;
else hp_source = false;

I somewhat arbitrarily choose a reporting interval of 3 seconds as the threshold, as some consumer GPS devices may update once a second but send position reports via their NMEA output stream every two seconds (this is to accommodate people using external GPS devices as a sensor via GPSDirect). The accuracy of 30 meters was also somewhat arbitrary: consumer GPS accuracy is typically on the order of a few meters, and car navigation systems can provide reasonable guidance with only 30 meters of accuracy.

Geolocation Events and Delegates

The DataLogger class implements event handlers for the PositionChanged and StatusChanged events so that the logger object can record the positions as they come in, as well as keep track of the status of the Geolocator sensor. One problem, though, is that the UI display needs to be updated as well, and so those events also need to reach the MainPage object. There are two options for accomplishing this:

  1. Have the MainPage object also register event handlers with the Geolocator object for the PositionChanged and StatusChanged events.
  2. Use delegates in the DataLogger object to call the appropriate methods in the MainPage class when PositionChanged and StatusChanged events arrive.

Both methods have their advantages and disadvantages. I went with second object because it limited the amount of redundant code, and also allowed me to pass additional information in the delegate that is not part of the PositionChanged event.

The callbacks are defined in the DataLogger class:


public delegate void Position_Changed_Delegate (Geocoordinate c, Boolean logged);
public delegate void Status_Changed_Delegate (PositionStatus s);
 
public class DataLogger
{
       Position_Changed_Delegate position_delegate;
       Status_Changed_Delegate status_delegate;
 
       …
 
       public void SetCallbackPositionChanged (Position_Changed_Delegate p)
       {
              position_delegate= p;
       }
 
       public void SetCallbackStatusChanged(Status_Changed_Delegate s)
       {
              status_delegate = s;
       }
 
       …
}

And registered in MainPage when that object is initialized:


logger.SetCallbackStatusChanged(update_status);
logger.SetCallbackPositionChanged(update_position);

The extra information passed in the Position_Changed_Delegate is whether or not the last received trackpoint was logged by the DataLogger object. This allows me to update the UI display with not only the device’s current position, but also with the number of data points that have been logged to one of our data files (and, as we’ll see later on, whether or not to add it to the visible breadcrumb trail in the map view). This would be difficult to accomplish if the MainPage object registered a PositionChanged event directly as it would need to then query the DataLogger object to get this extra information. This could potentially present a race condition if two PositionChanged events arrived in rapid succession.

← Part 2: User Interface

Read more >

Location Data Logger Design and Implementation, Part 2: User Interface

This is part 2 of a series of blog posts on the design and implementation of the location-aware Windows Store app “Location Data Logger”. Download the source code to Location Data Logger here.

The Main Page

Location Data Logger is a fairly simple application, so a single page is sufficient for its user interface. The operational controls such as the start/stop button and precision filter are placed in a sidebar for fast and easy access. The main content area holds either the map display or the grid display, and the user can toggle between them. This approach allows the content view to expand to fill the available screen space without having to know the display resolution. Finally, a lower app bar allows the user to set configuration items.

The screenshot, below, shows how the page is divided up. The top row is from Microsoft’s default template which leaves the first 140 pixels clear of main content. It is below that where the layout gets more interesting. The lower grid consists of two columns: a 320 pixel sidebar, and the main content area.

The width of the sidebar was not chosen arbitrarily. When a Windows Store app is running in a snapped view, it is assigned 320 pixels on the screen, no matter which side it was placed on. Location Data Logger is designed to fit cleanly in the snapped view by placing its primary operational controls and status information in this sidebar. Thus, the user still has complete control over the app’s operation, as well as a useful feedback on its progress.

The XAML for the grid layout is:

<Grid Style="{StaticResource LayoutRootStyle}">

       <Grid.RowDefinitions>
              <RowDefinition Height="140"/>
              <RowDefinition Height="*"/>
       </Grid.RowDefinitions>
       <Grid Grid.Row="1" Margin="0,0,0,0">
              <Grid.ColumnDefinitions>
                     <ColumnDefinition Width="320"/>
                     <ColumnDefinition Width="*" />
              </Grid.ColumnDefinitions>
       </Grid>
</Grid>

Read more >

Location Data Logger Design and Implementation, Part 1: Application Design

This is part 1 of a series of blog posts on the design and implementation of the location-aware Windows Store app “Location Data Logger”. Download the source code to Location Data Logger here.

What is a Data Logger?

Before descending into the details of the development of Location Data Logger, I want to spend some time on what, exactly, a data logger application is and why it’s useful.

The concept originates with GPS receivers, whether they be consumer, commercial, or military grade, and the idea that you can record a log of your position and movement over time. Consumer receivers refer to this as a track log, which consists of a series of track points that records the device’s position (including altitude), heading, speed, and the time at some pre-defined interval. Depending on the receiver other information might be logged as well, such as data from supplemental sensors like air pressure and compass heading. The result is a precise position log that can be reviewed, analyzed, and processed at a later time.

A hiker, for example, can save a track of his or her hike and then review it at home to determine the total mileage walked, total elevation gain, as well as plot the hike on a map. That track log can be exported to a data file and shared with others so that future hikers might benefit from the information. Such track logs function as rudimentary trail maps, giving other potential hikers valuable information about the location of trailheads and the route of the trails themselves, which is very useful when local maps are either incomplete or of questionable quality. At the commercial level, track logs are regularly used to ground-truth trails, roads, and other geographic features that are difficult to spot on or trace from aerial imagery and thus may not be accurately mapped.

Other uses include creating logs of vehicle travel, such as by delivery and transportation companies to monitor the efficiency of routing, and by insurance companies to monitor driver habits (with the driver’s permission, of course). Track logs are immensely useful, and dedicated GPS receivers with small form factors and minimal user interfaces, known as data loggers, are readily available in the market.

Location Data Logger turns your Windows 8 device into a data logger.

Requirements for the Location Data Logger app

Given the above, the list of requirements for a data logger app is mercifully short and simple. At minimum, the app must:

  1. Track position
  2. Start and stop logging
  3. Record position logs to a data file

As I said, that is a very short list. Practically speaking, however, the app needs to offer a bit more than this to provide a decent user experience, so I’ll expand it to:

  1. Track position
  2. Start and stop logging
  3. Record position logs to one or more commonly-accepted data file formats
  4. Per-session logging
  5. Stay active while logging
  6. Filter by estimated accuracy

The first two should be fairly obvious, but the last four probably require some explanation.

Recording to common-accepted file formats

A key function of a data logger is the ability to export and share the track logs, and that means choosing a file format that is accepted by other applications. In the GPS world, there are several standards for exchanging data points and Location Data Logger supports the three that are arguably the most common and most useful. They are:

  • CSV. The comma-separated value format is versatile because it does not have a fixed schema. Data is simply written out into a flat file that resembles a table, with one row of comma-separated values per data line.
  • GPX. The GPS Exchange format is an XML-based data format designed specifically for interchanging GPS data. It is an open standard first released in 2002 and has undergone some minor revisions since that time. GPX is widely supported by GIS systems and data converters.
  • KML. The Keyhole Markup Language is a newer XML-based format and also an open standard. Unlike GPX, KML files are not limited to just GPS logging, and can be used to describe arbitrary geographic elements such as points of interest/waypoints, lines, polygons, networks, overlays, and more, with styling information for each. Entire maps can be defined using just KML elements.

These three file formats are almost universally supported by Geographic Information Systems, mapping applications, and GPS data converters. In Location Data Logger, the user can choose to log to one or more of these three formats simultaneously, allowing greatest flexibility in sharing their track logs.

Per-session logging

This feature is a matter of log file hygiene. When the user starts the data logger, a new log file is created. When they stop the logger, the log file is closed. This allows users to separate their track logs into different files. Location Data Logger goes a step further in automatically naming log files by date and time so that the user does not have to be prompted, and it eliminates log file collisions (in the event that the user starts and stops the logger rapidly, unique filenames are generated to guarantee this).

Staying active while logging

It would be undesirable for the logger to be interrupted in middle of an active session since the point of running a data logger is to log every data point. This means keeping Windows from going to sleep, suspending the app, or taking any other action might cause the app to stop running. Of course, this means that the data logger will consumer quite a bit of power, but that’s the nature of this sort of application: you trade power savings for necessary functionality.

Filtering by estimated accuracy

The geolocation sensor in Windows 8 is not a single sensor but rather a collection of location inputs from multiple sources. The device’s position can come from an IP address, Wi-Fi triangulation, or  a high-precisions source such as a Global Navigation Satellite System (e.g., GPS). More than likely, the user is most interested in position data coming from the latter, but rather than force this on them Location Data Logger gives the user a choice: one can filter out “low precision” data, meaning position reports that are not regularly coming from a high-precision source such as GPS. This filtering will be discussed in a future installment of this series.

Additional features

In addition to the base requirements, I added two features to Location Data Logger to improve the overall user experience.

Map view

Location Data Logger displays the user’s current position on a map provided by the Bing mapping service along with an accuracy circle calculated from the estimated horizontal accuracy. While the online map does require an active internet connection to update, it is not a necessary component and the data logger will function without it (albeit without live maps). This gives the user something to look at and review during operation.

Data view

The user can bring up a tabular view of the track points logged to the current log files. This allows a review of the track points directly and in a user-friendly manner, without having to open up the log files in a separate text viewer.

Next: User interface design

In Part 2, I’ll discuss the design of the user interface.

← Introduction Part 2: User Interface →

Read more >

Location Data Logger Design and Implementation: Introduction

Today I am beginning a multi-part blog series on the design and development of a location-based Windows* Store app. My goal is to provide developers with a complete, real-world example of creating a location-aware application on the ultrabook and tablet platforms. While an internet search will turn up several examples of how to use the geolocation sensor within a Windows Store app, they tend to be either simple code snippets with little to no discussion on how to integrate them into a larger or more complex app, or narrowly-focused examples that provide only rudimentary functionality.

The application and how to get it

The application I created, and which I’ll be reviewing in this series, is called Location Data Logger. It creates position track logs from your Geolocator sensor and saves them out as CSV, GPX, and/or KML files. It turns your Windows 8 device into a position data logger, and is a very useful utility for recording your position over time. Track logs are used for everything from records of recreational travel to ground-truthing data in geographic databases. With a track log application you can save a log of a run, hike, bicycle ride, car trip, or static position measurements, and review it at a later date. You can plot the track log on a map, import the data into a Geographic Information System for analysis, or just share your data with others.

I chose this application because it is complex enough to not be trivial, but simple enough to not be overly-complex. In short, it demonstrates how one can integrate geolocation capability into a fully-functioning Windows Store app without being a merely academic exercise, and it is small enough that it can be easily reviewed and discussed.

Location Data Logger is written in C# and XAML, and the source code can be downloaded here on Developer Zone. You’ll need the following in order to build and run the app:

This app works best on systems that have an integrated GPS or GNSS receiver. If your system does not have an integrated GPS or GNSS, see the blog series “No GPS? No Problem! Using GPSDirect to develop location-aware apps” for information on using an external GPS as a Windows Geolocation sensor.

In part 1, I’ll start by describing the application requirements.

Part 1: Application Design →

Read more >

Develop location-aware, Metro style apps when your development system doesn’t have GPS

The Windows Sensor and Location platform, first introduced by Microsoft back in Windows 7, allows applications to obtain information about, and react to, the hardware’s physical environment. One of the many sensor types that are supported are those that provide location information, including GPS receivers. While integrated GPS receivers are common in small form factor [...] Read more >