package er.extensions.components; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import org.apache.commons.lang3.CharEncoding; import com.webobjects.appserver.WOHTTPConnection; import com.webobjects.appserver.WORequest; import com.webobjects.foundation.NSDictionary; import er.extensions.foundation.ERXProperties; public class ERXGMapUtilities { public static final String KEY_LONGITUDE = "longitude"; public static final String KEY_LATITUDE = "latitude"; public static final String KEY_ACCURACY = "accuracy"; public static final String KEY_STATUS = "status"; public static final String[] GEOCODER_RESPONSE_KEYS = new String[] { KEY_STATUS, KEY_ACCURACY, KEY_LATITUDE, KEY_LONGITUDE }; public static String apiKey() { return ERXProperties.stringForKey("ajax.google.maps.apiKey"); } public static void setApiKey(String apiKey) { ERXProperties.setStringForKey(apiKey, "ajax.google.maps.apiKey"); } public static NSDictionary resolveAddress(String address) { NSDictionary result = null; /* * http://www.google.com/apis/maps/documentation/#Geocoding_HTTP_Request * * To access the Maps API geocoder directly using server-side scripting, * send a request to http://maps.google.com/maps/geo? with the following * parameters in the URI: * * q -- The address that you want to geocode. * * key -- Your API key. * * output -- The format in which the output should be generated. The * options are xml, kml, csv, or json. */ WOHTTPConnection connection = new WOHTTPConnection("maps.google.com", 80); String encodedAddress = ""; try { encodedAddress = URLEncoder.encode(address, CharEncoding.UTF_8); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } String url = "/maps/geo?q=" + encodedAddress + "&output=csv&key=" + apiKey(); WORequest request = new WORequest("GET", url, "HTTP/1.0", null, null, null); boolean success = connection.sendRequest(request); if (success) { /* * A reply returned in the csv format consists of four numbers, * separated by commas. The first number is the status code, the * second is the accuracy, the third is the latitude, while the * fourth one is the longitude. */ String responseText = connection.readResponse().contentString(); result = new NSDictionary(responseText.split(","), GEOCODER_RESPONSE_KEYS); } return result; } public static Coordinate coordinateForAddress(String address) { Coordinate result = null; if (address != null) { try { NSDictionary dictionary = resolveAddress(address); if ("200".equals(dictionary.valueForKey(KEY_STATUS))) { result = new Coordinate( Double.parseDouble((String) dictionary.valueForKey(KEY_LATITUDE)), Double.parseDouble((String) dictionary.valueForKey(KEY_LONGITUDE))); } } catch (Exception e) { e.printStackTrace(); } } return result; } public static class Coordinate { public static final char UNIT_STATUTE_MILES = 'M'; public static final char UNIT_NAUTICAL_MILES = 'N'; public static final char UNIT_KILOMETERS = 'K'; private double latitude, longitude; public Coordinate(double latitude, double longitude) { this.latitude = latitude; this.longitude = longitude; } public double latitude() { return latitude; } public double longitude() { return longitude; } @Override public String toString() { return "(longitude: " + longitude + ", latitude: " + latitude + ")"; } public double distanceTo(Coordinate other, char unit) { if (other == null) { throw new IllegalArgumentException("Other coordinate must not be null."); } return distance(latitude(), longitude(), other.latitude(), other.longitude(), unit); } /** * Calculate distance between two coordinates. * * South latitudes are negative, east longitudes are positive. * * Based on code from http://www.zipcodeworld.com/developers.htm * * @param lat1 * Latitude of point 1 (in decimal degrees) * @param lon1 * Longitude of point 1 (in decimal degrees) * @param lat2 * Latitude of point 2 (in decimal degrees) * @param lon2 * Longitude of point 2 (in decimal degrees) * @param unit * one of UNIT_STATUTE_MILES, UNIT_NAUTICAL_MILES or * UNIT_KILOMETERS * @return distance between the two coordinates */ public static double distance(double lat1, double lon1, double lat2, double lon2, char unit) { double theta = lon1 - lon2; double dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta)); dist = Math.acos(dist); dist = rad2deg(dist); dist = dist * 60 * 1.1515; if (unit == UNIT_KILOMETERS) { dist = dist * 1.609344; } else if (unit == UNIT_NAUTICAL_MILES) { dist = dist * 0.8684; } return (dist); } private static double deg2rad(double deg) { return (deg * Math.PI / 180.0); } private static double rad2deg(double rad) { return (rad * 180 / Math.PI); } } }