Advertisement
Tools & Tips

How to Create a Mashup by Combining 3 Different APIs

by

This tutorial will show you how to create a mashup of three different APIs including integration with Google Maps. This idea came about when I was searching through ProgrammableWeb's API directory for a couple APIs that complimented each other enough that I could use one to provide the other with data. What I came up with will be known as the "Beer Mashup".

Step 1. API Discovery

While looking through ProgrammableWeb's directory, I found an IP geolocating tool called ipLoc. It simply accepts in an IP address and returns relevant location-specific data on it such as it's state, city, country, postal code, etc.

I then found the very cool Beer Mapping Project, which has a huge directory of bars and pubs from not only across the US, but many other countries as well. Instantly I noticed that this was a perfect compliment to ipLoc because the BeerMapping API requires a city (which we can get from the ipLoc API) to return a list of local bars and pubs.

Lastly, I also wanted to integrate Google Maps into this mashup to plot out the addresses of the bars and pubs to add a bit of interactivity to the page instead of just displaying each bar in a simple HTML list.

Step 2. Variable Initialization

I usually find it best to start PHP documents off with some of the variables that I want to set globally within the script. Here I add a line that silences PHP warning messages (Google Maps spits out many of these if you happen to try to map an invalid address) and my BeerMashup and Google Maps API keys. We will end up using these API keys when we get to their respective steps below.

<?php
	error_reporting(E_ERROR|E_PARSE);  //Silence Errors
	
//Initialize Variables
	$beer_api = 'YOUR_BEERMAPPING_API_KEY';
	$gmaps_api = 'YOUR_GOOGLEMAPS_API_KEY';

Step 3. IP Geolocation Setup

The ipLoc API allows you to either specify an IP address to get data on, or use the default IP address that the script finds.

Default Version: http://iploc.mwudka.com/iploc/json/
Static Version (IP address is hardcoded): http://iploc.mwudka.com/iploc/68.162.155.110/json/

//Set Location
	//Visitor IP Address
	$ip = getenv("REMOTE_ADDR");
	
	//via IPLoc
	$iploc = file_get_contents("http://iploc.mwudka.com/iploc/$ip/json/");  //Format: JSON
	$ipdata = json_decode($iploc, true);

After a little testing, I realized that the default version of the ipLoc API was finding the location (Scottsdale, AZ, USA) of my hosting provider's server rather than my home computer's IP location (Pittsburgh, PA, USA). To circumvent this, I decided to use the static IP version of the API (Line 2 above) and passed in the IP address that is detected by the getenv("REMOTE_ADDR") php variable.

After checking if the data has been successfully returned as a decoded json formatted string, we need to extract only the specific data we want to pass to the BeerMapping API, which is the city and state.

	// Error checking
	if ($ipdata['city']) { 
		$city = $ipdata['city'];
		$state = $ipdata['region'];
		$location = $city .", ". $state;
	} else {
		$err = "No location data returned for your IP address: ". $ip; 

	}

Step 4. Integrating Google Maps

This step needs to be done now because the next step will add the location points to Google Maps – and Google Maps has to be initialized before any of that can happen.

To make the Google Maps integration as easy and painless as possible, I've enlisted the help of a great PHP class called Phoogle from System Seven Designs. This class takes care of all the Google Maps API heavy lifting for us while just allowing us to worry about the data.

All we need to do to get this to work is to first include the class file we downloaded: phoogle.php, then set some basic map parameters such as the height, width, zoom level and your Google Maps API key. (Get one of those here).

//Phoogle - GoogleMaps PHP Class
	require_once 'phoogle.php';
	$map = new PhoogleMap();
	$map->setAPIKey($gmaps_api); //Using the variable we set in Step 2
	$map->setHeight(450);
	$map->setWidth(750);
	$map->zoomLevel = 6;
	$map->showType = false;

Step 5. BeerMapping API

Since we have the combined city and state in the variable $location from Step 3, we have everything we need to pull data from the BeerMapping API. Of course we also need one of their API keys, which can be requested here (about a 30 seconds process, start to finish).

A BeerMapping API call looks like this according to their examples:
Real Example: http://beermapping.com/webservice/loccity/71515667a86b8ec7f58cd22e3af86f6e/pittsburgh,pa

After substituting our variables in for the API Key (Step 2) and Location (Step 3), our BeerMapping API call now looks like this:
Our Example: http://beermapping.com/webservice/loccity/$beer_api/$location

After a little playing around with this API, I found that the location can't have any spaces. So the below code first gets rid of the space bewtween the "city, state" format. Then it replaces all other spaces within the location with underscores "_".

//Format Location for use with API
	$locationF = str_replace(", ", ",", $location); // Remove space before "State"
	$locationF = str_replace(" ", "_", $locationF); // Replace space with underscore in "City" name

Their data can only be returned in xml format, so we can easily extract the data returned by this call with the simplexml_load_file PHP function.

//BeerMapping - Format: XML
	$beerdata = simplexml_load_file ("http://beermapping.com/webservice/loccity/$beer_api/$locationF");

As the image shows, we first load the whole file into the variable $beerdata. After checking to see if we returned any results…

	// Error checking
	$beererr = $beerdata->location->id; //$beererr will be 0 if there were no locations returned
	if ($beererr == '0') { 
		$err = "No breweries were found in ". $location; 
	} else {

…the next step is to cycle through each bar/pub returned in the call, extracting all the data we need to pass into Google Maps (Step 4).

		$breweries = $beerdata->location;
		$barcount = count($breweries); //How Many?
		
		foreach ($breweries as $brewery) {
			$name = $brewery->name;
			$link = $brewery->reviewlink;
			$street = $brewery->street;
			$city = $brewery->city;
			$state = $brewery->state;
			$zip = $brewery->zip;
			$phone = $brewery->phone;
			
			//Location Point set for the Google Maps API
			$map->addAddress("$street $city $state $zip", "<a href='$link' title='$name BeerMap'>$name</a><br/>$street<br />$city, $state $zip<br />Phone: $phone");
		} 
	}
?>

Line 1 above sets the structure location of the "locations". Line 2 counts the amount of "locations" the API result returned. The remaining lines use a foreach loop to cycle though each "location" and pull out it's address information. Line 14 sets a "point" for each brewery on our Google Map.

Step 6. HTML Generation

After finishing all the PHP code that we've created, we can now work on displaying it. The first few lines shown below are standard in any HTML document, but after that we get back to using PHP. We first check to see if the variable $err is FALSE – which would mean that the $err variable is empty, or that we never received an error. If we never got an error, we spit out the Google Map, otherwise we spit out an error message.

<html xmlns="http://www.w3.org/1999/xhtml">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="styles.css" />
<title>Bars Near <?php echo $location; ?></title>
</head>
	<body>
	<div class="wrapper">
		<div class="header">
			<img class="logo" src="images/logo.png" alt="The Beer Mashup" />
			
			<p class="footer">Developed by <a href="http://www.cagintranet.com">Chris Cagle</a> for <a href="http://www.nettuts.com">NETTUTS</a></p>
			
			<div class="mapdiv">
				<?php 
				if (!$err) {
					echo "<div>";
					$map->printGoogleJS();
					$map->showMap();
					echo "<h3>". $location ." <span>(". $barcount ." Bars)</span></h3>";
				} else {
					echo "<p class=\"error\"><b>". $err ."</b></p>";;
				} 
				?>
			</div>
		</div>	
	</div>	
	</body>
</html>

After adding in some text and CSS and you now have a great looking web page that displays all the bars and pubs within the vicinity of the location of whoever is viewing the web page.

View the demo of the page as it stands now. The mashup is working great now, but we have one more improvement that will make all the difference when it comes to UI.

Step 7. Changing the Code to Allow Locations to be Input by the Visitor

At this point, our page works just fine but there is one small caveat: The visitor can only view bars from his or her current location. What if the visitor wants research bars in a different town rather than the one our IP API returned for him or her? Any visitor would want the ability to specify the location to research in.

To allow this, we will have a simple form that will accept in a city and state from the visitor then reload the page for the given location and skip the ipLoc API call completely. We will insert this code right before our <div class="mapdiv"> line in Step 6.

<form method="post" id="form" action="beermashup2.php">
	<span>location: (ex. New Orleans, LA)</span>
	<input type="text" value="" name="loc" id="loc" /><br class="clear" />
	<input type="submit" id="submitted" value="SEARCH" name="submitted" />
</form>

In order to make this work, we will need to wrap the code we made in the ipLoc step (Step 3) in an if-statement that checks if the form was submitted or not. If the form *was not* submitted (which will happen each time the page is initially loaded), then it will use the ipLoc IP geolocation code. If the form *was* submitted, it will take what the user submitted and set our $location variable to that.

//Set Location
	if ( isset($_POST['loc']) ) {
		//via user input
		$location = $_POST['loc']; 
	} else {
		//Visitor IP Address
		$ip = getenv("REMOTE_ADDR");

		//via IPLoc
		$iploc = file_get_contents("http://iploc.mwudka.com/iploc/$ip/json/");  //Format: JSON
		$ipdata = json_decode($iploc, true);
		
		// Error checking
		if ($ipdata['city']) { 
			$city = $ipdata['city'];
			$state = $ipdata['region'];
			$location = $city .", ". $state;
		} else {
			$err = "No location data returned for your IP address: ". $ip; 
		}

Step 8. Putting it all Together

View the demo of the final application.

You can view the source code of the final project (which is pretty much just the steps above pushed together) and see how I ended up combining 3 separate APIs into one application. Take a stroll for yourself through the API directory over at ProgrammableWeb and see what you can come up with on your own. Working with APIs is my new obsession because it is exciting to be able to create something new and useful from someone else's data. If this tutorial has helped you mashup a couple of APIs, post them here - I would love to see them.

Related Posts
  • Web Design
    HTML & CSS
    How to Add Branded Branch Locations to Google MapsMap thumb
    In this tutorial we'll walk through the process of creating a branded Google map for an imaginary client.Read More…
  • Code
    Creative Coding
    A Look at the WordPress HTTP API: Saving Data From wp_remote_postDiagram http api
    In the previous post in the series, we began working on a small plugin that provided a practical example of wp_remote_post. The thing is, the example was incomplete. Sure, it's nice to see how to make a call using the function and even how to setup a script responsible for receiving the data and returning the data, but it's of little use unless we do anything with it. In this final article in the series, we're going to revisit the plugin that we started with the last article and begin improving it a bit. Specifically, we will... Review what we've done Begin making some changes to the work that we created in the last article Style the presentation with LESS in order to keep some of our newer skills updated Review the arguments accepted by both wp_remote_get and wp_remote_post Finally, all of the work accomplished in this article will be available on GitHub and linked in the conclusion of the article. But before that, let's go ahead and get started.Read More…
  • Code
    Mobile Web Apps
    Create a Location-Aware Site with Sencha Touch - Displaying LocationsLogo
    This tutorial will guide you through the development of a Location-based mobile website using the Google Place search engine and Sencha Touch 2.1 . This is the second in a two-part series, and today we'll learn how to display map markers and location details.Read More…
  • Code
    Mobile Web Apps
    Create a Location-Aware Site with Sencha TouchSenchatouch preview
    This tutorial will guide you through the development of a Location-based mobile website using the Google Place search engine and Sencha Touch 2.1 . This is a two part tutorial and in this first part we'll learn how to create a project with Sencha cmd, create a cool theme using SASS/Compass, and find services close to the user's location.Read More…
  • Code
    JavaScript & AJAX
    Working With Data in Sails.jsSails preview 400 2
    Sails.js is an up and coming Node.js framework, with a focus on freedom and smart defaults. In this article we'll take a look at some of the data features Sails provides out-of-the-box, for easily making complex apps.Read More…
  • Code
    Android SDK
    Android SDK: Working with Google Maps - Displaying Places of InterestAndroid map preview retina
    With Google Maps in your Android apps, you can provide users with localization functions, such as geographical information. Throughout this series we have been building an Android app in which the Google Maps Android API v2 combines with the Google Places API. So far we have displayed a map, in which the user can see their current location, and we have submitted a Google Places query to return data about nearby places of interest. This required setting up API access for both services. In the final part of the series, we will parse the Google Places JSON data and use it to show the user nearby places of interest. We will also make the app update the markers when the user location changes. This is the last of four parts in a tutorial series on Using Google Maps and Google Places in Android apps: Working with Google Maps - Application Setup Working with Google Maps - Map Setup Working with Google Maps - Places Integration Working with Google Maps - Displaying Nearby Places This is a snapshot of the final app. 1. Process the Place Data Step 1 You will need to add the following import statements to your Activity class for this tutorial: [java] import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.util.Log; [/java] In the last tutorial we created an inner AsyncTask class to handle fetching the data from Google Places in the background. We added the doInBackground method to request and retrieve the data. Now we can implement the onPostExecute method to parse the JSON string returned from doInBackground, inside your AsyncTask class, after the doInBackground method: [java] protected void onPostExecute(String result) { //parse place data returned from Google Places } [/java] Step 2 Back in the second part of this series, we created a Marker object to indicate the user's last recorded location on the map. We are also going to use Markers to show the nearby places of interest. We will use an array to store these Markers. At the top of your Activity class declaration, add the following instance variable: [java] private Marker[] placeMarkers; [/java] By default, the Google Places API returns a maximum of 20 places, so let's define this as a constant too: [java] private final int MAX_PLACES = 20; [/java] When we create the Markers for each place, we will use MarkerOptions objects to configure the Marker details. Create another array instance variable for these: [java] private MarkerOptions[] places; [/java] Now let's instantiate the array. In your Activity onCreate method, after the line in which we set the map type, create an array of the maximum required size: [java] placeMarkers = new Marker[MAX_PLACES]; [/java] Now let's turn to the onPostExecute method we created. First, loop through the Marker array, removing any existing Markers. This method will execute multiple times as the user changes location: [java] if(placeMarkers!=null){ for(int pm=0; pm<placeMarkers.length; pm++){ if(placeMarkers[pm]!=null) placeMarkers[pm].remove(); } } [/java] When the app code first executes, new Markers will be created. However, when the user changes location, these methods will execute again to update the places displayed. For this reason the first thing we must do is remove any existing Markers from the map to prepare for creating a new batch. Step 3 We will be using Java JSON resources to process the retrieved place data. Since these classes throw certain exceptions, we need to build in a level of error handling throughout this section. Start by adding try and catch blocks: [java] try { //parse JSON } catch (Exception e) { e.printStackTrace(); } [/java] Inside the try block, create a new JSONObject and pass it to the result JSON string returned from doInBackground: [java] JSONObject resultObject = new JSONObject(result); [/java] If you look at the Place Search page on the Google Places API documentation, you can see a sample of what the query actually returns in JSON. You will see that the places are contained within an array named "results". Let's first retrieve that array from the returned JSON object: [java] JSONArray placesArray = resultObject.getJSONArray("results"); [/java] You should refer to the sample JSON result as we complete each section of this process - keep the page open in a browser while you complete the remainder of the tutorial. Next let's instantiate the MarkerOptions array we created with the length of the returned "results" array: [java] places = new MarkerOptions[placesArray.length()]; [/java] This should give us a MarkerOptions object for each place returned. Add a loop to iterate through the array of places: [java] //loop through places for (int p=0; p<placesArray.length(); p++) { //parse each place } [/java] Step 4 Now we can parse the data for each place returned. Inside the for loop, we will build details to pass to the MarkerOptions object for the current place. This will include latitude and longitude, place name, type and vicinity, which is an excerpt of the address data for the place. We will retrieve all of this data from the Google Places JSON, passing it to the Marker for the place via its MarkerOptions object. If any of the values are missing in the returned JSON feed, we will simply not display a Marker for that place, in case of Exceptions. To keep track of this, add a boolean flag: [java] boolean missingValue=false; [/java] Now add local variables for each aspect of the place we need to retrieve and pass to the Marker: [java] LatLng placeLL=null; String placeName=""; String vicinity=""; int currIcon = otherIcon; [/java] We create and initialize a LatLng object for the latitude and longitude, strings for the place name and vicinity and initially set the icon to use the default icon drawable we created. Now we need another try block, so that we can detect whether any values are in fact missing: [java] try{ //attempt to retrieve place data values } catch(JSONException jse){ missingValue=true; jse.printStackTrace(); } [/java] We set the missing value flag to true for checking later. Inside this try block, we can now attempt to retrieve the required values from the place data. Start by initializing the boolean flag to false, assuming that there are no missing values until we discover otherwise: [java] missingValue=false; [/java] Now get the current object from the place array: [java] JSONObject placeObject = placesArray.getJSONObject(p); [/java] If you look back at the sample Place Search data, you will see that each place section includes a "geometry" section which in turn contains a "location" section. This is where the latitude and longitude data for the place is, so retrieve it now: [java] JSONObject loc = placeObject.getJSONObject("geometry").getJSONObject("location"); [/java] Attempt to read the latitude and longitude data from this, referring to the "lat" and "lng" values in the JSON: [java] placeLL = new LatLng( Double.valueOf(loc.getString("lat")), Double.valueOf(loc.getString("lng"))); [/java] Next get the "types" array you can see in the JSON sample: [java] JSONArray types = placeObject.getJSONArray("types"); [/java] [tip] Tip: We know this is an array as it appears in the JSON feed surrounded by the "[" and "]" characters. We treat any other nested sections as JSON objects rather than arrays. [/tip] Loop through the type array: [java] for(int t=0; t<types.length(); t++){ //what type is it } [/java] Get the type string: [java] String thisType=types.get(t).toString(); [/java] We are going to use particular icons for certain place types (food, bar and store) so add a conditional: [java] if(thisType.contains("food")){ currIcon = foodIcon; break; } else if(thisType.contains("bar")){ currIcon = drinkIcon; break; } else if(thisType.contains("store")){ currIcon = shopIcon; break; } [/java] The type list for a place may actually contain more than one of these places, but for convenience we will simply use the first one encountered. If the list of types for a place does not contain any of these, we will leave it displaying the default icon. Remember that we specified these types in the Place Search URL query string last time: [text] food|bar|store|museum|art_gallery [/text] This means that the only place types using the default icon will be museums or art galleries, as these are the only other types we asked for. After the loop through the type array, retrieve the vicinity data: [java] vicinity = placeObject.getString("vicinity"); [/java] Finally, retrieve the place name: [java] placeName = placeObject.getString("name"); [/java] Step 5 After the catch block in which you set the missingValue flag to true, check that value and set the place MarkerOptions object to null, so that we don't attempt to instantiate any Marker objects with missing data: [java] if(missingValue) places[p]=null; [/java] Otherwise, we can create a MarkerOptions object at this position in the array: [java] else places[p]=new MarkerOptions() .position(placeLL) .title(placeName) .icon(BitmapDescriptorFactory.fromResource(currIcon)) .snippet(vicinity); [/java] Step 6 Now, at the end of onPostExecute after the outer try and catch blocks, loop through the array of MarkerOptions, instantiating a Marker for each, adding it to the map and storing a reference to it in the array we created: [java] if(places!=null && placeMarkers!=null){ for(int p=0; p<places.length && p<placeMarkers.length; p++){ //will be null if a value was missing if(places[p]!=null) placeMarkers[p]=theMap.addMarker(places[p]); } } [/java] Storing a reference to the Marker allows us to easily remove it when the places are updated, as we implemented at the beginning of the onPostExecute method. Notice that we include two conditional tests each time this loop iterates, in case the Place Search did not return the full 20 places. We also check in case the MarkerOptions is null, indicating that a value was missing. Step 7 Finally, we can instantiate and execute our AsyncTask class. In your updatePlaces method, after the existing code in which we built the search query string, start this background processing to fetch the place data using that string: [java] new GetPlaces().execute(placesSearchStr); [/java] You can run your app now to see it in action. It should display your last recorded location together with nearby places of interest. The colors you see on the Markers will depend on the places returned. Here is the app displaying a user location in Glasgow city center, UK: Perhaps unsurprisingly a lot of the places listed in Glasgow are bars. When the user taps a Marker, they will see the place name and snippet info: 2. Update With User Location Changes Step 1 The app as it stands will execute once when it is launched. Let's build in the functionality required to make it update to reflect changes in the user location, refreshing the nearby place Markers at the same time. Alter the opening line of the Activity class declaration to make it implement the LocationListener interface so that we can detect changes in the user location: [java] public class MyMapActivity extends Activity implements LocationListener { [/java] A Location Listener can respond to various changes, each of which uses a dedicated method. Inside the Activity class, implement these methods: [java] @Override public void onLocationChanged(Location location) { Log.v("MyMapActivity", "location changed"); updatePlaces(); } @Override public void onProviderDisabled(String provider){ Log.v("MyMapActivity", "provider disabled"); } @Override public void onProviderEnabled(String provider) { Log.v("MyMapActivity", "provider enabled"); } @Override public void onStatusChanged(String provider, int status, Bundle extras) { Log.v("MyMapActivity", "status changed"); } [/java] The only one we are really interested in is the first, which indicates that the location has changed. In this case we call the updatePlaces method again. Otherwise we simply write out a Log message. At the end of the updatePlaces method, add a request for the app to receive location updates: [java] locMan.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 30000, 100, this); [/java] We use the Location Manager we created earlier in the series, requesting updates using the network provider, at delays of 30 seconds (indicated in milliseconds), with a minimum location change of 100 meters and the Activity class itself to receive the updates. You can, of course, alter some of the parameters to suit your own needs. [tip] Tip: Although the requestLocationUpdates method specifies a minimum time and distance for updates, in reality it can cause the onLocationChanged method to execute much more often, which has serious performance implications. In any apps you plan on releasing to users, you should therefore limit the frequency at which your code responds to these location updates. The alternative requestSingleUpdate method used on a timed basis may be worth considering. [/tip] Step 2 Last but not least, we need to take care of what happens when the app pauses and resumes. Override the two methods as follows: [java] @Override protected void onResume() { super.onResume(); if(theMap!=null){ locMan.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 30000, 100, this); } } @Override protected void onPause() { super.onPause(); if(theMap!=null){ locMan.removeUpdates(this); } } [/java] We check for the GoogleMap object before attempting any processing, as in onCreate. If the app is pausing, we stop it from requesting location updates. If the app is resuming, we start requesting the updates again. [tip] Tip: We've used the LocationManager.NETWORK_PROVIDER a few times in this series. If you are exploring localization functionality in your apps, check out the alternative getBestProvider method with which you can specify criteria for Android to choose a provider based on such factors as accuracy and speed. [/tip] Before We Finish That pretty much completes the app! However, there are many aspects of the Google Maps Android API v2 that we have not even touched on. Once you have your app running you can experiment with features such as rotation and tilting. The updated maps service displays indoor and 3D maps in certain places. The following image shows the 3D facility with the app if the user location was in Venice, Italy: This has the map type set to normal - here is another view of Venice with the hybrid map type set: Conclusion In this tutorial series we have worked through the process of integrating both Google Maps and Google Places APIs in a single Android app. We handled API key access, setting up the development environment, workspace and application to use Google Play Services. We utilized location data, showing the user location together with nearby places of interest, and displaying the data with custom UI elements. Although what we have covered in this series is fairly extensive, it really is only the beginning when it comes to building localization features into Android apps. With the release of Version 2 of the Maps API, Android apps are set to take such functions to the next level.Read More…