Offline Geocode

Screenshot of Offline Geocode App

Description

This sample demonstrates how you can geocode addresses to locations and reverse geocode locations to addresses when the device does not have network connectivity.

Features

  • GeocodeResult
  • GeocodeParameters
  • LocatorTask
  • ReverseGeocodeParameters

How to Use

Type the address in the Search menu option or select from the list to Geocode the address and view the result on the map. Long-press on the location you want to Reverse Geocode. Selecting the output pin enables real-time reverse geocoding. Select the pin to highlight the PictureMarkerSymbol and tap-hold and drag on the map to get real-time geocoding.

Developer Pattern

Use the path of SanFrancisco.loc to create an object of LocatorTask. Set up GeocodeParameters and run asynchronous method geoCodeAsync() to get GeocodeResults. Methods getDisplayLocation() and getLabel() on geocode results is then used to fetch location and address.

// Execute async task to find the address
        mLocatorTask.addDoneLoadingListener(new Runnable() {
            @Override
            public void run() {
                if (mLocatorTask.getLoadStatus() == LoadStatus.LOADED) {
                    // Call geocodeAsync passing in an address
                    final ListenableFuture<List<GeocodeResult>> geocodeFuture = mLocatorTask.geocodeAsync(address,
                            mGeocodeParameters);
                    geocodeFuture.addDoneListener(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                // Get the results of the async operation
                                List<GeocodeResult> geocodeResults = geocodeFuture.get();

                                if (geocodeResults.size() > 0) {
                                    // Use the first result - for example
                                    // display on the map
                                    mGeocodedLocation = geocodeResults.get(0);
                                    displaySearchResult(mGeocodedLocation.getDisplayLocation(), mGeocodedLocation.getLabel());

                                } else {
                                    Toast.makeText(getApplicationContext(),
                                            getString(R.string.location_not_foud) + address,
                                            Toast.LENGTH_LONG).show();
                                }
                            } catch (InterruptedException|ExecutionException e) {
                                // Deal with exception...
                                e.printStackTrace();
                            }
                            geocodeFuture.removeDoneListener(this);
                        }
                    });
                }
            }
        });
        mLocatorTask.loadAsync();
On similar basis, after setting up ReverseGeocodeParameters, LocatorTask.reverseGeocodeAsync() returns a list of geocode results.

Provision your device

The sample depends on basemap data to be located on the device. This includes installing a local tile map cache (tpk) to device as described below:

  1. Download the data from the table below.
  2. Extract the contents of the downloaded zip file to disk.
  3. Create an ArcGIS/samples/OfflineGeocoding folder on your device. You can use the Android Debug Bridge (adb) tool found in <sdk-dir>/platform-tools.
  4. Open up a command prompt and execute the adb shell command to start a remote shell on your target device.
  5. Navigate to your sdcard directory, e.g. cd /sdcard/.
  6. Create the ArcGIS/samples/FLGdb directory, mkdir ArcGIS/samples/OfflineGeocoding.
  7. You should now have the following directory on your target device, /sdcard/ArcGIS/samples/OfflineGeocoding. We will copy the contents of the downloaded data into this directory. Note: Directory may be slightly different on your device.
  8. Exit the shell with the, exit command.
  9. While still in your command prompt, navigate to the folder where you extracted the contents of the data from step 1 and execute the following command:
    1. adb push streetmap_SD.tpk /sdcard/ArcGIS/samples/OfflineGeocoding
    2. adb push san-diego-locator.loc /sdcard/ArcGIS/samples/OfflineGeocoding
    3. adb push san-diego-locator.locb /sdcard/ArcGIS/samples/OfflineGeocoding
    4. adb push san-diego-locator.lox /sdcard/ArcGIS/samples/OfflineGeocoding
    5. adb push san-diego-locator.loc.x /sdcard/ArcGIS/samples/OfflineGeocoding

LinkLocal Location

San Diego Streets Tile Package

<sdcard>/ArcGIS/samples/OfflineGeocoding/streetmap_SD.tpk

San Diego Offline Locator

<sdcard>/ArcGIS/samples/OfflineGeocoding/san-diego-locator.loc

Code

/* Copyright 2016 Esri
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.esri.arcgisruntime.sample.offlinegeocode;

import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutionException;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.MotionEventCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.SearchView;
import android.widget.SearchView.OnQueryTextListener;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.esri.arcgisruntime.concurrent.ListenableFuture;
import com.esri.arcgisruntime.data.TileCache;
import com.esri.arcgisruntime.geometry.Point;
import com.esri.arcgisruntime.geometry.SpatialReference;
import com.esri.arcgisruntime.layers.ArcGISTiledLayer;
import com.esri.arcgisruntime.loadable.LoadStatus;
import com.esri.arcgisruntime.mapping.ArcGISMap;
import com.esri.arcgisruntime.mapping.Basemap;
import com.esri.arcgisruntime.mapping.Viewpoint;
import com.esri.arcgisruntime.mapping.view.Callout;
import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener;
import com.esri.arcgisruntime.mapping.view.Graphic;
import com.esri.arcgisruntime.mapping.view.GraphicsOverlay;
import com.esri.arcgisruntime.mapping.view.IdentifyGraphicsOverlayResult;
import com.esri.arcgisruntime.mapping.view.MapView;
import com.esri.arcgisruntime.symbology.PictureMarkerSymbol;
import com.esri.arcgisruntime.tasks.geocode.GeocodeParameters;
import com.esri.arcgisruntime.tasks.geocode.GeocodeResult;
import com.esri.arcgisruntime.tasks.geocode.LocatorTask;
import com.esri.arcgisruntime.tasks.geocode.ReverseGeocodeParameters;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "OfflineActivity";
    private final String extern = Environment.getExternalStorageDirectory().getPath();
    final int requestCode = 2;
    final String[] permission = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
    private GraphicsOverlay graphicsOverlay;
    private GeocodeParameters mGeocodeParameters;
    private PictureMarkerSymbol mPinSourceSymbol;
    ArcGISMap mMap;
    ArcGISTiledLayer tiledLayer;
    private MapView mMapView;
    private LocatorTask mLocatorTask;
    private ReverseGeocodeParameters mReverseGeocodeParameters;
    private Callout mCallout;
    private SearchView mSearchview;
    private String mGraphicPointAddress;
    private Point mGraphicPoint;
    private GeocodeResult mGeocodedLocation;
    Spinner mSpinner;
    private boolean isPinSelected;
    private TextView mCalloutContent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // inflate MapView from layout
        mMapView = (MapView) findViewById(R.id.mapView);

        // Check permissions to see if failure may be due to lack of permissions.
        boolean permissionCheck = ContextCompat.checkSelfPermission(MainActivity.this, permission[0]) ==
                PackageManager.PERMISSION_GRANTED;

        if (!permissionCheck) {
            // If permissions are not already granted, request permission from the user.
            ActivityCompat.requestPermissions(MainActivity.this, permission, requestCode);
        } else { // if permission was already granted, set up offline map and geocoding, reverse geocoding and LocatorTask
            setUpOfflineMapGeocoding();
            setSearchView();
        }
        mMapView.setOnTouchListener(new MapTouchListener(getApplicationContext(), mMapView));
    }

    private void setSearchView() {


        mSearchview = (SearchView) findViewById(R.id.searchView1);
        mSearchview.setIconifiedByDefault(true);
        mSearchview.setQueryHint(getResources().getString(R.string.search_hint));
        mSearchview.setOnQueryTextListener(new OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                hideKeyboard();
                geoCodeTypedAddress(query);
                mSearchview.clearFocus();
                return true;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                return false;
            }
        });

        mSpinner = (Spinner) findViewById(R.id.spinner);
        // Create an ArrayAdapter using the string array and a default spinner layout
        final ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(this, android.R.layout.simple_spinner_dropdown_item) {
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {

                View v = super.getView(position, convertView, parent);
                if (position == getCount()) {
                    mSearchview.clearFocus();
                }

                return v;
            }

            @Override
            public int getCount() {
                return super.getCount() - 1; // you dont display last item. It is used as hint.
            }

        };

        // Specify the layout to use when the list of choices appears
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        adapter.addAll(getResources().getStringArray(R.array.suggestion_items));

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            // set vertical offset to spinner dropdown for API less than 21
            mSpinner.setDropDownVerticalOffset(80);
        }
        // Apply the adapter to the spinner
        mSpinner.setAdapter(adapter);
        mSpinner.setSelection(adapter.getCount());


        mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                if (position == adapter.getCount()) {
                    mSearchview.clearFocus();
                } else {
                    hideKeyboard();
                    mSearchview.setQuery(getResources().getStringArray(R.array.suggestion_items)[position], false);
                    geoCodeTypedAddress(getResources().getStringArray(R.array.suggestion_items)[position]);
                    mSearchview.setIconified(false);
                    mSearchview.clearFocus();
                }
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });

    }

    private void setUpOfflineMapGeocoding() {
        // create a basemap from a local tile package
        TileCache tileCache = new TileCache(extern + getResources().getString(R.string.sandiego_tpk));
        tiledLayer = new ArcGISTiledLayer(tileCache);
        Basemap basemap = new Basemap(tiledLayer);

        // create ArcGISMap with imagery basemap
        mMap = new ArcGISMap(basemap);

        mMapView.setMap(mMap);

        mMap.addDoneLoadingListener(new Runnable() {
            @Override
            public void run() {
                Point p = new Point(-117.162040, 32.718260, SpatialReference.create(4326));
                Viewpoint vp = new Viewpoint(p, 10000);
                mMapView.setViewpointAsync(vp, 3);
            }
        });


        // add a graphics overlay
        graphicsOverlay = new GraphicsOverlay();
        graphicsOverlay.setSelectionColor(Color.CYAN);
        mMapView.getGraphicsOverlays().add(graphicsOverlay);


        mGeocodeParameters = new GeocodeParameters();
        mGeocodeParameters.getResultAttributeNames().add("*");
        mGeocodeParameters.setMaxResults(1);

        //[DocRef: Name=Picture Marker Symbol Drawable-android, Category=Fundamentals, Topic=Symbols and Renderers]
        //Create a picture marker symbol from an app resource
        BitmapDrawable startDrawable = (BitmapDrawable) ContextCompat.getDrawable(this, R.drawable.pin);
        mPinSourceSymbol = new PictureMarkerSymbol(startDrawable);
        mPinSourceSymbol.setHeight(90);
        mPinSourceSymbol.setWidth(20);
        mPinSourceSymbol.loadAsync();
        mPinSourceSymbol.setLeaderOffsetY(45);
        mPinSourceSymbol.setOffsetY(-48);

        mReverseGeocodeParameters = new ReverseGeocodeParameters();
        mReverseGeocodeParameters.getResultAttributeNames().add("*");
        mReverseGeocodeParameters.setOutputSpatialReference(mMap.getSpatialReference());
        mReverseGeocodeParameters.setMaxResults(1);

        mLocatorTask = new LocatorTask(extern + getResources().getString(R.string.sandiego_loc));

        mCalloutContent = new TextView(getApplicationContext());
        mCalloutContent.setTextColor(Color.BLACK);
        mCalloutContent.setTextIsSelectable(true);
    }

    /**
     * Geocode an address typed in by user
     *
     * @param address
     */
    private void geoCodeTypedAddress(final String address) {
        // Null out any previously located result
        mGeocodedLocation = null;

        // Execute async task to find the address
        mLocatorTask.addDoneLoadingListener(new Runnable() {
            @Override
            public void run() {
                if (mLocatorTask.getLoadStatus() == LoadStatus.LOADED) {
                    // Call geocodeAsync passing in an address
                    final ListenableFuture<List<GeocodeResult>> geocodeFuture = mLocatorTask.geocodeAsync(address,
                            mGeocodeParameters);
                    geocodeFuture.addDoneListener(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                // Get the results of the async operation
                                List<GeocodeResult> geocodeResults = geocodeFuture.get();

                                if (geocodeResults.size() > 0) {
                                    // Use the first result - for example
                                    // display on the map
                                    mGeocodedLocation = geocodeResults.get(0);
                                    displaySearchResult(mGeocodedLocation.getDisplayLocation(), mGeocodedLocation.getLabel());

                                } else {
                                    Toast.makeText(getApplicationContext(),
                                            getString(R.string.location_not_foud) + address,
                                            Toast.LENGTH_LONG).show();
                                }

                            } catch (InterruptedException | ExecutionException e) {
                                // Deal with exception...
                                e.printStackTrace();
                                Toast.makeText(getApplicationContext(),
                                        getString(R.string.geo_locate_error),
                                        Toast.LENGTH_LONG).show();

                            }
                            // Done processing and can remove this listener.
                            geocodeFuture.removeDoneListener(this);
                        }
                    });

                } else {
                    Log.i(TAG, "Trying to reload locator task");
                    mLocatorTask.retryLoadAsync();
                }
            }
        });
        mLocatorTask.loadAsync();
    }

    /**
     * Hides soft keyboard
     */
    private void hideKeyboard() {
        mSearchview.clearFocus();
        InputMethodManager inputManager = (InputMethodManager) getApplicationContext()
                .getSystemService(Context.INPUT_METHOD_SERVICE);
        inputManager.hideSoftInputFromWindow(mSearchview.getWindowToken(), 0);
    }

    private void displaySearchResult(Point resultPoint, String address) {


        if (mMapView.getCallout().isShowing()) {
            mMapView.getCallout().dismiss();
        }
        //remove any previous graphics/search results
        //mMapView.getGraphicsOverlays().clear();
        graphicsOverlay.getGraphics().clear();
        // create graphic object for resulting location
        Graphic resultLocGraphic = new Graphic(resultPoint, mPinSourceSymbol);
        // add graphic to location layer
        graphicsOverlay.getGraphics().add(resultLocGraphic);

        // Zoom map to geocode result location
        mMapView.setViewpointAsync(new Viewpoint(resultPoint, 8000), 3);

        mGraphicPoint = resultPoint;
        mGraphicPointAddress = address;
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        // If request is cancelled, the result arrays are empty.
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // Location permission was granted. This would have been triggered in response to failing to start the
            // LocationDisplay, so try starting this again.
            setUpOfflineMapGeocoding();
            setSearchView();
        } else {
            // If permission was denied, show toast to inform user what was chosen. If LocationDisplay is started again,
            // request permission UX will be shown again, option should be shown to allow never showing the UX again.
            // Alternative would be to disable functionality so request is not shown again.
            Toast.makeText(MainActivity.this, getResources().getString(R.string.storage_permission_denied), Toast
                    .LENGTH_SHORT).show();

        }
    }

    private class DragTouchListener extends DefaultMapViewOnTouchListener {

        float dX, dY;

        public DragTouchListener(Context context, MapView mapView) {
            super(context, mapView);
        }

        @Override
        public boolean onTouch(View view, MotionEvent event) {

            switch (event.getAction()) {

                case MotionEvent.ACTION_DOWN:
                    dX = view.getX() - event.getRawX();
                    dY = view.getY() - event.getRawY();
                    break;

                case MotionEvent.ACTION_MOVE:
                    final int pointerIndex = MotionEventCompat.getActionIndex(event);
                    final float x = MotionEventCompat.getX(event, pointerIndex);
                    final float y = MotionEventCompat.getY(event, pointerIndex);
                    android.graphics.Point screenPoint = new android.graphics.Point(Math.round(x), Math.round(y));
                    final Point singleTapPoint = mMapView.screenToLocation(screenPoint);
                    final ListenableFuture<List<GeocodeResult>> results = mLocatorTask.reverseGeocodeAsync(singleTapPoint,
                            mReverseGeocodeParameters);
                    graphicsOverlay.getGraphics().clear();
                    Graphic resultLocGraphic = new Graphic(singleTapPoint, mPinSourceSymbol);
                    resultLocGraphic.setSelected(true);
                    // add graphic to location layer
                    graphicsOverlay.getGraphics().add(resultLocGraphic);
                    // display callout with reverse-geocode result on UI thread
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                List<GeocodeResult> geocodes = results.get();
                                if (geocodes.size() > 0) {
                                    // get the top result
                                    GeocodeResult geocode = geocodes.get(0);
                                    String detail;
                                    // attributes from a click-based search
                                    String street = geocode.getAttributes().get("Street").toString();
                                    String city = geocode.getAttributes().get("City").toString();
                                    String state = geocode.getAttributes().get("State").toString();
                                    String zip = geocode.getAttributes().get("ZIP").toString();
                                    detail = city + ", " + state + " " + zip;

                                    String address = street + "," + detail;
                                    mCalloutContent.setText(address);
                                    // get callout, set content and show
                                    mCallout = mMapView.getCallout();
                                    mCallout.setLocation(singleTapPoint);
                                    mCallout.setContent(mCalloutContent);
                                    mCallout.show();

                                    mGraphicPoint = singleTapPoint;
                                    mGraphicPointAddress = address;
                                }

                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    });


                    break;
                case MotionEvent.ACTION_UP:
                    if (graphicsOverlay.getGraphics().size() > 0) {
                        graphicsOverlay.getGraphics().get(0).setSelected(false);
                        isPinSelected = false;
                        mMapView.setOnTouchListener(new MapTouchListener(getApplicationContext(), mMapView));
                    }
                    break;
                default:
                    return false;
            }
            return true;
        }
    }

    private class MapTouchListener extends DefaultMapViewOnTouchListener {

        public MapTouchListener(Context context, MapView mapView) {
            super(context, mapView);
        }

        @Override
        public void onLongPress(MotionEvent e) {
            android.graphics.Point screenPoint = new android.graphics.Point(Math.round(e.getX()),
                    Math.round(e.getY()));

            Point longPressPoint = mMapView.screenToLocation(screenPoint);

            ListenableFuture<List<GeocodeResult>> results = mLocatorTask.reverseGeocodeAsync(longPressPoint,
                    mReverseGeocodeParameters);
            results.addDoneListener(new ResultsLoadedListener(results));

        }


        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {


            if (mMapView.getCallout().isShowing()) {
                mMapView.getCallout().dismiss();
            }
            if (graphicsOverlay.getGraphics().size() > 0) {
                if (graphicsOverlay.getGraphics().get(0).isSelected()) {
                    isPinSelected = false;
                    graphicsOverlay.getGraphics().get(0).setSelected(false);
                }
            }
            // get the screen point where user tapped
            final android.graphics.Point screenPoint = new android.graphics.Point((int) e.getX(), (int) e.getY());

            // identify graphics on the graphics overlay
            final ListenableFuture<IdentifyGraphicsOverlayResult> identifyGraphic = mMapView.identifyGraphicsOverlayAsync(graphicsOverlay, screenPoint, 1.0, false, 1);

            identifyGraphic.addDoneListener(new Runnable() {
                @Override
                public void run() {
                    try {
                        IdentifyGraphicsOverlayResult grOverlayResult = identifyGraphic.get();
                        // get the list of graphics returned by identify
                        List<Graphic> graphic = grOverlayResult.getGraphics();
                        // if identified graphic is not empty, start DragTouchListener
                        if (!graphic.isEmpty()) {

                            if (!isPinSelected) {
                                isPinSelected = true;
                                graphic.get(0).setSelected(true);
                                Toast.makeText(getApplicationContext(),
                                        getString(R.string.reverse_geocode_message),
                                        Toast.LENGTH_SHORT).show();
                                mMapView.setOnTouchListener(new DragTouchListener(getApplicationContext(), mMapView));
                            }

                            mCalloutContent.setText(mGraphicPointAddress);
                            // get callout, set content and show
                            mCallout = mMapView.getCallout();
                            mCallout.setContent(mCalloutContent);
                            mCallout.setLocation(mGraphicPoint);
                            mCallout.show();
                        }
                    } catch (InterruptedException | ExecutionException ie) {
                        ie.printStackTrace();
                    }

                }
            });

            return super.onSingleTapConfirmed(e);
        }
    }

    /**
     * Updates marker and callout when new results are loaded.
     */
    private class ResultsLoadedListener implements Runnable {

        private final ListenableFuture<List<GeocodeResult>> results;

        /**
         * Constructs a runnable listener for the geocode results.
         *
         * @param results results from a {@link LocatorTask#geocodeAsync} task
         */
        ResultsLoadedListener(ListenableFuture<List<GeocodeResult>> results) {
            this.results = results;
        }


        @Override
        public void run() {

            try {
                List<GeocodeResult> geocodes = results.get();
                if (geocodes.size() > 0) {
                    // get the top result
                    GeocodeResult geocode = geocodes.get(0);

                    // set the viewpoint to the marker
                    Point location = geocode.getDisplayLocation();
                    // get attributes from the result for the callout
                    String title;
                    String detail;
                    Object matchAddr = geocode.getAttributes().get("Match_addr");
                    if (matchAddr != null) {
                        // attributes from a query-based search
                        title = matchAddr.toString().split(",")[0];
                        detail = matchAddr.toString().substring(matchAddr.toString().indexOf(",") + 1);
                    } else {
                        // attributes from a click-based search
                        String street = geocode.getAttributes().get("Street").toString();
                        String city = geocode.getAttributes().get("City").toString();
                        String state = geocode.getAttributes().get("State").toString();
                        String zip = geocode.getAttributes().get("ZIP").toString();
                        title = street;
                        detail = city + ", " + state + " " + zip;
                    }

                    // get attributes from the result for the callout
                    HashMap<String, Object> attributes = new HashMap<>();
                    attributes.put("title", title);
                    attributes.put("detail", detail);


                    // create the marker
                    Graphic marker = new Graphic(geocode.getDisplayLocation(), attributes, mPinSourceSymbol);
                    graphicsOverlay.getGraphics().clear();

                    // add the markers to the graphics overlay
                    graphicsOverlay.getGraphics().add(marker);

                    if (isPinSelected) {
                        marker.setSelected(true);
                    }
                    String calloutText = title + ", " + detail;
                    mCalloutContent.setText(calloutText);
                    // get callout, set content and show
                    mCallout = mMapView.getCallout();
                    mCallout.setLocation(geocode.getDisplayLocation());
                    mCallout.setContent(mCalloutContent);
                    mCallout.show();

                    mGraphicPoint = location;
                    mGraphicPointAddress = title + ", " + detail;
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

}