Augmented reality with GART - what's changed on Windows Phone 8

I wrote an article a year and a half ago about using GART with Windows Phone to create augmented reality scenarios in really easy steps. It's linked in the GART project documentation and it gets a lot of attention and even questions from people working on Windows Phone 8 apps. The article was written with Windows Phone 7 in mind, but conidering it's still being used as reference getting started implementation, I decided to write about what has changed.

The old article is here: http://igrali.com/2012/05/24/implementing-augmented-reality-using-gart/

The 6 easy implementation steps I mention are:

  • Add an ARDisplay control to your Windows Phone page
  • Add the views you want as children of the ARDisplay control
  • Start and stop services when navigating to and from the page
  • Create a collection of ARItem objects (or your own custom type that inherits from ARItem) and include real geolocation
  • Set ARDisplay.Items equal to your new collection
  • Optionally, style the elements

Before getting started, you need to add the necessary references to your project. Nothing's changed much regarding that, just go to

http://gart.codeplex.com/releases/view/108573

get the latest release and add it to your project reference.

Adding ARDisplay control

The XAML namespace is now different, you need to add:

xmlns:gart="clr-namespace:GART.Controls;assembly=GART.WP8"

The code behind for this demo needs the following namespaces added:

using GART;  
using GART.BaseControls;  
using GART.Controls;  
using GART.Data;

The control itself remains the same, it's called ARDisplay

<Grid x:Name="LayoutRoot">  
    <gart:ARDisplay Name="ardisplay" AttitudeRefreshRate="50" MovementThreshold="10">

    </gart:ARDisplay>
</Grid>

This time I'm using MovementThreshold property which is used for location services (default value 10), and AttitudeRefreshRate which defines time between updates used for reading attitude heading from compass (default value 500).

Add view layers to ARDisplay control

The layers are pretty much the same.

<gart:ARDisplay Name="ardisplay" AttitudeRefreshRate="50" MovementThreshold="10">  
    <gart:VideoPreview x:Name="videoPreview"/>
    <gart:OverheadMap x:Name="overheadMap" Credentials="{StaticResource Credentials}"/>
    <gart:WorldView x:Name="worldView" MinItemScale="0.1" MaxItemScale="1.0" FarClippingPlane="300.0" NearClippingPlane="1.0"/>
    <gart:HeadingIndicator x:Name="headingIndicator" Width="300" Height="300" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</gart:ARDisplay>

Define credentials in resources:

<gart:MapCredentials x:Key="Credentials" ApplicationId="Your AppId" AuthenticationToken="Your AppToken" />

Wondering where to find those? In your Windows Phone dashboard, when submitting an app or an update to app, go to:

augmented reality gart windows phone dashboard

And when you choose Map services, you get the keys...

credentials2

 

If you're wondering what to do if you don't/can't have the credentials - I think that for testing purposes, you can even omit those.

WorldView uses MinItemScale (default value 0.1) and MaxItemScale (default value 1.0), FarClippingPlane (default is 275 meters == 300 yards) and NearClippingPlane (default value 1.0). So, everything is pretty much as simple as before.

Start and stop services on navigating to and from the page

This part remained the same! In the code behind for the page, just do this:

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)  
{
    ardisplay.StartServices();
    base.OnNavigatedTo(e);
}

protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)  
{
    ardisplay.StopServices();
    base.OnNavigatedFrom(e);
}

Create a collection of ARItem objects or your own custom type inheriting from ARItem

This has also remained the same - we still use ARItem or our own types that inherit from the ARItem. Here's an example of such type:

public class CityPlace : ARItem  
{
    private string description;

    public string Description
    {
        get
        {
            return description;
        }
        set
        {
            if (description != value)
            {
                description = value;
                NotifyPropertyChanged(() => Description);
            }
        }
    }
}

Set ARDisplay.Items to your new collection

Binding remained the same, just do:

ardisplay.ARItems = locations; //locations is a collection of ARItems

Styling the elements

This is where I was having some issues. And just like John Lennon, 'I'm not the only one'. See here for more info: http://gart.codeplex.com/discussions/454455

When I try to use the itemtemplate like this one:

<DataTemplate x:Key="CityItemTemplate">  
    <Border BorderBrush="Black" BorderThickness="4" CornerRadius="8" Background="#FF003847" Width="320">
        <StackPanel Margin="4">
            <TextBlock x:Name="NameBlock" TextWrapping="NoWrap" Text="{Binding Content}" FontSize="38" VerticalAlignment="Center" Margin="0,0,4,0"  Grid.Column="1" TextTrimming="WordEllipsis"/>
            <TextBlock x:Name="DescriptionBlock" TextWrapping="Wrap" Text="{Binding Description}" FontSize="24" VerticalAlignment="Center" Margin="0,0,4,0" Grid.Column="1" TextTrimming="WordEllipsis" MaxHeight="168"/>
        </StackPanel>
    </Border>
</DataTemplate>

the items in world view flicker and disappear. Luckily, I figured out a solution. Set the Canvas.ZIndex on all the views in ARDisplay object, and a larger ZIndex on your template.

So for example, this can be your template:

<DataTemplate x:Key="CityItemTemplate">  
    <Border BorderBrush="Black" BorderThickness="4" CornerRadius="8" Background="#FF003847" Width="320" Canvas.ZIndex="2">
        <StackPanel Margin="4">
            <TextBlock x:Name="NameBlock" TextWrapping="NoWrap" Text="{Binding Content}" FontSize="38" VerticalAlignment="Center" Margin="0,0,4,0"  Grid.Column="1" TextTrimming="WordEllipsis"/>
            <TextBlock x:Name="DescriptionBlock" TextWrapping="Wrap" Text="{Binding Description}" FontSize="24" VerticalAlignment="Center" Margin="0,0,4,0" Grid.Column="1" TextTrimming="WordEllipsis" MaxHeight="168"/>
        </StackPanel>
    </Border>
</DataTemplate>

And then the items in ARDisplay need to have Canvas.ZIndex set like this:

<gart:ARDisplay Name="ardisplay" AttitudeRefreshRate="50" MovementThreshold="10">  
     <gart:VideoPreview x:Name="videoPreview" Canvas.ZIndex="1"/>
     <gart:OverheadMap x:Name="overheadMap" Canvas.ZIndex="1"/>
     <gart:WorldView x:Name="worldView" Canvas.ZIndex="1" ItemTemplate="{StaticResource CityItemTemplate}"  MinItemScale="0.1" MaxItemScale="1.0" FarClippingPlane="300.0" NearClippingPlane="1.0"/>
     <gart:HeadingIndicator x:Name="headingIndicator" Canvas.ZIndex="1" Width="300" Height="300" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</gart:ARDisplay>

And it works, just like in this video, which I recorded back when I wrote the original article.

http://www.youtube.com/watch?v=1mKlYz5wraY

comments powered by Disqus