MapQuest Tiled Map API and .NET


I blogged last night about whether Microsoft sucks and one of the topics was the documentation present for Microsoft. Whether you realize it or not, Microsoft spends a lot of time creating documentation and samples to make your life easier. Look at the MSDN library, for example. Nearly every method has a bit of sample code to help you learn how to use the method. Add on top the MSDN site and various MS funded and community sites and you have a pretty easy go of foraging the forest to create your applications in less time and actually understand what you are doing.

The same is not completely true for all other products. I have been dinking with the MapQuest Tiled Map API for a bit of time and the documents are a bit below par once you step out of the box, even slightly. The information is there, but perhaps I have been spoiled by examples in MSDN? For Enterprise Customers, like my company, this is not a major issue, as Enterprise Support is excellent. There is also a forum for everyone to ask things, so I am not stating that support is lacking, by any means. I am just a OJT type of guy and would like the docs to give me more information.

Rather than continue to sound like I am whining, I need to get to the point of this entry, which is how to get the tiled map API working in .NET. I am still working on the solution, so this is a work in progress. If anyone comes up with a better solution, I am all ears, especially one that does not require loading another JavaScript file to accomplish proper loading.

The Problem

First, a bit about the company I work for. Microtrak GPS (under the corporation: The Tracking Corporation) is a company that sells GPS tracking products. Behind the scenes, we get GPS messages sent to our company and store them in a database. On the front end, customers log in to a web site and see where their vehicle is (or has been). There are three high level functions of any "mapping" toolkit that we have to use.

  1. Reverse Geocoding – to get addresses for the raw GPS messages
  2. Mapping – Show locations
  3. Geocoding – For users that wish to place their own locations on the map (saved points of interest, like their home)

In our implementation, the executives have fallen in love with the Sat maps (hybrid actually) and the ability to drag a map to see other locations. This provides a bit of challenge, as the Tiled Map Toolkit has some issues out of the box (the primary issue is a browser issue and does not reflect on MapQuest, however).

I will now cover the Reverse Geocode "project" as well as the mapping project, as each requires a bit more information than is presented in the documentation.

Reverse Geocoding

Reverse Geocoding is the process of taking a set of lat/long coordinates and turning them into a meaningful address that humans can understand. After all, very few of us would say, "my address is 36 01.917 N, 86 57.789 W", although that is a correct "address" for me right now, albeit about 10 feet outside the window, but that is a topic for another day.

The MapQuest document for .NET has little information for Reverse Geocoding, however. Specifically:

Advantage API customers who want USA city and state lookup should supply the geocode pool
us_postal. For other countries, contact Technical Support. For details about your server configuration,
refer to “Learning Your Server Configuration” on page 13.

So, I attempt reverse geocoding using the sample application Geocodeit as a code map. It appears I simply have to look at the ReverseGeocode method in the documentation and then make sure I have it set to us_postal.

Find the Exec object in the download (or online) documentation and I find the followign methods:

ReverseGeocode (LatLng ll, LocationCollection lc, System.String mapCovName)
ReverseGeocode (LatLng ll, LocationCollection lc, System.String mapCovName, System.String covName)

Assume now I can simply add the us_postal as the mapCovName and go. So I code this:

using System;
using MQClientInterface;

namespace ReverseGeocodeIt
{
  class SimpleReverseGeocode
  {
    static void Main(string[] args)
    {
      LocationCollection locs = new LocationCollection();

      Exec geocodeClient = new Exec("map.access.mapquest.com", "mq", 80);
      geocodeClient.ClientId = "{Client ID here}";
      geocodeClient.Password = "{Password Here}";
      try
      {
        geocodeClient.ReverseGeocode(new LatLng(36.032102F, -86.962859F), locs, "us_postal");
      }
      catch(MQException ex)
      {
        //Code to display error message
        return;
      }

      if (locs.Size > 0)
      {
        //Get the first one
        GeoAddress loc = (GeoAddress) locs.GetAt(0);
        Console.WriteLine("Address:rn{0}rn{1}, {2}  {3}", loc.Street, loc.City, loc.State, loc.PostalCode);
        Console.Read();
      }
    }
  }
}

But, this one failed. I scoured the document for a clue (and the web for a sample). After attempting for awhile, I contacted tech support. First, we had to make sure my ID was set up. Still failed, so I was told I was missing something, which was the MapCovName (us_postal is a covName). The corrected line is:

geocodeClient.ReverseGeocode(new LatLng(36.032102F, -86.962859F), locs, "navt", "us_postal");

This is actually in the document, but it is not extremely clear. First, you find it in the server configuration XML. It is also located in a table on page 34, under Advanced Geocoding, although there are no samples of using this in code.

CoverageName The name of a geocode pool on the server. Common examples include
tana, navt, and gaz.

In addition, it is unclear how you use this properly, as the code sample on page 35 appears to show navt as a covName, not a mapCovName.

geocodeOptions.CoverageName = "navt";

The Reverse Geocode now works, so I moved it into a class with a factory method on top and I am golden here. The factory is important here, as I still have Pepperwhite as a fallback and may be told to move to Google or MapPoint, etc., in the future. My basic interface, for those wanting to code their own clients, is currently like this (although it needs a bit of a refactor before going live):

public interface IReverseGeocode
{
    double Latitude { get; set; }
    double Longitude { get; set; }

    RGAddress RunReverseGeocode();
    RGAddress RunReverseGeocode(double latitude, double longitude);
}

Getting Tiled Maps to Work

My next problem centers around the Tiled Map API (part of the JavaScript API). I have to use it as the execs want to give customers the ability to drag the map around. I am not convinced it is a requirement, from a user standpoint, but it sure makes things look pretty and makes it easier to market our product. For the marketing aspect alone, it is worth the pain of figuring this one out.

My final goal is to place a Tiled Map (all JavaScript) into a .NET control so I can dynamically change lat/longs and dynamically add Points of Interest (POIs). In my case, POIs are either the asset a unit is attached to (person, vehicle, etc.) or saved locations (my home, my work, etc.). I will also have to add circles for geofences, but that is a topic for another blog entry.

First, start with the basics. I grab code from page 5 of the JavaScript/Tiled Map Developer’s Guide (this is a conglomeration of the two samples)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Quick Map</title>
<!– THE JS INCLUDE Tag that gets Map Toolkit –
Change Key value to one provided on signup –>
<script src="
http://btilelog.access.mapquest.com/tilelog
/transaction?transaction=script&key=YOUR_KEY_HERE&ipr=true&itk=
true&v=5.2.0"
type="text/javascript"</script>
</head>
<body>
<!– The DIV to hold the Map itself –>
<div id="mapWindow" style="width:900px; height:520px;"></div>
<!– Create the base MQTileMap object and pass in the id of
the DIV you
want to hold the map –>
<script language="javascript">
myMap=new MQTileMap(document.getElementById(‘mapWindow’),9,new
MQLatLng(40.0446,-76.4131),"sat");

</script>
</body>
</html>

This works great once I allow redirect from localhost at the Technical Resource Center (trc.mapquest.com). Seems like this one is going to be easy. I then go through and add points of interest and recenter the map, using an altered version of the script on page 8. Everything works.

Next, I place this in a .NET page and the whole things blows chunks.

MapQuesterror

Ouch! Fortunately, I recognize this one as an error in JavaScript that occurs when a "DOM" is not completely loaded before loading methods. Yes, it does work fine in Firefox, for those asking. Opera? Yes. Safari for Windows? Yes. This is a unique Internet Exploder problem. But, I cannot put a "This would work in Firefox" message (like some open source sites), as most of our customers use IE. Yes, I am ripping a bit on Internet Exploder, at least IE 7 (have not tested in IE 6 yet).

The solution is to move to MooTools. Below is the entire code for this simple example, using the DOM ready functionality of MooTools. I will follow with a few pointers.

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="map2.aspx.cs" Inherits="map2" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
    <script type="text/javascript" src="js/mootools.js"></script>
      <script src="http://btilelog.access.mapquest.com/tilelog/transaction?transaction=script&key=YOUR_KEY_HERE&ipr=true&itk=true&v=b2btk&quot;
        type="text/javascript"></script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <div id="mapWindow" style="width: 900px; height: 520px;" >
    </div>
    </div>
    <script language="javascript" type="text/javascript">
        function loadMapQuestMap()
        {
            myMap=new MQTileMap(document.getElementById(‘mapWindow’),9,new MQLatLng(40.0446,-76.4131),"hyb");
        }
            window.addEvent(‘domready’, function(){
              loadMapQuestMap();
            });
    </script>
    </form>
</body>
</html>

Here are some notes that will help you.

  1. The <script> tag that points to mootools must be loaded prior to the <script> tag that points to MapQuest. Flip it and IE blows chunks again.

    MapQuestChunks2

  2. You have to load the DOM ready bits from the download page. Open the page at http://www.mootools.net/download and scroll down to check Window.DomReady. This will also check the Class section, all of the Native section and Element.Event. If you just download the Core (default), you will not get DOM Ready.
  3. You must encapsulate the mapping logic in a function and call that function from the block that adds the domready event as a function.
  4. The domready event can be placed anywhere on the page, rather than just at the bottom. I just placed it here as I was typing at this point. Since the entire DOM has to be loaded prior to the loadMapQuestMap() function being called, it really does not matter if you place this first or last, as long as the placement is legal.

My next task is to place a geofence on the map. The samples in the Dev Guide all use pixel placement, so I am searching to see if I can place a circle on the map using lat/long.

Peace and Grace,
Greg

Advertisements

3 Responses to MapQuest Tiled Map API and .NET

  1. IkeLove13959 says:

    Instead of introducing MooTools dependencies, you could have simply used window.onload callback to delay javascript processing until the page has fully loaded.  For example:window.onload = function() {   loadMapQuestMap();}

  2. Gregory says:

    I have retooled it since this incarnation, but I wiill have to look at it. The original reason for MooTools was the way the site had been coded, before I got to it. I know I am no longer using the MooTools on the site. I also have to revisit this, as MapQuest has a new API out.
     
    One issue I still have is all of the client side libs running into each other, but as more people go OO, this is "less likely" to happen, at least in theory.
     
    Peace and Grace,Greg

  3. Jeff says:

    whoa…you saved me some time. MQ documentation is a time-sink…way too many omissions/mistakes/etc. According to the .net docs, the constructor for the reverserGeoode object is:ReverseGeocode (LatLng ll, LocationCollection lc, System.String mapCovName)But you were right…you need both the mapSource and geoSource. This isnt the first time I wasted some hours with MQ’s api. But the boss wants to use MQ because the license is cheaper than Google. I really like Virtual Earth, but the $24,000 per year for up to 1k assets is crazy. Personaly, I think Google is the way to go…not the cheapest, but the international coverage is the best so far. And with Google Map_Maker, you can get street coverage AND reverseGeocodes for anywhere in the world where someone is contributing to mapMaker. Pretty cool!Oh….I just noticed that MicroTrak switched to Google. Well worth the extra price IMO, especialy if you plan to track stuff outside the US.Tnx again! Jeff

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: