The sample above contains three parts:
If you haven't used the Yahoo Maps API before, you can find lots of information on how to display a map and attach various widgets and markers on this Yahoo! Maps Flash API general information page, and in the Yahoo! Maps Web Services - Flash APIs Reference Manual. Another useful resource is the Yahoo Flash Developer Group email list, with developers from Yahoo who I've found to be very quick to respond to queries on the list.
To achieve step 1 of this sample, we needed a way to supply an address and get the corresponding latitude/longitude data back. Because there is a Yahoo Maps Web Service available which takes appid and address parameters as querystring parameters and returns an XML object, we can easily implement this in Flash with a LoadVars object to do the sending and an XML handler function to deal with the returned object. With input textfields addr, city and state, a dynamic textfield msg, and a v2 Button component named btnGetGeocode on stage, the code to set up variables and initiate the call to the web service looks like this:
import mx.utils.Delegate;
var lvOut:LoadVars;
var xmlGeo:XML;
// set button handler, loadVars, XML instance and onload handler
// called on program startup
function init() {
btnGetGeocode.addEventListener("click", onGeocodeRequest);
lvOut = new LoadVars();
xmlGeo = new XML();
xmlGeo.ignoreWhite = true;
xmlGeo.onLoad = Delegate.create(this, onGeocodeReceived);
}
// handler for "GET" button: Calls Yahoo routine to get geocode for passed address
function onGeocodeRequest() {
lvOut = new LoadVars();
lvOut.appid = "yourappid";
lvOut.street = streetaddr.text;
lvOut.city = city.text;
lvOut.state = state.text;
var uri:String = "http://local.yahooapis.com/MapsService/V1/geocode";
msg.text = "Requesting geocode from Yahoo...";
lvOut.sendAndLoad(uri, xmlGeo, "GET");
}
We created a LoadVars object to call the service, setting each of its parameters to one of the required inputs as described on the Web Service page referenced above (there are other possible input parameters you can look at on that page). We also created an XML object, xmlGeo, which will contain the result. The onLoad handler for that object is set to onGeocodeReceived (using Delegate to give it the same scope as the rest of the code), which looks like:
// handler for response from Yahoo (passed back as XML)
function onGeocodeReceived(ok:Boolean) {
if (ok) {
// trace(xmlGeo.firstChild); // to view returned XML object from its root down
msg.text = "Done";
lat.text = Number(xmlGeo.firstChild.firstChild.childNodes[0].firstChild.nodeValue);
lon.text = Number(xmlGeo.firstChild.firstChild.childNodes[1].firstChild.nodeValue);
} else {
msg.text = "Error reading XML";
}
}
To figure out what node we needed to access to get the latitude and longitude information, we first ran the program with the trace line uncommented, to view the object returned. (Alternately, you could just type the uri into the browser along with some querystring parameters to see the resulting XML displayed in the browser window). With all that in place, the only other line of code required is one to call the init function and start things off:
init();
Getting the latitude/longitude value for an address is nice (really nice, actually), but displaying it on a map is even better. From this Getting Started page, we learned how to attach a map dynamically to a blank movieclip, at the same time specifying the zoom level, the lat/lon to center on, and the size of the map. That was implemented in our movie by adding a blank movieclip called mapContainer to the stage, and adding this code to the main movie:
// import the necessary classes:
import com.yahoo.maps.LatLon;
import com.yahoo.maps.MapViews;
// add thisLatLon var
var thisLatLon:LatLon;
// function called when lat/lon has been returned
// creates a map with properties specified in mapdata (centered on lat/lon)
function mapDisplay(lat:Number, lon:Number):Void {
thisLatLon = new LatLon(lat, lon);
var mapdata:Object = { latlon:thisLatLon,
zoomLevelStr:3,
mapViewType:MapViews.MAP,
_xscale:2000, // from default 25 to 500
_yscale:800, // from default 25 to 200
appidStr:"yourappid" };
map = mapContainer.attachMovie("com.yahoo.maps.api.flash.YahooMap", "map", 1, mapdata);
// we'll uncomment this line for the next step:
// map.addEventListener(com.yahoo.maps.api.flash.YahooMap.EVENT_INITIALIZE, Delegate.create(this, onMapInit));
}
Note that to use that code, you'll need to have your own app id, which you can apply for on the page referenced above. You'll also need to have the Yahoo Map Component installed in your Flash IDE -- you can get that from the same page. Once it's available, you can (and must, to use it) put a copy in the library of your fla by dragging it out of the Components window onto the stage and then deleting it from the stage (it will still be in the library). The mapDisplay function is called from within onGeocodeReceived, passing the lat/lon returned from the Yahoo Web Service.
Now that the map is being displayed, it's time to make it draggable and to place a marker at the specified lat/lon. That is done by creating a listener for EVENT_INITIALIZE, which is broadcast once the map has been drawn. So uncomment the last line in mapDisplay above and create a function onMapInit to set up dragging and add a marker:
// import the necessary classes:
import com.yahoo.maps.tools.PanTool;
import com.yahoo.maps.markers.CustomSWFMarker;
// function called when map has been created: adds pan capability and places a marker
// (a movieclip in the library with linkage id "marker") at lat, lon specified by thisLatLon
function onMapInit(o:Object):Void {
var pantool:PanTool = new PanTool();
map.addTool(pantool, true);
// add marker at location specified by lat lon passed in
map.addMarkerByLatLon(CustomSWFMarker, thisLatLon, {url:"marker", ref:1, useClip:true} );
}
The last line of that function says to add a marker from the library with linkage id "marker" at the lat/lon specified.
This step illustrates another way to add markers to a Flash Yahoo Map: with an external swf instead of a clip in the library. The advantage of doing that is that then any function in the external swf can be called by the main movie using callCustomMethod as described at the bottom of the Getting Started page.
So we created a separate fla (yahooMapMarker.fla) with a marker movieclip on one layer and this setup function on the actions layer:
// function to be called when marker is attached to map, to set parameters as described
// in the program description above
function setView($lat:Number, $lon:Number, $l:Number, $r:Number, $t:Number, $b:Number, $w:Number, $h:Number, $savelat:TextField, $savelon:TextField):Void {
lat = $lat;
lon = $lon;
rbound = $r;
lbound = $l;
tbound = $t;
bbound = $b;
lonPerPixel = ($r - $l) / $w; // a positive number, representing delta l -> r
latPerPixel = ($t - $b) / $h; // a positive number, representing delta b -> t
savelat = $savelat;
savelon = $savelon;
}
(and declaring all those variables first). That function gets called when the marker is attached, with code we'll show in a bit, and saves all the information needed to calculate the new position of the marker when it is dragged. That calculation is done when the mouse is released after dragging the marker, with this code:
// save start location when marker is dragged
marker.onPress = function() {
startx = this._x;
starty = this._y;
this.startDrag(false);
}
// when dragging stops and mouse is released, calculate new lat/lon and display
marker.onRelease = marker.onReleaseOutside = function() {
this.stopDrag();
var newlon:Number = (this._x - startx) * lonPerPixel + Number(savelon.text);
var newlat:Number = (starty - this._y) * latPerPixel + Number(savelat.text);
savelat.text = newlat.toString();
savelon.text = newlon.toString();
}
Obviously from the code, the newly calculated value is also displayed in the textfield specified when setView was called above.
So, how is the setView function called? It's called with callCustomMethod, a method of the marker itself, once it has been initialized. To use it, we first need to add code to calculate the bounds of the map (requiring the LatLonRect class) in onMapInit:
import com.yahoo.maps.LatLonRect;
var mapBounds:LatLonRect;
function onMapInit(o:Object):Void {
var pantool:PanTool = new PanTool();
map.addTool(pantool, true);
mapBounds = o.bounds;
// add marker from external swf (with its own setView function) at location
// specified by thisLatLon
map.addMarkerByLatLon(CustomSWFMarker, thisLatLon, {url:"yahooMapMarker.swf", ref:2, useClip:false} );
}
and include another listener assignment in the mapDisplay function:
map.addEventListener(com.yahoo.maps.api.flash.YahooMap.EVENT_CUSTOM_MARKER_LOADED, Delegate.create(this, onMarkerLoaded));
and add this onMarkerLoaded function to the program:
function onMarkerLoaded(o:Object):Void {
// setView expects L, R, T, B
o.marker.callCustomMethod("setView", [thisLatLon.y, thisLatLon.x, mapBounds.minLon, mapBounds.maxLon, mapBounds.minLat, mapBounds.maxLat, 500, 200, lat, lon]);
}
The combination of those last two pieces of code (listener & function) causes the setView function inside yahooMapMarker.swf to be called once the marker has been attached to the map.
So, that's it. I've found this code really useful in the admin section of my Rockville Living site, to make sure the geocode information displayed is as correct as possible. A zip of the fla and .as files representing each of the steps above (including yahoomap3, which contains the complete code, and yahooMapMarker.fla, which contains the marker and its code), may be downloaded via the subscription link at right.
last update: 8 Mar 2007
Discussed on this page:
Use Yahoo map api with Flash, geocode, latitude, longitude, marker, swf, library, loadVars in, XML out
Files:
yahoomap1.fla and .as
yahoomap2.fla and .as
yahoomap3.fla and .as
yahooMapMarker.fla
In yahoomap.zip, password required
Subscription:
A password may be obtained by subscription ($20 for one month)
An access password will be emailed to the address you specify within 24 hours of receipt of payment, and will remain active for 30 days thereafter. A list of all files currently available at the site may be viewed here.