Using The New Yahoo! Maps ActionScript 3 API - Create A SearchMarker Using Your Own Data
The Yahoo! maps AS3 API provides a SearchMarker class that is used to display on the map an icon representing a LocalSearchItem class object. The LocalSearchItem class is used to store data contained in a result returned from searching the Yahoo! Local listings (see the related entry below). This data includes phone, address, website, title, etc. However, you can create your own LocalSearchItem objects and use these objects to create SearchMarker objects and display those on the map. The LocalSearchItem class documentation is a bit confusing on how to create an object of the class, so I thought a blog entry and example (right click on the app to view source code) might be helpful.
To create a LocalSearchItem object you must provide the constructor with an XML object that contains various nodes (for example <addr>, <city>, <state>). What confused me about the XML was that in the documentation for LocalSearchItem it lists a latlon property of type latlon. So I was initially stumped as how I could create an XML object that contained a node with a latlon object. But then Dan Tavelli pointed out to me that the XML results returned from doing a search of Yahoo! Local contained a <lat> and a <lon> node and not some kind of latlon object.
So I experimented with creating an XML object that contained nodes to match the properties of the LocalSearchItem class, except for using separate <lat> and <lon> nodes. Also, I found out that if you want the phone number to display on the SeachMarker object you should use a node named <dphone> not phone (as the LocalSearchItem class documentation lists).
In my example I created an Address object and then called the geocode method. If the Address object can be successfully geocoded, my function handleGeocodeSuccess is executed. Now my Address object has a geocoderResultSet property that I can use to pull out the information I need to help create the XML used to create the LocalSearchItem object. For example to get the latitude value I can use:
address.geocoderResultSet.firstResult.latitude
or to get the city value I can use:
address.geocoderResultSet.firstResult.city
Take a look at class GeocodeResult to see all the values you can access after your address is successfully geocoded.
After building the XML object, I used it to create the LocalSearchItem object. I then used the LocalSearchItem object to create the SearchMarker object and then added the SearchMarker object to the map. When you click on the SearchMarker object the information I used to create the XML is displayed.
Using the above technique, it would not be difficult to get data from your database (using ColdFusion of course!) that included, an address string, a phone number, a URL, a title, etc and then use that data to create SearchMarker objects and display them on your map.
I am still left missing the old poi marker. I really prefer the style of it much more than the new ultra-minimalist look. I also found it much more usable, like how the old marker displayed the name of the listing even when collapsed which makes the results stand out from the map more I thought, and the rollover transitions which gave an indication the marker was cilckable. Anyway I'm going to see if I can port the old poi marker over to the as3 marker class. I'll let you know if I ever get it working.
<strike>I couldn't right-click for the source (did nothing), so I'm still searching for the XML format</strike>. (Couldn't post to your blog either - my Firebird must be funked up, both worked in Satari)
my modified handler...
private function handleGeocodeResult(event:GeocoderEvent):void {
var resultset:GeocoderResultSet = event.data as GeocoderResultSet;
var result:GeocoderResult = resultset.firstResult as GeocoderResult;
var item:LocalSearchItem = new LocalSearchItem(<listing> <id>{result.woeid}</id>
<title>TITLE</title>
<addr>{result.line1}</addr>
<city>{result.city}</city>
<state>{result.statecode}</state>
<zip>{result.uzip}</zip>
<lat>{result.latitude}</lat>
<lon>{result.longitude}</lon>
</listing>);
var marker:SearchMarker = new SearchMarker(item);
yahooMap.markerManager.addMarker(marker);
}
that the asynch geocode method doesn't hand over a token is a broken API! If I want to volley off 20 geocode requests, how am I to tell the results apart? (so that titles and phones can appropriately be connected)
Thanks for your excellent examples! How can I use the example above to add more than one marker (address). Can I use an external xml file to set up a number of addresses? Also, how can I open the "more info" url in a new window (_blank).
Many thanks again!
package custom
{
import com.yahoo.maps.api.core.location.LatLon;
import com.yahoo.maps.api.markers.Marker;
import com.yahoo.maps.api.utils.Distance;
import com.yahoo.maps.api.utils.DistanceResult;
import com.yahoo.maps.webservices.local.LocalSearchItem;
import flash.display.*;
import flash.events.*;
import flash.filters.DropShadowFilter;
import flash.geom.*;
import flash.net.URLRequest;
import flash.net.navigateToURL;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
public class CustomSearchMarker extends Marker
{
public var markerID:String;
public var groupID:String;
public var markerColor:uint;
private var _ds:DropShadowFilter;
private var _titleText:TextField;
private var _addressText:TextField;
private var _cityStZipText:TextField;
private var _phoneText:TextField;
private var _urlText:TextField;
private var _distanceText:TextField;
private var titleFormat:TextFormat;
private var markerTextFormat:TextFormat;
private var urlFormat:TextFormat;
private var custMarkerShape:Sprite;
private var lgMarkerShape:Sprite;
private var closeButton:Sprite;
private var localSearchItem:LocalSearchItem;
private var calcDCCDistance:DistanceResult;
public function CustomSearchMarker(color:uint, centerLatLon:LatLon, localSearchItem:LocalSearchItem, localSearchCategory:String, catFilter:String)
{
super();
this.groupID = catFilter;
this.markerColor = color;
this.markerID = localSearchItem.id;
this.localSearchItem = localSearchItem;
custMarkerShape = new Sprite();
custMarkerShape.graphics.lineStyle(1,0xFFFFFF);
custMarkerShape.graphics.beginFill(color,1);
custMarkerShape.graphics.lineTo(3,-5);
custMarkerShape.graphics.lineTo(7,-5);
custMarkerShape.graphics.lineTo(7,-15);
custMarkerShape.graphics.lineTo(-7,-15);
custMarkerShape.graphics.lineTo(-7,-5);
custMarkerShape.graphics.lineTo(-3,-5);
custMarkerShape.graphics.lineTo(0,0);
custMarkerShape.graphics.endFill();
addChild(custMarkerShape);
custMarkerShape.buttonMode = true;
custMarkerShape.useHandCursor = true;
_ds = new DropShadowFilter(3, 45, 0x000000, .7, 2, 2, 1, 3);
custMarkerShape.addEventListener(MouseEvent.CLICK, onClick);
custMarkerShape.addEventListener(MouseEvent.MOUSE_OVER, onOver);
custMarkerShape.addEventListener(MouseEvent.MOUSE_OUT, onOut);
calcDCCDistance = Distance.getGreatCircleDistance(centerLatLon,localSearchItem.latlon);
}
public function onClick(event:Event):void{
promoteToTop(); //Bring the marker to the top of the stack
custMarkerShape.visible = false; //Hide the little marker
titleFormat = new TextFormat();
titleFormat.size = 11;
titleFormat.color = 0xFFFFFF;
titleFormat.bold = true;
titleFormat.font = "Arial";
markerTextFormat = new TextFormat();
markerTextFormat.size = 10;
markerTextFormat.bold = false;
markerTextFormat.color = 0x333333;
markerTextFormat.font = "Arial";
urlFormat = new TextFormat();
urlFormat.size = 11;
urlFormat.color = 0x0000FF;
urlFormat.font = "Arial";
//Add Title Textfield
_titleText = new TextField();
_titleText.width = 1;
_titleText.height = 1;
_titleText.autoSize = TextFieldAutoSize.LEFT;
_titleText.text = localSearchItem.title;
//Add Address Textfield and concat City, State ZIP phone and distance
_addressText = new TextField();
_addressText.width = 0;
_addressText.height = 0;
_addressText.autoSize = TextFieldAutoSize.LEFT;
_addressText.text = localSearchItem.addr+"\n"+localSearchItem.city + ", " + localSearchItem.state + " " + localSearchItem.zip+"\n"+localSearchItem.phone+"\n"+calcDCCDistance.miles.toFixed(1) + " miles from Dattoli";
/*
//Add City, State, Zip Textfield
_cityStZipText = new TextField();
_cityStZipText.width = 0;
_cityStZipText.height = 0;
_cityStZipText.autoSize = TextFieldAutoSize.LEFT;
_cityStZipText.text = localSearchItem.city + ", " + localSearchItem.state + " " + localSearchItem.zip;
//Phone Textfield
_phoneText = new TextField();
_phoneText.width = 0;
_phoneText.height = 0;
_phoneText.autoSize = TextFieldAutoSize.LEFT;
_phoneText.text = localSearchItem.phone;
//Distance TextField
_distanceText = new TextField();
_distanceText.width = 0;
_distanceText.height = 0;
_distanceText.autoSize = TextFieldAutoSize.LEFT;
_distanceText.text = calcDCCDistance.miles.toFixed(1) + " miles from Dattoli";
*/
//Calculate max width & height of all Textfields so we can use in dynaimic background shape calculations
var w:Number = Math.round(_titleText.textWidth + 20);
var h:Number = _titleText.textHeight + _addressText.textHeight;// + _cityStZipText.textHeight + _phoneText.textHeight + _distanceText.textHeight + 10;
var radius:Number = 8;
var padding:Number = 8;
if(_addressText.textWidth > _titleText.textWidth){
w = _addressText.textWidth;
}
//Add URL Textfield
if(localSearchItem.websiteURL().length > 0){
_urlText = new TextField();
//_urlText.text = "More Info";
_urlText.htmlText = "<A HREF='event:" + localSearchItem.websiteURL() + "' target='_blank'>More Info</A>";
_urlText.x = w /2 - _urlText.textWidth + 5;
_urlText.y = -_urlText.textHeight -padding - 3;
_urlText.setTextFormat(urlFormat);
_urlText.addEventListener(TextEvent.LINK, goToMarkerURL);
h += Math.round(_urlText.textHeight);
}
//Draw Marker Background Shape
lgMarkerShape = new Sprite();
var tipShape:Array;
tipShape = [[0, 0], [7, -7], [w / 2, -7], [w / 2 + radius + padding, -7, w / 2 + radius + padding, -7 -radius], [w / 2 + radius + padding, -h], [w / 2 + padding + radius, -radius-h, w / 2, -radius-h], [-w / 2 - padding , -radius-h], [-w / 2 -padding -radius, -radius-h, -w / 2 -padding -radius, -h],[-w/2 -padding -radius, -7-radius],[-w / 2 - padding -radius, -7, -w /2 - padding, -7], [-7, -7], [0,0]];
//---------[--1--]--[--2--]--[---3-----]--[-----------4------------------------------------------------------]--[------------5---------------]--[---------------------------6-------------------------]--[---------7------------------]--[--------------------------8------------------------------------]-[-------------------9------------]-[--------------10---------------------------------]--[--11--]--[-12-]
var len:int = tipShape.length;
lgMarkerShape.graphics.lineStyle(2,0xFFFFFF);
lgMarkerShape.graphics.beginFill(markerColor,1);
for (var i:int = 0; i < len; i++) {
if (i == 0) {
lgMarkerShape.graphics.moveTo(tipShape[i][0], tipShape[i][1]);
}
else if (tipShape[i].length == 2) {
lgMarkerShape.graphics.lineTo(tipShape[i][0], tipShape[i][1]);
}
else if (tipShape[i].length == 4) {
lgMarkerShape.graphics.curveTo(tipShape[i][0], tipShape[i][1], tipShape[i][2], tipShape[i][3]);
}
}
lgMarkerShape.graphics.endFill();
lgMarkerShape.filters = [_ds];
lgMarkerShape.useHandCursor = false;
addChild(lgMarkerShape);
//Draw Close Button
closeButton = new Sprite();
closeButton.graphics.lineStyle(2,0xFFFFFF);
closeButton.graphics.beginFill(0xCCCCCC, 1);
closeButton.graphics.drawCircle(0,0,8);
closeButton.graphics.endFill();
closeButton.graphics.lineStyle(2,0x666666);
closeButton.graphics.moveTo(-3,-3);
closeButton.graphics.lineTo(3,3);
closeButton.graphics.moveTo(-3,3);
closeButton.graphics.lineTo(3,-3);
closeButton.x = Math.round(w/2 + padding + (radius /2));
closeButton.y = Math.round(-h -(padding /2));
closeButton.buttonMode = true;
closeButton.addEventListener(MouseEvent.CLICK, onCloseHandler);
//Layout Textfields
_titleText.x = Math.round(-w / 2 - padding);
_titleText.y = Math.round(-h - padding/1.5);
_addressText.x = _titleText.x;
_addressText.y = _titleText.y + _titleText.textHeight;
//Set Text Formats
_titleText.setTextFormat(titleFormat);
_addressText.setTextFormat(markerTextFormat);
//Add text Children to Background Shape
lgMarkerShape.addChild(_titleText);
lgMarkerShape.addChild(_addressText);
if(localSearchItem.websiteURL().length > 0){
lgMarkerShape.addChild(_urlText);
}
lgMarkerShape.addChild(closeButton); //add this last so it stays on top.
}
private function onOver(event:MouseEvent):void{
custMarkerShape.filters = [_ds];
}
private function onOut(event:MouseEvent):void{
custMarkerShape.filters = null;
}
private function lgOnOver(event:MouseEvent):void{
urlFormat.underline = true;
_urlText.setTextFormat(urlFormat);
}
private function lgOnOut(event:MouseEvent):void{
urlFormat.underline = false;
_urlText.setTextFormat(urlFormat);
}
private function onCloseHandler(event:MouseEvent):void{
custMarkerShape.removeEventListener(MouseEvent.CLICK, onCloseHandler);
removeChild(lgMarkerShape);
custMarkerShape.visible = true;
}
private function goToMarkerURL(event:TextEvent):void{
var urlRequest:URLRequest = new URLRequest(event.text);
navigateToURL(urlRequest, "_blank");
}
}
}
Here we go:
First of all i set up an HttpService grabbing a yahoo Pipe who actually searches for apartements in craigslist near Stanford University.
Then i'm iterating the result and placing the search Markers.
The Code-Snippet:
public function useHttpService(event:Event):void {
service = new HTTPService();
service.url = "http://pipes.yahoo.com/pipes/pipe.run?_id=1mrlkB23...;;
service.resultFormat = "object";
service.addEventListener("result", httpResult);
service.addEventListener("fault", httpFault);
service.send();
}
public function httpResult(event:ResultEvent):void
{
var results:Object = event.target.lastResult.rss.channel.item;
trace(print_r(results,""));
trace("-----------------------------");
trace("-----------------------------");
for (var resultItem:String in results)
{
var xmlItem:XML =
<listing>
<title>{results[resultItem].title.substr(0,5)}</title>
<lat>{results[resultItem].lat}</lat>
<lon>{results[resultItem].long}</lon>
<detailurl>{results[resultItem].link}</detailurl>
</listing>
var myLocalSearchItem:LocalSearchItem = new LocalSearchItem(xmlItem);
var mySearchMarker:SearchMarker = new SearchMarker(myLocalSearchItem);
_yahooMap.markerManager.addMarker(mySearchMarker);
}
P.S.:
print_r is a php-like print_r method written by me. Good for looking inside objects.
I'll share if you like.
Feature Request to Yahoo:
Yahoo! Please hand in a customFlexMarker !!! Or even a customPOIMarker!!! Or an customLocalSearchItem.
http://www.ktservis.com
All i want is how to create the lines........:(