Virtual Earth, GeoCoding on the fly and Oomph

December 10 2008

One big chunk of work during Oomph development was getting the Virtual Earth aspect of the code working.  I ended up combining two features of Virtual Earth. On the one hand, I might have a lat/long, from the Geo Microformat. In that case, I could just pass the lat/long to Virtual Earth to plot the point. But, on the other hand, I might need to geo-code the location on the fly if all I have to work with is an address.   I ended up writing some recursive code to support geocoding on the fly.  Here’s the nuts and bolts of the code:

First, I load the map. 

try {
    mainMap = new VEMap('iwmf_mapFrame');
    mainMap.LoadMap();
}
catch (err){
    setTimeout(buildMap,1000);
    return;
}
//seems the only way to force the size we want for the map                       
mainMap.Resize(1024,175);

//firefox hack because it always places the map over about 350 px
if (oomph.browser.mozilla)
    mainMap.Pan(-350,0);

Note that before even trying to load the map, I do some checking to make sure the script finished loading. I then position it inside Oomph.  Lastly, for some reason, I was having issues in Firefox getting my pushpins to show correctly, thus the pan.

Next, I do the geocoding. I already have an array of addresses, culled from the hCards, to work with:

if (addresses.length > 0)
    geoCode(addresses[0]);

 

Note how I pass only the first address to the geoCode method. That’s because the call to the Virtual Earth geocoding service (which is Find()) is async.  Notice how I provide a callback method as the last parameter to the geoCode method.  Also, notice how I ask the method to only return one result.  By default, it returns multiple results, allowing the user to determine the best one. But for the purpose of Oomph, we just want to make the call for the user and hope that it geocodes correctly, which it usually does:

 

function geoCode(address){
    //because we might be mapping multiple locations, we aren't going to zoom in 
    //unless there is only one location microformatTotal
    //hmm, VE doesn't seem to be zooming even if there is only one
    
    var zoom = true;
    if (addresses.length > 1 || geoinfos.length > 0)
    {
        zoom = false; 
    }         
    mainMap.Find( null,
          address.location,
          null,
          null,
          null,
          null,
          true,
          true,
          false, //only return one geocode
          zoom,
          GetCoordinates);    //provide the GetCoordinates callback for the response
}

In the GetCoordinates callback, I actually push the location onto the map and then call geoCode again, with the next address:

function GetCoordinates(layer, resultsArray, places, hasMore, veErrorMessage)
     {
        if (places == null)
            return;
        geoinfos.push(places[0].LatLong);
        var myShape = new VEShape(VEShapeType.Pushpin, places[0].LatLong);
        geoinfos2.push(new VEShape(VEShapeType.Pushpin, places[0].LatLong))
        myShape.SetTitle(addresses[addressCounter].name);
        mainMap.AddShape(myShape);
        if (geoinfos.length == 1)
            mainMap.SetCenterAndZoom(places[0].LatLong, 13);
        //we can only do another geocode after the first one completes, thus the recursion
        addressCounter++;
                       
        if (addressCounter < addresses.length)
            geoCode(addresses[addressCounter]);
         //if we've got them all, reset map view
         if (addressCounter == addresses.length && addresses.length > 1)
             mainMap.SetMapView(geoinfos2);

     } 

(In looking at this code, I just noticed that I bail if null – I should probably fix this because, if for some reason I get null, I’ll end up exiting out of the whole loop and never get to the other addresses – just made myself a workitem to do this!)

Once I get a place back from Virtual Earth, I can put it on the map.  I also put the location into my other array, which I walk later. I then go ahead and center/zoom the map if I only had one.  I then call geoCode again after incrementing my address counter.  Lastly, if I have everything, I call SetMapView to center and zoom the map in the event I have more than one location on the map.

After dealing with geoCoding, I then go and parse any lat/longs I received through the geo Microformat.  (Again, looking at the code, there’s some redundancy – the lot of it could probably be cleaned up, but hey, it is eXtreme Programming methodology at work (been reading Extreme Programming Adventures in C# incidentally…)

All of this code can be found here in oomph.js, lines 215 – 295. Overall, working with Virtual Earth was pretty easy, once I figured out how to do async geocoding.

Comments (1) -

12/22/2008 10:20:00 PM #

Thanks for this great information!

Getting the callbacks to synchronize properly was a problem for me. Your excellent code and writing provided the solution.

Again, thanks!
          

Bob Reselman

Add comment

biuquote
  • Comment
  • Preview
Loading

VSAchievements
Visual Studio Achievements
Karsten Januszewski (207 Points)