Against the Browser’s Will: Make Mashups Talk Across Domains

aps are not only very useful, but they are also cool! In recent months, we have seen a lot of innovation in the area of online maps. Today we are far beyond the time when Photoshop was the most efficient way to overlay data onto a map within your homepage. There are a couple of different new solutions to choose from if you want to embed interactive maps. The “oldest” developer product is Google Maps. Microsoft offers Virtual Earth. And my employer, Yahoo!, offers Yahoo! Maps! This is all really good stuff!

These solutions and tools enable third-party developers to create applications that overlay all sorts of location-based data onto commercial-quality map implementations. I want to highlight two great examples: Housingmaps.com was one of the first popular applications that uses Google Maps to create a so-called mashup. In this example, the user gets Craigslist’s housing posts displayed onto a map. The Local Event Browser is another example that shows what can be done by combining various data sources.

This article will discuss the basics of creating a mashup. The three ingredients you need to build your applications are:

  • A mapping solution that provides an API for use by third parties
  • Access to the data source
  • A way to get around the browser’s cross-domain restriction

There are essentially two approaches to get data on a map. The most common way is to manipulate the data dynamically; all mapping solutions allow the user to manipulate the overlays dynamically. However, Yahoo’s Maps API supports an additional method: you can create a GeoRSS file and refer to it when the map loads. Passing in a GeoRSS file is the easier of the two solutions?so long as you don’t have to deal with cross-domain restrictions.

Dealing with Cross-Domain Restrictions
Cross-domain restrictions are the primary foe of the mashup: Browser security only allows calls to the same domain as the originating web page, which restricts where you can retrieve data from during runtime. Originally, this restriction was designed to protect content owners from web applications that pulled data from their sources. Unfortunately, this makes it harder to use Web services since, most of the time, the data source and the mashup do not live on the same domain.

This article will cover ways to overlay data on a map and discuss different options to get around the browser cross-domain restriction. I’ll walk through the following solutions:

  • GeoRSS Solution
  • PHP Proxy Sample
  • JSON?Proxy-free mapping with the dynamic script tag

GeoRSS is an XML-based format based on RSS 2.0 but which adds latitude and longitude elements that specify the location of an item: and . Yahoo! Maps took it one step further and added additional elements that support a street address, city, ZIP, etc. on top of longitude and latitude. The GeoRSS file that contains the data to overlay onto the map can be hosted on any public accessible server. ACME GeoRSS Map Viewer is an application that drills down further into the different additions from the RSS standard.

Listing 1 describes two groups of items where each group has a specific image associated to it. Below the group definitions, are the item definitions with locations specified. Each item is assigned to a group, which specifies where the image is on the map.

To display the information in Listing 1, I will use the Yahoo! Maps Ajax API. The only things that need to be done are to create a ‘div’ for the map on the web page, create a new map, and then call a function that overlays the GeoRSS data. To run the code on your own page, copy Listing 1 into a file and point your web page to your own GeoRSS file. The Web page code for my example application, mapGeoRSS.html, is shown below.

mapGeoRSS.html 

Once you have your page up and running, you also need to sign up with Yahoo to get an application ID. After you have your own ID, you should replace the appid in line three of the web page code above with your own.

This approach is great if you are looking for a simple way to get data on a map without writing your own code. However, the solution is fairly static. The GeoRSS file could be generated upon request, but after that, there is no way to dynamically manipulate the data in case the user pans or zooms. I’ll discuss some of the more dynamic solutions in the second part of this article.

Ways Around Cross-Domain Requests
An application that wants to retrieve data at runtime can use JavaScript and the XMLHTTPRequest object to make an HTTP request. Unfortunately, the XMLHTTPRequest object that browsers like Firefox and IE provide enforces a cross-domain restriction that does not allow HTTP calls from one domain to another. Modern Web applications depend on the ability to make these calls to provide a richer user experience.

The sample below will use Yahoo’s geocoding Web service. This is a REST service where REST gets transported over HTTP; it is, therefore, subject to the cross-domain restrictions. There are a couple of ways to get around this restriction:

In this article I will only discuss the first two approaches since these do not require installation of Apache or write-access to the .htaccess file.

Software Proxy: What can PHP Do for You?
The easiest approach is to write a proxy that lives on the same server as the application, which passes only the request and response to and from the web server. There is a slight security risk since every proxy runs the risk of getting abused, but the sample in Listing 2 shows that you can limit the access. This works great for my example scenario. Listing 2 shows how to display a map with a marker in the middle, which opens a box when it gets clicked.

All that and more can be found in the GettingStartedGuide for AJAX. It gets a little more interesting when data sources come into play. A PHP file will serve as a proxy to overcome the cross-domain restrictions.

yproxy.php

This is a really simple proxy that is transparent for GET requests. To run the proxy, the PHP Curl extension needs to be enabled.

Now that the proxy is in place I am able to make a REST call, but only to api.local.yahoo.com. In the next example I will fix this by allowing the user to type in an address, which will be displayed on the map. MapProxy is the complete file, found in the downloadable ZIP file that accompanies this article.

Here, I will run through the MapProxy code block and discuss each step:

Once the button is pressed, the function addGeocode() gets called. It assembles the REST query and does a XmlHttpRequest to the proxy with the REST URL as parameter. While it waits for the proxy to complete the request it displays ‘Loading’ on the bottom of the page. The PHP proxy uses CURL to make the actual request, and the callback method parseResult() gets executed once the call has succeeded.

var query = document.getElementById("geoquery").value;var target = "http://api.local.yahoo.com/MapsService/V1/geocode?appid=devxsample&location=" + query; if(target !== ""){  var url = 'yproxy.php?' + encodeURI(target);  xmlhttp.open('GET', url, true);  xmlhttp.onreadystatechange = function() {    if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {      document.getElementById('result').innerHTML = '';      //callback method       parseResult(xmlhttp.responseText);    } else {      document.getElementById('result').innerHTML = "Loading...";    }  };  xmlhttp.send(null);}

From here, all that’s left is parsing the XML and adding the markers. Because this is a very simple result string, I use ‘regex’ to pull out the latitude and longitude, and then use the values to create and add the marker as seen above.

function parseResult(result, service) { var start = (result.search(//) +10 );  var end = result.search(//)  var lat = result.substr(start, (end -start) );  start = (result.search(//) +11 );  end = result.search(//)  var lon = result.substr(start, (end -start) );  var point = new YGeoPoint(parseFloat(lat),parseFloat(lon));  var smart = "
" smart += "Long: " + lon + "
" + "Lat: " + lat; var myMarker = createYMarker(point, 1, smart); ymap.drawZoomAndCenter(point, 6); ymap.addOverlay(myMarker);}

Script Tag Hack: JSON?Dynamic Script Tag
The previous example used the software proxy method to make the Web service requests. This section discusses a way to make a request without a proxy and instead use the dynamic script tag method. Yahoo! added a new output option, called JSON, as part of its Web service. JSON makes it possible to make the JavaScript WS request without using the XMLHTTPRequest object. It is a great way to pull data from another domain because you can dump the proxy and the client will make the call directly without producing traffic on your server. But first, an overview of what JSON is, how it works, and then some sample code.

What is JSON?
Doug Crockford, a Yahoo! employee and the inventor of JSON, writes the following: “JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate.”

And this is how it looks:

   {"ResultSet":{"totalResultsAvailable":"290000","totalResultsReturned":   1,"firstResultPosition":1,"Result":[{"Title":"Introducing JSON","Summary":   "This page...","Url":"http://www.crockford.com/JSON/","ClickUrl":   "http://www.crockford.com/JSON/","ModificationDate":1139472000,   "MimeType":"text/html"}]}}

The string above is returned by Y! Web Search for the query “JSON”. JSON is a serialized JavaScript object that JavaScript can turn back into an object. For Yahoo! Web services the structure of the JSON string is similar to the XML result but the difference between the attribute and element can’t be made. The following is a comparison of the XML result for the same call.

      Introducing JSON    This page ...    http://www.crockford.com/JSON/    http://www.crockford.com/JSON/...    1139472000    text/html  

The Secret Sauce
So what’s the JSON secret sauce? Adding the

Overview

Recent Articles: