MapQuest Developer Blog

  • JavaScript, AS3, & FUJAX 5.3 RC4 APIs Released: Now With More Traffic!

    As we get closer to the final 5.3 release, we just rolled-out Release Candidate 4 of our 5.3 JavaScript, AS3, and FUJAX APIs. The major update this time: the addition of more features for adding traffic information into your application. We've also added traffic documentation and code samples for those of you who like that sort of thing.

    More details on the Developer Network Beta page, but here's a quick rundown of the updates:

    • You can now set minimum and maximum zoom levels on POIs. The POI would then only be visible on the map between the specified zoom levels.
    • You can now add traffic incident POIs to your map.
    • You can now add traffic flow to your map.
    • Retrieve item by key from collection or map: You can now get a shape back out of the map, or any collection, by using it's Key property.
    • Get reference to a Shape's parent collection: You can now ask a shape to tell you all the different ShapeCollections it is in

    Happy trafficking!
    Hrmm... that didn't come out quite the way I intended.

  • Getting started with the MapQuest API 5.3 and C#

    If you're a C# .NET developer and want to get started using the new MapQuest 5.3 API then you can get started today with the beta/release candidate version available on http://developer.mapquest.com/Beta.
    Download the .NET library and start exploring.

    From a .NET point of view not much has changed (yet) in the 5.3 API:
    - The library has been renamed to mapquest20.dll to reflect that this is for .Net 2.0 and above only
    - It has been tested with the 2.0 framework AND the 3.5 framework
    - It has been tested with both 32-bit and 64-bit environments
    - No interface changes have been made

    Getting a free developer account. Right now there are two types of developer account available:
    - MapQuest Platform: Free Edition
    - Free Developer License for MapQuest Platform: Enterprise Edition [1]

    Just apply to the first one since that will give you access to both. After signing up and confirming your account, you may have to wait a couple of hours for your account to become active. Mine took about 6 hours to become active.

    In the meantime you can download the .NET library. The whole library is contained in one DLL named mapquest20.dll, start a new project, add an assembly reference and you're ready to go.

    Check your account confirmation email for a list of the server names that you can use to access the MapQuest Platform, note that the information in the email will be more accurate than what you may find on the beta website.

    There are four MapQuest servers, each offering a unique set of services to make the MapQuest offering whole. These servers are:

    • Map Server - used for creating basic maps
    • Geocode Server - used for finding latitude/longitude for addresses
    • Route Server - used for created routes
    • Spatial Server - used for searching in a specific area

    The C# API offers one central class which is responsible for all server calls and it is called the Exec class. Depending on what server you point the Exec class to the appropriate methods become available.

    In order to produce a basic map you'll need at minimum the Map Server and probably the Geocode Server for locating an address.

    One of the things you'll notice when using the mapquest20.dll is that it uses very few C#2.0 features (like generics). In order to make the provided API easier to use from my own code I've created some wrapper functions.
    The first method can be used to geocode an address:

    
    /// <summary>
    /// Find a list of geo encoded addresses based on a regular adress.
    /// </summary>
    /// <param name="address">Address</param>
    /// <returns>List of geocoded address, or null if none are available.</returns>
    public List<GeoAddress> Geocode( Address address )
    {
        Disposable.ClassVariableCannotBeNull( this, Exec, "Exec" );
        Parameters.NotNullOrEmpty( this, address.Country, "address.Country", "Geocode" );
    
        LocationCollection locations = new LocationCollection();
        try
        {
            Exec.Geocode( address, locations );
        }
        catch ( Exception exception )
        {
            // TODO: Log exception
            return null;
        }
        List<GeoAddress> results = new List<GeoAddress>( locations.Size );
        for ( int i = 0, size = locations.Size; i < size; i++ )
        {
            results.Add( (GeoAddress) locations.GetAt( i ) );
        }
        return results;
    }
    
    /// <summary>
    /// Find a list of geo encoded addresses based on individual parts of an address.
    /// </summary>
    /// <param name="street"></param>
    /// <param name="city"></param>
    /// <param name="county"></param>
    /// <param name="state"></param>
    /// <param name="postalCode"></param>
    /// <param name="country"></param>
    /// <returns>List of geocoded address, or null if none are available.</returns>
    public List<GeoAddress> Geocode( string street, string city, string county, string state, string postalCode, string country )
    {
        Address address = new Address();
    
        address.Init();
        address.Street = street;
        address.City = city;
        address.County = county;
        address.State = state;
        address.PostalCode = postalCode;
        address.Country = country;
    
        return Geocode( address );
    }
    
    

    The second method will create an image based on the latitude/longitude of the point of interest. It will also show a basic point of interest at the specified location.

    /// <summary>
    /// Return a (bit)map centered on the provided address.
    ///
    /// Address needs to have LatLng property set.
    /// </summary>
    /// <param name="address">The address to center on.</param>
    /// <param name="width">Width of the map in pixels (DPI = 72)</param>
    /// <param name="height">Height of the map in pixels (DPI = 72)</param>
    /// <param name="scale">Scale of the map. Level of detail displayed varies depending on the scale of the map.</param>
    /// <returns>Return a bitmap of the map centered on provided address.</returns>
    public Bitmap GetMap( GeoAddress address, int width, int height, int scale )
    {
        return GetMap( address.LatLng, width, height, scale );
    }
    
    /// <summary>
    /// Return a (bit)map centered on the provided latitude/longitude.
    /// </summary>
    /// <param name="center">Coordinates for the center of the map.</param>
    /// <param name="width">Width of the map in pixels (DPI = 72)</param>
    /// <param name="height">Height of the map in pixels (DPI = 72)</param>
    /// <param name="scale">Scale of the map. Level of detail displayed varies depending on the scale of the map.</param>
    /// <returns>Return a bitmap of the map centered on provided latitude/longitude.</returns>
    public Bitmap GetMap( LatLng center, int width, int height, int scale )
    {
        // The MapState object contains the information necessary to display the map,
        // such as size, scale, and latitude/longitude coordinates for centering the map.
        MapState map = new MapState();
    
        // Define the width and height of the map in pixels.
        map.WidthPixels = width;
        map.HeightPixels = height;
    
        // The MapScale property tells the server the scale at which to display the map.
        // Level of detail displayed varies depending on the scale of the map.
        map.MapScale = scale;
    
        // Specify the latitude/longitude coordinate to center the map.
        map.Center = center;
    
        // The MapQuest Session object is composed of multiple objects,
        // such as the MapState and CoverageStyle.
        Session session = new Session();
    
        // Add objects to the session.
        session.AddOne( map );
        session.AddOne( Features.CreateBasicPOI(center) );
    
        sbyte[] imageBytes = Exec.GetMapImageDirect( session );
        byte[] bytes = (byte[]) (Array) imageBytes;
    
        MemoryStream stream = new MemoryStream( bytes );
        Bitmap bitmap = new Bitmap( stream );
    
        return bitmap;
    }
    

    The MapQuest API returns an image as an array of sbytes. Since .NET has a basic type for images, named Bitmap, it is much more useful to have the method return a bitmap which can then be used anyway you please. To do this the array is converted from sbytes to bytes since that is all the MemoryStream can handle.

    The samples posted on the MapQuest site are a great help in figuring out how to use the C# MapQuest API and is actually more detailed than the provided documentation.

    Happy coding!

    - Mark Blomsma

    [1] MapQuest Platform Services offers a free Developer License for Enterprise Edition, providing you access to the features and functionality of the Enterprise Edition while you are developing and prototyping your map application. Register for the free Developer License at MapQuest Developer Network and get started today.

  • Where is MapQuest

    Continuing our string of weekly trade show announcements, we'd like to let you know that MapQuest is a Platinum Sponsor of and will be attending the annual Where 2.0 Conference being held next week in Burlingame, CA from May 12th to the 14th. (For those of you not familiar with the Valley, the show is right outside the San Francisco Airport.)

    If you're attending the show, stop by and tell us what you're working on and how MapQuest can help.

  • Map It! - Building a MapQuest Mac OS X Dashboard Widget - Part 7 - Address Book Integration

    There's nothing that makes you so aware of the improvisation of human existence as a song unfinished. Or an old address book. - Carson McCullers

    In Part 1 I showed you how to get started with the MapQuest Advantage API by getting a developer key. In Part 2 I put that key to use by providing access to a basic map in the Map It! widget. Part 3 showed you how to incorporate basic geocoding. In Part 4 I discussed more advanced geocoding topics - including handling multiple matches and specifying geocode search options. Part 5 discussed adding widget options including the default zoom level, specifying point of interest icons, and the default map type. Part 6 discussed how to add direction capabilities to the widget. In this final installment I'll talk about how easy it is to integrate address searching with the Mac OS X Address Book application.

    Address Book Searching

    The Mac OS X Address Book application is bundled with the OS X operating system, providing a way for users to organize their contacts and associated information including their addresses. Also included with Mac OS X is an Address Book widget, which provides access to the Address Book database from a widget. I've explored the Address Book widget code, and extracted the AddressBookPlugIn. Plug-ins are native code that can be used to access operating system levels features from a widget's JavaScript interface.

    Listing 1 demonstrates how I've modified the address parsing method. If an address is entered that does not match one of the pre-defined formats I will assume that it's a name to retrieve from the Address Book. The address book is searched for the name with the best match. Then the street, city, state, and zip code are retrieved from the best match. The retrieved address is then parsed as discussed in Part 3!

    Listing 1 - Retrieving From the Address Book

    function onAddressSearch(event)
    {
      // user hit a return?
        if(event.keyCode == 13) {
        if(!$(address).value.blank()) {
    
        var street = "";
        var city = "";
        var state = "";
        var zip = "";
        var country = "";
    
         $(lblErrorMsg).innerText = "";
           $(multMatches).style.visibility = "hidden";
         $(lblMultMatch).style.visibility = "hidden";
    
           var splitAddr = $(address).value.split(',');
    
         // if we only get 2 elements assume address, zip
         if(splitAddr.length == 2) {
          street = splitAddr[0];
          zip = splitAddr[1];
    
        // for three elements assume address, city, state
         } else if(splitAddr.length == 3) {
          street = splitAddr[0];
          city = splitAddr[1];
          state = splitAddr[2];
    
        // for 4 elements assume address, city, state, zip
         } else if(splitAddr.length == 4) {
          street = splitAddr[0];
          city = splitAddr[1];
          state = splitAddr[2];
          zip = splitAddr[3];
    
        // for 5 elements assume address, city, state, zip, country
         } else if(splitAddr.length == 5) {
          street = splitAddr[0];
          city = splitAddr[1];
          state = splitAddr[2];
          zip = splitAddr[3];
          country = splitAddr[4];
    
         } else {
            // otherwise assume it's a name to look up in the Address Book
          AddressBookPlugin.searchForStringWithBestMatch($(address).value);
          // retrieve the values from the address book
          street = AddressBookPlugin.displayedValueAtIndex(7);
          city = AddressBookPlugin.displayedValueAtIndex(8)
          state = AddressBookPlugin.displayedValueAtIndex(9);
          zip = AddressBookPlugin.displayedValueAtIndex(10);
         }
    
         var result = widget.system('java -classpath .:mq.jar GetLocations
           -street "' + street + '" -city "' + city + '" -state "' + state +
           '" -zip "' + zip + '" -country "' +country + '"',null).outputString;
    
         // any errors?
         if(result == "ERROR") {
          $(lblErrorMsg).innerText = "Error! Unknown Address Format!";
         // address not found?
         } else if (result == "NOT FOUND") {
          $(lblErrorMsg).innerText = "Address Was Not Found!";
         } else {
          // split on return - a line is printed for each individual result
          var eachresult = result.split('\n');
    
          // just one result returned? - then just plot the point -
          // there are two extra returns so 3 really means 1 item
          if(eachresult.length == 3) {
            // get the coordinates from the returned string
            var coords = eachresult[0].split('|');
            // create a new point based on the coordinates
            newCenter = new MQLatLng(parseFloat(coords[0]),parseFloat(coords[1]));
    
            //create a new icon object
            myIcon = new MQMapIcon();
    
            // set the icon image: icon file location, width, height, recalc infowindow offset,
            // is it a PNG image?
    
            if(defIcon == "pin") {
              myIcon.setImage("images/pinpoint_red.gif",32,32,true,false);
            } else if(defIcon == "star") {
              myIcon.setImage("images/starsmall_red",18,18,true,false);
            } else if(defIcon == "x") {
              myIcon.setImage("images/xspot.gif",17,17,true,false);
            }
    
            // create a point
            myPoint = new MQPoi(newCenter);
    
            // set the custom icon
            myPoint.setIcon(myIcon);
    
            // recenter the map on the point,
            // the second parameter specifies the zoom level
            myMap.setCenter(newCenter,defZoom);
            // add the point as a Point of Interest
            myMap.addPoi(myPoint);
          } else {
            // clear out existing items
            $(multMatches).options.length = 0;
            for(var i=0; i
    
    

    Conclusion

    For your reference, here are some references for the MapQuest Platform:

  • Zoom Levels and Image Overlays

    In this post I will continue my discussion of overlays in the MapQuest JavaScript API 5.2. My last two posts covered a couple of methods for adding rollover functionality to overlays. Over the next couple of posts I will discuss some of the options that are available when working with image overlays.

    There are a few things that set image overlays apart from the other overlay types. The one that poses the biggest obstacle is image resolution. Since an image overlay is "pinned" to a map with Lat and Lng coordinates, there is a significant difference in resolution required to display an image properly at different zoom levels. If this becomes an issue for your application, one of the options that is available is the setImageOverlayLevels method provided by the API.

    The setImageOverlayLevels() method takes an array of MQImageOverlayLevel objects as an argument. You create an MQImageOverlayLevel object by passing a string representing the URL of the image and an integer between 1 and 16 representing the zoom level the image should be tied to. Although you can specify 16 different images, you don't need to, as the nearest image to the current zoom level will always be displayed. I have included a function here that I created as an example. The function can be used in your code to implement the use of MQImageOverlayLevel's in your application.

    Listing 1: The addImageUrls() function

      function addImageUrls(MAX_ZOOM, MIN_ZOOM, imageOverlay){   // Lowest resolution to highest.
        var SPAN = MAX_ZOOM-MIN_ZOOM+1;
        if(arguments.length > (SPAN+3)){
          arguments.length = SPAN+3;  // Ensure a maximum of 1 image per zoom level.
        }
        var incrementor = MIN_ZOOM;
        var incrementSize = Math.round(SPAN / (arguments.length - 3));
        var levels = new Array();
        var addImageLevel = function(url,lvl){
          var newLevel = new MQImageOverlayLevel(url,lvl);
          newLevel ? levels.push(newLevel) : alert('Error: Level ' +
            lvl + ' image initialization has failed.'); // No real error checking implemented here.
        };
        // In a production environment, something more
        // thorough would become necessary.
        for(var i=3;i<arguments.length;i++){
          if(incrementor > MAX_ZOOM){
            incrementor = MAX_ZOOM;
          }
          addImageLevel(arguments[i], incrementor);
          incrementor = incrementor+incrementSize;
        }
        imageOverlay.setImageOverlayLevels(levels);
        imageOverlay.setMaxZoomLevel(MAX_ZOOM);
        imageOverlay.setMinZoomLevel(MIN_ZOOM);
      }
    

    The function is called with the following arguments:

    Listing 2: Calling the addImageUrls() function

    	addImageUrls(MAX_ZOOM, MIN_ZOOM, imageOverlay,
    	/* and up to 16 string URLís */)
    

    The values for max and min zoom are used to determine which zoom level to place each image at, as well as to determine when the image won't be displayed at all (because of the incredible difference between zoom level 16 and zoom level 1, there is a good chance that the image overlay will only be applicable for a certain range of values). Next you pass your MQImageOverlay object, followed by a series of strings containing the URL's of the images. You can pass in anywhere from 2 to 16 values, ordered from lowest resolution to highest.

    To close, I've included two screenshots that show MQImageOverlayLevels in a sample application. Other than setting up the map and collecting the shape points, all of the code is contained within the sample function in Listing 1. For simplicity sake, I created two square images of separate colors. When the map is zoomed in beyond the half-way point, the brown image is displayed. When it is zoomed out, a blue image is displayed.

    Image 1: The Image, Zoomed In

    Image 2: The Image, Zoomed Out

  • Meet Us at JavaOne

    MapQuest will be in attendance at JavaOne being held next week at the Moscone Center in San Francisco, CA. You can visit us in the Pavilion at booth #1124; a number of the MapQuest Developers will also be running around and attending sessions. Feel free to stop and talk with us.

  • Map It! - Building a MapQuest Mac OS X Dashboard Widget - Part 6 - Directions

    Stand in the place where you live, Now face North, Think about direction, Wonder why you haven't before - REM

    In Part 1 I showed you how to get started with the MapQuest Advantage API by getting a developer key. In Part 2 I put that key to use by providing access to a basic map in the Map It! widget. Part 3 showed you how to incorporate basic geocoding. In Part 4 I discussed more advanced geocoding topics - including handling multiple matches and specifying geocode search options. Part 5 discussed adding widget options including the default zoom level, specifying point of interest icons, and the default map type. In this installment I'll discuss how to add direction capabilities to the widget!

    Directions

    Listing 1 demonstrates how to add directions to our widget map. I've added an additional search box where the user can enter the destination address. I've use the Java application developed in Part 3 to obtain the coordinates of the route beginning and destination points. Using the coordinates, I've created the MQGeoAddress object for both points. A new session is created for routing. The starting point and destination are added to the way points. Once the route is calculated through the doRoute method, the highlighted route is added to then added to the map.

    Listing 1 - Adding Directions

    // create the first address point
    var geoAddr1 = new MQGeoAddress();
    
    // create the second address point
    var geoAddr2 = new MQGeoAddress();
    
    // set the cooridinates
    geoAddr1.setMQLatLng(newCenter1);
    geoAddr2.setMQLatLng(newCenter2);
    
    // create a new session
    var session = new MQSession();
    var routeRes = new MQRouteResults();
    var wayPoints = new MQLocationCollection();
    var myBB = new MQRectLL(new MQLatLng(),new MQLatLng());
    
    // add the beginning and ending way points
    wayPoints.add(geoAddr1);
    wayPoints.add(geoAddr2);
    var routeOpt = new MQRouteOptions();
    
    //create sessionID from the route server
    var routeServerName = "route.access.mapquest.com";
    var routeServerPort = "80";
    var routeServerPath = "mq";
    
    // obtain a route
    var routeExec = new MQExec(routeServerName, routeServerPath,
      routeServerPort, routeServerName, routeServerPath, routeServerPort );
    var sessId = routeExec.createSessionEx(session);
    routeExec.doRoute(wayPoints,routeOpt,routeRes,sessId,myBB);
    
    // add the route to the map!
    myMap.addRouteHighlight(myBB,"http://map.access.mapquest.com",sessId,true);
    

    Conclusion

    In Part 7 I'll wrap up the widget by showing you how to retrieve addresses from the Mac OS X Address Book application. For your reference, here are some references to the MapQuest Platform:

  • Using Map Overlays with Flex and the Mapquest 5.2 APIs

    Ok, I was driving (very carefully, mind you) on a very sunny afternoon to the airport, and I got a call from my very bright and brilliant niece who recently enrolled into Michigan State University (MSU if you're a Michigan native). During the midpoint of the conversation, all I heard was "cell phone silence", for lack of a better term, and within a few more seconds the call was dropped -- argh!

    Obviously, I'm not the only person in the world who gets aggravated when a mobile call gets dropped. Of course, my wireless carrier has to go unnamed here in order to protect the guilty... er, uh... innocent.

    So, after I called my niece to finish our conversation, I started to think. "Shouldn't there a way to show folks where the various dead spots are for the wireless carriers?" As you will see shortly, the MapQuest 5.2 APIs actually come in quite handy for solving this sort of problem. If you read my previous blog post and tried out the example code that I provided, then you should be familiar with the concept of creating a POI (point of interest) on a map and displaying it. Today, we're going to use overlays to highlight a region of interest. Take a look at the image below to see my "dead spot" awareness system:

    Now, the code snippet below is the all the code needed to add a circular overlay to a map. As you can see, I'm able to set the radius of the circle (currently set at '60' here in the code), fill color, border color, border width, and transparency value.

    
              var rect:RectLL = getShapePoints(deadspot_coords, 60);
    var circle:EllipseOverlay = new EllipseOverlay();
    circle.setFillColor(0xffff00);
    circle.setFillColorAlpha(.55);
    circle.setBorderWidth(-1);
    circle.setShapePoints(rect);
    tilemap.addOverlay(circle);

    So as you can see, adding a circular overlay is pretty easy with the Mapquest 5.2 APIs. As always, below is the full source code so you can copy/paste and get things going.

    <?xml version="1.0" encoding="utf-8"?>
      <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
        xmlns:ns1="com.mapquest.tilemap.*" creationComplete="main();">
    <mx:Canvas x="36" y="10" width="635" height="353" backgroundColor="#ffffff">
    <ns1:TilemapComponent x="175" y="34" width="452"
      height="302" id="tilemap" key="mjtd%7Clu6y29u2n0%2C7s%3Do5-0ura9"/>
    <mx:Text x="19" y="77"
      text="Tired of dropped calls from you wireless provider?
      Check here for the latest on wireless dead zones."
      width="134" height="94"/>
    <mx:ComboBox id="mapTypeComboBox" x="19"
       y="34" width="148" dataProvider="{mapTypeComboData}"
      change="changeMapType();"></mx:ComboBox>
    <mx:Label x="198" y="1" text="Wireless Dead Spot Locator"
      fontSize="16" width="266"/>
    </mx:Canvas>
    
      <mx:Script>
     <![CDATA[
     import com.mapquest.tilemap.pois.*;
     import com.mapquest.PointLL;
     import com.mapquest.LatLngCollection;
     import com.mapquest.LatLng;
     import com.mapquest.tilemap.overlays.PolygonOverlay;
     import com.mapquest.tilemap.overlays.EllipseOverlay;
     import com.mapquest.RectLL;
     import com.mapquest.tilemap.controls.ZoomControl;
    
      [Bindable]
        public var mapTypeComboData: Array = [ {label:"Map View", data:"map"},
        {label:"Satellite View", data:"sat"},
        {label:"Hybrid View", data:"hyb"} ];
    
        public function main(): void{
    
        var zoomControl:ZoomControl = new ZoomControl();
        tilemap.addControl(zoomControl);
    
        // now let's set the points of interest
        var deadspot_coords:PointLL = new PointLL(32.720409,-91.994637);
        var deadspot_Poi:Poi = new Poi(deadspot_coords);
        deadspot_Poi.setInfoTitle("deadspot reported on 2/5/2008");
    
    
        var rect:RectLL = getShapePoints(deadspot_coords, 60);
        var circle:EllipseOverlay = new EllipseOverlay();
        circle.setFillColor(0xffff00);
        circle.setFillColorAlpha(.55);
        circle.setBorderWidth(-1);
        circle.setShapePoints(rect);
        tilemap.addOverlay(circle);
    
    
        deadspot_Poi.setKey("A1");
        tilemap.addPoi(deadspot_Poi);
        tilemap.setZoomLevel(4);
        tilemap.setCenter(deadspot_coords);
    
        }
    
        public function getShapePoints(pointLL: PointLL, radius: int): RectLL{
    
        var rLat:Number = radius / 69;
        var rLng:Number = radius / 53;
        return new RectLL(new PointLL(pointLL.lat-rLat, pointLL.lng-rLng),
          new PointLL(pointLL.lat+rLat, pointLL.lng+rLng));
        }
    
        public function changeMapType(): void{
        tilemap.mapType = mapTypeComboData[mapTypeComboBox.selectedIndex].data;
        }
    
    
        ]]>
      </mx:Script>
      </mx:Application>
    
  • Visit Us at Location Intelligence

    MapQuest will be out in Santa Clara, CA this week attending the Location Intelligence conference. We have a booth set-up and will be happy to demo and discuss the MapQuest Platform with you.

    Hope to see you there!

  • Create a Geo File

    In my previous MapQuest posts I've mostly been demonstrating how to use MapQuest to display the the various types of geo-formats (KML, GeoRSS, etc). Building on the examples of map event interaction from my previous posts, we can also build an interactive map interface where users can build their own geo-format files. Here's an example where users can interactively click on the map to create a polyline.

    <html>
    <head>
    <title>Create a Geo File</title>
    <script src="http://btilelog.access.mapquest.com/tilelog/transaction?
      transaction=script&key=YOUR-API-KEY&ipr=true&itk=true&v=5.2.0"
      type="text/javascript">&t;/script>
    <script language="javascript">
    MQInitDojo(initMap);
    var myOverlayColl = new MQOverlayCollection();
    var myShapePts = new MQLatLngCollection();
    var myOL = new MQLineOverlay();
    var allclicks  = new Array(0);
    function showClick(event) {
        // log all clicks
        allclicks.push(event.ll.getLatitude(),
          event.ll.getLongitude());
        // add clicks to polyline overlay
        myShapePts.add (new MQLatLng(event.ll.getLatitude(),
         event.ll.getLongitude()));
        myOL.setShapePoints(myShapePts);
        myOverlayColl.add(myOL);
        myMap.replaceOverlays(myOverlayColl);
        // clear previous markers and add new start and end points
        myMap.removeAllPois()
        myMap.addPoi(new MQPoi(new MQLatLng(allclicks[0], allclicks[1])));
        if ( allclicks.length > 1 ) {
            myMap.addPoi(new MQPoi(new
             MQLatLng(allclicks[allclicks.length-2],
             allclicks[allclicks.length-1])));
        }
    }
    function display() {
        alert (allclicks);
    }
    function initMap() {
       myMap = new MQTileMap(document.getElementById('mapDiv'),8,
        new MQLatLng(33.173676, -116.714889));
       myMap.addControl(new MQLargeZoomControl(myMap));
       MQEventManager.addListener(myMap,"click",showClick);
    }
    </script>
    </head>
    <body>
    <div id="mapDiv" style="width:384px; height:384px; border:2px solid"></div>
    <a href="#" onclick="display();">Display Coordinates</a>
    </body>
    </html>
    

    As you can see, I'm simply using an 'alert' to display the captured latitudes and longitudes of the polyline that the user created. Obviously the next task is to transform that array of coordinates into your favorite geo-format. Here is a screenshot of the result: