Introduction
In this article you will learn Reverse Geocoding using the XML request format. Reverse Geocoding is the process of getting the address of a location whose latitude and longitude are given by the user. The request from the user can be in XML format or in JSON format.
The reverse geocoding request format used in this article is of the format: http://maps.googleapis.com/maps/api/geocode/xml?latlng=latitude_and_longitude_value_by_user&sensor=false.
Note that for giving the latitude and longitude, the format should be: latitude,longitude. No space should be present between the two values.
Step 1
First let us make a search layout where the user will enter the latitude and longitude value of the required location. Right-click on layout then select "New" -> "Layout Resource file". Name this file as "search_layout" and add the following code to it:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffdbac">
<EditText
android:id="@+id/enter"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="20dp"
android:hint="Enter the required Location...."
android:layout_marginTop="40dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"/>
<Button
android:id="@+id/search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="130dp"
android:layout_marginTop="40dp"
android:text="Search"
android:background="@drawable/button_lay"
android:paddingRight="10dp"
android:paddingLeft="10dp"
/>
</LinearLayout>
The layout looks like:
Step 2
Open the "activity_main" layout file and add the following code to it: (Inside LinearLayout element)
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
Step 3
Create one more layout file. Name it "list_item" and add the following code to it (inside the LinearLayout element):
<TextView
android:id="@+id/address"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textStyle="bold"
android:paddingTop="6dip"
android:paddingBottom="2dip" />
Step 4
Right-click on your package name then select "New" -> "Java class". Name this Java class as "Search" and add the following code to it:
package com.chhavi.reversegeocoding;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class Search extends Activity {
EditText enter;
Button b;
String place;
final Context context=this;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search_layout);
enter=(EditText)findViewById(R.id.enter);
b=(Button)findViewById(R.id.search);
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i=new Intent(context,MainActivity.class);
place=enter.getText().toString();
i.putExtra("place",place);
startActivity(i);
}
});
}
}
Step 5
Create another class and name it "XmlParsing". Write the following code in it:
package com.chhavi.reversegeocoding;
import android.util.Log;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
public class XmlParsing {
public XmlParsing() {
}
public String getXmlFromUrl(String url) {
String xml = null;
try {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
Log.i("URL.......",url);
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
xml = EntityUtils.toString(httpEntity);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return xml;
}
public Document getDomElement(String xml){
Document doc = null;
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder db = dbf.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(xml));
doc = db.parse(is);
} catch (ParserConfigurationException e) {
Log.e("Error: ", e.getMessage());
return null;
} catch (SAXException e) {
Log.e("Error: ", e.getMessage());
return null;
} catch (IOException e) {
Log.e("Error: ", e.getMessage());
return null;
}
return doc;
}
public final String getElementValue( Node elem ) {
Node child;
if( elem != null){
if (elem.hasChildNodes()){
for( child = elem.getFirstChild(); child != null; child = child.getNextSibling() ){
if( child.getNodeType() == Node.TEXT_NODE ){
return child.getNodeValue();
}
}
}
}
return "";
}
public String getValue(Element item, String str) {
NodeList n = item.getElementsByTagName(str);
return this.getElementValue(n.item(0));
}
}
HttpEntity can send or receive HTTP messages. Entities can be found in a request or a response.
Document is the root of document tree.
DocumentBuilderFactory defines a factory API that enables applications to obtain a parser that produces DOM object trees from XML documents.
DocumentBuilder takes XML as input and gives a DOM document object as output.
Step 6:
Open "MainActivity" and add the following code to it:
package com.chhavi.reversegeocoding;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.util.ArrayList;
import java.util.HashMap;
public class MainActivity extends ListActivity {
private static String url = "http://maps.googleapis.com/maps/api/geocode/xml?latlng=";
static final String KEY_RESULT = "result"; // parent node
static final String KEY_FORMATTED_ADDRESS = "formatted_address";
ArrayList<HashMap<String, String>> address_list = new ArrayList<HashMap<String, String>>();
ListView listView ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = getIntent();
String enter= intent.getStringExtra("place");
Log.i("place..",enter+"");
url=url+enter+"&sensor=false";
new Progress(MainActivity.this).execute();
}
private class Progress extends AsyncTask<String, Void, Boolean> {
private ProgressDialog dialog;
public Progress(ListActivity activity) {
Log.i("constructor....", "Called");
context = activity;
dialog = new ProgressDialog(context);
}
private Context context;
protected void onPreExecute() {
this.dialog.setMessage("Getting location....");
this.dialog.show();
}
@Override
protected void onPostExecute(final Boolean success) {
if (dialog.isShowing()) {
dialog.dismiss();
}
ListAdapter adapter = new SimpleAdapter(context, address_list, R.layout.list_item, new String[] { KEY_FORMATTED_ADDRESS }, new int[] { R.id.address });
setListAdapter(adapter);
listView = getListView();
}
protected Boolean doInBackground(final String... args) {
XmlParsing parser = new XmlParsing();
String xml = parser.getXmlFromUrl(url);
Document doc = parser.getDomElement(xml);
NodeList nl = doc.getElementsByTagName(KEY_RESULT);
for (int i = 0; i < nl.getLength(); i++) {
HashMap<String, String> map = new HashMap<String, String>();
Element e = (Element) nl.item(i);
map.put(KEY_FORMATTED_ADDRESS, parser.getValue(e, KEY_FORMATTED_ADDRESS));
address_list.add(map);
}
return null;
}
}
@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);
return true;
}
}
I wanted to reach the "formatted_address" tag in the XML file. For reaching "formatted_address", the following parsing is required inside the "GeocodeResponse" tag of the XML file: results->formatted_address.
Step 7
Make the following changes in "AndroidManifest":
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.chhavi.reversegeocoding"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-sdk
android:minSdkVersion="7"
android:targetSdkVersion="16" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.chhavi.reversegeocoding.Search"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity"
android:label="Reverse Geocoding"/>
</application>
</manifest>
Output snapshots:
On entering the latitude and longitude value (enter the latitude and longitude values without any space in between):
On clicking the search button:
You can check the output at http://maps.googleapis.com/maps/api/geocode/xml?latlng=40.714224,-73.961452&sensor=false
Thank you... Enjoy coding :)