8.1: Places API
Contents:
- Introduction
- Using the place-picker UI
- Getting the device's current place
- Using the place-autocomplete service
- Getting place photos
- Using the place ID
- Getting place details
- Related practical
- Learn more
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
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:
- Use the
PlacePicker.IntentBuilder()
to construct anIntent
. 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 aLatLngBounds
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.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.
- To retrieve a
Place
object, callPlaceLikelihood.getPlace()
. - Each place result includes an indication of the likelihood that the place is the right one. To get the place's likelihood value, call
PlaceLikelihood.getLikelihood()
.
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.
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 thePlaceFilter
. - 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 aCollection
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.
There are two ways to add the autocomplete UI to your app:
- Option 1: Embed a
PlaceAutocompleteFragment
fragment, as described below. - Option 2: If you want to launch the autocomplete activity from someplace other than a search dialog, use an intent to launch the autocomplete activity, as described in Using an intent to launch the autocomplete activity.
Embedding a PlaceAutocompleteFragment fragment
To add a PlaceAutocompleteFragment
to your app, take the following steps:
Add a fragment to your activity's XML layout, with the
android:name
attribute set tocom.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 aCardView
.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 thePlaceAutocompleteFragment
: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:
- Use
PlaceAutocomplete.IntentBuilder
to create an intent. Pass in the desiredPlaceAutocomplete
display mode—you can choose overlay or full-screen. The intent must callstartActivityForResult()
, 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. }
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:
- Call
AutocompleteFilter.Builder
to create a newAutocompleteFilte
r. - Call
setCountry()
to set the country code. - 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.
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 aCharacterStyle
parameter. Set the parameter tonull
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 thePlace
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:
- Call
GeoDataClient.getPlacePhotos()
, passing a string with a place ID. This method returns aPlacePhotoMetadataResult
instance in aTask
. - Call
getPhotoMetadata()
on thePlacePhotoMetadataResult
instance. The method returns aPlacePhotoMetadataBuffer
that holds a list ofPlacePhotoMetadata
instances. The list includes onePlacePhotoMetadata
instance for each photo. Call
get()
on thePlacePhotoMetadataBuffer
instance. Pass in an integer to retrieve thePlacePhotoMetadata
instance at the given index.- To return a bitmap image, call either
PlacePhotoMetadata.getPhoto()
, orPlacePhotoMetadata.getScaledPhoto()
on aPlacePhotoMetadata
instance. - To return attribution text, call
PlacePhotoMetadata.getAttributions()
on aPlacePhotoMetadata
instance.
- To return a bitmap image, call either
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:
- Call
Place.getId()
. - The place-autocomplete service also returns a place ID for each place that matches the supplied search query and filter.
Store the place ID and use it to retrieve the Place
object again later.
To get a place by its ID:
- Call
GeoDataClient.getPlaceById()
, passing in one or more place IDs. - The API returns a
PlaceBuffer
in aTask
. ThePlaceBuffer
data structure contains a list ofPlace
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:
- Call
PlaceDetectionClient.getCurrentPlace()
. For details about this process, see Current Place. - Add the
PlacePicker
UI, then callPlacePicker.getPlace()
. - Call
GeoDataClient.getPlaceById()
. For more information, see Get a place by ID.
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. Returnsnull
if no website is known.getLatLng()
: The geographical location of the place, specified as latitude and longitude coordinates.getViewport()
: A viewport, returned as aLatLngBounds
object, useful for displaying the place on a map. If the size of the place is not known, this method may returnnull
.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 thePlace
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 afloat
with values ranging from 1.0 to 5.0, based on aggregated user reviews.
Related practical
The related practical documentation is in 8.1: Using the Places API.
Learn more
Android developer documentation: