package org.smartly.commons.network.api.yahoo.placefinder;
import org.json.JSONArray;
import org.json.JSONObject;
import org.smartly.commons.logging.Level;
import org.smartly.commons.logging.Logger;
import org.smartly.commons.logging.util.LoggingUtils;
import org.smartly.commons.network.URLUtils;
import org.smartly.commons.util.FormatUtils;
import org.smartly.commons.util.JsonWrapper;
import org.smartly.commons.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
* Yahoo PlaceFinder implementation.
* http://developer.yahoo.com/geo/placefinder/
* http://developer.yahoo.com/geo/placefinder/guide/
* http://developer.yahoo.com/geo/placefinder/guide/examples.html
* <p/>
* flags=J&appid=yourappid
* <p/>
* User: angelo.geminiani
*/
public class YahooPlaceFinder {
private final String _appId;
private static final String FLD_RESULTSET = "ResultSet";
private static final String FLD_ERROR = FLD_RESULTSET + ".Error";
private static final String FLD_ERRORMSG = FLD_RESULTSET + ".ErrorMessage";
private static final String FLD_RESULTS = FLD_RESULTSET + ".Results";
//-- patterns --//
private static final String REVERSE_GEOCODING = "http://where.yahooapis.com/geocode?locale={locale}&location={latitude}+{longitude}&gflags=R&flags=J&appid={appid}";
// private static final String GEOCODING = "http://where.yahooapis.com/geocode?locale={locale}&location={address}&flags=J&appid={appid}";
// http://query.yahooapis.com/v1/public/yql?q=select * from geo.placefinder where text="1600 Pennsylvania Ave, Washington, DC"&appid=test
//private static final String GEOCODING = "http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20geo.placefinder%20where%20text%3D%221600%20Pennsylvania%20Ave%2C%20Washington%2C%20DC%22&appid=D9qFIW3e&format=json";
private static final String GEOCODING = "http://query.yahooapis.com/v1/public/yql?q=select+*+from+geo.placefinder+where+text=\"{address}\"&appid={appid}&format=json";
private static final String GEOCODING_COORD = "http://where.yahooapis.com/geocode?locale={locale}&location={address}&flags=JC&appid={appid}";
public YahooPlaceFinder(final String appId) {
_appId = appId;
}
/**
* Returns Address object.
* i.e. {"quality":99,"latitude":"44.005531","longitude":"12.639030","offsetlat":"44.005531",
* "offsetlon":"12.639030","radius":500,"name":"44.00553135 12.63902965",
* "line1":"via Ortles, 5","line2":"47838 Riccione RN","line3":"","line4":"Italy","house":"5",
* "street":"via Ortles","xstreet":"","unittype":"","unit":"","postal":"47838","neighborhood":"",
* "city":"Riccione","county":"Rimini","state":"Emilia Romagna","country":"Italy","countrycode":"IT",
* "statecode":"","countycode":"RN","hash":"","woeid":12846205,"woetype":11,"uzip":"47838"}
* <p/>
* FULL OBJECT:
* {
* "ResultSet":
* {
* "version":"1.0",
* "Error":0,
* "ErrorMessage":"No error",
* "Locale":"us_US",
* "Quality":99,
* "Found":1,
* "Results":[
* {"quality":99,"latitude":"44.005531","longitude":"12.639030","offsetlat":"44.005531",
* "offsetlon":"12.639030","radius":500,"name":"44.00553135 12.63902965",
* "line1":"via Ortles, 5","line2":"47838 Riccione RN","line3":"","line4":"Italy","house":"5",
* "street":"via Ortles","xstreet":"","unittype":"","unit":"","postal":"47838","neighborhood":"",
* "city":"Riccione","county":"Rimini","state":"Emilia Romagna","country":"Italy","countrycode":"IT",
* "statecode":"","countycode":"RN","hash":"","woeid":12846205,"woetype":11,"uzip":"47838"}
* ]}
* }
*
* @param latitude
* @param longitude
* @return Address Object:
* {"quality":99,"latitude":"44.005531","longitude":"12.639030","offsetlat":"44.005531",
* "offsetlon":"12.639030","radius":500,"name":"44.00553135 12.63902965",
* "line1":"via Ortles, 5","line2":"47838 Riccione RN","line3":"","line4":"Italy","house":"5",
* "street":"via Ortles","xstreet":"","unittype":"","unit":"","postal":"47838","neighborhood":"",
* "city":"Riccione","county":"Rimini","state":"Emilia Romagna","country":"Italy","countrycode":"IT",
* "statecode":"","countycode":"RN","hash":"","woeid":12846205,"woetype":11,"uzip":"47838"}
*/
public JSONObject reverseGeocoding(final double latitude, final double longitude) {
return this.reverseGeocoding("en_US", latitude, longitude);
}
public JSONObject reverseGeocoding(final String locale, final double latitude, final double longitude) {
try {
final Map<String, Object> params = new HashMap<String, Object>();
params.put("locale", locale);
params.put("appid", _appId);
params.put("latitude", latitude);
params.put("longitude", longitude);
final String url = FormatUtils.format(REVERSE_GEOCODING, params);
final String jsonData = URLUtils.getUrlContent(url);
return parseResponse(jsonData);
} catch (Throwable t) {
this.getLogger().log(Level.SEVERE, null, t);
}
return null;
}
public JSONObject geocoding(final String locale, final JSONObject address) {
final String text_address = getAddress(address, false);
return this.geocoding(locale, text_address);
}
/**
* http://where.yahooapis.com/geocode?location=San+Francisco,+CA&flags=J&appid=yourappid
*
* @param address format: First line of address (street address), a comma, and the second line of address (city-state-zip in US).
* @return json
*/
public JSONObject geocoding(final String locale, final String address) {
try {
final Map<String, Object> params = new HashMap<String, Object>();
params.put("locale", locale);
params.put("appid", _appId);
params.put("address", address);
// http://where.yahooapis.com/geocode?locale=it_IT&location=riccione rn 47838 IT&flags=J&appid=APPID
final String url = FormatUtils.format(GEOCODING, params);
final String jsonData = URLUtils.getUrlContent(url, 5000, URLUtils.TYPE_JSON);
return parseResponse(jsonData);
} catch (Throwable t) {
this.getLogger().log(Level.SEVERE, null, t);
}
return null;
}
public JSONObject coordinates(final String locale, final JSONObject address) {
return this.coordinates(locale, getAddress(address, false));
}
public JSONObject coordinates(final String locale, final String address) {
try {
final Map<String, Object> params = new HashMap<String, Object>();
params.put("locale", locale);
params.put("appid", _appId);
params.put("address", address);
// http://where.yahooapis.com/geocode?locale=it_IT&location=riccione rn 47838 IT&flags=JC&appid=APPID
final String url = FormatUtils.format(GEOCODING_COORD, params);
final String jsonData = URLUtils.getUrlContent(url);
return parseResponse(jsonData);
} catch (Throwable t) {
this.getLogger().log(Level.SEVERE, null, t);
}
return null;
}
// ------------------------------------------------------------------------
// p r i v a t e
// ------------------------------------------------------------------------
private Logger getLogger() {
return LoggingUtils.getLogger(this);
}
// --------------------------------------------------------------------
// S T A T I C
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// S T A T I C - p r i v a t e
// --------------------------------------------------------------------
private static JSONObject parseResponse(final String jsonData) throws Exception {
if (StringUtils.isJSON(jsonData)) {
final JSONObject json = new JSONObject(jsonData);
final int err = getErrorCode(json);
if (err == 0) {
final JSONArray results = getResults(json);
if (null != results && results.length() > 0) {
return results.optJSONObject(0);
} else {
// system error
final String sys_err = JsonWrapper.getString(json, "error");
if (StringUtils.hasText(sys_err)) {
final String cause = JsonWrapper.getString(json, "error_message");
if (StringUtils.hasText(cause)) {
throw new Exception("[" + cause + "] - " + sys_err);
}
throw new Exception(sys_err);
}
}
} else {
throw new Exception(getErrorMessage(json));
}
return json;
}
return null;
}
private static int getErrorCode(final JSONObject json) {
return JsonWrapper.getInt(json, FLD_ERROR);
}
private static String getErrorMessage(final JSONObject json) {
return JsonWrapper.getString(json, FLD_ERRORMSG);
}
private static boolean hasError(final JSONObject json) {
return getErrorCode(json) > 0;
}
private static JSONArray getResults(final JSONObject json) {
return JsonWrapper.getArray(json, FLD_RESULTS);
}
private static String getAddress(final JSONObject address, final boolean includeStreet) {
// format: (street address), a comma, and (city-state-zip in US).
final String street = address.optString("street").replaceAll(",", "");
final String city = address.optString("city");
final String state = address.optString("state");
final String country = address.optString("country");
final String zip = address.optString("zip");
final StringBuilder sb = new StringBuilder();
if (StringUtils.hasText(city) ||
StringUtils.hasText(state) ||
StringUtils.hasText(zip) ||
StringUtils.hasText(country)) {
if (includeStreet && StringUtils.hasText(street)) {
sb.append(street);
sb.append(",");
}
if (StringUtils.hasText(city)) {
sb.append(city).append(" ");
}
if (StringUtils.hasText(state)) {
sb.append(state).append(" ");
}
if (StringUtils.hasText(zip)) {
sb.append(zip).append(" ");
}
if (StringUtils.hasText(country)) {
sb.append(country);
}
}
return sb.toString().replaceAll(" ", "+");
}
}