Wednesday, December 03, 2008    
Home My Books Blog ColdFusion About Me Back    

Calendar
<< Jun 2008 >>
S M T W T F S
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30          

Search

Categories
 • Acrobat (2) [RSS]
 • Adobe (71) [RSS]
 • AdobeMAX06 (45) [RSS]
 • AdobeMAX07 (59) [RSS]
 • AdobeMAX08 (57) [RSS]
 • AdobeMAX09 (1) [RSS]
 • AIR (148) [RSS]
 • Appearances (140) [RSS]
 • Books (69) [RSS]
 • CFEclipse (14) [RSS]
 • ColdFusion (1179) [RSS]
 • Data Services (20) [RSS]
 • Fish Tank (2) [RSS]
 • Flash (112) [RSS]
 • Flex (385) [RSS]
 • Home Automation (3) [RSS]
 • Jobs (101) [RSS]
 • JRun (13) [RSS]
 • Labs (29) [RSS]
 • LiveCycle (23) [RSS]
 • MAX (189) [RSS]
 • Regular Expressions (15) [RSS]
 • RIA (13) [RSS]
 • SQL (38) [RSS]
 • Stuff (506) [RSS]
 • Tips (CF Studio) (80) [RSS]
 • Tips (CF) (795) [RSS]
 • Tips (Dreamweaver) (91) [RSS]
 • Tips (Flex Builder) (2) [RSS]
 • Using CF (140) [RSS]
 • Wireless (100) [RSS]

Other BLOGs
 • Charlie Arehart
 • Lee Brimelow
 • Ray Camden
 • Christophe Coenraets
 • Sean Corfield
 • Mihai Corlan
 • Cornel Creanga
 • John Dowdell
 • Danny Dura
 • Enrique Duvos
 • Steven Erat
 • Kevin Hoyt
 • Serge Jespers
 • Adam Lehman
 • Duane Nickull
 • Miti Pricope
 • Andrew Shorten
 • Ryan Stewart
 • James Ward
 • Greg Wilson
 • Full As A Goog

RSS Feeds
 • Feed
 • Subscribe

Join my mailing list and find out about new books and other topics of interest.

Thoughts, ideas, tips, musings, and pontifications (not necessarily in that order) by Ben Forta ...
NOTE: This is my personal blog, and the opinions and statements voiced here are my own.

Viewing By Day : June 12, 2008 / Main
June 12, 2008

Getting Started With Flex Based Yahoo! Maps

The Yahoo! Maps AS3 Component is nothing short of astounding, a clean and powerful Flex component that delivers sophisticated mapping quickly and easily. I needed to add mapping to an app recently and was pleased to see how well this component delivered. If you are interested in adding mapping to your apps, I highly recommend that you take a look at this component. And here are detailed steps to help you get started.

To use the Yahoo! Maps AS3 Component you'll need your own Yahoo! Developer Network Application ID. If you have one already, then you're all set. If not, click on the "Get an App ID" link on the component page to obtain one.

Once you have an App ID, you'll need to download the Flex component itself. There is a download link in the middle of the component page, download the ZIP file, and expand it in a local folder somewhere. There are lots of files in the ZIP file, but the really important file is the YahooMap.swc file. Make sure you know the path to this file, you'll need it when you create your Flex project.

Then create a new Project in Flex Builder. You can define a Server Technology if needed, although for tinkering the first time you may want to keep the Project as simple as possible. In the 3rd screen in the New Flex Project wizard you can specify Source Paths and Library Paths. Select the "Library path" tab as you'll need to add the YahooMap.swc. Click the "Add SWC" button, and then browse to the YahooMap.swc file and click "OK". You should see the YahooMap.swc listed in the "Build path libraries" list. You can then finish creating the Project.

Now let's create the basic UI. This example will display the map in a large box on the right, and the left will contain simple text boxes, one to enter the map location, and the other to specify map searches. Here's the MXML code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">

   
   <mx:Panel layout="horizontal" title="Yahoo! Maps Example"
            height="100%" width="100%">

      <mx:VBox height="100%" width="200">
         <mx:Label text="Location:" fontWeight="bold"/>
         <mx:HBox width="100%">
            <mx:TextInput id="txtLocation" width="100%"/>
            <mx:Button label="Go!"/>
         </mx:HBox>
         <mx:Label text="Find:" fontWeight="bold"/>
         <mx:HBox width="100%">
            <mx:TextInput id="txtFind" width="100%"/>
            <mx:Button label="Go!"/>
         </mx:HBox>
      </mx:VBox>
      <mx:UIComponent id="mapUI" height="100%" width="100%" />
   </mx:Panel>

</mx:Application>

The code is pretty simple, and the only really interesting item is the <mx:UIComponent> which will be used to display the map. Paste this code into your app, and compile and run it, just to make sure all is working.

Next we'll add basic mapping. Here is the updated code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
            creationComplete="initApp()">


   <mx:Script>
      <![CDATA[
         import mx.events.ResizeEvent;
         import com.yahoo.maps.api.core.location.LatLon;
         import com.yahoo.maps.api.YahooMapEvent;
         import com.yahoo.maps.api.YahooMap;

         public var appID:String = "YOUR APP ID";
         public var yahooMap:YahooMap;

         // Called on app creationComplete
         public function initApp():void
         {
            // Create YahooMap instance
            yahooMap=new YahooMap();
            // Set initialization handler
            yahooMap.addEventListener(YahooMapEvent.MAP_INITIALIZE, yahooMapInitialize);
            // Initialize map
            yahooMap.init(appID, mapUI.width, mapUI.height);
            // Turn on map panning
            yahooMap.addPanControl();
            // Display scale bar
            yahooMap.addScaleBar();
            // Turn on map type control
            yahooMap.addTypeWidget();
            // Turn on zoom control
            yahooMap.addZoomWidget();
            // And finally add the map to the UI
            mapUI.addChild(yahooMap);
         }

         // Initialize event handler
         public function yahooMapInitialize(event:YahooMapEvent):void
         {
            // Set a resize event handler
            mapUI.addEventListener(ResizeEvent.RESIZE, yahooMapResize);
            // Set initial zoom level
            yahooMap.zoomLevel = 14;
            // Set the map somwehere for starters
            yahooMap.centerLatLon = new LatLon(40.81,-96.7);
         }

         // Resize event handler
         public function yahooMapResize(event:ResizeEvent):void
         {
            // Reset map size
            yahooMap.setSize(mapUI.width, mapUI.height);
         }
      ]]>
   </mx:Script>
   
   <mx:Panel layout="horizontal" title="Yahoo! Maps Example"
            height="100%" width="100%">

      <mx:VBox height="100%" width="200">
         <mx:Label text="Location:" fontWeight="bold"/>
         <mx:HBox width="100%">
            <mx:TextInput id="txtLocation" width="100%"/>
            <mx:Button label="Go!"/>
         </mx:HBox>
         <mx:Label text="Find:" fontWeight="bold"/>
         <mx:HBox width="100%">
            <mx:TextInput id="txtFind" width="100%"/>
            <mx:Button label="Go!"/>
         </mx:HBox>
      </mx:VBox>
      <mx:UIComponent id="mapUI" height="100%" width="100%" />
   </mx:Panel>

</mx:Application>

Let's take a look at the above code. The big change is the <mx:Script> block. First come a series of import statements, and then two variables are defined. The first, appID, contains your Yahoo! Developer Network ID, replace the placeholder text in quotes with your own ID. The second defines the YahooMap itself.

The initApp() function (called on creationComplete) first creates a new YahooMap instance, and then defines the handler that will be called upon map initialization. It then initializes the map, passing the appID, along with the desired height and width (using the height and width of the mapUI UIComponent). Next a series of options are set, turning on panning, the scale bar, the map type widget, and the zoom control (you can omit these, or add other options if desired). And then finally, addChild() is used to add the yahooMap object to the mapUI UIComponent.

The code also contains two other functions. yahooMapInitialize() is called once the map is initialized (in response to a YahooMapEvent.MAP_INITIALIZE event). It sets the default zoom level and initial map location, and also specifies a resize handler. This is important, without a resize handler the displayed map would not resize if the app were resized. The resize handler function itself, yahooMapResize(), is called whenever the app is resized (in response to a ResizeEvent.RESIZE event), and it simply resets the map size (again using the height and width of the mapUI UIComponent).

You can paste this code into your app, and run it. You'll have a fully functioning map, allowing zooming and panning, and supporting multiple display types (Map, Satellite, and Hybrid).

Next we'll add code to allow users to enter a map location. Here's the updated code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
            creationComplete="initApp()">


   <mx:Script>
      <![CDATA[
         import mx.events.ResizeEvent;
         import com.yahoo.maps.webservices.geocoder.GeocoderResult;
         import com.yahoo.maps.webservices.geocoder.GeocoderResultSet;
         import com.yahoo.maps.webservices.geocoder.events.GeocoderEvent;
         import com.yahoo.maps.api.core.location.Address;
         import com.yahoo.maps.api.core.location.LatLon;
         import com.yahoo.maps.api.YahooMapEvent;
         import com.yahoo.maps.api.YahooMap;

         public var appID:String = "YOUR APP ID";
         public var yahooMap:YahooMap;
         public var address:Address;


         // Called on app creationComplete
         public function initApp():void
         {
            // Create YahooMap instance
            yahooMap=new YahooMap();
            // Set initialization handler
            yahooMap.addEventListener(YahooMapEvent.MAP_INITIALIZE, yahooMapInitialize);
            // Initialize map
            yahooMap.init(appID, mapUI.width, mapUI.height);
            // Turn on map panning
            yahooMap.addPanControl();
            // Display scale bar
            yahooMap.addScaleBar();
            // Turn on map type control
            yahooMap.addTypeWidget();
            // Turn on zoom control
            yahooMap.addZoomWidget();
            // And finally add the map to the UI
            mapUI.addChild(yahooMap);
         }

         // Initialize event handler
         public function yahooMapInitialize(event:YahooMapEvent):void
         {
            // Set a resize event handler
            mapUI.addEventListener(ResizeEvent.RESIZE, yahooMapResize);
            // Set initial zoom level
            yahooMap.zoomLevel = 14;
            // Set the map somwehere for starters
            yahooMap.centerLatLon = new LatLon(40.81,-96.7);
         }

         // Resize event handler
         public function yahooMapResize(event:ResizeEvent):void
         {
            // Reset map size
            yahooMap.setSize(mapUI.width, mapUI.height);
         }

         // Set map to passed location
         public function setMapLocation(location:String):void
         {
            // Create address object for passed location
            address = new Address(location);
            // Add listener to process geocode event
            address.addEventListener(GeocoderEvent.GEOCODER_SUCCESS, yahooGeocodeSuccess);
            // Geocode it
            address.geocode();
         }

         //Geocode success event handler
         public function yahooGeocodeSuccess(event:GeocoderEvent):void
         {
            var geocoderResults:GeocoderResultSet = address.geocoderResultSet;
   
            // May return multiple result, just use 1st
            // Set zoom level
            yahooMap.zoomLevel = geocoderResults.firstResult.zoomLevel;
            // Set map location
            yahooMap.centerLatLon = geocoderResults.firstResult.latlon;
         }
      ]]>
   </mx:Script>
   
   <mx:Panel layout="horizontal" title="Yahoo! Maps Example"
            height="100%" width="100%">

      <mx:VBox height="100%" width="200">
         <mx:Label text="Location:" fontWeight="bold"/>
         <mx:HBox width="100%">
            <mx:TextInput id="txtLocation" width="100%"
                     enter="setMapLocation(txtLocation.text)"/>

            <mx:Button label="Go!"
                     click="setMapLocation(txtLocation.text)"/>

         </mx:HBox>
         <mx:Label text="Find:" fontWeight="bold"/>
         <mx:HBox width="100%">
            <mx:TextInput id="txtFind" width="100%"/>
            <mx:Button label="Go!"/>
         </mx:HBox>
      </mx:VBox>
      <mx:UIComponent id="mapUI" height="100%" width="100%" />
   </mx:Panel>

</mx:Application>

Not a lot of changes here. First, we've added some more needed import statements. We've also added a variable of type Address (to store the set map location). Two functions have been added to the code. setMapLocation() accepts a string (whatever the user typed in the txtLocation box) and then creates an Address object for the passed string. As you are probably noticing, the Yahoo! Maps component is used asynchronously and is thus highly event driven. setMapLocation() sets an event handler that will be called when the Address object has been successfully geocoded, and then it perform the geocode. The geocode operation does not do anything to the map, rather it locates matches and converts them into map locations. The event handler, yahooGeocodeSuccess(), first obtains any results generated by the geocode operation. It is possible for a geocode operation to return multiple results, but this code ignores all but the first match, and then uses that first match to set the map zoom level and location. And finally, setMapLocation() was added to the first <mx:TextInput> and <mx:Button> to the enter and click events respectively.

Save these changes, and then run the app. Now you can type in a country name, or a city and state, or a US zipcode, and the map will be repositioned to display the best match.

Now for the final change, this time allowing you to search for matches within the displayed map. Here's the code one last time:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
            creationComplete="initApp()">


   <mx:Script>
      <![CDATA[
         import mx.events.ResizeEvent;
         import com.yahoo.maps.webservices.geocoder.GeocoderResult;
         import com.yahoo.maps.webservices.geocoder.GeocoderResultSet;
         import com.yahoo.maps.webservices.geocoder.events.GeocoderEvent;
         import com.yahoo.maps.api.core.location.Address;
         import com.yahoo.maps.api.core.location.LatLon;
         import com.yahoo.maps.api.markers.SearchMarker;
         import com.yahoo.maps.webservices.local.LocalSearch;
         import com.yahoo.maps.webservices.local.LocalSearchItem;
         import com.yahoo.maps.webservices.local.LocalSearchResults;
         import com.yahoo.maps.webservices.local.events.LocalSearchEvent;
         import com.yahoo.maps.api.YahooMapEvent;
         import com.yahoo.maps.api.YahooMap;

         public var appID:String = "YOUR APP ID";
         public var yahooMap:YahooMap;
         public var address:Address;
         public var localSearch:LocalSearch;


         // Called on app creationComplete
         public function initApp():void
         {
            // Create YahooMap instance
            yahooMap=new YahooMap();
            // Set initialization handler
            yahooMap.addEventListener(YahooMapEvent.MAP_INITIALIZE, yahooMapInitialize);
            // Initialize map
            yahooMap.init(appID, mapUI.width, mapUI.height);
            // Turn on map panning
            yahooMap.addPanControl();
            // Display scale bar
            yahooMap.addScaleBar();
            // Turn on map type control
            yahooMap.addTypeWidget();
            // Turn on zoom control
            yahooMap.addZoomWidget();
            // And finally add the map to the UI
            mapUI.addChild(yahooMap);

            // Local search object
            localSearch = new LocalSearch();
            // Set search success listener
            localSearch.addEventListener(LocalSearchEvent.SEARCH_SUCCESS, yahooSearchSuccess);
         }

         // Initialize event handler
         public function yahooMapInitialize(event:YahooMapEvent):void
         {
            // Set a resize event handler
            mapUI.addEventListener(ResizeEvent.RESIZE, yahooMapResize);
            // Set initial zoom level
            yahooMap.zoomLevel = 14;
            // Set the map somwehere for starters
            yahooMap.centerLatLon = new LatLon(40.81,-96.7);
         }

         // Resize event handler
         public function yahooMapResize(event:ResizeEvent):void
         {
            // Reset map size
            yahooMap.setSize(mapUI.width, mapUI.height);
         }

         // Set map to passed location
         public function setMapLocation(location:String):void
         {
            // Create address object for passed location
            address = new Address(location);
            // Add listener to process geocode event
            address.addEventListener(GeocoderEvent.GEOCODER_SUCCESS, yahooGeocodeSuccess);
            // Geocode it
            address.geocode();
         }

         //Geocode success event handler
         public function yahooGeocodeSuccess(event:GeocoderEvent):void
         {
            var geocoderResults:GeocoderResultSet = address.geocoderResultSet;
   
            // May return multiple result, just use 1st
            // Set zoom level
            yahooMap.zoomLevel = geocoderResults.firstResult.zoomLevel;
            // Set map location
            yahooMap.centerLatLon = geocoderResults.firstResult.latlon;
         }

         // Search map
         public function mapSearch(search:String):void
         {
            // Clear any markers
            yahooMap.markerManager.removeAllMarkers();
            // Then do search
            localSearch.searchLocal(search, yahooMap.zoomLevel,
                                    yahooMap.centerLatLon, 25);
         }

         // Search success handler
         public function yahooSearchSuccess(event:LocalSearchEvent):void
         {
            var localSearchResults:LocalSearchResults = event.data as LocalSearchResults;
            var results:Array = localSearchResults.results;

            // Loop through results
            for(var i:int=0; i<results.length; i++)
            {
               // Get a search result
               var item:LocalSearchItem = results[i];
               // Create a marker
               var marker:SearchMarker = new SearchMarker(item);
               // Add marker
               yahooMap.markerManager.addMarker(marker);
            }
         }
      ]]>

   </mx:Script>
   
   <mx:Panel layout="horizontal" title="Yahoo! Maps Example"
            height="100%" width="100%">

      <mx:VBox height="100%" width="200">
         <mx:Label text="Location:" fontWeight="bold"/>
         <mx:HBox width="100%">
            <mx:TextInput id="txtLocation" width="100%"
                     enter="setMapLocation(txtLocation.text)"/>

            <mx:Button label="Go!"
                     click="setMapLocation(txtLocation.text)"/>

         </mx:HBox>
         <mx:Label text="Find:" fontWeight="bold"/>
         <mx:HBox width="100%">
            <mx:TextInput id="txtFind" width="100%"
                     enter="mapSearch(txtFind.text)"/>

            <mx:Button label="Go!"
                     click="mapSearch(txtFind.text)"/>

         </mx:HBox>
      </mx:VBox>
      <mx:UIComponent id="mapUI" height="100%" width="100%" />
   </mx:Panel>

</mx:Application>

Once again, more import statements have been added. We've also added a variable of type LocalSearch to perform local searches (searches within the displayed map). The second <mx:TextInput> and <mx:Button> controls now invoke a function named mapSearch(). This function first removes any map markers (left from a previous search, there will obviously be none the first time this function is invoked), and then performs a local search specifying the search text, map zoom level and location, and search radius (in miles). This search is also asynchronous, and the yahooSearchSuccess() function is invoked when the search completes. (This event handler is added in the initApp() function above). yahooSearchSuccess() obtains the results and then loops through them, creating a LocalSearchItem for each and then using that to create a SearchMarker, and then adding that marker to the map.

Save this code, and then run the app. Type in a location, and then do a search for "ATM" or "pizza" or anything else. You can try setting the map location to "San Jose, CA" and then find "Adobe Systems, Inc.".

Pretty cool, and not a lot of code either. And that's just the start of it. Yahoo! has posted lots of great examples, so check those out. And the component also supports distance, traffic data, and much more, so be sure to browse the API documentation. And once you have the basics down, adding integration to backend data (perhaps pulling addresses from a backend ColdFusion app to map them) is just as easy.

Have fun with this, and if you create anything cool and public facing, please share the URL.

TrackBacks
Forta discusses Yahoo's AS3 Map API
Ben Forta wrote a nice introduction to get started with the Yahoo Map API for AS3. Check out his examples to get up and running quickly. Be careful to add the SWC file exactly in the right place, where you'll remember where it is. I fumbled around for a few minutes before I realized that it wasn't loading the SWC. I just had to clean the project, compile and run again for the map to appear.
Tracked by The Daily Process | Tracked on 6/13/08 12:09 AM

No trackback URL. Trackbacks are only allowed via interactive form.

Comments
I like the geocoding of the Yahoo! Maps API, but for actually displaying the maps, I prefer the MapQuest API. IMO, the map component is a bit cleaner. I routinely use the Yahoo! geocoder, then plot on the MapQuest map. It just goes to show how interchangeable these components can all be when used together.
# Posted By Andy Powell | 6/12/08 3:11 PM

  © Copyright 1997-2008 Ben Forta, All Rights Reserved