Share via


Exercise 3: Using the Location Device and Bing Maps

In this exercise you will:

  • Use the WP7 location device (GPS) to track and find your current location and display it on the map
  • Resolve your current location and street address
  • Display captured pictures on the exact location on the map
    Note:
    This exercise requires a valid Bing Maps Application ID to access Bing Maps Services and the Bing Map Silverlight control for Windows Phone. Please refer to the Using Bing Maps lab to learn how to use the Bing Map control, access Bing Map Services and get Bing Maps Application ID. Before you begin, please open the App.xaml.cs file and set the BingId field with a valid application ID.

Task 1 – Integrating GPS with Bing Maps

In this task you’ll learn how to access the WP7 location device to track your current location and display it on the Bing Map.

To create an application which can be easily tested on the WP7 emulator you’ll use a GPS helper provided by this lab that works with both the GPS Emulator provided with this lab and the WP7 GPS device.

Before you begin, please open the Source\Ex3 – UsingLocationDevice\Begin.sln solution file, and review the code. The project is comprised of Models, Pages, Resources, the Application class, and the assets library.

  1. Add the GpsHelper.cs file located in <lab-assets-directory>\Source to the project’s Common folder and review it.
  2. The GpsHelper class is a singleton object that abstracts the location device by exposing a property of type IGeoPositionWatcher named Watcher. This property is initialized by calling the GpsHelper.WarmUp static method or by accessing the singleton instance. Depending on whether the GPS_EMULATOR symbol is defined or not, the GpsHelper selects the WP7 GPS or the GPS Emulator Client. Both implement the IGeoPositionWatcher interface which is an abstraction of the WP7 location device.
  3. You’ll start by testing the phone application using the WP7 emulator and the GPS emulator provided with this lab. Make sure that the GpsHelper.cs file you’ve added contains #defineGPS_EMULATOR as its first line. This tells the GpsHelper class to work with the GPS Emulator Client and not the real device.
  4. To warm-up the GPS device, call the GpsHelper.WarmUp static method from both the Application_Launching and Application_Activated event handlers located in the App.xaml.cs file.

    Note:
    The reason for calling the WarmUp method from both the activated and launching event handlers is that the launching event handler is called only for fresh application instance and the activated event handler is called when returning back from tombstoning. Either way, the ctor is not a good place to put such a call.

    C#

    private void Application_Launching(object sender, LaunchingEventArgs e) { GpsHelper.WarmUp(); } private void Application_Activated(object sender, ActivatedEventArgs e) { GpsHelper.WarmUp(); }

  5. To display your location on the map, first you should add some properties to the MapModel class located in the Models folder. The MapModel class holds the MapPage data and exposes it for Data Binding. To the MapModel class add a new property called MyLocation of the type GeoCoordinate and add the DataMember attribute for tombstoning. This property will be used to bind the map to the location provided by the location device.

    C#

    [DataMember] public GeoCoordinate MyLocation { get; set; }

  6. Implement the property getter by calling the base.GetValue method that is defined in the NotifyingObject base class; implement the setter by calling the base.SetValue method. This method which is defined in the NotifyingObject base class notifies when a property is changed.

    C#

    [DataMember] public GeoCoordinate MyLocation { get { return GetValue(() => MyLocation); } set { SetValue(() => MyLocation, value); } }

  7. To keep tracking your location on the map, add a new property called TrackLocation of the type bool, add the DataMember attribute for tombstoning, and implement both the setter and the getter in order to use the SetValue/GetValue methodsrespectively.

    C#

    [DataMember] public bool TrackLocation { get { return GetValue(() => TrackLocation); } set { SetValue(() => TrackLocation, value); } }

  8. Update the MyLocation property setter to set the Center property with the new location when TrackLocation is true. The Center property centers the map to the specified location.

    C#

    [DataMember] public GeoCoordinate MyLocation { get { return GetValue(() => MyLocation); } set { SetValue(() => MyLocation, value); if (TrackLocation) { Center = value; } } }

  9. To display your location on the map and synchronize it with the location device, open MapPage.xaml.cs, override both OnNavigatedTo and OnNavigatedFrom, and register/unregister the GpsHelper.Instance.Watcher.PositionChanged respectively.

    C#

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { GpsHelper.Instance.Watcher.PositionChanged += Watcher_PositionChanged; base.OnNavigatedTo(e); } protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e) { GpsHelper.Instance.Watcher.PositionChanged -= Watcher_PositionChanged; base.OnNavigatedFrom(e); } private void Watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e) { }

  10. Implement the event handler to get the position from the event argument, and update the model’s MyLocation property.

    C#

    private void Watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e) { var position = e.Position.Location; if (position != GeoCoordinate.Unknown) { Model.MyLocation = e.Position.Location; } }

  11. To start the map with a default location while loading, localize the InitializeLocation method and add the following highlighted lines.

    C#

    private void InitializeLocation() { var defaultCoordinate = new GeoCoordinate(47.639631, -122.127713); Model.MyLocation = defaultCoordinate; Model.Center = defaultCoordinate; }

  12. To center the map on the current location, locate the ApplicationBarMyLocation_Click event handler and set the model’s Center property to the model’s MyLocation.

    C#

    private void ApplicationBarMyLocation_Click(object sender, EventArgs e) { Model.Center = Model.MyLocation; }

  13. To keep tracking your location, locate the ApplicationBarTrackLocation_Click and inverse the Boolean value of the model’s TrackLocation property.

    C#

    private void ApplicationBarTrackLocation_Click(object sender, EventArgs e) { Model.TrackLocation = !Model.TrackLocation; }

  14. Open MapPage.xaml and add a new MapLayer with one Pushpin to the map control. This pushpin is used to display your current location on the map.

    XAML

    <maps:MapLayer> <maps:Pushpin /> </maps:MapLayer>

  15. Bind the Pushpin.Location to the model’s MyLocation property (the model is already set with the page’s DataContext).

    XAML

    <maps:MapLayer> <maps:Pushpin Location="{Binding MyLocation}" /> </maps:MapLayer>

  16. Set the Pushpin.Style to the static resource keyed MyLocationPushpinStyle.

    XAML

    <maps:MapLayer> <maps:Pushpin Style="{StaticResource MyLocationPushpinStyle}" Location="{Binding MyLocation}" /> </maps:MapLayer>

  17. Compile and run. To test the results, open the GPS Emulator and then:
  18. Go to the map by clicking the map app-bar icon.You should see a blinking red point; if not, click the target app bar icon to center the map. This is the location provided by the emulator
  19. Change the position in the emulator and you should see the change reflected on the phone’s map
  20. To center the map on your location, click the target icon
  21. Create a route using the GPS Emulator
  22. Click “Start”
  23. Click the route app-bar iconYou should see the map tracking your location

    Figure 4

    The GPS Emulator

    Figure 5

    Location on Map

    Figure 6

    GPS Emulator Route

Task 2 – Assigning Pictures with Geographical Locations

In this task you will assign each captured picture with the exact geographical location provided by the location device and resolve the street address using the Bing Maps Service.

  1. To set each picture with its geographical location, open the CapturedPicturePage.xaml.cs and create a new private method called ResolvePictureAddress, which has one parameter of the type CapturedPicture.

    C#

    private void ResolvePictureAddress(CapturedPicture picture) { }

  2. In the ResolvePictureAddress method use the GpsHelper to check if the GPS device is ready, and if so update the CapturedPicture position with the current location. If the GPS device is not ready, set the position to GeoCoordinate.Unknown.

    C#

    private void ResolvePictureAddress(CapturedPicture picture) { if (GpsHelper.Instance.Watcher.Status == GeoPositionStatus.Ready) { picture.Position = GpsHelper.Instance.Watcher.Position.Location; } else { picture.Position = GeoCoordinate.Unknown; } }

  3. Now that the picture has a location, use the GeocodeHelper to resolve the street address by calling the GeocodeHelper.ReverseGeocodeAddress static method.

    C#

    private void ResolvePictureAddress(CapturedPicture picture) { if (GpsHelper.Instance.Watcher.Status == GeoPositionStatus.Ready) { picture.Position = GpsHelper.Instance.Watcher.Position.Location; GeocodeHelper.ReverseGeocodeAddress( Dispatcher, _credentialsProvider, picture.Position, result => picture.Address = result.Address.FormattedAddress); } else { picture.Position = GeoCoordinate.Unknown; } }

  4. Locate the OnNavigatedTo override. In this method, confirm that the Model is not null and call the ResolvePictureAddress.

    C#

    protected override void OnNavigatedTo(NavigationEventArgs e) { var model = Model; if (model != null) { ResolvePictureAddress(model); } base.OnNavigatedTo(e); }

  5. To display the picture location and address, open the CapturedPicturePage.xaml and add two TextBlocks to the StackPanel, right after the Image control.

    XAML

    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <StackPanel> <Image Grid.Column="1" Grid.Row="1" Stretch="Uniform" Source="{Binding Source}" HorizontalAlignment="Left" Width="178" Margin="16" /> <TextBlock Margin="19,5,19,5" Opacity="0.5" /> <TextBlock Margin="19,5,19,5" Opacity="0.5"/> <TextBlock Text="{Binding DateTaken}" Margin="19,5,19,5" Opacity="0.5"/> <TextBox AcceptsReturn="True" TextWrapping="Wrap" Text="{Binding Note, Mode=TwoWay}" Height="110" Margin="8,5,8,5" /> </StackPanel> </Grid>

  6. Bind the first TextBlock to the picture’s Position property.

    XAML

    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <StackPanel> <Image Grid.Column="1" Grid.Row="1" Stretch="Uniform" Source="{Binding Source}" HorizontalAlignment="Left" Width="178" Margin="16" /> <TextBlock Text="{Binding Position}" Margin="19,5,19,5" Opacity="0.5" /> <TextBlock Text="{Binding Address}" Margin="19,5,19,5" Opacity="0.5"/> <TextBlock Text="{Binding DateTaken}" Margin="19,5,19,5" Opacity="0.5"/> <TextBox AcceptsReturn="True" TextWrapping="Wrap" Text="{Binding Note, Mode=TwoWay}" Height="110" Margin="8,5,8,5" /> </StackPanel> </Grid>

  7. Bind the second TextBlock to the picture’s Address property.

    XAML

    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <StackPanel> <Image Grid.Column="1" Grid.Row="1" Stretch="Uniform" Source="{Binding Source}" HorizontalAlignment="Left" Width="178" Margin="16" /> <TextBlock Text="{Binding Position}" Margin="19,5,19,5" Opacity="0.5" /> <TextBlock Text="{Binding Address}" Margin="19,5,19,5" Opacity="0.5"/> <TextBlock Text="{Binding DateTaken}" Margin="19,5,19,5" Opacity="0.5"/> <TextBox AcceptsReturn="True" TextWrapping="Wrap" Text="{Binding Note, Mode=TwoWay}" Height="110" Margin="8,5,8,5" /> </StackPanel> </Grid>

  8. To test the results, make sure that you’re connected to the Internet, run the GPS Emulator as Administrator, then build and run the project. Capture a picture. You should see the geographical location initialized by the GPS Emulator and the street address provided by Bing Maps Services.

    Figure 7

    Picture with Location and Address

Task 3 – Displaying Captured Pictures on a Bing Map

In this task, you’ll create a picture layer on the Bing Map to display each captured picture at the exact geographical location of capture.

  1. To display pictures on the map, first you should add a collection of relevant pictures that has the valid location for the MapModel class. Open MapModel.cs and add a new read-only field of the type IList<Picture> named pictures.

    C#

    [DataContract] public class MapModel : NotifyingObject { ... private IList<Picture> _pictures; ... }

  2. Create a public get-only property of type IEnumerable<Picture> named Pictures that initializes the pictures field and populates it with pictures that have valid geographical locations (not equal to GeoCoordinate.Unknown), and then returns it.

    C#

    [DataContract] public class MapModel : NotifyingObject { ... private IList<Picture> _pictures; public IEnumerable<Picture> Pictures { get { if (_pictures == null) { var pictures = from picture in PictureRepository.Instance.Pictures where picture.Position != GeoCoordinate.Unknown select picture; _pictures = pictures.ToArray(); } return _pictures; } } ... }

  3. To add a new pictures layer to the map, open MapPage.xaml and add a new map layer of type MapItemsControl just before the my-location layer. MapItemsControl supports collection binding.

    XAML

    <maps:Map ...> <maps:MapItemsControl x:Name="picturesLayer"> </maps:MapItemsControl> </maps:Map>

  4. Register to the MapItemsControl.MouseLeftButtonUp event with the picturesLayer_MouseLeftButtonUp event handler already located in the MapPage. This centers the relevant picture on first touch and scales it on second touch.

    XAML

    <maps:MapItemsControl x:Name="picturesLayer" MouseLeftButtonUp="picturesLayer_MouseLeftButtonUp"> </maps:MapItemsControl>

  5. Bind MapItemsControl.ItemsSource to the model’s Pictures property.

    XAML

    <maps:MapItemsControl x:Name="picturesLayer" ItemsSource="{Binding Pictures}" MouseLeftButtonUp="picturesLayer_MouseLeftButtonUp"> </maps:MapItemsControl>

  6. Create an item template for displaying each picture as Pushpin contains one child of type Image.

    XAML

    <maps:MapItemsControl x:Name="picturesLayer" ItemsSource="{Binding Pictures}" MouseLeftButtonUp="picturesLayer_MouseLeftButtonUp"> <maps:MapItemsControl.ItemTemplate> <DataTemplate> <maps:Pushpin> <Image Width="64" /> </maps:Pushpin> </DataTemplate> </maps:MapItemsControl.ItemTemplate> </maps:MapItemsControl>

  7. Bind the Pushpin.Location to the picture’s Position property.

    XAML

    <maps:MapItemsControl x:Name="picturesLayer" ItemsSource="{Binding Pictures}" MouseLeftButtonUp="picturesLayer_MouseLeftButtonUp"> <maps:MapItemsControl.ItemTemplate> <DataTemplate> <maps:Pushpin Location="{Binding Position}"> <Image Width="64" /> </maps:Pushpin> </DataTemplate> </maps:MapItemsControl.ItemTemplate> </maps:MapItemsControl>

  8. Bind the Image.Source property to the picture’s Source property.

    XAML

    <maps:MapItemsControl x:Name="picturesLayer" ItemsSource="{Binding Pictures}" MouseLeftButtonUp="picturesLayer_MouseLeftButtonUp"> <maps:MapItemsControl.ItemTemplate> <DataTemplate> <maps:Pushpin Location="{Binding Position}"> <Image Width="64" Source="{Binding Source}" /> </maps:Pushpin> </DataTemplate> </maps:MapItemsControl.ItemTemplate> </maps:MapItemsControl>

  9. To test the results, make sure that you’re connected to the Internet, run the GPS Emulator as Administrator, then build and run the project. Capture a picture, save it to local store, and go to the map and center the map to the current location. You should see your pictures at the exact geographical location on the map.

    Figure 8

    My pictures on Bing Map

    This concludes the exercise and the lab.

    Note:
    You can find the complete solution for this exercise at Source\Ex3 - UsingLocationDevice\End.