MapQuest Developer Blog

  • Other Ways to Track MapQuest

    So you're reading this, which means you either follow our RSS feed, check in on the blog directly, or happened to land here looking for something (which we hope you found BTW). What you might not know is that we have a few other ways to keep tabs on MapQuest news and happenings for both the Developer Blog you're reading and the MapQuest.com Blog:

  • Got Flash 10?

    One of the challenges all web developers face is future versions of platforms your application runs on. When a new browser or in this case version 10 of the Adobe Flash Player is on the horizon, it's good to run your application against it to see if any changes will need to be made before the new version goes to production and screens across the Web light up with messages of "An upgrade is available."

    We've recently launched an unsupported version of our AS3 API for those of you working with Flash 10. Some developers have found that our 5.3 release and it's version checking of the Flash Player would cause errors to be thrown. This unsupported release (5.3.1_U) fixes that.

    For the curious, it's an unsupported build because the Flash 10 player is still in development. This update will however allow you to run and test your applications on Flash 10 using the MapQuest AS3 API.

    Oh, one other note: 5.3.1_U also was built for Adobe Flex 3 and AIR.

    We also have unsupported versions of our C++ API for Mac OSX 10.5 (Intel) and a PHP API. You can download them on our Unsupported Tools page.

  • Vote for MapQuest at SXSW!

    We're big fans of the annual SXSW Interactive Festival in Austin, TX. Some of us at MapQuest have been attending for years.

    One of the great things about the conference is that it's constantly evolving with the interests of the attendees. The public even contributes to picking the panels that will be available. For SXSW 2009, we have a session up for consideration in the Panel Picker voting:

    "Whereify Your Content; Gain an Audience of Millions"
    Building an audience for your content is expensive. What's a start-up to do to? In this session, we'll talk about some of the ways making your content location-aware can help you quickly build an audience. We'll also show you how to do it.

    It's a timely topic in an increasingly mobile and context-sensitive world and we know a little about helping people find places. So, here's where you can vote and hear what we have to say on the topic of "Whereification."

    • Fill out a quick sign-up at the SXSW Panel Picker. You don't have to be planning to attend SXSW (although you totally should) to vote.
    • Click the "Create an Account to Begin Voting" link
    • Fill out the form and submit
    • Check email for verification link and click.
    • Visit the panel page for: "Whereify Your Content; Gain an Audience of Millions"
    • Vote!

    Voting runs until August 29th and we'll see you in Austin next March!

  • AOL Tour Tracker

    AOL Music just launched a great site called Tour Tracker. It allows you to see when and where your favorite bands are playing and shows coming to a town near you.

    They also just happen to have some really cool interactive maps built on the same MapQuest Platform that you use, specifically the JavaScript API. How about that?

    Go over and check it out while I get some tickets to go see the Foo Fighters.

  • New Feature: Printable Maps!

    You can now print MapQuest maps!

    "Well duh," you're probably thinking. "I've been printing MapQuest maps and driving directions for years."

    Well of course you have. MapQuest.com generates millions-and-millions of maps specifically for printing every day. What I'm talking about here is printing from the applications you're making using the Free Edition of the MapQuest Platform.

    "I couldn't do that before?"

    That was a little unclear in our old Terms and Conditions. Some observant developers asked about it, and we said "Why not?" We wound up drawing straws to see who had to ask Legal for the revisions. I lost. So I took the ask up the cliffside, knocked on the iron plated door, dropped the paperwork and ran.

    So here's the deal for printing with the Free Edition:

    • You can print up to 5000 copies of a map per run/publication
    • The printed map can be up to a maximum of 8.5" x 11" inches printed
    • It has to be a free publication (flyer, newsletter, etc); you can't use it in a publication you're charging for

    Just another example of how we're working with developers and responding to your needs.

    Happy Printing!

  • Beta Gone Bye-Bye

    With the 5.3 release of the MapQuest Platform, we've now deactivated the beta versions (until the next version is ready for beta of course). If you were using the 5.3 beta versions, here's some quick tips to keep your application running smoothly:

    Take the following <script> tag for example:
    <script src="http://btilelog.beta.mapquest.com/tilelog/transaction? transaction=script&key=<YOUR_KEY_HERE>&itk=true&v=5.3.0_RCx" type="text/javascript"></script>
    • You need to change "&v=5.3.0_RCx" to "&v=5.3.0," "x" being the Release Candidate number you were using
    • If you were pointing to the beta servers "http://btilelog.beta.mapquest.com," you need to point back to the production servers: "http://btilelog.access.mapquest.com"

    These simple steps will help keep your development from grinding to a halt -- like in a traffic jam. Since real-time traffic is also one of the new features in 5.3, you can help keep your users from actually winding up in a real traffic jam.

  • MapQuest Platform 5.3 Released

    We are pleased to announce the release of version 5.3 of the MapQuest Platform! This update focuses on enhancements to our 3 client-side APIs: JavaScript, AS3, and FUJAX.

    Some of the big changes include:

    • Collections: Support for multiple and remote collections (KML and GeoRSS); easier handling of shape collections
    • Custom Tile Layer support
    • Add real-time traffic to your map
    • Globe view enhancements
    • All 3rd party JavaScript libraries removed (decreases JS footprint)

    For the server-side APIs, .NET works with 2.0 and 3.5 on 32 and 64-bit; C++ now has been upgraded to support Visual Studio 2008 and 32 and 64-bit libraries are available.

    Here's the full list of release notes:

    New Features: General — All Languages

    • ShapeCollections To make it easier to handle multiple shape collections on the map, MQA.ShapeCollection and MQW.ShapeCollection (FUJAX) replaces both PoiCollection and OverlayCollection. POIs and overlays now go into the same collection. Adding, removing, replacing, and appending include new methods.
    • Custom Tile Layers Custom tile layers can now be added to maps.
    • Multiple Collections Multiple shape collections can now be added to the map. A collection can include POIs and Overlays. Once a collection is attached to the map, adding shapes to and/or removing shapes from the collection will be automatically reflected on the map. Decluttering can also be done by collection.
    • Remote Collections KML and GeoRSS support has been built into the Platform. When creating a RemoteCollection, all you need to do is tell it the location of the feed and its format, and the RemoteCollection will be displayed on the map.
    • Minimum and Maximum Zoom Levels on POIs Minimum and maximum zoom levels can be set on any POI. The POI would then be visible on the map only between the set zoom levels.
    • Retrieving an item by key A shape (POI or overlay) can now be retrieved from the map or any collection by its key.
    • Reference to parent collection A reference to the shape's (POI or overlay) parent collection can be returned.

    JavaScript and AS3 (ActionScript™ 3) Only

    • Traffic Flow, Incidents, and Market Data Traffic information can now be displayed on the map in the form of market data, incident data (construction, incidents, events), and a traffic flow overlay.

    AS3 and FUJAX Only

    • Globe View Globe view has been upgraded to include the following enhancements:
      • Spins on all axis
      • Setting the number of triangles to adjust for performance
      • Smooth zoom animations
      • Overlays & route highlights
      • Map rotation setting

    JavaScript and FUJAX Only

    • 3rd Party Libraries All 3rd party libraries have been removed from the API (Prototype and Dojo)

    JavaScript

    • MQA Namespacing The Tilemap toolkit portion of the API has been namespaced to MQA and all MQ leading characters have been removed. For example, it would now be MQA.Tilemap instead of MQTilemap.
    • MQMapIcon The MQMapIcon is now MQA.Icon and is purely an interface, taking the image URL and the setup values in the constructor. For example: myIcon = new MQA.Icon(imageURL, width, height, offsetX, offsetY);
    • POIs & Overlays Overhaul These are both now extended off of the same base object and their getters & setters have changed. Examples:
      myPoi.setValue(propertyNameAsString, valueObject);
      returnedValue = myPoi.getValue(NameAsString);
      • A .setValues(); method exists that takes literal notation to set as many properties at once as you want. Refer to the documentation for a complete list of property names and examples.
      • Existing getters and setters will still function, but are deprecated. Effort should be made to convert these to the new methods.
    • Drop Shadow A visual drop-shadow graphic has been added to the map and can be turned on by calling map.setMapShadowState(Boolean). The shadow is off by default.

    FUJAX

    • MQW Namespacing The FUJAX Tilemap toolkit portion of the API has been namespaced to MQW and all MQ leading characters have been removed. For example, it would now be: MQW.Tilemap instead of MQTilemap.

    Supported Environments

    JavaScript

    • Windows
      • Internet Explorer 6.0 and above
      • Mozilla Firefox 2.0 and above
    • Macintosh
      • Firefox 2.0 and above
      • Safari 3.0 and above

    Adobe® ActionScript™

    • Flash player: version 9,0,115 or greater
    • AS3 SWCs: all environments
    • Flex2 Components: Adobe® Flex™ Builder™ 2
    • CS3 Components: Adobe® Creative Suite® 3 (CS3) Flash

    Updates

    • .NET
      • The library has been renamed to mapquest20.dll to reflect that this is for .NET 2.0 and above only. It has also been tested with the 2.0 and 3.5 frameworks and in both 32-bit and 64-bit environments.
    • C++
      • Windows support has been upgraded to Visual Studio 2008
      • Both 32-bit and 64-bit versions of the libraries now exist
  • Creating a custom ASP.NET control for MapQuest

    In my last post we saw that using the MapQuest NET API version 5.3 in an ASP.NET web application was not all that much harder than using it in a winforms application. Now in order to easily reuse some of the code it would be nice to create an ASP.NET custom control which displays a map just based on an origin and a destination.

    So what to do?

    1. We want to put the control in a class library, so we add an item of type 'ASP.NET Server control named 'RouteMapControl'. This will create a class which inherits from WebControl.
    2. Next we add an Origin and Destination propert of type 'GeoAddress' (from the MQClientInterface namespace).
    3. Lastly we override the 'protected override void RenderContents( HtmlTextWriter output )' method and have it use the Origin and Address to create a route and use the route to create a map.

    This is what the code for the class looks like:

    using System;
    using System.ComponentModel;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using MQClientInterface;
    using DevelopOne.MapQuest.Proxies;
    using DevelopOne.MapQuest.Extensions;
    
    namespace DevelopOne.MapQuest.Web.Controls
    {
        [DefaultProperty( "Text" )]
        [ToolboxData( "{0}:RouteMapControl>" )]
        public class RouteMapControl : WebControl
        {
            [Bindable( true )]
            [Category( "MapQuest" )]
            [DefaultValue( "" )]
            [Localizable( false )]
            public GeoAddress Origin
            {
                get
                {
                    GeoAddress o = new GeoAddress();
                    o.Init();
                    o.LatLng.Latitude = (double) ViewState["Origin_Latitude"];
                    o.LatLng.Longitude = (double) ViewState["Origin_Longitude"];
                    return o;
                }
    
                set
                {
                    ViewState["Origin_Longitude"] = value.LatLng.Longitude;
                    ViewState["Origin_Latitude"] = value.LatLng.Latitude;
                }
            }
    
            [Bindable( true )]
            [Category( "MapQuest" )]
            [DefaultValue( "" )]
            [Localizable( false )]
            public GeoAddress Destination
            {
                get
                {
                    GeoAddress o = new GeoAddress();
                    o.Init();
                    o.LatLng.Latitude = (double) ViewState["Destination_Latitude"];
                    o.LatLng.Longitude = (double) ViewState["Destination_Longitude"];
                    return o;
                }
    
                set
                {
                    ViewState["Destination_Longitude"] = value.LatLng.Longitude;
                    ViewState["Destination_Latitude"] = value.LatLng.Latitude;
                }
            }
    
            protected override void RenderContents( HtmlTextWriter output )
            {
                if ( this.Origin == null || this.Destination == null )
                {
                    return;
                }
                else
                {
                    if ( this.Width.IsEmpty )
                        this.Width = new Unit( 800 );
    
                    if ( this.Height.IsEmpty )
                        this.Height = new Unit( 600 );
    
                    RouteResults route;
                    using ( RouteServerProxy proxy = new RouteServerProxy() )
                    {
                        route = proxy.CreateRoute( this.Origin, this.Destination );
                    }
                    using ( MapServerProxy proxy = new MapServerProxy() )
                    {
                        string url = proxy.GetMapUrl( route.ShapePoints.ToLine(), (int)this.Width.Value, (int)this.Height.Value, true).ToString();
                        url = HttpUtility.HtmlEncode( url );
                        string html = "<img src=\"" + url + "\"/><br/>Distance: " + Math.Round(route.Distance, 2).ToString() + " miles.";
                        output.Write( html );
    
                    }
                }
            }
        }
    }
    

    Notice in the RenderContents method that:

    1. We're creating an HTML image tag, but also some text to display the distance of the route.
    2. The proxy.GetMapUrl has a boolean parameter as the fourth parameter. I discovered that for longer routes the amount of information in the Url becomes too much, making the Url too long and it won't load. Using the true parameter makes the proxy use a serverside session. Now the Url just contains a reference to the session and stays much shorter. The code needed to create a session is straightforward, use the Exec object in the MapQuest library.:
      string sessionId = Exec.CreateSessionEx( session );
      url = Exec.GetMapFromSessionURL( sessionId, new MQClientInterface.DisplayState() );

    By adding a reference to the class library Visual Studio 2008 will detect the control and make it available in the toolbar when you're editing an ASP.NET page. Just drag and drop the control on you form and your ready to go.

    I like my controls to have meaning full tags so I add a control reference in the web.config:

    
    <pages>
       <controls>
            <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
            <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
            <add tagPrefix="mq" namespace="DevelopOne.MapQuest.Web.Controls" assembly="DevelopOne.MapQuest"/>
      </controls>
    </pages>
    
    
    

    The markup in the page now looks like this:

    <mq:RouteMapControl ID="RouteMapControl1" runat="server" />
    

    If you choose to skip the web.config step you'll see the tag as 'cc1:RouteMapControl'.

    Happy coding!

    - Mark Blomsma

    Download the code here.

  • The AIM Map Phone

    Where Open Voice meets Geocoding

    Geocoding is hot. "Geocoding is the process of assigning geographic identifiers (e.g., codes or geographic coordinates expressed as latitude-longitude) to map features and other data records, such as street addresses. You can also geocode media, for example where a picture was taken, IP addresses, and anything that has a geographic component. With geographic coordinates the features can be mapped and entered into Geographic Information Systems" - wikipedia. Using the MapQuest API we can geocode any address in the world. In my recent article on Open Voice I showed how to use C# and .NET to build a Voice over IP phone. I've also been exploring the possibilities of the MapQuest .NET API on my blog. Now wouldn't it be great if those two worlds came together to build a phone that displays the location of the person you're calling? I call it the AIM Map Phone. And it looks like this:

    So what are the technologies used to build this phone? Well I found a commercial offering by Conaito which combines Voice over IP with and SIP programming stack: the VoIP SIP SDK. The SDK offers excellent .NET examples and implementing a basic VoIP phone took about an hour, not bad right? Having the phone working, the next step is to geocode the phone number. This turned out to be a two step process. ServiceObjects is a company which offers an XML web service for translating a phone number into an address. There are two separate services, DOTS GeoPhone which is for landlines and DOTS GeoPhone Wireless for wireless phones. The translated address can be used to find the geographical location using the MapQuest Geocode Server. The resulting coordinates can be used by the MapQuest Static Map Server to create a map of the location.
    Let's look at some of the code involved.

    Making the call

    In order to place a call using the AIM Call Out service we need to use the Open Voice API. The Open Voice API consists of an infrastructure which supports a number of open standards, allowing any standards compliant application or SDK to make use of the infrastructure. The Conaito VoIP SDK supports these standards and works like a charm with AIM Call Out. The client API is accessible from C# / .NET by dragging an ActiveX control onto the form. Calls are made by programming against this ActiveX control. In the AIM Map Phone the 'Dial' button works as a toggle and uses the following code:

      private bool _calling = false;
      private int _callId = -1;
      private void btnDial_Click( object sender, EventArgs e )
        {
        if ( _calling == true )
        {
        // hang up
        HangUp();
        }
        else
        {
        // make call
        DialNumber( txtNumber.Text );
            // ... update map ...
        }
        }
      private void DialNumber( string phoneNumber )
        {
        try
        {
        // check if previous call closed down correctly
        if ( _callId != -1 )
        {
        HangUp();
        }
        // First of all transport must be configured.
        this.axUserAgent.AddTransport( conaito.Transport.UDP, 5060 );
             // Find available RTP (media) port
        int port = this.axUserAgent.FindPort( 4000, 8000, 2, conaito.Transport.UDP
          );
             // Startup User Agent
        this.axUserAgent.Startup( port, 1,
        String.Empty,
        PhoneSetting.Default.STUNServer );
             // AIM account is used for SIP authentication
        string aimAccount = PhoneSetting.Default.AIMAccount;
        // name is used for registration
        string name = aimAccount.Substring( 0, aimAccount.IndexOf( "@" )
        );
            // REGISTER is not support by SIP.AOL.COM
        // but is required to initialize the axUserAgent
        this.axUserAgent.Registrator.AuthenticationId = aimAccount;
        this.axUserAgent.Registrator.Register(
        PhoneSetting.Default.SIPServer,
        name,
        PhoneSetting.Default.AIMDevicePassword,
        name );
            // Setup eventhandler for cleanup when call terminates
        this.axUserAgent.OnTerminated += new Axconaito._IUserAgentEvents_OnTerminatedEventHandler(
          axUserAgent_OnTerminated );
            // Make the call (asynchronous)
        // Number needs to be post fixed by "@sip.aol.com"
        _callId = this.axUserAgent.CallMaker.Invite(
        phoneNumber + "@" + PhoneSetting.Default.SIPServer );
           // Update UI
        _calling = true;
        this.btnDial.Text = "Hang up";
        }
        catch ( COMException exception )
        {
        ShowError( this.axUserAgent.LastError, exception.Message );
        }
        catch ( Exception exception )
        {
        ShowError( exception.Message, "General Exception" );
        }
        }
        private void HangUp()
        {
        // Hang up
        this.axUserAgent.CallMaker.Hangup( _callId );
        // Shutdown User Agent
        this.axUserAgent.Shutdown();
        _callId = -1;
        // Update UI
        _calling = false;
        this.btnDial.Text = "Dial";
        }
    void axUserAgent_OnTerminated( object sender, Axconaito._IUserAgentEvents_OnTerminatedEvent
        e )
        {
        HangUp();
        }
        

    The DialNumber methods follows the basic steps needed to start a call:

    1. Add a transport mechanism, in this case UDP.
    2. Prepare for RTP transport
      1. Find a port
      2. Startup the RTP host

        Note that the SDK allows for all sorts of custom tweaking of codecs, volume and input/output devices. This sample is using the default settings for all of those.
    3. Setup an eventhandler for when the call terminates.
    4. Send a SIP INVITE message to call the actual number.

      Note that the format of the phonenumber needs to be <phonenumber>@sip.aol.com.

    As you can see the sample retrieves configuration settings from the app.config file. These can be set using the Phone Settings configuration form:


    Geocode the number

    Using the ServiceObjects XML web service the phone number can be translated into an address. There are two API's one for landlines and one for wireless phones. It must be said that the service for landlines is excellent, but the one for wireless phone does not seem to know all the numbers, especially for where I live, numbers in Maine.

      private bool _calling = false;
      private int _callId = -1;
    private void btnDial_Click( object sender, EventArgs e )
        {
        if ( _calling == true )
        {
        // hang up
        HangUp();
        }
        else
        {
        // make call
        DialNumber( txtNumber.Text );
        GeoAddress address = GetAddress( txtNumber.Text );
        if ( address == null )
        {
        // unable to resolve phone number
        MessageBox.Show( this,
        "Phone number could not be translated to a geographical location.",
        "Information", MessageBoxButtons.OK, MessageBoxIcon.Information );
        }
        else
        {
        UpdateMap( address );
        }
        }
        }
    private GeoAddress GetAddress( string phoneNumber )
        {
        string americanPhoneNumber = phoneNumber.Replace( "+1", "" );
        PhoneInfo info;
        ServiceObjects.DOTSGeoPhone service = new DOTSGeoPhone();
        info = service.GetPhoneInfo( americanPhoneNumber, ServiceObjectSetting.Default.DOTSGeoPhoneLicenseKey
        );
        if ( info != null &&
        info.Contacts != null &&
        info.Contacts.Length > 0 )
        {
        Address address = new Address()
        {
        City = info.Contacts[0].City,
        Country = "US",
        PostalCode = info.Contacts[0].Zip,
        State = info.Contacts[0].State,
        Street = info.Contacts[0].Address
        };
            GeocodeServerProxy proxy = new GeocodeServerProxy();
        List<GeoAddress> result = proxy.Geocode( address );
        if ( result != null && result.Count > 0 )
        {
        return result[0];
        }
        return null;
        }
        else
        {
        return GetWirelessAddress( americanPhoneNumber );
        }
        }
    

    From a .NET perspective the service works really, really simple. Just add a web reference from your project (don't use VS 2008 Service Reference) to the DOTS GeoPhone service ( http://trial.serviceobjects.com/gp/GeoPhone.asmx?WSDL ), use the generated proxy and call the GetPhoneInfo service. Note: You will need to get a license key.

    The PhoneInfo object that is returned contains zero, one or more contacts. I the sample I take the first and use that to create a MapQuest .NET API Address object. This is passed to the GeocodeServerProxy, a custom proxy class I created (read this blog post for more details). This results in the GeoAddress of the phone number. Should the phone number not lead to a contact, then I try and use a similar method using the DOTS GeoPhone Wireless service.

    The GetAddress method uses the app.config to retrieve the license key for the ServiceObjects service. These can be set with the ServiceObjects Settings screen:


    Display the location

    The AIM Map Phone uses a custom Winforms control which will load and display a map based on the GeoAddress. The code is based on the MapQuest .NET 5.3 API, see this blog post on more information on building such a control. You will need a MapQuest account in order to use the service. The information can be set in the MapQuest Settings screen:


    The code

    Download the code for the sample here.
    In order to compile and run the code you will need to install VoIP SIP SDK v2.6 from Conaito. You will also need a (trial) account DOTS GeoPhone * DOTS GeoPhone Wireless from ServiceObjects.

    Happy coding!

  • 5.3rc5 Release for JavaScript and FUJAX APIs

    As we get closer to the final release of version 5.3 of the MapQuest Platform, we've just added Release Candidate 5 of our JavaScript and FUJAX APIs to the Beta page.

    The highlight in this release is the ability to define specific packages to be included in the code. With all of the new features we've been adding to the Platform, we recognized the need to optimize the size of your download with only the code necessary for your application.

    From the RC5 notes:

    If you want your application to have a traffic control, you can specify to include this package via the '&ipkg=controls1,traffic' URL name/value pair. However, if your application does not need the traffic control, simply use '&ipkg=controls1' and you'll save about 12k of download. This concept will be used going forward in order to optimize download sizes/speed.

    NOTE: We have separated out the map controls as a modular package - if you are using our default controls (Zoom Control, View Control, etc), you will want '&ipkg=controls1' on the string. If you are using fully customized map controls, you can now eliminate ours from the script download.

    The "ipkg" parameter

    controls1 - will bring down all 4 controls (largezoom, zoom, pan, and view).
    traffic - will bring down the traffic package. For example:

    • &ipkg=controls1 - will bring down just the controls package
    • &ipkg=traffic - will bring down just the traffic package
    • &ipkg=controls1,traffic - will bring down both the controls and traffic packages
    • &ipkg=controls1,traffic,package1,etc. - will bring down all packages listed by commas

    Also a reminder: If your a Free Edition developer, you will also need to sign-up for a Developer License for developing with MapQuest Platform Beta code.