Geocoding and fetching maps with VirtualEarthKit
Posted by Colin
Today we released a framework for interacting with Microsoft’s Virtual Earth Web Services called VirtualEarthKit. I thought I’d put together a little tutorial on using this framework.
To get started you’ll need:
• A Mac running Mac OS 10.5 (this is Objective C 2.0 so Mac OS 10.4 is not supported)
• Phillipe Casgrain’s CoreLocation framework for Mac OS X (included with VirtualEarthKit, if you’re on the iPhone you don’t need this)
• A Virtual Earth user id and password
• VirtualEarthKit
This tutorial expects that you know your way around Cocoa a bit, but most of the VirtualEarthKit classes are analogous to classes in the official Windows Communication Foundation API.
To get VirtualEarthKit, go to Terminal and type the following command:
svn co http://svn.consonancesw.com/vekit/trunk VirtualEarthKit
This will download the current version of VirtualEarthKit into the working directory in Terminal. We do plan to make a binary version available in the near future, but for now this is the quickest way to acquire VirtualEarthKit.
(Note: VirtualEarthKit works on the iPhone, but this tutorial is targeted for Mac OS X.)
Before you create a project, you should compile VirtualEarthKit via the included XCode project, and install both it and CoreLocation.framework into a frameworks directory of your choice (in Library/Frameworks at either the root of your drive or the home folder, for those of you new to Mac programming, this is where OS X looks for frameworks that your program may depend on.)
Next, create a new project in XCode. You can either create a command line utility, or a gui application. We just need to get to a place where we can execute code at some point. The example included at the end of this post just is a GUI app with some code included in an awakeFromNib function, but feel free to improve upon that.
Wherever it is you’ve decided to put this code, you’ll need to import the VirtualEarthKit header.
#import <VirtualEarthKit/VirtualEarthKit.h>
The first thing we have to do is acquire a Virtual Earth token. This is a magical string that you attach to every request you make to Virtual Earth. It lets Microsoft keep track of what resources of theirs you are using, and may impact your billing. To get a token, we need to first create a VECommonService object. The common service is just a representation for the general functions of Virtual Earth. While we’re at it we’ll declare the NSString to hold the token.
VECommonService *commonService = [[VECommonService alloc] init];
NSString *token;
Now to actually fetch the token… You’ll need your Virtual Earth user id (not your user name) and your password. Virtual Earth wants us also to supply the client IP address. As far as I can tell, this is mostly useful for servers who may be vending requests coming from different ips. For desktop apps, it may be a little more difficult to determine the outside ip of the machine. In talking with Microsoft I have found that the IP is simply for record keeping purposes for you. Don’t worry, you can supply a fake ip here and the world won’t explode. If you want to track usage by IP, you can ping off of whatismyip.com or something.
[commonService getToken:&token forUserID:@"yourUserID" password:@"yourPassword" ipAddress:@"192.168.0.1"];
See? Not at all painful. In the event of an error, getToken:… will return an NSError, but we’re going to be bad and assume everything went fine.
Now we’re going to perform a geocode operation. A geocode consists of giving Virtual Earth a place name, and it gives us back a coordinate. Virtual Earth also can do the reverse, find a place name for a coordinate (called a reverse geocode) but we won’t be doing that today…
To talk to Virtual Earth’s geocode service, we need to create a VEGeocodeService object.
VEGeocodeService *geocodeService = [[VEGeocodeService alloc] init];
Again, not too painful.
Now we need to create the actual request to send to the geocode service. This is represented by another object.
VEGeocodeRequest *geocodeRequest = [[VEGeocodeRequest alloc] init];
We need to set the place we want to find. For this example, I’m going to use Portland State University, the school I’m currently attending (as a CS student, of course.)
Don’t forget, we also need to attach that magic token to the request.
geocodeRequest.query = @"Portland State University";
geocodeRequest.token = token;
Now it’s time to send that request on it’s way using the geocode: function. The result is a VEServiceResponse.
VEServiceResponse *geocodeResponse = [geocodeService geocode:geocodeRequest];
The response is a wrapper class for any error from the server, and any actual results. Notice I used the plural form of the word result. A geocode can have multiple results. For example, searching for “London” might return London, UK or London, TX. For this example, I’m going to assume that we only care about the first example, and even worse, that we got one result. Note that if we didn’t get back any results this line will crash. Extra credit for testing to make sure that there is at least one result.
VEGeocodeResult *geocodeResult = [geocodeResponse.results objectAtIndex:0];
The VEGeocodeResult contains the nice gooey morsels of information we want. We can log them out to the console.
NSLog([geocodeResult.location description]);
NSLog([geocodeResult.address description]);
Note that VirtualEarth gave us back the location, and the nice clean address of Portland State. Nifty! The location property is going to be a CLLocation. The address property is an NSDictionary formatted for the Address Book framework, so you can shove that right into a contact or whatever.
(Here is the extra special iPhone note: The iPhone and Mac OS X use slightly different constants in their Address Book framework keys. I don’t know what this means for serialized addresses being moved between Mac OS X and an iPhone. VirtualEarthKit will always use the native constants of the current platform.)
So at this point you’re probably thinking this is pretty cool, but wait! There’s more! We can actually open a map of Portland State University based on this information!
Our starting point will be a lot like where we started with the geocode. We’re going to need to create the service object (this time for the imagery service…)
VEImageryService *imageryService = [[VEImageryService alloc] init];
Now we need to create the request. For a static map, we use the VEMapURIRequest object. If you want to fetch map tiles, this is not the best way, you’d want to use the VEImageryMetadataRequest object, but that’s the topic of a different tutorial. For now, we want Virtual Earth to do the rendering for us.
VEGetMapURIRequest *mapRequest = [[VEGetMapURIRequest alloc] init];
Now we set the center of the map request to the location we got from our geocode. Remember, we need to include the magic token.
mapRequest.center = geocodeResult.location;
mapRequest.token = token;
We need to send the request and get a response back. Note that the header defines the response class as a VEGetMapURIResponse.
VEGetMapURIResponse *mapResponse = [imageryService getMapURI:mapRequest];
The VEGetMapURIResponse class doesn’t vend it’s results as individual result classes. Instead we can query it directly for a url of the resulting map. We’ll go ahead and tell NSWorkspace to open the URL, which should cause it to appear in your web browser of choice.
[[NSWorkspace sharedWorkspace] openURL:mapResponse.mapURL];
And that’s it! Pretty painless, eh?
The framework is a work in progress. Virtual Earth provides four services: geocoding, imagery, route guidance, and yellow pages lookup. VirtualEarthKit current provides geocoding and imagery, with route guidance and yellow pages lookup coming. We’re also working on including native map views. The NMobile program we worked on with Njection.com currently implements a native map view on the iPhone in OpenGL. We’re just cleaning that up for release.
The API can probably be made nicer by adding better init functions that could shorten this code, but in it’s current form it’s a very powerful API and it makes interacting with Virtual Earth much simpler for the Mac programmer.
Here is a link to the finished code:
11 Responses to “Geocoding and fetching maps with VirtualEarthKit”
Leave a Reply
You must be logged in to post a comment.

November 14th, 2008 at 10:44 am
Hi, thanks for the tutorial. When i build everything and run the test app i get an error in sendVERequestWithBody at *error = nil; (EXC_BAD_ACCESS)
November 14th, 2008 at 10:53 am
There was a bug in the version in SVN. Go ahead and re-download or update your copy from SVN. It should work now.
November 25th, 2008 at 12:24 am
Forgive me for the noob question but when I pull the latest from svn trunk and attempt to build the project I’m not seeing a VirtualEarthKit framework product appearing. I do see a libVirtualEarthKit.a file created as a result of the finished build process in XCode. Can you please point me in the right direction?
December 7th, 2008 at 3:33 pm
Hi,first of all, thanks for this How-to.
But i get three errors in the LiveEarthApp when i try to compile.
error: VirtualEarthKit/MPFindRequest.h: No such file or directory
error: VirtualEarthKit/MPFindService.h: No such file or directory
error: VirtualEarthKit/MPRequest.h: No such file or directory
I look in the framework and in the project, and this headers doesn´t exists.If i include them in the target, i get the same three errors, what is happening???
i just checkout the svn
thanks
January 1st, 2009 at 9:47 pm
How do we use this with the iPhone? I’ve opened the included project, and built the iphone target. I then dragged the libVirtualEarthKit.a file into my project’s “Frameworks” group, and then double clicked on my application target and added the the library into the “linked libraries” group. I then copied the “../usr/include/VirtualEarthKit” directory into my own project and added that path to the headers search path. The built and it complained about libxml. Added that. Built again and complained about addressbook, so I added the addressbook framework. Built again and it complained about corelocation stuff, added core location framework. Now the app finally builds, but when I run it in the simulator, it crashes immediately and it says:
“dyld: Library not loaded: @loader_path/../Frameworks/CoreLocation.framework/Versions/A/CoreLocation
Referenced from: /Users/juan/Library/Application Support/iPhone Simulator/User/Applications/4BB90EC9-EFCA-4687-A573-DA614ED83277/Map.app/Map
Reason: image not found
”
Someone else in the mailing list seems to be having my same problem. What are the correct steps to get this working on the iphone? I’ve gotten it working fine on OSX, but I’m having no luck on the iPhone.
January 1st, 2009 at 10:21 pm
If I make CoreLocation.framework a “weak” type, then the program builds and gets a bit further along. No more “library not loaded” errors, but it crashes whenever I send a request. I just added the code on this page inside “viewDidLoad”. GDB output is this
This GDB was configured as “i386-apple-darwin”.warning: Unable to read symbols for “/System/Library/Frameworks/UIKit.framework/UIKit” (file not found).
warning: Unable to read symbols from “UIKit” (not yet mapped into memory).
warning: Unable to read symbols for “/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics” (file not found).
warning: Unable to read symbols from “CoreGraphics” (not yet mapped into memory).
warning: Unable to read symbols from “CoreLocation” (not yet mapped into memory).
Program loaded.
sharedlibrary apply-load-rules all
Help! How do I fix these “Unable to read symbols” errors. What am I doing wrong?
January 1st, 2009 at 10:34 pm
Got it working!
Just needed to add “/System/Library/Frameworks” to the framework search paths.
March 5th, 2009 at 8:10 am
When i set the user and the password i received the error:
dyld: Library not loaded: /Users/bla/Library/Frameworks/VirtualEarthKit.framework/Versions/A/VirtualEarthKit
Referenced from: /Users/bla/LiveEarthApp/build/Debug/LiveEarthApp.app/Contents/MacOS/LiveEarthApp
Reason: image not found
The Debugger ha existed due to signal 5(Sigtrap)
any ideas? Thank’s in advance
March 16th, 2009 at 7:26 pm
LiveEarthApp worked fine for me but I recieve the same problems the others get when trying to include VirtualEarthKit in my iPhone app.
I tried setting the Framework Search Path, but no luck.
July 2nd, 2009 at 12:54 am
I have been struggling for the last 5 hours and not even able to add framework to my iphone app project. what ever I am doing it is not taking #import. It is throwing error on that. secondly when i build virtualearthkit i found one framework in its build option. now where to add it my mac have various framework folder and i dont know where to put that one. if i give the path and add that to my project it is not importing it. you can understand how much trouble i am facing to use this. is there any one who is looking at these posts, I can see only questions and no answers at all.
waiting for the reply.
July 2nd, 2009 at 6:42 am
Hi Juan,
you have made it on iphone so I hope you will help me,
i spend my full day on it and result is zero.
Please suggest me how to implement that.
I searched a lot on google and found nothing.
I want to make it for iphone.
Please let me know
Thanks
Naren
narender.mudgal@gmail.com