Using Geospatial Data


This CTP of WCF Data Services adds support for geospatial data. The release allows use of all of the OData geospatial data types and the geo.distance() canonical function. This enables two key scenarios:

  • Read and write geospatial data (all types supported by Sql Server 2008 R2).
  • Find all entities (i.e. coffee shops) near a location.

Before I illustrate use of these features, I’d like to mention some limitations of this CTP. First, (and most significantly) WCF Data Services providers only support geospatial data with custom or reflection providers. You can’t use Entity Framework at this time. OData will support geospatial data over EF as soon as there is an EF release that supports geospatial data.

Second, this CTP does not allow null values in geospatial properties. Nulls will be added by RTM.

OK, enough on what it doesn’t do. Let’s interact with some geospatial data!

Adding Geospatial Data to the Model

https://gist.github.com/1293201 is a simple OData service which lets a user find people and businesses near them. I’ll describe the key geospatial parts here.

First, the entities each have a property of type GeometricPoint (one of the new geospatial types):

[DataServiceKey(“BusinessId”)]
public
class Business
{
public
int BusinessId { get; set; }

public
string Name { get; set; }

public
string Description { get; set; }

public GeographicPoint Location { get; set; }
}

[DataServiceKey(“Username”)]
public
class User
{

public User()
{

this.Friends = new List<User>();
}


public
string Username { get; set; }

public IList<User> Friends { get; set; }

public GeographicPoint LastKnownLocation { get; set; }
}

To create sample data values for geospatial types, I use the GeographyFactory (the data creation API is likely to change before the RTM, but this correct for now):

new
User { Username = “Chai”, LastKnownLocation = GeographyFactory.Point(47.7035614013672, -122.329437255859) }

Finally, geospatial data is only supported in V3 of the OData protocol:

public
static
void InitializeService(DataServiceConfiguration config)
{
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
}

That’s it. Geospatial data values are just primitive data values like any other. It requires as little effort to use them as it does to use a DateTime.

Writing the client

Just use Add Service Reference to codegen a client. Consuming geospatial data is no different than consuming any other V3 OData service.

Reading and writing geospatial values

There’s nothing special about geospatial values. For example, to update your last known location, you would query for your User entity, set the value of its LastKnownLocation, and call SaveChanges().

Enabling geo.distance queries

This sample service is interesting because it allows users to find nearby friends and businesses. We want to write queries that filter or orderby geo.distance().

Unfortunately, the October CTP does not include an in-memory implementation for distance. Computing distance on a round earth is complicated. You’ll need to find a good implementation for this operation (Sql Server has such an implementation). Once you have it, you can use the following glue code to hook it up.

public
static
void InitializeService(DataServiceConfiguration config)
{

// …

// Register my operations

SpatialOperations.Register(2.0, new MyOperations());
}

internal
class MyOperations : SpatialOperations
{

public
override
double Distance(Geometry operand1, Geometry operand2)
{

// TODO: Put your code here.

throw
new
NotImplementedException();
}


public
override
double Distance(Geography operand1, Geography operand2)
{

// TODO: Put your code here.

throw
new NotImplementedException();
}
}

Making a distance query

Now that you’ve got a service that supports geo.distance(), we want to query it. Here are a couple of queries we can run:

var localStuff = new
LocalStuff(new
Uri(“http://localhost/LocalStuff.svc”, UriKind.Absolute));

var me = localStuff.Users.First(u => u.Username == “Chang”);

var myNearbyFriends = me.Friends
.Where(friend => friend.LastKnownLocation.Distance(me.LastKnownLocation) < 1000.0);
var moviesNearMe = localStuff.Businesses
.Where(b => b.Description.Contains(“movie”))
.OrderBy(b => b.Location.Distance(me.LastKnownLocation))
.Take(3);

Have fun with the new geospatial data features. Please provide any feedback on the OData.org mailing list.

Comments (3)

  1. Matt says:

    Nice! Now when will Geo be available in EF???

  2. Pawel says:

    @Matt

    EF June CTP contained Geo. Here is a link to a walkthrough:

    blogs.msdn.com/…/walkthrough-spatial-june-ctp.aspx

  3. Quick update for those using the 5.0 RTM API:

    SpatialOperations.Register was removed before RTM due to it being overly complex for what was needed. It was replaced by:

       SpatialImplementation.CurrentImplementation.Operations = <my operations instance>;