Tuesday, September 4, 2007

Whip Up a Yahoo! Mashup Using PHP Part IV

Let's begin our AjaxMap class:

php
require_once('yahooapi.class.php');
class AjaxMap
{
private $mapContainer;
private $mapType;
private $markers = array();
public $showZoom;
public $showPan;

We begin by including the YahooAPI class, which we'll use to generate the Yahoo! API calls. Our class has three private properties: $mapContainer will contain the ID of the HTML element acting as the map container, $mapType will represent the type of map desired and must be one of YAHOO_MAP_REG, YAHOO_MAP_SAT or YAHOO_MAP_HYB, and the final private property, $markers will contain an array of map location markers. The API offers the ability to add zoom and pan controls, so we'll add the public properties $showZoom and $showPan, which can be set to true when required.

So, first to the easy methods: getHeadHTML, the set functions and addMarker. All the getHeadHTML method needs to do is return a \n";
}

The set functions are just as simple -- they act as wrapper methods for modifying private properties. Here's the code:

public function setMapContainer($id)
{
$this->mapContainer = $id;
}
public function setMapType($type)
{
$this->mapType = $type;
}

The addMarker method will add a new map marker entry to the private $markers array and takes a latitude value, a longitude value and description text as its arguments:

public function addMarker($lat,$long,$descr)
{
$this->markers[] = array($lat,$long,$descr);
}

initMarker is a private method called for each of the desired map markers and generates the JavaScript code required for the marker:

private function initMarker($id,$lat,$long,$descr,$init_geo = TRUE)
{
$js = '';
if($init_geo) $js .= "\nvar mPoint$id = ".
"new YGeoPoint($lat,$long);\n";
$js .= "var currmarker = new YMarker(mPoint$id);\n";
$js .= "currmarker.addLabel('$id');\n";
$js .= "currmarker.addAutoExpand('

".
addslashes($descr)."
');\n";
$js .= "ymap.addOverlay(currmarker);\n\n";
return $js;
}

initMarker takes all the information about the marker -- latitude and longitude for position, a short description and some notes, plus a unique 'id' parameter -- and generates the JavaScript we need in order to draw the marker. The $init_geo parameter for initMarker indicates whether or not we need to create a YGeoPoint object for the marker; this may already have been done.

All that's left to do is bring everything together within the main JavaScript block. The getMapScript method will generate this JavaScript and assign it to the $js variable:

public function getMapScript()
{
$js = '';

First, we have to initialise a YMap object. This is our main map object which will handle the drawing and customisation of the map. The first part is simple -- we output the code required to create a new YMap object:

$js .= 'var ymap = new YMap(document.getElementById(\''.
$this->mapContainer.'\'),'.$this->mapType.");\n";

In this instance, the properties $mapContainer and $mapType include the relevant information about the map, so setMapType and setMapContainer should be called before getMapScript.

Next, we output the JavaScript to add the zoom and pan controls if $showZoom and $showPan are set to true. To add a zoom control in JavaScript, we use the addZoomShort method of the YMap object, and addPanControl for a pan control:

if($this->showZoom) $js .= "ymap.addZoomShort();\n";
if($this->showPan) $js .= "ymap.addPanControl();\n";

We may have a number of markers to display, but the map can only be centred on one of them. To keep it simple, we'll remove the last marker from the main set of markers, centre the map on it and draw it on the map before proceeding to draw the remaining markers. Obviously, none of this is needed if no markers are to be drawn on the map, so we check that markers exist here too. Here's the code that outputs the JavaScript required for centring the map on the last marker, and drawing that marker:

if(count($this->markers) > 0)
{
$lastmarker = array_pop($this->markers);
$js .= 'var mPoint'.count($this->markers).' = new YGeoPoint('.
$lastmarker[0].','.$lastmarker[1].");\n";

$js .= 'ymap.drawZoomAndCenter(mPoint'.count($this->markers).
", 3);\n";

$js .= $this->initMarker(count($this->markers), $lastmarker[0],
$lastmarker[1], $lastmarker[2],
FALSE);

First we check if there are more than 0 markers (that is, we see if any have been set), and if so, extract the last of these markers and use that marker's data to write the JavaScript required to create a new YGeoPoint object. We then output the JavaScript required to draw the map, centre it on our last marker and set the zoom level to 3. In JavaScript, we do this via the drawZoomAndCenter method of the YMap object. We then call our initMarker function to generate the rest of the JavaScript, and through its last parameter, tell it not to output the JavaScript to create a YGeoPoint object, as we've already taken care of it.

Finally, we generate the code for each remaining marker by quickly iterating over the markers array, calling initMarker for each one and returning the $js string variable:

foreach($this->markers as $id=>$obj)
{
$js .= $this->initMarker($id,$obj[0],$obj[1],$obj[2],TRUE);
}
}
return $js;
}
}
?>

That also represents the end of our AjaxMap class!

Now we just have to use our local search class and AjaxMap class in a proper application. I've put together a quick demonstration. First we need to include our two classes:

Next, we use our local search class to search for "pizza". We collect the locations from our search results and store them in an array called $points:

$localSearch = new LocalSearch();
$localSearch->locationSearch('Pizza','Palo Alto, CA');
$apiOutput = $localSearch->extractResults();

foreach($apiOutput as $id => $result)
{
$points[$id] = array($result['Title'],
$result['Latitude'],
$result['Longitude']);
}
unset($apiOutput);

We then create a new AjaxMaps object and add to it all our locations:

$ajaxMap = new AjaxMap();
$ajaxMap->setMapContainer('mC');
$ajaxMap->setMapType('YAHOO_MAP_REG');
$ajaxMap->showPan = true;
$ajaxMap->showZoom = true;

foreach($points as $point)
{
$ajaxMap->addMarker($point[1],$point[2],$point[0]);
}
?>

Now that all our location markers have been added to our AjaxMap object, the only task that's left to do is write the page HTML and output the JavaScript:

W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


getHeadHTML(); ?>






The entire script is available in the code archive. Set your application IDs, load it up on your web server, and with any luck you should see something like this:

Our app in action! (click to view image)

Congratulations! You've just built a mashup using the Yahoo! APIs. With a bit of tweaking, you could add search functionality, allowing the user to look for more than just 'Pizza' in 'Palo Alto, CA'. You could even integrate the functionality into an existing application (although be aware of the terms of use for both APIs). The possibilities are endless.

Where to From Here?

As you can see, exploiting the Yahoo! APIs with PHP5 to create useful mashups is a piece of cake, and there are many interesting applications that can be built with the data. Rasmus Lerdorf himself has written a similar article, taking a more in depth look at the Yahoo! Geocoding API, and how to easily use it with PHP5. It's also worth noting that while we've used the output=php parameter throughout this article, most of the APIs also offer JSON output for use via Ajax. The Yahoo! Developer Network's PHP Developer Center has an excellent collection of tutorials, code samples and other resources for consuming the APIs with PHP5.

Also check out Yahoo's Application Gallery for inspiration and to see some great web apps built with various Yahoo! APIs, If you build an interesting application, submit it to the gallery for some excellent exposure and useful feedback.

No comments:

About Me

Ordinary People that spend much time in the box
Powered By Blogger