Google places autocompletes API Android : bounds not working

I'm defining my bounds as follow:

private static final LatLngBounds BOUNDS_CHENNAI = new LatLngBounds(
        new LatLng(12.8339547, 80.0817007), new LatLng(13.2611661, 80.33632279999999)); // Chennai city bounds.

Creating Goolge API client as follow:

mGoogleApiClient = new GoogleApiClient.Builder(this)
            .enableAutoManage(this,this)
            .addApi(Places.GEO_DATA_API)
            .addConnectionCallbacks(this)
            .build();

And using those bounds in adapter as follow:

PendingResult<AutocompletePredictionBuffer> results =
                Places.GeoDataApi
                        .getAutocompletePredictions(mGoogleApiClient, constraint.toString(),
                                mBounds, mPlaceFilter);

According to the documentation is should return me only location within Chennai city, but it returns me location from around the world.

Eg When I type "Sola" it returns "Sola road" of Ahmedabad city instead of showing matched results within bounds.


I have the same problem with the bounds in android. I've tried everything and can't solve the problem.

Looking for other web pages, i found this in the documentation for javascript when trying to set bounds to an area:

The results are biased towards, but not restricted to, Places contained within these bounds.

It looks like the android places api documentation is not complete. We will have to wait.


I found a primitive workaround in case you have to show a results of specified country or nearby countries:

private ArrayList<PlaceAutocomplete> getAutocomplete(CharSequence constraint) {
    if (mGoogleApiClient.isConnected()) {

        // Submit the query to the autocomplete API and retrieve a PendingResult that will
        // contain the results when the query completes.
        PendingResult<AutocompletePredictionBuffer> results =
                Places.GeoDataApi
                        .getAutocompletePredictions(mGoogleApiClient, constraint.toString(),
                                mBounds, mPlaceFilter);

        AutocompletePredictionBuffer autocompletePredictions = results
                .await(60, TimeUnit.SECONDS);

        final Status status = autocompletePredictions.getStatus();
        if (status.isSuccess()) {
            Log.i(LOG_TAG, "Query completed. Received " + autocompletePredictions.getCount()
                    + " predictions.");

            // Copy the results into our own data structure, because we can't hold onto the buffer.
            // AutocompletePrediction objects encapsulate the API response (place ID and description).

            Iterator<AutocompletePrediction> iterator = autocompletePredictions.iterator();
            ArrayList<PlaceAutocomplete> resultList = new ArrayList<>(autocompletePredictions.getCount());

            while (iterator.hasNext()) {
                AutocompletePrediction prediction = iterator.next();
                // Get the details of this prediction and copy it into a new PlaceAutocomplete object.
                String data = prediction.getDescription();

                // here we manually checking whether description contains our needed country(ies)
                if (predictionIsInNeededCountry(data)) {
                    resultList.add(new PlaceAutocomplete(prediction.getPlaceId(), prediction.getDescription(),
                            prediction.getPrimaryText(mCharacterStyle), prediction.getSecondaryText(mCharacterStyle),
                            prediction.getFullText(mCharacterStyle)));
                }
            }
        } else {
            Toast.makeText(mContext, "Error contacting API: " + status.toString(),
                    Toast.LENGTH_SHORT).show();
            Log.e(LOG_TAG, "Error getting autocomplete prediction API call: " + status.toString());
            autocompletePredictions.release();
            return null;
        }

        // Release the buffer now that all data has been copied.
        autocompletePredictions.release();

        return resultList;
    }
    Log.e(LOG_TAG, "Google API client is not connected for autocomplete query.");
    return null;
}

private boolean predictionIsInNeededCountry(String data) {

    // here you can add countries (in different languages if you want)
    // also you can try get current country programmatically with use of Geocoder
    if (data.contains("Ukraine") || data.contains("Украина") || data.contains("Україна")) {
        return true;
    }
    return false;
}

For the bounds I use current coordinates (current latitude and longitude for south west and for north east).

Also I implemented another solution:

  • Get predictions
  • Get Place details of each prediction (you can pass an array to get all the Points at once).
  • Compare Point's coordinates with current and sort them from closest to furthest.
  • Show sorted by distance results.
  • It seems to work more accurate, but requires additional requests and calculations.
    In my case I need only the results from 2 countries so I end up with the first simpler solution.


    import android.content.Context;
    import android.content.res.Resources;
    import android.net.Uri;
    import android.os.Bundle;
    import android.support.v4.app.FragmentActivity;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.Toolbar;
    import android.text.Html;
    import android.text.Spanned;
    import android.view.MenuItem;
    import android.view.View;
    import android.widget.AdapterView;
    import android.widget.AutoCompleteTextView;
    import android.widget.Button;
    import android.widget.TextView;
    import com.google.android.gms.common.ConnectionResult;
    import com.google.android.gms.common.api.GoogleApiClient;
    import com.google.android.gms.common.api.PendingResult;
    import com.google.android.gms.common.api.ResultCallback;
    import com.google.android.gms.location.places.AutocompletePrediction;
    import com.google.android.gms.location.places.Place;
    import com.google.android.gms.location.places.PlaceBuffer;
    import com.google.android.gms.location.places.Places;
    import com.google.android.gms.maps.model.LatLng;
    import com.google.android.gms.maps.model.LatLngBounds;
    
    public class SearchPlacesActivity extends AppCompatActivity implements    GoogleApiClient.OnConnectionFailedListener {
    
    
    protected GoogleApiClient mGoogleApiClient;
    
    private PlaceAutocompleteAdapter mAdapter;
    
    private AutoCompleteTextView mAutocompleteView;
    
    private TextView mPlaceDetailsText;
    
    private TextView mPlaceDetailsAttribution;
    Toolbar toolbar;
    
    private static final LatLngBounds BOUNDS_GREATER_SYDNEY = new LatLngBounds(
            // new LatLng(-34.041458, 150.790100), new LatLng(-33.682247, 151.383362));
            new LatLng(0, 0), new LatLng(0, 0));
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        // Construct a GoogleApiClient for the {@link Places#GEO_DATA_API} using AutoManage
        // functionality, which automatically sets up the API client to handle Activity lifecycle
        // events. If your activity does not extend FragmentActivity, make sure to call connect()
        // and disconnect() explicitly.
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .enableAutoManage(this, 0 /* clientId */, this)
                .addApi(Places.GEO_DATA_API)
                .build();
    
        setContentView(R.layout.activity_search_places);
        //Set toolbar
        toolbar = (Toolbar) findViewById(R.id.maintoolbar);
    
        toolbar.setTitle("Change Location");
    
        toolbar.setTitleTextColor(0xFFFFFFFF);
    
        setSupportActionBar(toolbar);
    
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setHomeButtonEnabled(false);
    
    
    
        // Retrieve the AutoCompleteTextView that will display Place suggestions.
        mAutocompleteView = (AutoCompleteTextView) findViewById(R.id.autocomplete_places);
    
        // Register a listener that receives callbacks when a suggestion has been selected
        mAutocompleteView.setOnItemClickListener(mAutocompleteClickListener);
    
        // Retrieve the TextViews that will display details and attributions of the selected place.
        mPlaceDetailsText = (TextView) findViewById(R.id.place_details);
        mPlaceDetailsAttribution = (TextView) findViewById(R.id.place_attribution);
    
        // Set up the adapter that will retrieve suggestions from the Places Geo Data API that cover
        // the entire world.
        mAdapter = new PlaceAutocompleteAdapter(this, mGoogleApiClient, BOUNDS_GREATER_SYDNEY,
                null);
        mAutocompleteView.setAdapter(mAdapter);
    
        // Set up the 'clear text' button that clears the text in the autocomplete view
        Button clearButton = (Button) findViewById(R.id.button_clear);
        clearButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mAutocompleteView.setText("");
            }
        });
    }
    
    /**
     * Listener that handles selections from suggestions from the AutoCompleteTextView that
     * displays Place suggestions.
     * Gets the place id of the selected item and issues a request to the Places Geo Data API
     * to retrieve more details about the place.
     *
     * @see com.google.android.gms.location.places.GeoDataApi#getPlaceById(GoogleApiClient,
     * String...)
     */
    private AdapterView.OnItemClickListener mAutocompleteClickListener
            = new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            /*
             Retrieve the place ID of the selected item from the Adapter.
             The adapter stores each Place suggestion in a AutocompletePrediction from which we
             read the place ID and title.
              */
            final AutocompletePrediction item = mAdapter.getItem(position);
            final String placeId = item.getPlaceId();
            final CharSequence primaryText = item.getPrimaryText(null);
    
            AppLog.i("Search", "Autocomplete item selected: " + primaryText);
    
            /*
             Issue a request to the Places Geo Data API to retrieve a Place object with additional
             details about the place.
              */
            PendingResult<PlaceBuffer> placeResult = Places.GeoDataApi
                    .getPlaceById(mGoogleApiClient, placeId);
            placeResult.setResultCallback(mUpdatePlaceDetailsCallback);
    
            /*Toast.makeText(getApplicationContext(), "Clicked: " + primaryText,
                    Toast.LENGTH_SHORT).show();*/
            AppLog.i("Search", "Called getPlaceById to get Place details for " + placeId);
        }
    };
    
    /**
     * Callback for results from a Places Geo Data API query that shows the first place result in
     * the details view on screen.
     */
    private ResultCallback<PlaceBuffer> mUpdatePlaceDetailsCallback
            = new ResultCallback<PlaceBuffer>() {
        @Override
        public void onResult(PlaceBuffer places) {
            if (!places.getStatus().isSuccess()) {
                // Request did not complete successfully
                AppLog.e("Search", "Place query did not complete. Error: " + places.getStatus().toString());
                places.release();
                return;
            }
            // Get the Place object from the buffer.
            final Place place = places.get(0);
    
            // Format details of the place for display and show it in a TextView.
        /*    mPlaceDetailsText.setText(formatPlaceDetails(getResources(), place.getName(),
                    place.getId(), place.getAddress(), place.getPhoneNumber(),
                    place.getWebsiteUri(),place.getLatLng()));*/
    
            AppLog.e("LAtlng", ">>>>>>>>>>>>" + place.getLatLng());
            LatLng latLng = place.getLatLng();
            double lat = latLng.latitude;
            double lng = latLng.longitude;
    SearchFood.searchresult = place.getName().toString() + "," +   place.getAddress().toString();
    SearchActivity.searchresult = place.getName().toString() + "," + place.getAddress().toString();
    
            onBackPressed();
            places.release();
        }
    };
    
    private static Spanned formatPlaceDetails(Resources res, CharSequence name, String id,
                                              CharSequence address, CharSequence phoneNumber, Uri websiteUri, LatLng latLng) {
        AppLog.e("Search", res.getString(R.string.place_details, name, id, address, phoneNumber,
                websiteUri));
        return Html.fromHtml(res.getString(R.string.place_details, name, id, address, phoneNumber,
                websiteUri));
    
    }
    
    /**
     * Called when the Activity could not connect to Google Play services and the auto manager
     * could resolve the error automatically.
     * In this case the API is not available and notify the user.
     *
     * @param connectionResult can be inspected to determine the cause of the failure
     */
    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
    
        AppLog.e("Search", "onConnectionFailed: ConnectionResult.getErrorCode() = "
                + connectionResult.getErrorCode());
    
        // TODO(Developer): Check error code and notify the user of error state and resolution.
      /*  Toast.makeText(this,
                "Could not connect to Google API Client: Error " + connectionResult.getErrorCode(),
                Toast.LENGTH_SHORT).show();*/
    }
    
    
    @Override
    public void onBackPressed() {
    
        super.onBackPressed();
        SearchPlacesActivity.this.finish();
        overridePendingTransition(R.anim.trans_right_in, R.anim.trans_right_out);
    
    }
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
    
        //noinspection SimplifiableIfStatement
        if (id == android.R.id.home) {
            onBackPressed();
        }
        return super.onOptionsItemSelected(item);
    }
    }
    
    // ADAPTER CLASS
    
    
    import android.content.Context;
    import android.graphics.Typeface;
    import android.text.style.CharacterStyle;
    import android.text.style.StyleSpan;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ArrayAdapter;
    import android.widget.Filter;
    import android.widget.Filterable;
    import android.widget.TextView;
    import com.eatcommunity.util.AppLog;
    import com.google.android.gms.common.api.GoogleApiClient;
    import com.google.android.gms.common.api.PendingResult;
    import com.google.android.gms.common.api.Status;
    import com.google.android.gms.common.data.DataBufferUtils;
    import com.google.android.gms.location.places.AutocompleteFilter;
    import com.google.android.gms.location.places.AutocompletePrediction;
    import   com.google.android.gms.location.places.AutocompletePredictionBuffer;
    import com.google.android.gms.location.places.Places;
    import com.google.android.gms.maps.model.LatLngBounds;
    
    import java.util.ArrayList;
    import java.util.concurrent.TimeUnit;
    
    public class PlaceAutocompleteAdapter
        extends ArrayAdapter<AutocompletePrediction> implements Filterable     {
    
    private static final String TAG = "PlaceAutocompleteAdapter";
    private static final CharacterStyle STYLE_BOLD = new StyleSpan(Typeface.BOLD);
    /**
     * Current results returned by this adapter.
     */
    private ArrayList<AutocompletePrediction> mResultList;
    
    
    private GoogleApiClient mGoogleApiClient;
    
    
    private LatLngBounds mBounds;
    private AutocompleteFilter mPlaceFilter;
    public PlaceAutocompleteAdapter(Context context, GoogleApiClient googleApiClient,
                                    LatLngBounds bounds, AutocompleteFilter filter) {
        super(context, android.R.layout.simple_expandable_list_item_2, android.R.id.text1);
        mGoogleApiClient = googleApiClient;
        mBounds = bounds;
        mPlaceFilter = filter;
    }
    public void setBounds(LatLngBounds bounds) {
        mBounds = bounds;
    }
    @Override
    public int getCount() {
        return mResultList.size();
    }
    @Override
    public AutocompletePrediction getItem(int position) {
        return mResultList.get(position);
    }
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent)     {
        View row = super.getView(position, convertView, parent);
    
        // Sets the primary and secondary text for a row.
        // Note that getPrimaryText() and getSecondaryText() return a CharSequence that may contain
        // styling based on the given CharacterStyle.
    
        AutocompletePrediction item = getItem(position);
    
        TextView textView1 = (TextView) row.findViewById(android.R.id.text1);
        TextView textView2 = (TextView) row.findViewById(android.R.id.text2);
        textView1.setText(item.getPrimaryText(STYLE_BOLD));
        textView2.setText(item.getSecondaryText(STYLE_BOLD));
    
        return row;
    }
    
    @Override
    public Filter getFilter() {
        return new Filter() {
            @Override
            protected FilterResults performFiltering(CharSequence constraint) {
                FilterResults results = new FilterResults();
                // Skip the autocomplete query if no constraints are given.
                if (constraint != null) {
                    // Query the autocomplete API for the (constraint) search string.
                    mResultList = getAutocomplete(constraint);
                    if (mResultList != null) {
                        // The API successfully returned results.
                        results.values = mResultList;
                        results.count = mResultList.size();
                    }
                }
                return results;
            }
    
            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                if (results != null && results.count > 0) {
                    // The API returned at least one result, update the data.
                    notifyDataSetChanged();
                } else {
                    // The API did not return any results, invalidate the data set.
    
    
                    //notifyDataSetInvalidated();
                }
            }
    
            @Override
            public CharSequence convertResultToString(Object resultValue) {
                // Override this method to display a readable result in the AutocompleteTextView
                // when clicked.
                if (resultValue instanceof AutocompletePrediction) {
                    return ((AutocompletePrediction) resultValue).getFullText(null);
                } else {
                    return super.convertResultToString(resultValue);
                }
            }
        };
    }
    private ArrayList<AutocompletePrediction> getAutocomplete(CharSequence constraint) {
        if (mGoogleApiClient.isConnected()) {
            AppLog.i(TAG, "Starting autocomplete query for: " + constraint);
    
            // Submit the query to the autocomplete API and retrieve a PendingResult that will
            // contain the results when the query completes.
            PendingResult<AutocompletePredictionBuffer> results =
                    Places.GeoDataApi
                            .getAutocompletePredictions(mGoogleApiClient, constraint.toString(),
                                    mBounds, mPlaceFilter);
    
            AutocompletePredictionBuffer autocompletePredictions = results
                    .await(60, TimeUnit.SECONDS);
    
            final Status status = autocompletePredictions.getStatus();
            if (!status.isSuccess()) {
               /* Toast.makeText(getContext(), "Error contacting API: " + status.toString(),
                        Toast.LENGTH_SHORT).show();*/
                AppLog.e(TAG, "Error getting autocomplete prediction API call: " + status.toString());
                autocompletePredictions.release();
                return null;
            }
    
            return DataBufferUtils.freezeAndClose(autocompletePredictions);
        }
        AppLog.e(TAG, "Google API client is not connected for autocomplete query.");
        return null;
    }
    }
    
    
    
    
    <?xml version="1.0" encoding="utf-8"?>
    
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <LinearLayout
        android:id="@+id/container_toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    
    <include
    android:id="@+id/maintoolbar"
    layout="@layout/application_toolbar" />
    </LinearLayout>
    
    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/container_toolbar">
    
    <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin">
    
    <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:text="Search Places"
    android:textAppearance="?  android:attr/textAppearanceMedium" />
    
    <AutoCompleteTextView
    android:id="@+id/autocomplete_places"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:hint="Enter your place"
    android:singleLine="true" />
    
    <ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="right"
    android:src="@drawable/powered_by_google_light"
    android:visibility="gone" />
    
    <Button
    android:id="@+id/button_clear"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:text="Clear text" />
    
    <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Selelcted Place"
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:visibility="gone" />
    
    <TextView
    android:id="@+id/place_details"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:autoLink="all"
    android:text=""
    android:textAppearance="? android:attr/textAppearanceMedium" />
    
    <TextView
    android:id="@+id/place_attribution"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:autoLink="all"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:text=""
    android:textAppearance="?android:attr/textAppearanceSmall" />
    </LinearLayout>
    </ScrollView>
    </RelativeLayout>
    
    Menifest File change
    
            <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="YOURAPIKEY" />
    
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
    
    链接地址: http://www.djcxy.com/p/88496.html

    上一篇: 用php标记图像

    下一篇: Google将自动完成API安卓设置为无效