Маршруты на картах Google в вашем Android-приложении. Альтернативный подход

в 7:27, , рубрики: android, api, Google, Google API, Google Maps, java, Maps API, overlay, SDK, Разработка под android, метки: , , , , , ,

Прочитав недавно появившуюся статью Маршруты на картах Google в вашем Android-приложении, я решил показать еще один альтернативный подход, для решения этой задачи. Возможно он даст немного большее представление о работе с google-maps и поможет новичкам быстрее разобраться с этим вопросом.

Получаем координаты

Хорошая новость, что google предоставляет нам api к службе для вычисления маршрутов между координатами. По этому общая структура предстоящей работы приблизительно такая:

  • Формируем запрос с параметрами
  • Отправляем запрос и получаем ответ
  • Парсим ответ
  • Рисуем overlay на картах

Формирование запроса

Полную информацию о том, как выглядит запрос можно найти в оффициальной документации на Google developers. Здесь же я рассмотрю основные аспекты. В общем запрос выглядит так:

http://maps.googleapis.com/maps/api/directions/output?parameters

Где output может принимать 2 значения:

  • json – задает вывод в формате JavaScript Object Notation (JSON);
  • xml – задает вывод в формате XML.

Сам google рекомендует нам использовать JSON, ну и я тоже.

parameters — это куча разный обязательных и не очень параметров, которые вы можете указать по вашим требованиям. О необязательных параметрах говорить не буду, скажу, что есть 3 основных, которые должны быть указаны в обязательном порядке:

  • origin – адрес или текстовое значение широты и долготы отправного пункта маршрута.
  • destination – адрес или текстовое значение широты и долготы конечного пункта маршрута*.
  • sensor – указывает, исходит ли запрос маршрута от устройства с датчиком местоположения. Допустимые значения – true или false.

В конечном итоге, самый простой запрос (от Воронежа до Москвы) c JSON ответом выглядит так:

http://maps.googleapis.com/maps/api/directions/json?origin=Воронеж&destination=Москва&sensor=false

Не буду приводить код к этой части, так как сформировать нужную вам строчку кода, дело довольно тривиальное.

Отправка запроса и получение результата

Итак, запрос сформирован, пора отправлять. Существует наверно куча методов, как выполнить запрос к сервису, но я что-бы облегчить себе жизнь до 2-ух строчек, воспользуюсь Spring Fraemwork for android. Ну а вы как сами знаете. Скачал либу, положил в проект, запрос выглядит как то так:

RestTemplate rTemplate = new RestTemplate();
String jsonResponse = rTemplate.getForObject(query, String.class));
Парсинг ответа

Мы получили ответ в json формате, который содержит много различной информации о маршруте, но нам нужны только координаты, по которым мы будем рисовать наш overlay, по этому все остальные результаты я опущу, о них можно прочитать все там-же на Google developers.

Давайте распарсим наш json, код выглядит как-то так:

public ArrayList<Section> parse(JSONObject jsonObject) throws AuthenticationException {
	        ArrayList<Section> list = new ArrayList<Section>();
		Location startGeo;
		Location endGeo;
		JSONArray results = jsonObject.optJSONArray("routes");
		JSONObject route = results.optJSONObject(0);
		JSONArray legs = route.optJSONArray("legs");
		JSONObject leg = legs.optJSONObject(0);
		JSONArray steps = leg.optJSONArray("steps");
		
		for (int i=0; i < steps.length(); ++i) {
			JSONObject step = steps.optJSONObject(i);
			JSONObject startP = step.optJSONObject("start_location");
			JSONObject endP = step.optJSONObject("end_location");
			JSONObject polyline = step.optJSONObject("polyline");
			String encodedPoints = polyline.optString("points");
			
			startGeo = new Location("");
			endGeo = new Location("");
			
			startGeo.setLatitude(startP.optDouble("lat"));
			startGeo.setLongitude(startP.optDouble("lng"));
			endGeo.setLatitude(endP.optDouble("lat"));
			endGeo.setLongitude(endP.optDouble("lng"));
			
			list.add(new Section(startGeo.getLatitude(), startGeo.getLongitude(), 
                                      endGeo.getLatitude(), endGeo.getLongitude() , encodedPoints));
		}
		
		return list;
	}

Основная, интересующая нас информация лежит в массиве «steps», структура которого выглядит так:

"steps": [ {
        "travel_mode": "DRIVING",
        "start_location": {
          "lat": 41.8507300,
          "lng": -87.6512600
        },
        "end_location": {
          "lat": 41.8525800,
          "lng": -87.6514100
        },
        "polyline": {
          "points": "a~l~Fjk~uOwHJy@P",
          "levels": "B?B"
        },
        "duration": {
          "value": 19,
          "text": "1 min"
        },
        "html_instructions": "Head u003cbu003enorthu003c/bu003e on u003cbu003eS Morgan Stu003c/bu003e toward u003cbu003eW Cermak Rdu003c/bu003e",
        "distance": {
          "value": 207,
          "text": "0.1 mi"
        }
      },
   ... 

Каждый элемент массива «steps» содержит поле «start_location» и «end_location» — это координаты некоторых важных, опорных точек маршрута, таких как например перекрестки. Если выполнить запрос от Москвы до Воронежа, то таких точек будет около 20. Понятное дело, рисовать по 20 точкам не получится, что-же делать? На самом деле нам стоит обратить внимание на поле «polyline». Оно содержит в себе еще два поля «points» и «level». Содержимое «points» выглядит жутковато, но на самом деле это и есть наши координаты, которые кодируются вот так. Бояться не стоит, за нас уже все придумали, берем и пользуемся. Так мы получаем огромное количество координат, по которым мы будем рисовать нашу дорогу. Не забудьте что, строка «polyline» содержится в каждом элементе «steps».

Рисуем Overlay

На данный момент мы разобрались как выглядит запрос, как его можно отправить, как получить и распарсить ответ. Из всего этого имеем кучу координат, по которым нам нужно рисовать наш путь. Код может выглядить так:

private class Road extends Overlay {
		private ArrayList<GeoPoint> list;
		private Paint paint;

		public Road(ArrayList<GeoPoint> list) {
			this.list = new ArrayList<GeoPoint>();
			this.list.addAll(list);
			paint = new Paint();
			paint.setColor(Color.MAGENTA);
			paint.setStyle(Paint.Style.STROKE);
			paint.setStrokeWidth(4);
		}

		@Override
		public void draw(Canvas canvas, MapView mapView, boolean shadow) {
			drawPath(mapView, canvas);
		}
		
		private void drawPath(MapView mv, Canvas canvas) {
			int x1 = -1;  
			int y1 = -1;
			int x2 = -1;
			int y2 = -1;

			Point point = new Point();
			
			for (GeoPoint gPoint: list) {
				mv.getProjection().toPixels(gPoint, point);
				x2 = point.x;
				y2 = point.y;
				
				if (i > 0) {
					canvas.drawLine(x1, y1, x2, y2, paint);
				}
				
				x1 = x2;
				y1 = y2;
			}
		} 

Вот и вся работа. В результате мы легко можем получить маршрут между двумя любыми точками и отобразить его на нашей карте. Надеюсь, эта статья будет вам в помощь. Удачи.

Еще раз продублирую все ссылки в кучке:
Google direction API — полная информация по данному сервису.
Spring fraemwork — фрэймворк для rest запроса к google сервису. Как альтернатива.
Формат шифрования ломаных линий
Как раскодировать эти ваши ломаные линии?

Автор: deM1d

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js