Windows Support Number

  • Subscribe to our RSS feed.
  • Twitter
  • StumbleUpon
  • Reddit
  • Facebook
  • Digg

Friday, 22 July 2011

WP7Contrib: Bing Maps REST Services Wrapper - Deep Dive

Posted on 13:28 by Unknown
Following on from Rich's post introducing the Bing Maps Service in the WP7Contrib I'm going to explain in more detail how we built this service and how to use the API.

Before I get into the details let’s get some background on what is provided by Microsoft out of the box.  Microsoft provides multiple APIs for programmatically integrating maps and map data into your WP7 applications as well as the UI based controls included in the Microsoft.Phone.Controls.Maps namespace. The APIs are designed to used in conjunction with the UI controls to annotate the visual elements of Bing Maps.

The Bing Maps has it's own area up on MSDN, here.


Microsoft supports both SOAP and REST APIs for accessing location, imagery, route and services information.  Each implementation has its own taxonomy and these are heavily influenced by the underlying protocol (as much as you can call REST a protocol).  The only advantage we can currently see with using the SOAP implementation over REST is the ease with which you can integrate this into your application.  In my opinion this is not a reason to use the SOAP implementation, although it may appear to be quicker and easier.
The important point to remember about using a SOAP based API (over HTTP) is the fact that SOAP is based around the HTTP POST method and is not utilising the power of the HTTP headers and caching when retrieving read-only data.  Since the Bing Maps are a read-only service, why would I want to use a protocol on top of HTTP that does not utilise the potentials of HTTP for caching out in the web?


After using both Bing Maps API implementations, we believe the REST version is cleaner, easier to understand and more powerful from a caching perspective.  After using this on several WP7 apps we found ourselves either repeating the same steps to use the REST API or copy-pasting the code project to project.  We wanted to get away from this - say hello to the WP7Contrib Bing Maps Service!

Simply put, the Bing Maps service is a client-side wrapper for the Microsoft Bing Maps REST API for use in WP7 applications. It abstracts the developer away from having to deal with REST or HTTP concerns.  It utilises Rx (reactive extensions) for exposing the request data as well as handling caching, timeouts and retry strategies.  We also provide a criteria factory to help create the required search criterion - the number of configurations a developer can use is mind boggling and the criteria factory is there to help reduce the information overload. The complexity of a criterion and the results can be seen from the number of classes required to support searching for locations, routes, imagery and services shown below:


The Bing Maps service does not expose these resource classes to developer, this is done via our model classes, see below. The resources are mapped into the model classes by the service - the reason for this distinction is that we see them as falling into two distinct areas, one fulfilling the communications side (resources) and the other supporting the application and UI side (models) via binding, equality and cloning.


The next thing to look at is the service interfaces and the methods exposed.  As I said earlier we use Rx to expose the results because all operations over HTTP are asynchronous and Rx is very well suited to handling this requirement.  The interfaces are separated into roles - an interface for location methods, an interface for route method etc.  These are then aggregated under a single interface called IBingMapsService which is implemented by the BingMapsService class.

public interface IBingLocationService

{

    IObservable SearchForLocationUsingAddress(ILocationSearchAddressCriterion criterion);

    IObservable SearchForLocationUsingPoint(ILocationSearchPointCriterion criterion);

    IObservable FindLocationUsingQuery(ILocationSearchQueryCriterion criterion);

}



public interface IBingRouteService

{

    IObservable CalculateARoute(IRouteSearchCriterion criterion);

    IObservable CalculateRoutesFromMajorRoads(IRouteSearchMajorRoadCriterion criterion);

}



public interface IBingImageryService

{

    Uri ImageryUrlForCenterPoint(IImageryUrlForCenterPointCriterion criterion);

    Uri ImageryUrlForCenterPointWithRoute(IImageryUrlForCenterPointWithRouteCriterion criterion);



    Uri ImageryUrlForQuery(IImageryUrlForQueryCriterion criterion);



    Uri ImageryUrlForSpecificArea(IImageryUrForSpecificAreaCriterion criterion);

    Uri ImageryUrlForSpecificAreaWithRoute(IImageryUrForSpecificAreaWithRouteCriterion criterion);



    Uri ImageryUrlForRoute(IImageryUrlForRouteCriterion criterion);

}



public interface IBingImageryMetadataService

{

    IObservable GetImageryMetadataForAnImagerySet(ImagerySearchCriterion criterion);

    IObservable GetImageryMetadataForAnImagerySetAtASpecificLocation(ImagerySearchCriterion criterion);

    IObservable GetImageryMetadataForABasicImagerySetAtASpecificLocation(ImagerySearchCriterion criterion);

}



public interface IBingSearchService

{

   IObservable SearchForServices(ISearchCriterion criterion);

}



public interface IBingMapsService : IBingLocationService,

           IBingRouteService,

           IBingSearchService,

           IBingImageryService,

           IBingImageryMetadataService

{

}


We also have a settings interface.  This is to allow the developer to setup the Bing Maps Service with the required config values.  This is passed to the constructor of the service and the service extracts the required values, thus preventing a constructor with 20 parameters!  The interface definition has read-only properties but the implementation class Settings allows the setting of properties as well.

public interface ISettings

{

    string AppId { get; }

    string CalculateRouteUrl { get; }

    string CalculateRoutesFromMajorRoadsUrl { get; }

    string CredentialsId { get; }

    string ImageryCenterPointUrl { get; }

    string ImageryCenterPointWithRouteUrl { get; }

    string ImageryMapAreaUrl { get; }

    string ImageryMapAreaWithRouteUrl { get; }

    string ImageryMapRouteUrl { get; }

    string ImageryQueryUrl { get; }

    string FindLocationUsingQueryUrl { get; }

    string SearchLocationUsingAddressUrl { get; }

    string SearchLocationUsingPointUrl { get; }

    string SearchUrl { get; }

    int CacheTimeout { get; }

    int Timeout { get; }

    int Retry { get; }

}




The last set of interfaces of interest is Criteria such as ILocationSearchAddressCriterion and IRouteSearchCriterion. You can see in the above code snippets these are used as parameters to the Bing Map service APIs.  They encapsulate all the parameters required for making the call to the backend service.  These interfaces are returned by the CriterionFactory class; this is a helper class and is designed to make creating a criterion easier.  The code below for the IRouteSearchCriterion inteface shows the number of properties that can be used for calculating a route.  The criterion factory makes creating these easier because it has overloaded parameter signatures for specific search requirements, e.g.  search by address only, route search using only way points, or route search using way points with mode of travel and route optimisation.  Shown below are some of the criterion interfaces.


public interface ILocationSearchAddressCriterion : ICloneable<ILocationSearchAddressCriterion>

{

    Address Address { get; }

    

    bool HasAddress { get; }

}



public interface ILocationSearchPointCriterion : ICloneable<ILocationSearchPointCriterion>

{

    GeoCoordinate Point { get; }

    ObservableCollection<LocationEntity> IncludedEntities { get; }



    bool HasPoint { get; }

    bool HasIncludedEntities { get; }

}



public interface IRouteSearchCriterion : ICloneable<IRouteSearchCriterion>

{

    Avoid Avoid { get; }

    ObservableCollection<WayPoint> WayPoints { get; }

    int? Heading { get; }

    Optimize Optimize { get; }

    RoutePathOutput PathOutput { get; }

    DistanceUnit DistanceUnit { get; }

    DateTime? DateTime { get; }

    TimeType TimeType { get; }

    int MaxSolutions { get; }

    ModeOfTravel TravelMode { get; }

    string PointOfInterest { get; }



    bool HasAvoid { get; }

    bool HasWayPoints { get; }

}



public interface IRouteSearchMajorRoadCriterion : ICloneable<IRouteSearchMajorRoadCriterion>

{

    RouteDestination Destination { get; }

    RoutePathOutput PathOutput { get; }

    RouteExclude Exclude { get; }

    DistanceUnit DistanceUnit { get; }



    bool HasDestination { get; }

}


The criterion factory also has simple validation rules to prevent calling the Bing Maps service with an invalid criterion, see screen shot below.




The following screen shots and code snippet bring all of the above together.  This is an example application to get the detailed address information for a post code (zip code).




This code is taken from the BingMapsLocationDemo in the Spikes directory of the WP7Contrib code base. If you use any of the Bing Map demos in the Spike directory you will have to register with Microsoft for a Bing Maps account - this can be done here.

See how simply the location can be determined for a post code - 4 lines of code!.

private void getAddress_Click(object sender, RoutedEventArgs e)

{

   var criterion = CriterionFactory.CreateLocationSearchForAddress(this.postCode.Text);

   

   this.bingMapsService.SearchForLocationUsingAddress(criterion)

      .ObserveOnDispatcher()

      .Subscribe(result =>

             {

               this.address.Text = result.Locations[0].Address.Locality;

               this.address.Text += Environment.NewLine;

               this.address.Text += result.Locations[0].Address.PostalCode;

               this.address.Text += Environment.NewLine;

               this.address.Text += result.Locations[0].Address.AdminDistrict;

               this.address.Text += Environment.NewLine;

               this.address.Text += result.Locations[0].Address.CountryRegion;

             });

}


The code below shows how the service and its dependencies are created.  The WP7Contrib is based around the idea of using an IoC container for dependencies and injecting all dependencies via constructors.  The example application does not use an IoC container but you can see how we pass the dependencies to the Bing Maps service.

private readonly ILog log = new DebugLog();

private readonly IBingMapsService bingMapsService = null;



// Constructor

public MainPage()

{

    InitializeComponent();

    

    this.log = new DebugLog();

    this.bingMapsService = new BingMapsService(new ResourceClientFactory(this.log),

            new UrlEncoder(),

            new Settings("MyApplicationId", "MyCredentialId", 10000, 5));

}


The last to area to cover is the implementation of the SearchForLocationUsingAddress() method inside the Bing Maps service.  This uses a standard approach to making a query to the backend service.  First of all we check to see if the search has already been executed and if so return the value from the cache;  if not we then instantiate the resource client and then construct the search parameters.  Finally we execute the search and persist the results into the cache.  You can also see the timeout and retry strategy.

public IObservable<LocationSearchResult> SearchForLocationUsingAddress(ILocationSearchAddressCriterion criterion)

{

    this.log.Write("BingMapsService: SearchForLocationUsingAddress...");



    try

    {

        var keyTuple = new CacheTuple<string, ILocationSearchAddressCriterion>

        {

            Val1 = "SearchForLocationUsingAddress",

            Val2 = criterion.DeepClone()

        };



        var locationResult = this.cacheProvider.Get<CacheTuple<string, ILocationSearchAddressCriterion>, LocationSearchResult>(keyTuple);

        if (locationResult != null)

        {

            this.log.Write("BingMapsService: SearchForLocationUsingAddress results retrieved from cache, hash code - {0}", criterion.GetHashCode());

            return Observable.Return(locationResult).AsObservable();

        }



        var resourceHandler = resourceHandlerFactory.Create()

            .ForType(ResourceType.Json)

            .UseUrlForGet(this.settings.SearchLocationUsingAddressUrl);



        var @params = new[]

            {

                // path parameters...



                // query string parameters...

                propertyEncoder.Encode(criterion.Address.CountryRegion),

                propertyEncoder.Encode(criterion.Address.AdminDistrict),

                propertyEncoder.Encode(criterion.Address.Locality),

                propertyEncoder.Encode(criterion.Address.PostalCode),

                propertyEncoder.Encode(criterion.Address.AddressLine),

                this.settings.CredentialsId

        };



        return resourceHandler.Get<Resources.Location.Result>(@params)

                                            .Timeout(this.timeout)

                                            .Retry(this.retry)

                                            .Select(response => {

                                                var locations = ProcessResponse(response);

                                                this.cacheProvider.Add(keyTuple, locations, this.cacheTimeout);

                                                return locations;

                                            });

    }

    catch (Exception exn)

    {

        var message = string.Format(FailedLocationSearch, exn.Message);

        this.log.Write(message);

        throw new ServiceException(message, exn);

    }

}


As I said before you can find the demo application BingMapsLocationDemo in the Spikes directory of the WP7Contrib code base.

Email ThisBlogThis!Share to XShare to FacebookShare to Pinterest
Posted in WP7 WP7Contrib CodePlex | No comments
Newer Post Older Post Home

0 comments:

Post a Comment

Subscribe to: Post Comments (Atom)

Popular Posts

  • Unit testing Rx methods Timeout & Retry with moq
    Earlier this week I was trying to unit test an asynchronous service (Foo) which used another asynchronous service (Bar) internally and ran i...
  • Understanding RefCount in Reactive Extensions
    A couple of weeks ago  @LordHanson  & I ran into an issue converting a stateless async service exposed as an Rx cold observable to a  co...
  • StructureMap: ILifecycle
    The other day I wanted to control the scope of a service inside a web based app with semantics which didn't fit either 'HttpContextS...
  • MVVM anti-pattern: Injecting the IoC container into a View Model
    This is another anti-pattern I've seen a lot recently, the dynamic use of the IoC container inside a view model to resolve child view mo...
  • How many pins can Bing Maps handle in a WP7 app - part 1
    part2 -  http://awkwardcoder.blogspot.com/2011/10/how-many-pins-can-bing-maps-handle-in.html part3 -  http://awkwardcoder.blogspot.com/2011/...
  • Bad developers love 'The Daily WTF'
    When 'The Daily WTF' started up back in 2003/2004 it was a great laugh looking at shocking code other developers wrote, but after a ...
  • Using CompositeDisposable in base classes
    To help make an object eligible for collection by the GC (garbage collector) one would implement the IDisposable interface. Executing the di...
  • Implementing a busy indicator using a visual overlay in MVVM
    This is a technique we use at work to lock the UI whilst some long running process is happening - preventing the user clicking on stuff whil...
  • Daily Dilbert Service - the most important service I've ever written...
    NuGet package available here ... First off a big shout to  @hamish  &  @leeoades  on this one - I'm just blogging about it. At work ...
  • Comparing performance of .Net 4.5 to .Net 4.0 for WPF
    Currently I'm working on a .Net 4.0 WPF app and we've had some discussion about moving to .Net 4.5, we don't get to make the dec...

Categories

  • .Net
  • .Net 4.5
  • Abstractions
  • Advertising
  • Agile
  • Agile Courage
  • AOP
  • Async
  • automated testing
  • Azure
  • Azure IIS RESTful development
  • BDD
  • Bing Maps
  • Bounded Context
  • C#
  • C# 5.0
  • Caching
  • Chocolatey
  • CLoud
  • CodePlex
  • Coding
  • Coding Building CI Testing
  • Coding C#
  • coding C# IoC StructureMap
  • Coding Functional-Programming
  • Coding REST Knowledge
  • Coding Services
  • Coding TDD Refactoring Agile
  • Command
  • continuous testing
  • coupling
  • CultureInfo
  • DAL
  • databases
  • DDD
  • DDD Coaching
  • DDD Domain Events Auditing nHibernate
  • DDD Entities Value Objects
  • Debugging
  • Design Patterns
  • Design Patterns Databases Auditing
  • Developement
  • Development
  • Development Coding
  • Development Process
  • Development unit testing
  • Development VS 2011
  • Diagnostics
  • Disposable
  • Exceptions
  • FINDaPAD
  • FindaPad Property Rental Windows Phone 7 Mobile Devices
  • Fun Coding Duct-Tape
  • Hotfixes
  • integration testing
  • IoC
  • jasmine
  • javascript
  • Jobs Development
  • LINQ
  • marketplace
  • Mobile Devices
  • Mocking
  • MSDN Coding
  • MSpec
  • Multilingual
  • MVC
  • MVVM
  • nCrunch
  • nHbiernate Repository Pattern Criteria
  • nHibernate Auditing Design Fluent
  • nHibnerate Entities Events Listeners
  • node.js
  • nodes.js
  • Nokia
  • NoSQL RavenDB Azure Development
  • Observations
  • OO
  • ORM
  • Performance
  • Portable Class Library
  • Portable Library
  • PostSharp
  • Process
  • Rants
  • RavenDB IIS 7.5 Development
  • Reactive
  • Reactive Extension
  • Reactive Extensions
  • ReadOnlyCollections
  • Resharper
  • REST Distributed-Systems
  • REST HTTP
  • rest web
  • RESTful
  • Rx
  • Serialization
  • Silverlight
  • Silverlight Installation
  • Task
  • TDD
  • TDD IoC DI
  • TDD Mocking
  • TDD Team Observation
  • Telerik
  • testing
  • threading
  • TPL
  • UI
  • Undo-Redo
  • unit testing
  • ViewModels
  • VS 2012
  • wcf
  • web api
  • Web Services
  • web services mobile devices data
  • WebAPI
  • Windows
  • Windows 8
  • windows phone
  • Windows Phone 7
  • WP7
  • WP7 Bing Maps Development Network HTTP
  • WP7 Bing Maps Development UK Crime
  • WP7 Bing Maps Development UK Crime Clustering
  • WP7 Bing Maps Development UK Polygons Clustering Performance
  • WP7 cryptography bouncy castle
  • WP7 Cultures C#
  • WP7 feedback development app store
  • WP7 Javascript web browser
  • WP7 MSBuild
  • WP7 ORM Databases performance
  • WP7 Serialisation
  • WP7 SilverlightSerializer C#
  • WP7 sqlite performance development
  • WP7 WP7Contrib Bing Maps Development
  • WP7 WP7Contrib Bing Maps Polygon Development
  • WP7 WP7Contrib CodePlex
  • WP7 WP7Contrib CodePlex Bing Maps Development
  • WP7 WP7Contrib CodePlex ObservableCollection
  • WP7 WP7Contrib ILMerge .Net
  • WP7 WP7Contrib Phone Maps
  • WP7 WP7Contrib SilverlightSerializer C#
  • WP7Contrib
  • WP7Contrib Bing Maps WP7
  • WP7Contrib WP7 Geo-Location development C#
  • WP7Contrib WP7 HTTP Compression
  • WP7Contrib WP7 Url Development Rx
  • WP7Dev
  • WPF
  • WPF Cultures
  • WuApi
  • XAML

Blog Archive

  • ►  2013 (16)
    • ►  November (5)
    • ►  September (3)
    • ►  August (1)
    • ►  July (1)
    • ►  June (3)
    • ►  May (2)
    • ►  January (1)
  • ►  2012 (44)
    • ►  November (2)
    • ►  October (8)
    • ►  September (5)
    • ►  August (2)
    • ►  July (4)
    • ►  June (3)
    • ►  May (1)
    • ►  April (2)
    • ►  March (13)
    • ►  February (4)
  • ▼  2011 (52)
    • ►  December (3)
    • ►  November (5)
    • ►  October (7)
    • ►  September (7)
    • ►  August (11)
    • ▼  July (4)
      • SilverlightSerializer MissingMethodException on WP7
      • WP7Contrib: Transient caching with In Memory Cache...
      • WP7Contrib: Bing Maps REST Services Wrapper - Deep...
      • WP7Contrib: Timing out HTTP requests on Windows Ph...
    • ►  May (2)
    • ►  April (1)
    • ►  March (5)
    • ►  February (3)
    • ►  January (4)
  • ►  2010 (1)
    • ►  August (1)
  • ►  2009 (32)
    • ►  December (3)
    • ►  November (7)
    • ►  October (6)
    • ►  September (11)
    • ►  April (1)
    • ►  March (4)
Powered by Blogger.

About Me

Unknown
View my complete profile