8.1: Places API

Contents:

The Location API is great for providing your app with the device's current location and requesting periodic updates. However, the Location API falls short when you need details about a location, or when you need to allow the user to search for a specific place that's not tied to the device location.

A place is defined as a physical space that has a name. Another way of thinking about a place is that it's anything you can find on a map. Examples include local businesses, points of interest, and geographic locations.

In the Google Places API for Android, a Place object represents a place. A Place object can include the place's name and address, geographical location, place ID, phone number, place type, website URL, and more.

There are several ways to access the Places API:

  • The place-picker UI lets users select a place on an interactive map.
  • The PlaceDetectionClient interface provides quick access to a device's current place and the place's details.
  • The GeoDataClient interface provides access to Google's database of local place and business information, including high-quality images.
  • The place-autocomplete service provides a UI element that lets a user search for a specific place with autocomplete suggestions. You can also build your own autocomplete UI and use place-autocomplete API calls.

To include the Places API in your app, you must add a dependency to your app-level build.gradle file and set up an API key. These steps are described in the related practical for this lesson.

Using the place-picker UI

 The PlacePicker UI

The PlacePicker provides a UI dialog that displays an interactive map and a list of nearby places. Users choose a place by selecting the place on a map or by searching for the place. Your app can then retrieve the details of the selected place.

The place picker requires the ACCESS_FINE_LOCATION permission.

Launching the place-picker UI

To use the place-picker UI, complete the following steps:

  1. Use the PlacePicker.IntentBuilder() to construct an Intent.
  2. If you want to change the default latitude and longitude bounds of the map that the place picker displays, call setLatLngBounds() on the builder. Pass in a LatLngBounds object.

    The bounds that you set in the LatLngBounds object define an area called the viewport . By default, the viewport is centered on the device's location, with the zoom at city-block level.

  3. Call startActivityForResult(). Pass in the intent and a predefined request code. The request code lets you identify the request when the result is returned.

     PlacePicker.IntentBuilder builder =
            new PlacePicker.IntentBuilder();
     try {
        // Launch the PlacePicker.
        startActivityForResult(builder.build(MainActivity.this)
                , REQUEST_PICK_PLACE);
     } catch (GooglePlayServicesRepairableException
            | GooglePlayServicesNotAvailableException e) {
        e.printStackTrace();
     }
    

Obtaining the selected place

Once the user has selected a place in the place-picker dialog, your activity's onActivityResult() method is called. The onActivityResult() method uses the integer request code you used in startActivityForResult().

To retrieve the place, call PlacePicker.getPlace() in onActivityResult(). Pass in the activity context and the data Intent. If the user has not selected a place, the method returns null.

You can also retrieve the most recent bounds of the map by calling PlacePicker.getLatLngBounds().

@Override
    protected void onActivityResult(int requestCode, int resultCode,
            Intent data) {
        if (resultCode == RESULT_OK) {
            Place place = PlacePicker.getPlace(this, data);
            setAndroidType(place);
            mLocationTextView.setText(
                    getString(R.string.address_text, place.getName(),
                            place.getAddress(), System.currentTimeMillis()));

        } else {
            mLocationTextView.setText(R.string.no_place);
        }

        super.onActivityResult(requestCode, resultCode, data);
    }

Getting the device's current place

To get information about the device's current location, call the PlaceDetectionClient.getCurrentPlace() method. Optionally, pass in a PlaceFilter instance. (Filtering is described below, in Filtering the list of places.)

The getCurrentPlace() method returns a Task that contains a PlaceLikelihoodBuffer. The PlaceLikelihoodBuffer contains a list of PlaceLikelihood objects that represent likely Place objects. If no known place corresponds to the filter criteria, the buffer may be empty.

About the likelihood values:

  • A place's likelihood value is the relative probability that this place is the best match within the list of returned places for a single request. You can't compare likelihoods across different requests.
  • The value of the likelihood is between 0 and 1.0. A higher likelihood value means a greater probability that the place is the best match.
  • The sum of the likelihoods in a given PlaceLikelihoodBuffer is always less than or equal to 1.0. The sum isn't necessarily 1.0.

For example, there might be a 55% likelihood that the correct place is place A and a 35% likelihood that the correct place is place B. To represent this scenario, the PlaceLikelihoodBuffer has two members: Place A with a likelihood of 0.55, and Place B with a likelihood of 0.35.

Important: To prevent a memory leak, release the PlaceLikelihoodBuffer object when your app no longer needs it. Read more about handling buffers.
Task<PlaceLikelihoodBufferResponse> placeResult =
       mPlaceDetectionClient.getCurrentPlace(null);
placeResult.addOnCompleteListener
       (new OnCompleteListener<PlaceLikelihoodBufferResponse>() {
   @Override
   public void onComplete(@NonNull
           Task<PlaceLikelihoodBufferResponse> task) {

       // If you get a result, get the most likely place and
       // update the place name.
       if (task.isSuccessful()) {
           PlaceLikelihoodBufferResponse likelyPlaces =
                   task.getResult();
           float maxLikelihood = 0;
           Place currentPlace = null;
           for (PlaceLikelihood placeLikelihood : likelyPlaces) {
               if (maxLikelihood < placeLikelihood.getLikelihood())
               {
                   maxLikelihood = placeLikelihood.getLikelihood();
                   currentPlace = placeLikelihood.getPlace();
               }
           }
           likelyPlaces.release();

           // Update the UI.
           if (currentPlace != null) {
               mLocationTextView.setText(
                       getString(R.string.address_text,
                               currentPlace.getName(), result,
                               System.currentTimeMillis()));
               setAndroidType(currentPlace);
           }
           // Otherwise, show an error.
       } else {
           mLocationTextView.setText(
                   getString(R.string.address_text,
                           getString(R.string.no_place),
                           result, System.currentTimeMillis()));
       }

   }
});

Filtering the list of places

To limit the results that the PlaceLikelihoodBuffer includes when you request information about the device's current place, use a PlaceFilter.

  • To limit the results to places that are currently open, pass true as the first parameter in the PlaceFilter.
  • To limit the results to places that match certain place IDs, pass in the place IDs as the second parameter in the PlaceFilter. Provide the place IDs as a Collection of strings.

Using the place-autocomplete service

The Places API provides a PlaceAutocomplete service that returns Place predictions based on user search queries. As the user types, the autocomplete service returns suggestions for places such as businesses, addresses, and points of interest.

You can add the place-autocomplete service to your app in either of the following ways:

  • Use the autocomplete UI, as described below. This approach saves development time and ensures a consistent user experience.
  • Get place predictions programmatically, as described in Getting place predictions programmatically. With this approach, you can create a customized user experience.

Adding an autocomplete UI element

The autocomplete UI is a search dialog with built-in autocomplete functionality. As a user enters search terms, the search dialog presents a list of predicted places to choose from. When the user makes a selection, a Place instance is returned. Your app can use the Place instance to get details about the selected place.  The place-autocomplete search dialog

There are two ways to add the autocomplete UI to your app:

Embedding a PlaceAutocompleteFragment fragment

To add a PlaceAutocompleteFragment to your app, take the following steps:

  1. Add a fragment to your activity's XML layout, with the android:name attribute set to com.google.android.gms.location.places.ui.PlaceAutocompleteFragment.

     <fragment
       android:id="@+id/place_autocomplete_fragment"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:name="com.google.android.gms.location.places.ui.PlaceAutocompleteFragment"
       />
    
    Note: By default, the fragment has no border or background. To provide a consistent visual appearance, nest the fragment within another layout element such as a CardView.
  2. Add a listener to your activity or fragment.

    The PlaceSelectionListener handles returning a place in response to the user's selection. The following code creates a reference to the fragment and adds a listener to the PlaceAutocompleteFragment:

    PlaceAutocompleteFragment autocompleteFragment = 
          (PlaceAutocompleteFragment)getFragmentManager()
                 .findFragmentById(R.id.place_autocomplete_fragment);
    autocompleteFragment.setOnPlaceSelectedListener(new 
                                   PlaceSelectionListener() {
     @Override
     public void onPlaceSelected(Place place) {
         // TODO: Get info about the selected place.
         Log.i(TAG, "Place: " + place.getName());
     }
    
     @Override
     public void onError(Status status) {
         // TODO: Handle the error.
         Log.i(TAG, "An error occurred: " + status);
         }
     });
    

Using an intent to launch the autocomplete activity

You might want your app to use a navigational flow that's different from what the autocomplete UI provides. For example, your app might trigger the autocomplete experience from an icon, rather than from a search field.

To launch the autocomplete activity from someplace other than the default autocomplete search dialog, use an intent:

  1. Use PlaceAutocomplete.IntentBuilder to create an intent. Pass in the desired PlaceAutocomplete display mode—you can choose overlay or full-screen. The intent must call startActivityForResult(), passing in a request code that identifies your intent.
     int PLACE_AUTOCOMPLETE_REQUEST_CODE = 1;
     ...
     try {
         Intent intent =
                 new PlaceAutocomplete.IntentBuilder(PlaceAutocomplete.MODE_FULLSCREEN)
                         .build(this);
         startActivityForResult(intent, PLACE_AUTOCOMPLETE_REQUEST_CODE);
     } catch (GooglePlayServicesRepairableException e) {
         // TODO: Handle the error.
     } catch (GooglePlayServicesNotAvailableException e) {
         // TODO: Handle the error.
     }
    
  2. Override the onActivityResult() method to receive the selected place. When a user has selected a place, check for the request code that you passed into your intent. For example:

     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (requestCode == PLACE_AUTOCOMPLETE_REQUEST_CODE) {
             if (resultCode == RESULT_OK) {
                 Place place = PlaceAutocomplete.getPlace(this, data);
                 Log.i(TAG, "Place: " + place.getName());
             } else if (resultCode == PlaceAutocomplete.RESULT_ERROR) {
                 Status status = PlaceAutocomplete.getStatus(this, data);
                 // TODO: Handle the error.
                 Log.i(TAG, status.getStatusMessage());
    
             } else if (resultCode == RESULT_CANCELED) {
                 // The user canceled the operation.
             }
         }
     }
    

    Restricting the search results

You can set the autocomplete search to bias results to a geographic region. You can also filter the results to one or more place types.

To bias autocomplete results to a geographic region, call setBoundsBias() off your app's PlaceAutocompleteFragment instance, or off your app's IntentBuilder instance. Pass in a LatLngBounds object.

The following code calls setBoundsBias() on a fragment instance. The code biases the fragment's autocomplete suggestions to a region of Sydney, Australia.

autocompleteFragment.setBoundsBias(new LatLngBounds(
        new LatLng(-33.880490, 151.184363),
        new LatLng(-33.858754, 151.229596)));

To filter autocomplete results to a specific place type, call AutocompleteFilter.Builder to create a new AutocompleteFilter. Call setTypeFilter() to set the filter to use. Then, pass the filter to the fragment or intent.

The following code sets an AutocompleteFilter on a PlaceAutocompleteFragment. The filter returns only results that have precise addresses.

AutocompleteFilter typeFilter = new AutocompleteFilter.Builder()
        .setTypeFilter(AutocompleteFilter.TYPE_FILTER_ADDRESS)
        .build();

autocompleteFragment.setFilter(typeFilter);

The following code sets the same AutocompleteFilter on an intent instead of on a fragment:

AutocompleteFilter typeFilter = new AutocompleteFilter.Builder()
        .setTypeFilter(AutocompleteFilter.TYPE_FILTER_ADDRESS)
        .build();

Intent intent =
        new PlaceAutocomplete.IntentBuilder(PlaceAutocomplete.MODE_FULLSCREEN)
                .setFilter(typeFilter)
                .build(this);

For information about place types, see the guide to place types and the AutocompleteFilter.Builder documentation.

To filter autocomplete results to a specific country:

  1. Call AutocompleteFilter.Builder to create a new AutocompleteFilter.
  2. Call setCountry() to set the country code.
  3. Pass the filter to a fragment or intent.

The following code sets an AutocompleteFilter on a PlaceAutocompleteFragment. The filter returns only results within the specified country.

AutocompleteFilter typeFilter = new AutocompleteFilter.Builder()
        .setCountry("AU")
        .build();

autocompleteFragment.setFilter(typeFilter);

Getting place predictions programmatically

You can create a custom search UI instead of using the Places API UI. To do this, your app must get place predictions programmatically.

To get a list of predicted place names or addresses from the autocomplete service, call GeoDataClient.getAutocompletePredictions(). Pass in the following parameters:

  • A query string containing the text typed by the user.
  • A LatLngBounds object that biases the results to a specific area specified by latitude and longitude bounds.
  • Optional: To restrict the results to one or more types of place, include an AutocompleteFilter that contains a set of place types.

The API returns an AutocompletePredictionBuffer in a Task. The AutocompletePredictionBuffer contains a list of AutocompletePrediction objects that represent predicted places. If no known place corresponds to the query and the filter criteria, the buffer may be empty.

Important: To prevent a memory leak, release the AutocompletePredictionBuffer object when your app no longer needs it. Read more about handling buffers.

For each predicted place, you can call the following methods to retrieve place details:

  • getPrimaryText(CharacterStyle matchStyle) returns the primary text that describes a place. This text is usually the name of the place, for example, "Eiffel Tower" or "123 Pitt Street."
  • getSecondaryText(CharacterStyle matchStyle) returns the secondary text that describes a place. For example, "Avenue Anatole France, Paris, France," or "Sydney, New South Wales." Secondary text is useful as a second line of text when showing autocomplete predictions.
  • getFullText(CharacterStyle matchStyle) returns the full text of a place description. This description combines the primary and secondary text. For example, "Eiffel Tower, Avenue Anatole France, Paris, France." To highlight sections of the description that match the search, include a CharacterStyle parameter. Set the parameter to null if you don't need any highlighting.
  • getPlaceId() returns the place ID of the predicted place. A place ID is a textual identifier that uniquely identifies a place. You can use the place ID to retrieve the Place object later.
  • getPlaceTypes() returns the list of place types associated with this place.

About usage limits on place predictions

When you get a place prediction programmatically, usage limits apply. In particular, the GeoDataClient.getAutocompletePredictions() method is subject to tiered query limits. For details, see Usage Limits.

Displaying attributions in your app

  • If your app uses the autocomplete service programmatically, your UI must display a "Powered by Google" attribution, or your UI must appear within a Google-branded map.
  • If your app uses the autocomplete UI, you don't need to do anything special, because the required attribution is displayed by default.
  • If you retrieve and display additional place information after getting a place by ID, you must display third-party attributions.

For more details, see Displaying Attributions.

Getting place photos

You can use the Places API to request place photos to display in your app. The photos come from several sources, including business owners and Google+ users. To retrieve photos for a place, take the following steps:

  1. Call GeoDataClient.getPlacePhotos(), passing a string with a place ID. This method returns a PlacePhotoMetadataResult instance in a Task.
  2. Call getPhotoMetadata() on the PlacePhotoMetadataResult instance. The method returns a PlacePhotoMetadataBuffer that holds a list of PlacePhotoMetadata instances. The list includes one PlacePhotoMetadata instance for each photo.
  3. Call get() on the PlacePhotoMetadataBuffer instance. Pass in an integer to retrieve the PlacePhotoMetadata instance at the given index.

The Places API requires network access, therefore all calls must be made in the background, off the main UI thread. For this reason, retrieve the photos in the background asynchronously.

About usage limits on place photos

When you get place photos programmatically, usage limits apply. Retrieving an image costs one unit of quota. There are no usage limits for retrieving photo metadata.

For more information, see Usage Limits.

Displaying attributions in your app

In most cases, place photos can be used without attribution, or the required attribution is included as part of the image. However, if a PlacePhotoMetadata instance includes an attribution, you must include the attribution in your app wherever you display the image.

For more information, see Displaying Attributions.

Using the place ID

A place ID is a textual identifier that uniquely identifies a place. To retrieve a place's ID:

Store the place ID and use it to retrieve the Place object again later.

To get a place by its ID:

  1. Call GeoDataClient.getPlaceById(), passing in one or more place IDs.
  2. The API returns a PlaceBuffer in a Task. The PlaceBuffer data structure contains a list of Place objects that match the supplied place IDs.
     mGeoDataClient.getPlaceById(placeId).addOnCompleteListener(new OnCompleteListener<PlaceBufferResponse>() {
         @Override
         public void onComplete(@NonNull Task<PlaceBufferResponse> task) {
             if (task.isSuccessful()) {
                 PlaceBufferResponse places = task.getResult();
                 Place myPlace = places.get(0);
                 Log.i(TAG, "Place found: " + myPlace.getName());
                 places.release();
             } else {
                 Log.e(TAG, "Place not found.");
             }
         }
     });
    

Getting place details

The Place object provides information about a specific place. You can get a Place object in the following ways:

Use the following methods to retrieve data from a Place object:

  • getName(): The place's name.
  • getAddress(): The place's address, in human-readable format.
  • getID(): The textual identifier for the place, as described in Using the place ID.
  • getPhoneNumber(): The place's phone number.
  • getWebsiteUri(): The URI of the place's website, if known. This website is maintained by the business or other entity associated with the place. Returns null if no website is known.
  • getLatLng(): The geographical location of the place, specified as latitude and longitude coordinates.
  • getViewport(): A viewport, returned as a LatLngBounds object, useful for displaying the place on a map. If the size of the place is not known, this method may return null.
  • getLocale(): The locale for which the name and address are localized.
  • getPlaceTypes(): A list of place types that characterize this place. For a list of available place types, see the Place constant documentation.
  • getPriceLevel(): The price level for this place, returned as an integer with values ranging from 0 (cheapest) to 4 (most expensive).
  • getRating(): An rating of the place, returned as a float with values ranging from 1.0 to 5.0, based on aggregated user reviews.

The related practical documentation is in 8.1: Using the Places API.

Learn more

Android developer documentation:

results matching ""

    No results matching ""