Examples of working with the Google Map API using Java

Good afternoon, Khabrovsk residents.

I’m in a hurry to share a little experience using the Google Maps API Web Services.
In this article I will tell you how to use services for geocoding, geodecoding.
Just as you can find distances and routes between points. And of course I’ll touch upon the solution of the problem “where to go closer there or there”.

To begin with, we will determine third-party libraries, the help of which we will need.

Instruments


The Google Maps API Web Services can return messages using json and xml . Google recommends using json, which I also like more, as it is smaller and more understandable. To work with json, we will use the org.json library , it is not large and performs all the tasks that I need.
Additionally, we will use the guava library to work with collections .
Well and, of course, jdk 1.6.

To contact web services and get a response in json, we write the JsonReader class .

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();
        }
    }
}


For convenience, we will store the request parameters in the map, in order to eventually get from the map a path of the form key1 = value1 & key2 = value2 ... , we will write a static method.

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;
}


We have decided on the tools, let's get to the point.

Geocoding and geodecoding


Геокодирование – процесс преобразования адресов (таких как 1600 Amphitheatre Parkway, Mountain View, CA) в географические координаты (такие как широта 37,423021 и долгота -122,083739), которые можно использовать для размещения маркеров или позиционирования карты. Служба Google Geocoding API предоставляет прямой доступ к геокодеру посредством HTTP-запроса. Также эта служба позволяет выполнять обратное действие (перевод координат в адреса). Этот процесс называется «обратное геокодирование».


Consider a request to the geocoding service, using the example of the address Russia, Moscow, Poklonnaya Street, 12.
The path to the maps.googleapis.com/maps/api/geocode/output?parameters service , the structure of the request and response is written in detail here and here .

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));// итоговая широта и долгота
}


Console:
 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 


Now we will perform the reverse operation, replacing address in the parameters with 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);// итоговый адрес
}


Console:
  http://maps.googleapis.com/maps/api/geocode/json?sensor=false&latlng=55.735893%2C37.527420&language=en  
Poklonnaya St., 12, Moscow, Russia, 121170 


Calculation of distances between points


To calculate the distance, it would be logical to get two points and calculate the total according to the formula.

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);
}


Console:
 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  
Russia, Moscow, Poklonnaya street, 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  
Russia, Moscow, metro station Victory Park 55.736217,37.516838  
distance: 0.6634200825814502 


But this option will indicate the distance between only points in a straight line, not taking into account the obstacles. Fortunately, Google offers a service for getting route data.

Calculation of distances between points using routes


Google Directions API – это служба, которая вычисляет маршруты между пунктами с помощью HTTP-запроса. В службе Directions пункты отправления и назначения могут указываться в виде текстовых строк (например, «Чикаго, Иллинойс» или «Дарвин, Новый Южный Уэльс, Австралия») либо как координаты широты и долготы. Служба Directions API может возвращать составные маршруты в виде последовательности путевых точек.


The path to the maps.googleapis.com/maps/api/directions/output?parameters service , about the structure of the request and response, is written in detail here and here .
For example, we want to know how long it takes to go from Poklonnaya Street 12 to the Park Pobedy metro station:

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);
}


Console
  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 km  
10 min. 


But what to do if we want to know which metro station Victory Park or metro station Kutuzovskaya is closer to go from Poklonnaya Street 12.
To solve this problem, it is proposed to use the Google Distance Matrix API service.

Calculation of distance and travel time for a matrix of starting and ending points


Google Distance Matrix API –это служба, предоставляющая информацию о расстоянии и времени пути для матрицы исходных и конечных точек. Информация предоставляется на основе данных о рекомендуемом маршруте между начальными и конечными точками, рассчитанном с помощью Google Maps API, и представляет собой строки, содержащие значения duration и distance для каждой пары точек.


The path to the maps.googleapis.com/maps/api/distancematrix/output?parameters service , the structure of the request and response, is written in detail here and here .

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);
}


Console
  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 = en & mode = walking  
Russia, Moscow, metro station Kutuzovskaya  
0.8 km  
9 minutes 

Having slightly improved the approach, you can find out which of the spouses is easier to call at the bank or go to the post office.

Usage restrictions


Google alone, but we all have a lot. It’s hard to imagine how many requests these services accept every day. In order not to worry about the load, the corporation of good simply set restrictions on the use of its services.
Today, you can make no more than 2500 requests from one IP per day, and no more than 10 requests per second. If you work out your limit, the query result will be returned empty with the status OVER_QUERY_LIMIT.

In addition, the number of characters in the URL should not exceed 2048, therefore it is best to use coordinates when plotting routes.

References


developers.google.com/maps/documentation/geocoding/?hl=en
developers.google.com/maps/documentation/directions/?hl=en
developers.google.com/maps/documentation/distancematrix/?hl=en
UPD: I posted the sources github.com/nestor-by/map-api-samples