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?