Skip To Content ArcGIS for Developers Sign In Dashboard

ArcGIS Runtime SDK for Android

Place Search

The PlaceSearch app uses the geocoding service to convert addresses to and from geographic coordinates. Search for places within a region and get places matching your search.

Sample Design

The Android API provides a Locator class that allows you to match addresses and place names to locations on the map (known as geocoding), and match locations on the map to real-world addresses (known as reverse geocoding). The locator relies on Esri's geocoding web services to perform these functions.

Features

  • Locator
  • LocatorFindParameters
  • LocatorGeocodeResult
  • AsyncTask

Sample Code

/* Copyright 2014 ESRI
 *
 * All rights reserved under the copyright laws of the United States
 * and applicable international laws, treaties, and conventions.
 *
 * You may freely redistribute and use this sample code, with or
 * without modification, provided you include the original copyright
 * notice and use restrictions.
 *
 * See the Sample code usage restrictions document for further information.
 *
 */

package com.arcgis.android.samples.search.placesearch;

import java.util.List;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.Toast;

import com.esri.android.map.GraphicsLayer;
import com.esri.android.map.MapView;
import com.esri.android.map.event.OnStatusChangedListener;
import com.esri.core.geometry.Envelope;
import com.esri.core.geometry.Point;
import com.esri.core.map.Graphic;
import com.esri.core.symbol.SimpleMarkerSymbol;
import com.esri.core.symbol.TextSymbol;
import com.esri.core.tasks.geocode.Locator;
import com.esri.core.tasks.geocode.LocatorFindParameters;
import com.esri.core.tasks.geocode.LocatorGeocodeResult;

public class MainActivity extends Activity {

	private static final String TAG = "MainActivity";

	private MapView mMapView;
	private EditText mSearchEditText;
	private String mMapViewState;

	// Graphics layer to show geocode and reverse geocode results
	private GraphicsLayer mLocationLayer;

	// UI components
	private static ProgressDialog mProgressDialog;

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

		// Setup and show progress dialog
		mProgressDialog = new ProgressDialog(this) {
			@Override
			public void onBackPressed() {
				// Back key pressed - just dismiss the dialog
				mProgressDialog.dismiss();
			}
		};

		// after the content of this activity is set
		// the map can be accessed from the layout
		mMapView = (MapView) findViewById(R.id.map);

		mLocationLayer = new GraphicsLayer();
		mMapView.addLayer(mLocationLayer);

		// set logo and enable wrap around
		mMapView.setEsriLogoVisible(true);
		mMapView.enableWrapAround(true);

		// Setup listener for map initialized
		mMapView.setOnStatusChangedListener(new OnStatusChangedListener() {

			private static final long serialVersionUID = 1L;

			@Override
			public void onStatusChanged(Object source, STATUS status) {
				if (source == mMapView && status == STATUS.INITIALIZED) {

					if (mMapViewState == null) {
						Log.i(TAG,
								"MapView.setOnStatusChangedListener() status="
										+ status.toString());
					} else {
						mMapView.restoreState(mMapViewState);
					}

				}
			}
		});
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);

		// get a reference to the EditText widget for the search option
		View searchRef = menu.findItem(R.id.action_search).getActionView();
		mSearchEditText = (EditText) searchRef.findViewById(R.id.searchText);

		// set key listener to start search if Enter key is pressed
		mSearchEditText.setOnKeyListener(new View.OnKeyListener() {
			@Override
			public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
				if (keyCode == KeyEvent.KEYCODE_ENTER) {
					onSearchButtonClicked(mSearchEditText);
					return true;
				}

				return false;
			}
		});

		return true;
	}

	@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();
		
		return id == R.id.action_search || super.onOptionsItemSelected(item);
	}

	/**
	 * Called from search_layout.xml when user presses Search button
	 *
	 * @param view
	 */
	public void onSearchButtonClicked(View view) {
		// Hide virtual keyboard
		InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
		inputManager.hideSoftInputFromWindow(view.getWindowToken(), 0);

		// obtain address and execute locator task
		String address = mSearchEditText.getText().toString();
		executeLocatorTask(address);

	}

	/**
	 * Set up the search parameters and execute the Locator task.
	 *
	 * @param address
	 */
	private void executeLocatorTask(String address) {
		// Create Locator parameters from single line address string
		LocatorFindParameters findParams = new LocatorFindParameters(address);

		// Use the center of the current map extent as the find location point
		findParams.setLocation(mMapView.getCenter(),
				mMapView.getSpatialReference());

		// Calculate distance for find operation
		Envelope mapExtent = new Envelope();
		mMapView.getExtent().queryEnvelope(mapExtent);
		// assume map is in meters, other units wont work, double current
		// envelope
		double distance = (mapExtent != null && mapExtent.getWidth() > 0) ? mapExtent
				.getWidth() * 2 : 10000;
		findParams.setDistance(distance);
		findParams.setMaxLocations(2);

		// Set address spatial reference to match map
		findParams.setOutSR(mMapView.getSpatialReference());

		// Execute async task to find the address
		new LocatorAsyncTask().execute(findParams);
	}

	/*
	 * This class provides an AsyncTask that performs a geolocation request on a
	 * background thread and displays the first result on the map on the UI
	 * thread.
	 */
	private class LocatorAsyncTask extends
			AsyncTask<LocatorFindParameters, Void, List<LocatorGeocodeResult>> {
		private Exception mException;

		public LocatorAsyncTask() {
		}

		@Override
		protected void onPreExecute() {
			// Display progress dialog on UI thread
			mProgressDialog.setMessage(getString(R.string.address_search));
			mProgressDialog.show();
		}

		@Override
		protected List<LocatorGeocodeResult> doInBackground(
				LocatorFindParameters... params) {
			// Perform routing request on background thread
			mException = null;
			List<LocatorGeocodeResult> results = null;

			// Create locator using default online geocoding service and tell it
			// to find the given address
			Locator locator = Locator.createOnlineLocator();
			try {
				results = locator.find(params[0]);
			} catch (Exception e) {
				mException = e;
			}
			return results;
		}

		@Override
		protected void onPostExecute(List<LocatorGeocodeResult> result) {
			// Display results on UI thread
			mProgressDialog.dismiss();
			if (mException != null) {
				Log.w(TAG, "LocatorSyncTask failed with:");
				mException.printStackTrace();
				Toast.makeText(MainActivity.this,
						getString(R.string.addressSearchFailed),
						Toast.LENGTH_LONG).show();
				return;
			}

			if (result.size() == 0) {
				Toast.makeText(MainActivity.this,
						getString(R.string.noResultsFound), Toast.LENGTH_LONG)
						.show();
			} else {
				// Use first result in the list
				LocatorGeocodeResult geocodeResult = result.get(0);

				// get return geometry from geocode result
				Point resultPoint = geocodeResult.getLocation();
				// create marker symbol to represent location
				SimpleMarkerSymbol resultSymbol = new SimpleMarkerSymbol(
						Color.RED, 16, SimpleMarkerSymbol.STYLE.CROSS);
				// create graphic object for resulting location
				Graphic resultLocGraphic = new Graphic(resultPoint,
						resultSymbol);
				// add graphic to location layer
				mLocationLayer.addGraphic(resultLocGraphic);

				// create text symbol for return address
				String address = geocodeResult.getAddress();
				TextSymbol resultAddress = new TextSymbol(20, address,
						Color.BLACK);
				// create offset for text
				resultAddress.setOffsetX(-4 * address.length());
				resultAddress.setOffsetY(10);
				// create a graphic object for address text
				Graphic resultText = new Graphic(resultPoint, resultAddress);
				// add address text graphic to location graphics layer
				mLocationLayer.addGraphic(resultText);

				// Zoom map to geocode result location
				mMapView.zoomToResolution(geocodeResult.getLocation(), 2);
			}
		}

	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
	}

	@Override
	protected void onPause() {
		super.onPause();

		mMapViewState = mMapView.retainState();
		mMapView.pause();
	}

	@Override
	protected void onResume() {
		super.onResume();

		// Start the MapView running again
		if (mMapView != null) {
			mMapView.unpause();
			if (mMapViewState != null) {
				mMapView.restoreState(mMapViewState);
			}
		}
	}

}
Feedback on this topic?