Articles and Tutorials

Using the Point Of Interest Web Service with the Maps API

Discovering information about the resources around you is made simple using the SmartFIND Point Of Interest (POI) web service with the Maps API. This HOWTO will take you through the steps of providing a proximity search web application using these services. Discovering information about the resources around you is made simple using the SmartFIND Point Of Interest (POI) web service with the Maps API. This HOWTO will take you through the steps of providing a proximity search web application using these services.

The example application can be seen here

Prerequisites

  • An account registered for the SmartFIND Maps API and Point of Interest Web Service
  • The Prototype Javascript library http://prototype.conio.net/

This example uses the Prototype Javascript library which provides a cross-browser object for making background network requests as well as many other convenient helper functions. It could just as easily be implemented using any of the other myriad of Ajax libraries available these days, or using the native objects available in the browser.

Creating the map

For this application a simple map will do, for more advanced options see Getting Started with the Maps API
 
function init() {
map = new GSMap('mapDiv'); // Maps API V2 initialization
map.addControl(GSMap.MAP_CONTROL);
map.centerOnNewZealand();
 
// add event handlers
$('searchForm').onsubmit = search;
$('clear').onclick = clear;
}
 
window.onload = init;
The map is instantiated in the init() function, called when the page is loaded. Also in init() event handlers are added to the search form and the reset button.

Because we want to avoid reloading the page and building the map each time the user searches we need to explicitly handle the form submission event ourselves. We assign the search function as the handler for the form's onsubmit event. We also set an event handler for the form's reset button so that we can reset the state of the map when the search form is reset.

Making the AJAX request

When the user clicks the search button the form's submit event is fired and the search() function we've defined is called. In search() the following sequence of events happens: a progress image graphic is displayed to provide visual feedback to the user that new content is being loaded, the search parameters are extracted from the search form and the coordinate of the current map center added to the parameter list, finally the background request is made using the Prototype Ajax.Request object.
 
function search() {
// display the loading graphic
Element.show('loader');
 
// use the Prototype Form object to get the form values as a string
var params = Form.serialize('searchForm');
 
// add the map center to the params string
var center = map.getMapCenter();
params += '&x=' + center.x + '&y=' + center.y;
 
var url = 'proxy.php';

var req = new Ajax.Request(url, {method: 'get', parameters: params, onComplete: searchComplete});
return false;
}
In the example above the serialize() method of the Prototype Form object is used to read the form's elements and turn them into a URL-encoded string. GSMap provides us with a way to get the geographic position of the center of the map through the getMapCenter() method. Concatenating the two strings together gives us all the parameters we need to query the web service.

The call to the web service is made using the Prototype Ajax.Request object. Internally Ajax.Request uses the browser's XMLHttpRequest object to make the request. Modern web browsers impose a security restriction on scripts which prevent them from making a network connection to any web server other than the one the web page was loaded from, this restriction includes calls to XMLHttpRequest. One of the more straightforward solutions to this problem is to install a server-side proxy, and in this example the request will be made to a proxy which will then pass-through the query to the POI web service itself.

For more information on making cross-domain XMLHttpRequest calls see Use a Web Proxy for Cross-Domain XMLHttpRequest Calls on the Yahoo Developer site.

The web server proxy

If you're running PHP on your web server it's trivial to write a proxy. For this application 3 lines of PHP is all it takes:
 
<?php
$serviceUrl = 'http://<user-name>:<password>@poi.geosmart.co.nz/pois.json';
 
$request = $serviceUrl . '?' . $_SERVER['QUERY_STRING'];
 
echo file_get_contents($request);
?>
In the proxy the actual URL of the POI web service is defined. Because the service is protected by HTTP authentication the username and password is provided in the URL before the hostname component.

The POI web service supports several output formats, we're using JSON because it's bandwidth friendly and the Maps API can consume JSON data directly.

Creating the callback function

The Ajax call is made asynchronously which means that the user can keep using your application while the network request is being made, and when the response is received it's processed in the background. Because of the asynchronous nature of the network communication the code that you write to initiate the call is independent from the code that processes the response.

When we created a new Ajax.Request we specified the function that should process the response data using the onComplete option:

 
var req = new Ajax.Request(url, {method: 'get', parameters: params, onComplete: searchComplete});
In the searchComplete() function the web service response, in JSON format, is evaluated by the Javascript interpreter to create a Javascript object using the eval() function.

Multiple layers of features can be added to the map but to keep things simple in this example we're going to use the map's base layer, the default layer that's created whenever you instantiate GSMap. After getting a reference to the base layer we call the layer's clear() method to remove any map features previously added.

After adding the JSON data to the map layer the progress image graphic is hidden so the user knows that the search has completed. How the map features are created from the JSON data is covered in more detail in the next section.

 
function searchComplete(response) {
// evaluate the JSON data
var data = eval('(' + response.responseText + ')');
 
// get the map's base layer and remove previous search results, if any
var layer = map.getLayer('base');
layer.clear();
 
// add the search results to the map
layer.addFeaturesJson(data.pois.poi, initFeature);
 
// hide the loading graphic
Element.hide('loader');
}

Adding features to the map

The addFeaturesJson() method is used to add the POIs to the map layer. This method takes 2 arguments: an array of POI data objects and a callback function which will be called by the layer object for each map feature that it creates. This callback function allows custom functionality to be added to the map features, in our case we're going to add a click handler to each feature that will show an info window with information about the POI.

The initFeature() callback function takes 2 arguments: the map feature object, and the data object it was created from. For the info window content we'll be showing the POIs name and locality information. This data is directly available as properties of the GSPointFeature object which a simple HTML string is built from.

The GSPointFeature object has a method for adding an event handler to the object addEventHandler(). This method expects an argument specifying the type of event the object should respond to, in our case a mouse click, and the function to be executed when the event is received. This function will be executed within the scope of the point feature instance itself so the this keyword can be used to refer to methods and properties of that object. In our case we use it to call the point feature's showInfoWindow() method.

 
function initFeature(feature, data) {
// build the HTML string to display in the popup window when the feature is clicked
var html = '<strong>' + feature.name + '</strong><br/>';
html += feature.suburb + ', ' + feature.region;
feature.infoHtml = html;

// attach a 'click' event handler to the feature to show the popup
feature.addEventHandler('click', function(e) {
this.showInfoWindow();
GSUtil.cancelEvent(e);
});
}

Clearing the search

The final step is to write the function for reseting the state of the map when the form is reset.
 
function clear() {
map.closeInfoWindow();
map.clearLayers();
map.centerOnNewZealand();
return true;
}
In this function the map's info window is closed, if open, any map features added to the map are removed and the map is re-centered on New Zealand. Returning true ensures that the reset button's default behaviour of clearing the form elements will also be invoked.