Using Google Places API to search for places around a location in JSF/PrimeFaces

The use of Google Places is simple, you only need your Google API credentials and the end point URL of the API. The only additional task your application has to do is get the current location of the user. In this demonstration, we will use Google Latitude API for the user location.

Although we tested this demonstration only in desktop environment, the procedure should work for mobile websites as well.

Here is the Places API end point URL.

https://maps.googleapis.com/maps/api/place/search/json?parameters

Its output can be in JSON or XML. Please refer to the Places documentation for more of the parameters to this URL. Please also check the requirements and limitations in using the service. We will get back to its parameters in more detail as we go on.

Getting user's current location through Google Latitude

Before we go further, make sure you already have registered your website or application with Google API console, in doing so, you will get an application App ID, APP secret, and API key which are needed in using both Latitude and Places APIs. You will also define at least one redirect URL in your API console.

The process starts with a user action as below. We use PrimeFaces menu/menuitem in this demonstration. If you are using core JSF library, you may want to change menuitem to standard HTML anchor tag or equivalent.

<p:menuitem  url="#{authCode.code}"  value="Search for places around your area"  />

This calls getCode from our backing bean class called AuthCode. Below is how the AuthCode class looks like. This is the start of the authentication/authorization process using Google OAuth 2.0. This is required for Google Latitude. This process gives us an authorization code.

package your-packge

import com.google.api.client.googleapis.auth.oauth2.\
	draft10.GoogleAuthorizationRequestUrl;
import java.io.IOException;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;


@ManagedBean
@RequestScoped
public class AuthCode implements java.io.Serializable {

    public AuthCode() {
    }

    private final String SCOPE =
    "https://www.googleapis.com/auth/latitude.current.best";
    
    // Should be one of the redirect_url defined in your Google API account
    private final String REDIRECT_URL = "http://localhost:8084/kauswagan/Places.xhtml";
    private final String CLIENT_ID = "YOUR-CLIENT-ID";
    String authorizeUrl;
    
    // This is bound to our PrimeFaces menuitem url attribute
    public String getCode() throws IOException {
        GoogleAuthorizationRequestUrl url =
                new GoogleAuthorizationRequestUrl(CLIENT_ID, REDIRECT_URL, SCOPE);
        authorizeUrl = url.build();
        return authorizeUrl;
    }
}

CLIENT_ID, REDIRECT_URL is from your Google API console account for your application. SCOPE is the scope of the request for user Latitude location.

authorizeUrl is presented to the user for approval that our application is going to get his/her location. If the user grants permission, an authorization code is appended to the redirect URL. It is an error if the user rejects our request. authorizeUrl looks like below:


Kahimyang Google Latitude

Any user action in the authorizeUrl page takes us to the redirect URL.

Searching Places

Our redirect URL is also the page where we display the Places search results. Below is part of the page that displays the results. Please note that we are using PrimeFaces in this demonstration. For mobile application using core JSF library, you may want to change p:dataGrid with corresponding core component like the ui:repeat and remove p:panel althogether.

<p:panel id="posts" style="margin-top: 20px;border:0px;width:740px;margin-right:20px;" >
    <p:dataGrid columns="2" value="#{placesLatitudes.places}" var="places">
        <h:panelGrid columns="2">
            <h:graphicImage value="#{places.icon}" />
            <h:panelGrid>
                <h:outputText value="#{places.name}" />
                <h:outputText value="#{places.vicinity}" />
            </h:panelGrid>
        </h:panelGrid>
    </p:dataGrid>
</p:panel>

The following is the entire backing bean of our redirect URL. This class exchanges authorization code for an acccess token, gets the current location of the user, and executes the Places search. Please see highlighted lines and inline comments.


package your-package;

import com.google.api.client.auth.oauth2.draft10.AccessTokenResponse;
import com.google.api.client.googleapis.auth.oauth2.\
	draft10.GoogleAccessProtectedResource;
import com.google.api.client.googleapis.auth.oauth2.\
	draft10.GoogleAccessTokenRequest.GoogleAuthorizationCodeGrant;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;


@ManagedBean
@ViewScoped
public class PlacesLatitudes implements java.io.Serializable {

    public PlacesLatitudes() {
    }

    private final String REDIRECT_URL = 
		"http://localhost:8084/kauswagan/Places.xhtml";
    private final String CLIENT_ID = "YOUR-CLIENT-ID";
    private final String CLIENT_SECRET = "APP-SECRET";
    private final HttpTransport TRANSPORT = new NetHttpTransport();
    private final JsonFactory JSON_FACTORY = new GsonFactory();
    private final String API_KEY = "YOUR-API-KEY";
    private final String PLACES_API = 
		"https://maps.googleapis.com/maps/api/place/search";
    private String token;
    AccessTokenResponse authResponse;

    @PostConstruct
    void token() {
        
        // Exchange code for access_token
        
        FacesContext context = FacesContext.getCurrentInstance();
        HttpServletRequest request =
                (HttpServletRequest) context.getExternalContext().getRequest();


		// You may want to check the error parameter as well for details of 
		// of the error.

        final String authorizationCode = request.getParameter("code");
		if (authorizationCode == null) {
            	places = new ArrayList();
            	Places p = new Places();
            	p.setIcon("");
            	p.setName("Not granted permission");
            	p.setVicinity("");
            	places.add(p);
			    return;
		}

        try {
            GoogleAuthorizationCodeGrant authRequest =
                    new GoogleAuthorizationCodeGrant(TRANSPORT,
                    JSON_FACTORY, CLIENT_ID, CLIENT_SECRET, 
			authorizationCode, REDIRECT_URL);
            authRequest.useBasicAuthorization = false;

            authResponse = authRequest.execute();
            token = authResponse.accessToken;
        } catch (Exception e) {
		    return;  // insert handing code here
        }

        if (latitude()) {
            search_places();
        } else {
            places = new ArrayList();
            Places p = new Places();
            p.setIcon("");
            p.setName("Error in getting user location");
            p.setVicinity("");
            places.add(p);

        }
    }

    // Get current location using Google Latitude
    boolean latitude() {
        boolean status = false;

        GoogleAccessProtectedResource access =
                new GoogleAccessProtectedResource(token,
                TRANSPORT, JSON_FACTORY, CLIENT_ID, 
		CLIENT_SECRET, authResponse.refreshToken);
        HttpRequestFactory rf = TRANSPORT.createRequestFactory(access);

        final String sendPoint = 
		"https://www.googleapis.com/latitude/v1/currentLocation";

        // Make an authenticated request
        GenericUrl gurl = new GenericUrl(sendPoint);

        try {
            HttpRequest lreq = rf.buildGetRequest(gurl);
            lreq.setEnableGZipContent(false);

            HttpResponse lresp = lreq.execute();
            BufferedReader instream =
                    new BufferedReader(new InputStreamReader(lresp.getContent()));

            StringBuilder respData = new StringBuilder();

            String line;
            while ((line = instream.readLine()) != null) {
                respData.append(line);
            }

            Map<String, Map<String, Object>> gsonData = new LinkedHashMap();
            com.google.gson.Gson gs = new com.google.gson.Gson();
            gsonData = gs.fromJson(respData.toString(), gsonData.getClass());
            Map<String, Object> data = (Map<String, Object>) gsonData.get("data");
            String kind = (String) data.get("kind");
            if (kind != null &&  kind.contains("latitude")) {
                Double lat = (Double) data.get("latitude");
                Double longi = (Double) data.get("longitude");
                location = lat.toString() + "," + longi.toString();
                status = true;
            }

        } catch (IOException e) {
            // Handling code here
        }

        return status;
    }
    

    // Search for places
    void search_places() {
	
		// This the places end point.  Please change this to your needs.
		// Please note that location is the lat,lang object returned by
		// Latitude.  Raduis is in meters.  We also did not use
		// type, we are searching for all establishments in this demostration.

		// You may want to control the parameters to the URL at the page UI.

        final String endpoint = PLACES_API + "/json?location="
                + location + "&radius=1000&sensor=false&key=" + API_KEY;

        GenericUrl gurl = new GenericUrl(endpoint);

        try {
            HttpRequestFactory rf = TRANSPORT.createRequestFactory();
            HttpRequest lreq = rf.buildGetRequest(gurl);
            lreq.setEnableGZipContent(false);
            HttpResponse lresp = lreq.execute();
            BufferedReader instream =
                    new BufferedReader(new InputStreamReader(lresp.getContent()));

            StringBuilder respData = new StringBuilder();

            String line;
            while ((line = instream.readLine()) != null) {
                respData.append(line);
            }

	
			// The status of the search request.  It should return OK
			// if the search went fine.  See docs for more status.

            Map<String, Object> gsonData = new LinkedHashMap();
            com.google.gson.Gson gs = new com.google.gson.Gson();
            gsonData = gs.fromJson(respData.toString(), gsonData.getClass());

            if (((String) gsonData.get("status")).equals("OK") == false) {
                places = new ArrayList();
                Places p = new Places();
                p.setIcon("");
                p.setName("Zero results/Quota over limit.");
                p.setVicinity("");
                places.add(p);
                return;
            }

			// This is the html_attribution which is part of the data
			// returned by Places API.  We do not use them here but
			// Google suggests displaying them to the user.

            /*
             * List attr = (List)gsonData.get("html_attributions"); if (attr !=
             * null && attr.size() > 0) { for (int i=0; i < attr.size(); i++) {
             * Map<String, String> at = (Map<String, String>) attr.get(i); } }
             */


			// The search results.  Please note how we deserialize the JSON data.  
			// You may want to use your own way of deserializing objects.  
             // Also maximum result is only 20, you may want to use type parameter 
             // to narrow your search.

            List result = (List) gsonData.get("results");

            places = new ArrayList();

            for (int i = 0; i < result.size(); i++) {

                Places p = new Places();

                Map<String, Object> res = (Map<String, Object>) result.get(i);

                Map<String, Map<String, Object>> geom =
                        (Map<String, Map<String, Object>>) res.get("geometry");

			// Lat-lang for a place is not used in this demostration but will be 
			// useful if you are going to draw a map for the Place.
                
			Map<String, Object> loc = geom.get("location");
                Double lat = (Double) loc.get("lat");
                Double lng = (Double) loc.get("lng");

                String icon = (String) res.get("icon");
                String name = (String) res.get("name");
                Double rating = (Double) res.get("rating");
                String vicinity = (String) res.get("vicinity");

                if (icon != null) {
                    p.setIcon(icon);
                }

                if (name != null) {
                    p.setName(name);
                }

                if (rating != null) {
                    p.setRating(rating.toString());
                }
                if (vicinity != null) {
                    p.setVicinity(vicinity);
                }

                places.add(p);
            }

        } catch (IOException e) {
		    // Handling code here
        }
    }
        
    List places;
    List attributions;
    String location;

    public List getAttributions() {
        return attributions;
    }

    public void setAttributions(List s) {
        attributions = s;
    }

	// The view
    public List getPlaces() {
        return places;
    }

    public void setPlaces(List s) {
        places = s;
    }


    // Bound to p:dataGrid
    // Some methods were removed for brevity.

    public class Places {

        String icon;
        String id;
        String name;
        String rating;
        String reference;
        String types;
        String vicinity;

        Places() {
        }

        public String getIcon() {
            return icon;
        }

        public void setIcon(String s) {
            icon = s;
        }

        public String getId() {
            return id;
        }

        public void setId(String s) {
            id = s;
        }

        public String getName() {
            return name;
        }

        public void setName(String s) {
            name = s;
        }

        public String getRating() {
            return rating;
        }

        public void setRating(String s) {
            rating = s;
        }


        public String getVicinity() {
            return vicinity;
        }

        public void setVicinity(String s) {
            vicinity = s;
        }
    }
}

In line 63, we retrieved the authorization code appended by Google OAuth to our redirect URL. You may also want to check for errors in processing by checking for the error parameter.

Google client library was used in this demonstration. I preferred to use Gson for my Json processing. I have both libraries in my project. Download them in the preceding link if you don't have them already.

Edited: 3/7/2012 to get rid of Jackson and used all Gson both for internal Google Client usage and our own JSON processing (Lines 15 and 43 of PlacesLatitudes class).

That's it. Good luck.

If you like the article, please share.
(Site URL pattern has changed as a result social actions counter was reset.)



Comment icon Comments (Newest first)