package net.atomcode.bearing.geocoding;
import android.content.Context;
import android.location.Address;
import android.location.Geocoder;
import android.util.Log;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
/**
* Task for geocoding a supplied query into latitude and longitude elements
*/
public class QueryGeocodingTask extends GeocodingTask<String>
{
public QueryGeocodingTask(Context context, String[] queries)
{
super(context, queries);
}
// public QueryGeocodingTask(Context context, String[] queries, Locale locale)
// {
// super(context, queries, locale);
// }
@Override protected List<Address> doInBackground(String... params)
{
if (params == null || params.length == 0)
{
// No query
return null;
}
String query = params[0];
if (deviceHasNativeGeocoding())
{
return addressForNativeGeocodedQuery(query);
}
else
{
return addressForRemoteGeocodedQuery(query);
}
}
/**
* Geocode the query natively and return the result.
*
* Note
* =====
* Some devices, namely Amazon kindles, will report native geocoding support but
* actually not support it. This is caught by a null response. If this occurs
* the fallback {@code addressForRemoteGeocodedQuery} will be called
*
* @param query The query to geocode
* @return The geocoded locations
*/
private List<Address> addressForNativeGeocodedQuery(String query)
{
Geocoder geocoder = new Geocoder(context, locale);
List<Address> results;
try
{
results = geocoder.getFromLocationName(query, resultCount);
if (results != null && !isCancelled())
{
return results;
}
else
{
return addressForRemoteGeocodedQuery(query);
}
}
catch (IOException ex)
{
return addressForRemoteGeocodedQuery(query);
}
}
/**
* A fallback alternative that will use a web request to geocode the query.
*
* @param query The query to geocode
* @return The geocoded location as returned from the web service.
*/
private List<Address> addressForRemoteGeocodedQuery(String query)
{
StringBuilder data = new StringBuilder();
try
{
// Make query API compliant
query = query.replace(" ", "+");
String params = "?address=" + query + "&sensor=false";
HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(WEB_API_URL + params);
HttpResponse response;
if (!isCancelled())
{
try
{
response = client.execute(request);
}
catch (ClientProtocolException ex)
{
ex.printStackTrace();
return null;
}
InputStream content = response.getEntity().getContent();
InputStreamReader inputStreamReader = new InputStreamReader(content);
BufferedReader reader = new BufferedReader(inputStreamReader);
String line;
while ((line = reader.readLine()) != null && !isCancelled())
{
data.append(line);
}
}
}
catch (IOException ex)
{
Log.e("Bearing", "Network error connecting to Google Geocoding API" + ex.getMessage());
return null;
}
try
{
/* The JSON response structure
{
"results": [
{
"formatted_address": <formatted_address>,
"geometry": {
"location": {
"lat": <latitude>
"lng": <longitude>
}
}
}
]
}
*/
if (!isCancelled())
{
JSONObject geocodeData = new JSONObject(data.toString());
JSONArray addresses = geocodeData.getJSONArray("results");
int resultsToRead = Math.min(resultCount, addresses.length());
List<Address> addressList = new ArrayList<Address>(resultsToRead);
for (int i = 0; i < resultsToRead; i++)
{
JSONObject result = addresses.getJSONObject(0);
JSONObject geometry = result.getJSONObject("geometry");
JSONObject locationData = geometry.getJSONObject("location");
Address address = new Address(locale);
address.setLatitude(locationData.getDouble("lat"));
address.setLongitude(locationData.getDouble("lng"));
// Temporary fix. TODO: Proper parsing.
address.setAddressLine(0, result.getString("formatted_address"));
JSONArray addressComponents = result.getJSONArray("address_components");
for (int componentIndex = 0; componentIndex < addressComponents.length(); componentIndex++)
{
JSONObject component = addressComponents.getJSONObject(componentIndex);
address.setAddressLine(componentIndex + 1, component.getString("short_name"));
}
addressList.add(address);
}
return addressList;
}
}
catch (JSONException ex)
{
Log.e("Bearing", "Google Geocoding API format parsing failed! " + ex.getMessage());
}
return null;
}
}