- PVSM.RU - https://www.pvsm.ru -
Добрый день.
Спешу поделиться небольшим опытом использования Google Maps API Web Services.
В этой статья я расскажу как использовать службы для геокодирования, геодекодирования.
Так же как можно находить расстояния и маршруты между точками. И конежно же коснусь решения задачи «куда сходить ближе туда или туда».
Для начала определимся со сторонними библиотеками, помощь которых нам понадобится.
Google Maps API Web Services могут возвращать сообщения с помощью json и xml. Google рекомендует использовать json, он мне тоже больше по душе, так как он меньше и понятнее. Для работы с json будем использовать библиотеку org.json [1], она не большая и выполняет все задачи, которые мне нужны.
Дополнительно для работы с коллекциями будем использовать библиотеку guava [2].
Ну и, конечно же, jdk 1.6.
Для обращения в вебсервисами и получения ответа в json, напишем класс JsonReader.
public class JsonReader {
private static String readAll(final Reader rd) throws IOException {
final StringBuilder sb = new StringBuilder();
int cp;
while ((cp = rd.read()) != -1) {
sb.append((char) cp);
}
return sb.toString();
}
public static JSONObject read(final String url) throws IOException, JSONException {
final InputStream is = new URL(url).openStream();
try {
final BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
final String jsonText = readAll(rd);
final JSONObject json = new JSONObject(jsonText);
return json;
} finally {
is.close();
}
}
}
Для удобства параметры запроса будем хранить в мапе, для того что бы в итоге получать из мапы путь вида key1=value1&key2=value2..., напишем статический метод.
private static String encodeParams(final Map<String, String> params) {
final String paramsUrl = Joiner.on('&').join(// получаем значение вида key1=value1&key2=value2...
Iterables.transform(params.entrySet(), new Function<Entry<String, String>, String>() {
@Override
public String apply(final Entry<String, String> input) {
try {
final StringBuffer buffer = new StringBuffer();
buffer.append(input.getKey());// получаем значение вида key=value
buffer.append('=');
buffer.append(URLEncoder.encode(input.getValue(), "utf-8"));// кодируем строку в соответствии со стандартом HTML 4.01
return buffer.toString();
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}));
return paramsUrl;
}
С инструменами определились, перейдем к сути.
Геокодирование – процесс преобразования адресов (таких как 1600 Amphitheatre Parkway, Mountain View, CA) в географические координаты (такие как широта 37,423021 и долгота -122,083739), которые можно использовать для размещения маркеров или позиционирования карты. Служба Google Geocoding API предоставляет прямой доступ к геокодеру посредством HTTP-запроса. Также эта служба позволяет выполнять обратное действие (перевод координат в адреса). Этот процесс называется «обратное геокодирование».
Рассмотрим запрос к службе геокодирования, на примере адреса Россия, Москва, улица Поклонная, 12.
Путь к службе maps.googleapis.com/maps/api/geocode/output?parameters [3], о структуре запроса и ответа подробно написано здесь [4] и здесь [5].
public static void main(final String[] args) throws IOException, JSONException {
final String baseUrl = "http://maps.googleapis.com/maps/api/geocode/json";// путь к Geocoding API по HTTP
final Map<String, String> params = Maps.newHashMap();
params.put("sensor", "false");// исходит ли запрос на геокодирование от устройства с датчиком местоположения
params.put("address", "Россия, Москва, улица Поклонная, 12");// адрес, который нужно геокодировать
final String url = baseUrl + '?' + encodeParams(params);// генерируем путь с параметрами
System.out.println(url);// Путь, что бы можно было посмотреть в браузере ответ службы
final JSONObject response = JsonReader.read(url);// делаем запрос к вебсервису и получаем от него ответ
// как правило наиболее подходящий ответ первый и данные о координатах можно получить по пути
// //results[0]/geometry/location/lng и //results[0]/geometry/location/lat
JSONObject location = response.getJSONArray("results").getJSONObject(0);
location = location.getJSONObject("geometry");
location = location.getJSONObject("location");
final double lng = location.getDouble("lng");// долгота
final double lat = location.getDouble("lat");// широта
System.out.println(String.format("%f,%f", lat, lng));// итоговая широта и долгота
}
Консоль:
http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D1%8F%2C+%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0%2C+%D1%83%D0%BB%D0%B8%D1%86%D0%B0+%D0%9F%D0%BE%D0%BA%D0%BB%D0%BE%D0%BD%D0%BD%D0%B0%D1%8F%2C+12 55.735893,37.527420
Теперь выполним обратную операцию, заменив в параметрах address, на latlng
public static void main(final String[] args) throws IOException, JSONException {
final String baseUrl = "http://maps.googleapis.com/maps/api/geocode/json";// путь к Geocoding API по HTTP
final Map<String, String> params = Maps.newHashMap();
params.put("language", "ru");// язык данных, на котором мы хотим получить
params.put("sensor", "false");// исходит ли запрос на геокодирование от устройства с датчиком местоположения
// текстовое значение широты/долготы, для которого следует получить ближайший понятный человеку адрес, долгота и
// широта разделяется запятой, берем из предыдущего примера
params.put("latlng", "55.735893,37.527420");
final String url = baseUrl + '?' + encodeParams(params);// генерируем путь с параметрами
System.out.println(url);// Путь, что бы можно было посмотреть в браузере ответ службы
final JSONObject response = JsonReader.read(url);// делаем запрос к вебсервису и получаем от него ответ
// как правило, наиболее подходящий ответ первый и данные об адресе можно получить по пути
// //results[0]/formatted_address
final JSONObject location = response.getJSONArray("results").getJSONObject(0);
final String formattedAddress = location.getString("formatted_address");
System.out.println(formattedAddress);// итоговый адрес
}
Консоль:
http://maps.googleapis.com/maps/api/geocode/json?sensor=false&latlng=55.735893%2C37.527420&language=ru Поклонная ул., 12, Москва, Россия, 121170
Для вычисления расстояния логично было бы получить две точки и посчитать по формуле итог.
private static final double EARTH_RADIUS = 6371.; // Радиус Земли
public static void main(final String[] args) throws IOException, JSONException {
final Point subwayStationPoint = getPoint("Россия, Москва, улица Поклонная, 12");
final Point addressPoint = getPoint("Россия, Москва, станция метро Парк Победы");
// Рассчитываем расстояние между точками
final double dlng = deg2rad(subwayStationPoint.lng - addressPoint.lng);
final double dlat = deg2rad(subwayStationPoint.lat - addressPoint.lat);
final double a = sin(dlat / 2) * sin(dlat / 2) + cos(deg2rad(addressPoint.lat))
* cos(deg2rad(subwayStationPoint.lat)) * sin(dlng / 2) * sin(dlng / 2);
final double c = 2 * atan2(sqrt(a), sqrt(1 - a));
System.out.println("distance: " + c * EARTH_RADIUS); // получаем расстояние в километрах
}
/**
* Класс точки, хранит значения в градусах
*
*/
private static class Point {
public double lat;
public double lng;
public Point(final double lng, final double lat) {
this.lng = lng;
this.lat = lat;
}
@Override
public String toString() {
return lat + "," + lng;
}
}
/**
* Геокодирует адрес
*
* @param address
* @return
* @throws IOException
* @throws JSONException
*/
private static Point getPoint(final String address) throws IOException, JSONException {
final String baseUrl = "http://maps.googleapis.com/maps/api/geocode/json";// путь к Geocoding API по HTTP
final Map<String, String> params = Maps.newHashMap();
params.put("sensor", "false");// указывает, исходит ли запрос на геокодирование от устройства с датчиком
// местоположения
params.put("address", address);// адрес, который нужно геокодировать
final String url = baseUrl + '?' + encodeParams(params);// генерируем путь с параметрами
System.out.println(url);// Можем проверить что вернет этот путь в браузере
final JSONObject response = JsonReader.read(url);// делаем запрос к вебсервису и получаем от него ответ
// как правило наиболее подходящий ответ первый и данные о координатах можно получить по пути
// //results[0]/geometry/location/lng и //results[0]/geometry/location/lat
JSONObject location = response.getJSONArray("results").getJSONObject(0);
location = location.getJSONObject("geometry");
location = location.getJSONObject("location");
final double lng = location.getDouble("lng");// долгота
final double lat = location.getDouble("lat");// широта
final Point point = new Point(lng, lat);
System.out.println(address + " " + point); // выводим адрес и точку для него
return point;
}
/**
* Преобразует значение из градусов в радианы
*
* @param degree
* @return
*/
private static double deg2rad(final double degree) {
return degree * (Math.PI / 180);
}
Консоль:
http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D1%8F%2C+%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0%2C+%D1%83%D0%BB%D0%B8%D1%86%D0%B0+%D0%9F%D0%BE%D0%BA%D0%BB%D0%BE%D0%BD%D0%BD%D0%B0%D1%8F%2C+12 Россия, Москва, улица Поклонная, 12 55.7358925,37.5274195 http://maps.googleapis.com/maps/api/geocode/json?sensor=false&address=%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D1%8F%2C+%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0%2C+%D1%81%D1%82%D0%B0%D0%BD%D1%86%D0%B8%D1%8F+%D0%BC%D0%B5%D1%82%D1%80%D0%BE+%D0%9F%D0%B0%D1%80%D0%BA+%D0%9F%D0%BE%D0%B1%D0%B5%D0%B4%D1%8B Россия, Москва, станция метро Парк Победы 55.736217,37.516838 distance: 0.6634200825814502
Но такой вариант укажет расстояние между только пунктами по прямой, не учитывая преграды. К счастью, Google предлагает службу для получения данных о маршрутах.
Google Directions API – это служба, которая вычисляет маршруты между пунктами с помощью HTTP-запроса. В службе Directions пункты отправления и назначения могут указываться в виде текстовых строк (например, «Чикаго, Иллинойс» или «Дарвин, Новый Южный Уэльс, Австралия») либо как координаты широты и долготы. Служба Directions API может возвращать составные маршруты в виде последовательности путевых точек.
Путь к службе maps.googleapis.com/maps/api/directions/output?parameters [6], о структуре запроса и ответа подробно написано здесь [7] и здесь [8].
Например мы хотим знать сколько времени идти от улица Поклонной, 12 до станции метро Парк Победы:
public static void main(final String[] args) throws IOException, JSONException {
final String baseUrl = "http://maps.googleapis.com/maps/api/directions/json";// путь к Geocoding API по
// HTTP
final Map<String, String> params = Maps.newHashMap();
params.put("sensor", "false");// указывает, исходит ли запрос на геокодирование от устройства с датчиком
params.put("language", "ru");// язык данные на котором мы хотим получить
params.put("mode", "walking");// способ перемещения, может быть driving, walking, bicycling
params.put("origin", "Россия, Москва, улица Поклонная, 12");// адрес или текстовое значение широты и
// отправного пункта маршрута
params.put("destination", "Россия, Москва, станция метро Парк Победы");// адрес или текстовое значение широты и
// долготы
// долготы конечного пункта маршрута
final String url = baseUrl + '?' + encodeParams(params);// генерируем путь с параметрами
System.out.println(url); // Можем проверить что вернет этот путь в браузере
final JSONObject response = JsonReader.read(url);// делаем запрос к вебсервису и получаем от него ответ
// как правило наиболее подходящий ответ первый и данные о координатах можно получить по пути
// //results[0]/geometry/location/lng и //results[0]/geometry/location/lat
JSONObject location = response.getJSONArray("routes").getJSONObject(0);
location = location.getJSONArray("legs").getJSONObject(0);
final String distance = location.getJSONObject("distance").getString("text");
final String duration = location.getJSONObject("duration").getString("text");
System.out.println(distance + "n" + duration);
}
Консоль
http://anonymouse.org/cgi-bin/anon-www.cgi/http://maps.googleapis.com/maps/api/directions/json?sensor=false&origin=%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D1%8F%2C+%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0%2C+%D1%83%D0%BB%D0%B8%D1%86%D0%B0+%D0%94%D0%B5%D0%BD%D0%B8%D1%81%D0%B0+%D0%94%D0%B0%D0%B2%D1%8B%D0%B4%D0%BE%D0%B2%D0%B0%2C+7&language=ru&destination=%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D1%8F%2C+%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0%2C+%D1%83%D0%BB%D0%B8%D1%86%D0%B0+%D0%9A%D1%83%D0%BB%D1%8C%D0%BD%D0%B5%D0%B2%D0%B0+3&mode=walking 0,9 км 10 мин.
Но что же делать, если мы хотим знать к какой станции метро Парк Победы или станции метро Кутузовская ближе идти от улица Поклонной, 12.
Для решения этой задачи предлагается использование службы Google Distance Matrix API.
Google Distance Matrix API –это служба, предоставляющая информацию о расстоянии и времени пути для матрицы исходных и конечных точек. Информация предоставляется на основе данных о рекомендуемом маршруте между начальными и конечными точками, рассчитанном с помощью Google Maps API, и представляет собой строки, содержащие значения duration и distance для каждой пары точек.
Путь к службе maps.googleapis.com/maps/api/distancematrix/output?parameters [9], о структуре запроса и ответа подробно написано здесь [10] и здесь [11].
public static void main(final String[] args) throws IOException, JSONException {
final String baseUrl = "http://maps.googleapis.com/maps/api/distancematrix/json";// путь к Geocoding API по HTTP
final Map<String, String> params = Maps.newHashMap();
params.put("sensor", "false");// указывает, исходит ли запрос на геокодирование от устройства с датчиком
params.put("language", "ru");// язык данных
params.put("mode", "walking");// идем пешком, может быть driving, walking, bicycling
// адрес или координаты отправных пунктов
final String[] origins = { "Россия, Москва, улица Поклонная, 12" };
params.put("origins", Joiner.on('|').join(origins));
// адрес или координаты пунктов назначения
final String[] destionations = { //
"Россия, Москва, станция метро Парк Победы", //
"Россия, Москва, станция метро Кутузовская" //
};
// в запросе адреса должны разделяться символом '|'
params.put("destinations", Joiner.on('|').join(destionations));
final String url = baseUrl + '?' + encodeParams(params);// генерируем путь с параметрами
System.out.println(url); // Можем проверить что вернет этот путь в браузере
final JSONObject response = JsonReader.read(url);// делаем запрос к вебсервису и получаем от него ответ
final JSONObject location = response.getJSONArray("rows").getJSONObject(0);
final JSONArray arrays = location.getJSONArray("elements");// Здесь лежат все рассчитанные значения
// Ищем путь на который мы потратим минимум времени
final JSONObject result = Ordering.from(new Comparator<JSONObject>() {
@Override
public int compare(final JSONObject o1, final JSONObject o2) {
final Integer duration1 = getDurationValue(o1);
final Integer duration2 = getDurationValue(o2);
return duration1.compareTo(duration2);// Сравниваем по времени в пути
}
/**
* Возвращает время в пути
*
* @param obj
* @return
*/
private int getDurationValue(final JSONObject obj) {
try {
return obj.getJSONObject("duration").getInt("value");
} catch (final JSONException e) {
throw new RuntimeException(e);
}
}
}).min(new AbstractIterator<JSONObject>() {// К сожалению JSONArray нельзя итереровать, по этому обернем его
private int index = 0;
@Override
protected JSONObject computeNext() {
try {
JSONObject result;
if (index < arrays.length()) {
final String destionation = destionations[index];
result = arrays.getJSONObject(index++);
result.put("address", destionation);// Добавим сразу в структуру и адрес, потому как его нет в
// этом расчёте
} else {
result = endOfData();
}
return result;
} catch (final JSONException e) {
throw new RuntimeException(e);
}
}
});
final String distance = result.getJSONObject("distance").getString("text");// расстояние в километрах
final String duration = result.getJSONObject("duration").getString("text");// время в пути
final String address = result.getString("address");// адрес
System.out.println(address + "n" + distance + "n" + duration);
}
Консоль
http://maps.googleapis.com/maps/api/distancematrix/json?sensor=false&destinations=%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D1%8F%2C+%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0%2C+%D1%81%D1%82%D0%B0%D0%BD%D1%86%D0%B8%D1%8F+%D0%BC%D0%B5%D1%82%D1%80%D0%BE+%D0%9F%D0%B0%D1%80%D0%BA+%D0%9F%D0%BE%D0%B1%D0%B5%D0%B4%D1%8B%7C%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D1%8F%2C+%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0%2C+%D1%81%D1%82%D0%B0%D0%BD%D1%86%D0%B8%D1%8F+%D0%BC%D0%B5%D1%82%D1%80%D0%BE+%D0%9A%D1%83%D1%82%D1%83%D0%B7%D0%BE%D0%B2%D1%81%D0%BA%D0%B0%D1%8F&origins=%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D1%8F%2C+%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0%2C+%D1%83%D0%BB%D0%B8%D1%86%D0%B0+%D0%9F%D0%BE%D0%BA%D0%BB%D0%BE%D0%BD%D0%BD%D0%B0%D1%8F%2C+12&language=ru&mode=walking Россия, Москва, станция метро Кутузовская 0,8 км 9 мин.
Немного усовершенствовав подход можно узнать кому из супругов проще заехать в банк или зайти на почту.
Google один, а нас всех много. Сложно представить сколько запросов принимают эти службы каждый день. Что бы не забоится о нагрузке, корпорация добра просто установила ограничения на использование своих служб.
Сегодня, можно сделать не больше 2500 запросов с одного IP в день, и не больше чем 10 запросов в секунду. Если вы выработаете свой лимит, то результат запроса будет возвращаться пустым со статусом OVER_QUERY_LIMIT.
К тому же количество символов в URL, не должно превышать 2048, по этому лучше всего использовать при прокладке маршрутов координаты.
developers.google.com/maps/documentation/geocoding/?hl=ru [12]
developers.google.com/maps/documentation/directions/?hl=ru [13]
developers.google.com/maps/documentation/distancematrix/?hl=ru [14]
Автор: nestor_by
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/12561
Ссылки в тексте:
[1] org.json: http://www.json.org/
[2] guava: http://code.google.com/p/guava-libraries/
[3] maps.googleapis.com/maps/api/geocode/output?parameters: http://maps.googleapis.com/maps/api/geocode/output?parameters
[4] здесь: https://developers.google.com/maps/documentation/geocoding/?hl=ru#GeocodingRequests
[5] здесь: https://developers.google.com/maps/documentation/geocoding/?hl=ru#GeocodingResponses
[6] maps.googleapis.com/maps/api/directions/output?parameters: http://maps.googleapis.com/maps/api/directions/output?parameters
[7] здесь: https://developers.google.com/maps/documentation/directions/?hl=ru#RequestParameters
[8] здесь: https://developers.google.com/maps/documentation/directions/?hl=ru#DirectionsResponses
[9] maps.googleapis.com/maps/api/distancematrix/output?parameters: http://maps.googleapis.com/maps/api/distancematrix/output?parameters
[10] здесь: https://developers.google.com/maps/documentation/distancematrix/?hl=ru#RequestParameters
[11] здесь: https://developers.google.com/maps/documentation/distancematrix/?hl=ru#DirectionsResponses
[12] developers.google.com/maps/documentation/geocoding/?hl=ru: https://developers.google.com/maps/documentation/geocoding/?hl=ru
[13] developers.google.com/maps/documentation/directions/?hl=ru: https://developers.google.com/maps/documentation/directions/?hl=ru
[14] developers.google.com/maps/documentation/distancematrix/?hl=ru: https://developers.google.com/maps/documentation/distancematrix/?hl=ru
Нажмите здесь для печати.