Построение маршрута google maps api android

Google MAPs API в android или как работать с картами быстрее

Принцип работы Google MAPs API

Вся документация для работы с картами приведена на (логично) официальном сайте google maps api. Сегодня я рассматриваю только Directions API (документация). Для того что бы получить какую-либо информацию от большого числа, вам необходимо сделать запрос. Ответ прийдет в формате JSON.

Общий вид запроса:

Пример: https://maps.googleapis.com/maps/api/directions/json?origin=55.754724,%2037.621380&destination=55.728466,%2037.604155&key=»Your MAPs API key»

В качестве ответа нам (ожидаемо) пришел JSON с большим набором разных точек с координатами и названиями этих мест.

А как вообще работать с этой страшной штукой?

Если вы только начинаете работать с Android, то советую вам почитать про такую замечательную библиотеку Retrofit, которая превращает работу с запросами в код из 2 строк. Рассматривать сейчас я её не буду.

Но я сегодня хочу рассмотреть пример использования библиотеки Java Client for Google Maps Services. Библиотека как по мне замечательная, освобождает от необходимости писать (пусть даже очень короткие) запросы вручную и отлично подходит в случаях когда нужно писать очень быстро, как например на хакатоне. Я хочу показать живой пример использования данной библиотеки на примере работы с Directions API.

Подключение библиотеки

Для начала нам потребуется получить ключ для нашего приложения. Топаем на оф. сайт, находим сверху кнопку «получить ключ», создаем новый проект, нажимаем далее и готово!
UPD: теперь бесплатно получить нельзя. С лета 2018 года Google обновили план и необходимо ввести данные своей карты для получения 200$ для запросов каждый месяц бесплатно. Этого должно хватать, но конечно тенденция не радует.

Firebase
Для правильной работы приложения нам необходимо получить файл google-service.json. Идем на firebase выбираем наш проект и добавляем его. Далее нам нужно выбрать Android проект, ввести название пакета, регистрируем приложение. Скачиваем файл и перетаскиваем в папку app. К слову её не будет видно в дереве проекта, для этого надо в Android Studio поменять отображение с Android на Project или залезть в наш проект через файловый менеджер. Далее следуем инструкциям где какой код писать.

Включаем в консоли
Так же нам необходимо включить Directions API (или любую другую необходимую вам API) в консоли, для этого идем сюда, выбираем наше приложение и включаем Directions API.

Gradle
В Gradle файлы так же необходимо добавить еще пару строк. В итоге новые строки выглядят вот так:

Обязательно проверяйте, актуальная ли это сейчас версия!

Встраиваем карту в приложение

Google map в андроид реализовывается как фрагмент (или как MapView, но об этом в другой раз, нам сейчас особой разницы нет). Просто встраиваем его в наш layout. В нашем классе, который работает с картой, необходимо найти эту карту и заимплементить интерфейс.

Код для фрагмента выглядит вот так. Я буду работать с MainActivity, соответственно если вы используете другой класс вам необходимо поменять контекст.

Отлично, фрагмент встроили, Android Studio на нас не ругается, едем дальше. Переходим в MainActivity.class и имплементим интерфейс OnMapReadyCallback.

В onCreate пишем

Так же идем в Manifests и прописываем вот такие штуки внутри тэга application

Где вместо @string/google_maps_key должен подставиться ваш ключ для карт, который мы получили ранее. Соответственно вам нужно создать нужный ресурс в файле string.

Пишем всякие интересности

Отлично, карта у нас есть, давайте наконец напишем хоть что-нибудь интересное. Пусть нашей целью будет нарисовать маршрут по Москве через несколько точек:

  • Гум (55.754724, 37.621380)
  • Большой театр (55.760133, 37.618697)
  • Патриаршие пруды (55.764753, 37.591313)
  • Парк культуры (55.728466, 37.604155)

Кладу все наши места в List и делаю это как глобальную переменную.

Для начала создадим по маркеру на каждое место. Маркер это просто объект, которому передаются координаты, а затем они накладываются на карту. Код:

Далее мы пишем вот такой код все в том же методе onMapReady

При запуске приложения мы получили вот такую картину:

Хм, Москва, конечно, весьма запутанная, но не настолько же. Почему же такой странный маршрут нам вернул Google? Потому что он построил маршрут для автомобилей, который идет по умолчанию, но мы можем это изменить. Чтобы построить маршрут для пешеходов, меняем код на:

Читайте также:  Субару форестер андроид авто

Теперь наш маршрут выглядит вот так

Существует еще множество настроек, о всех них можно прочитать в документации. Просто мы все параметры будем добавлять не в сырой запрос, а в код, поскольку методы библиотеки имеют те же названия что и просто в запросах.

Источник

Маршруты на картах Google в Android-приложении — некоторые уточнения

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

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

После проверки установки маркеров и запоминания исходных и конечных координат, стал вопрос о реализации отображения маршрута. Предложенный в указанной выше статье «Маршруты на картах Google в Android-приложении» вариант реализации оказался не совсем работоспособным.

Во-первых, возникала ошибка при обращении с запросом к сайту maps.googleapis.com. Ошибка выражалась в невозможности выполнения запроса GET с передачей в главный поток. Проверка выполнялась на устройстве с Android 5,0. По описанию, автор примера делает синхронный GET запрос, который имеет вид:

Поскольку ошибку давал именно Retrofit, на базе которого и построены все запросы и парсинг JSON-ответа, то решено было разобраться в нем. Для получения информации воспользовался статьей — «Retrofit – библиотека для работы с REST API» по ссылке java-help.ru/retrofit-library.

Помня, что ранее в статьях писалось о нежелательности работы в синхронном режиме при обращении к сети, было решено переделать запрос в асинхронную версию. В результате получилось так:

Соответственно произошли изменения в дальнейшем коде. Вместо:

После чего, по запросу начал возвращаться JSON-ответ без ошибок.

В дальнейшем осталось только выделить из всего кода маршрут и отразить его на карте. Автор цитируемой статьи упоминает, что для получения точек маршрута необходимо воспользоваться классом PolyUtil. Цитирую: «. PolyUtil содержит метод decode(), принимающий строку Points и возвращающий набор объектов LatLng, узлов нашего маршрута. Этого нам достаточно для того, чтобы нарисовать наш маршрут на карте.» Однако, образца применения метода в статье нет. В моей реализации это выглядит вот так:

Далее уже можно строить полилинию методом, описанным у автора.

Итоговый код приложения будет иметь следующий вид:

Результат работы ниже на скрине:

Ссылки на материалы, которыми воспользовался при решении задачи:

Источник

Работа с интерфейсом в Google Maps SDK для Android

Данная статья будет полезна тем, кто ранее не использовал в своей работе Google Maps SDK.

Под катом описаны основные приемы работы с картой, такие как добавление и управление маркерами, способы перемещения камеры над картой, управление зумом, построение маршрута и геокодинг. А так же ограничения и способы их обхода.

Источник

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

К сожалению Google Maps SDK for Android не позволяет изменять положение кнопок управления, т.н. UI controls, к ним относятся: IndoorLevelPicker — показ поэтажного плана строений, Compass — компас, My Location button — перейти на карте к текущему местоположению, Map toolbar — кнопи построения маршрута и открытия карты, а так же ZoomControls — увеличения и уменьшения маштаба карты.

На примере Map toolbar и ZoomControls посмотрим какие сложности могут возникнуть из-за невозможности сменить положение контролов и как это обойти.


Проблемы с отображением UI controls из SDK (выделено оранжевым) и их кастомные аналоги (выделено зеленым)

В данном случае у нас в правом нижнем углу расположена кнопка (floating action button) перехода к списку адресов заказов на доставку, на картинке слева видно, что ZoomControls оказались под ней и практически недоступны для нажатия. На картинке справа, при нажатии на маркер, появляются кнопки из Map toolbar, они так же оказались под кнопкой перехода к списку заказов.

Первое что нам необходимо сделать — это скрыть отображение оригинальных кнопок. Сделать это можно переопределив метод onMapReady, он вызывается в тот момент, когда карта готова к использованию.

Читайте также:  Esp32 ble server android

Добавляем в верстку фрагмента нужные кнопки, там где они должны быть в соответствии с нашим дизайном:


Расположение кастомных кнопок управления картой

Затем в методе onCreateView указываем действия, которые должны произойти при нажатии на наши кнопки:

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

Что бы выполнить какое либо действие с маркером(кроме перетаскивания), например, создать новый заказ, удалить случайно поставленный маркер, перейти в уже существующий заказ или позвонить по телефону казанному в заказе добавляем в верстку соответствующие кнопки управления и прописываем их обработчики.


Кнопки управления маркером

Еще одна особенность, это то что в SDK нет кнопки для удаления поставленного на карту маркера. Для этого тоже делаем свою кнопку:

При нажатии на маркер открывается его заголовок, нажатие на который так же можно использовать для совершения какого либо действия, я при нажатии на заголовок нового маркера создаю новый заказ на доставку курьеру, а на маркер уже существующего заказа — открываю подробную информацию о доставке, включающюю перечень товаров.

Процесс вывода нескольких маркеров (считай списка заказов) на карту ничем принципиально не отличается от вывода одного маркера. Маркер состоит из координат (position), заголовка (title), мелкого текста под заголовком (snippet) и тэга (setTag) — его можно использовать для идентификации множества маркеров на карте.


Несколько маркеров на карте

Пара слов о геокодере

Геокодер используется для получения адреса, на основании координат. Поставив маркер на карте и нажав кнопку добавить заказ, мы получаем географические координаты нужной точки, т.е. широту и долготу. Но для удобства пользователя неплохо будет показать адрес в человекочитаемом виде, т.е., например, страна, город, улица, дом.

Google Maps SDK содержит класс Geocoder, вызвав его метод getFromLocation можно получить массив адресов по указанным координатам.

Для того, что бы не блокировать UI thread долгими, особенно если медленный или недоступный интернет, вызовами — будем использовать RxJava:


Полученный адрес точки на карте на основании географических координат

Источник

Склад знаний

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

среда, 5 декабря 2012 г.

Android Google Maps Api v2. Урок 4. Строим маршрут на карте

Итак, продолжаем нашу серию спонтанных уроков, вызванных необходимостью переводить своё приложение на новый API
Сегодня мы разберемся, как получить маршрут и отобразить его на карте. Для этого обратимся к API Google Directions.

Получать маршрут мы будем грамотно, используя AsyncTask. Мы должны обратиться к Google Service Directions, передать координаты начальной и конечной точек и распарсить JSON ответ. Например вот такой: http://maps.google.com/maps/api/directions/json?origin=55.772851,37.586806&destination=55.415003,37.899904&sensor=false

При этом мы в ответе мы получаем шифрованную полилайн для каждого шага, я написал метод decodePolilyne. Можно конечно ограничиться начальными и конечными координатами шага, но полученный результат вас не устроит, проверено на себе.

Итак, пишем интерфейс для AsyncTask, сохраняем его в файл OnRouteCalcCompleted.java:

public interface OnRouteCalcCompleted <
void onRouteCalcBegin();
void onRouteCompleted( ArrayList route );
>

И саму AsyncTask RouteHandler.java:

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.os.AsyncTask;
import android.util.Log;

/**
* Класс возвращает полилинию дороги, если указан mapView, отрисовывается на нём
* @author awacs
*
*/
public class RouteHandler extends AsyncTask < private final static String TAG = «RouteHandler»; /**
* Для точного интерполирования марштура
*/
public static final int FINE_ROUTE = 1;

/**
* Для грубого интерполирования маршрута
*/
public static final int COARSE_ROUTE = 2;

private final HttpClient client = new DefaultHttpClient();
private String content;
private boolean error = false;
private String error_msg = «»;

private ArrayList polyline;

private int accuracyRoute = 1;

private long distance;

private OnRouteCalcCompleted listener;

public RouteHandler( OnRouteCalcCompleted l ) <
this.listener = l;
>

public double getDistance() <
Log.d(TAG, «distance = » + distance + «m»);
return distance/1000;
>

public void calculateRoute( double latStart, double lonStart, double latEnd, double lonEnd, int accuracy ) < this.accuracyRoute = accuracy; StringBuilder origin = new StringBuilder();
origin.append( Double.toString(latStart));
origin.append(«,»);
origin.append( Double.toString(lonStart));

StringBuilder destination = new StringBuilder();
destination.append( Double.toString(latEnd));
destination.append(«,»);
destination.append( Double.toString(lonEnd));

List nameValuePairs = new ArrayList ();
nameValuePairs.add(new BasicNameValuePair(«origin», origin.toString()));
nameValuePairs.add(new BasicNameValuePair(«destination», destination.toString() ));
nameValuePairs.add(new BasicNameValuePair(«sensor», «false»));
String paramString = URLEncodedUtils.format(nameValuePairs, «utf-8»);
execute( «http://maps.google.com/maps/api/directions/json» + «?» + paramString );

>

public boolean isError() <
return error;
>

public String getErrorMsg() <
return error_msg;
>

@Override
protected String doInBackground(String. urls) < Log.d(TAG, «RouteHandler::doInBackground»);

try <
Log.v(TAG, urls[0]);
HttpPost httppost = new HttpPost(urls[0]);
ResponseHandler responseHandler = new BasicResponseHandler();
content = client.execute( httppost, responseHandler );
> catch (ClientProtocolException e) <
Log.d(TAG, «GetRouteHandler::ClientProtocolException»);
e.printStackTrace();
error = true;
cancel(true);
> catch (IOException e) <
Log.d(TAG, «GetRouteHandler::IOException»);
e.printStackTrace();
error = true;
cancel(true);
>
return content;
>

Читайте также:  Custom swipe view android

protected void onPostExecute(String content) <
if (error) <
error_msg = «Offline»;
> else <
try <
JSONObject response = new JSONObject(content);
String status = response.getString(«status»);
Log.v(TAG, content);
if( status.equalsIgnoreCase(«OK») ) <
polyline = new ArrayList ();

JSONArray routesArray = response.getJSONArray(«routes»);
JSONObject route = routesArray.getJSONObject(0);
// массив с информацией об отрезке маршрута
JSONArray legs = route.getJSONArray(«legs»);
JSONObject leg = legs.getJSONObject(0);

JSONObject distanceObj = leg.getJSONObject(«distance»);
distance = distanceObj.getLong(«value»);

JSONObject durationObj = leg.getJSONObject(«duration»);

// содержит куб выделения информационного окна для маршрута.
JSONObject bounds = route.getJSONObject(«bounds»);
JSONObject bounds_southwest = bounds.getJSONObject(«southwest»);
JSONObject bounds_northeast = bounds.getJSONObject(«northeast»);

double maxLat = bounds_northeast.getDouble(«lat»);
double maxLon = bounds_northeast.getDouble(«lng»);
double minLat = bounds_southwest.getDouble(«lat»);
double minLon = bounds_southwest.getDouble(«lng»);

JSONArray steps = leg.getJSONArray(«steps»);
for( int i=0; i
JSONObject step = steps.getJSONObject(i);
JSONObject start_location = step.getJSONObject(«start_location»);
JSONObject end_location = step.getJSONObject(«end_location»);

double latitudeStart = start_location.getDouble(«lat»);
double longitudeStart = start_location.getDouble(«lng»);
double latitudeEnd = end_location.getDouble(«lat»);
double longitudeEnd = end_location.getDouble(«lng»);
LatLng startGeoPoint = new LatLng(latitudeStart,longitudeStart);
LatLng endGeoPoint = new LatLng(latitudeEnd,longitudeEnd);
JSONObject polylineObject = step.getJSONObject(«polyline»);
if( accuracyRoute == FINE_ROUTE ) <
List points = decodePoly(polylineObject.getString(«points»));
Log.d(TAG, » » + points.size());
polyline.addAll(points);
> else <
polyline.add(startGeoPoint);
polyline.add(endGeoPoint);
>
>

> else if( status.equalsIgnoreCase(«NOT_FOUND»)) <
// по крайней мере для одной заданной точки (исходной точки, пункта назначения или путевой точки) геокодирование невозможно.
> else if( status.equalsIgnoreCase(«ZERO_RESULTS»)) <
// между исходной точкой и пунктом назначения не найдено ни одного маршрута.
> else if( status.equalsIgnoreCase(«MAX_WAYPOINTS_EXCEEDED»)) <
// в запросе задано слишком много waypoints. Максимальное количество waypoints равно 8 плюс исходная точка и пункт назначения. ( (Пользователи Google Maps Premier могут выполнять запросы с количеством путевых точек до 23.)
> else if( status.equalsIgnoreCase(«INVALID_REQUEST»)) <
// запрос недопустим
>else if( status.equalsIgnoreCase(«OVER_QUERY_LIMIT»)) <
// служба получила слишком много запросов от вашего приложения в разрешенный период времени.
>else if( status.equalsIgnoreCase(«REQUEST_DENIED»)) <
// служба Directions отклонила запрос вашего приложения.
>else if( status.equalsIgnoreCase(«UNKNOWN_ERROR»)) <
// обработка запроса маршрута невозможна из-за ошибки сервера. При повторной попытке запрос может быть успешно выполнен
>
> catch (JSONException e) <
e.printStackTrace();
>
>

listener.onRouteCompleted( polyline );

> // end postExecute

/**
* Декодирует полилинию из переданной гуглом строки
* @param encoded
* @return
*/
private List decodePoly(String encoded) <

List poly = new ArrayList ();
int index = 0, len = encoded.length();
int lat = 0, lng = 0;

while (index
int b, shift = 0, result = 0;
do <
b = encoded.charAt(index++) — 63;
result |= (b & 0x1f)
shift += 5;
> while (b >= 0x20);
int dlat = ((result & 1) != 0 ?

(result >> 1) : (result >> 1));
lat += dlat;

shift = 0;
result = 0;
do <
b = encoded.charAt(index++) — 63;
result |= (b & 0x1f)
shift += 5;
> while (b >= 0x20);
int dlng = ((result & 1) != 0 ?

(result >> 1) : (result >> 1));
lng += dlng;

LatLng p = new LatLng( lat/1E5, lng/1E5);
poly.add(p);
>

return poly;
> // end decodePoly

> // end class

Для использования этого таска в нашей активити, нужно имплементить OnRouteCalcCompleted, соответвенно в Activity добавятся методы:

// а заодно и удалить прогрессбар 🙂
>

Да, теперь добавляем например в инициализацию mMap из урока 3 код для вызова AsyncTask и компилируем готовое приложение.

private RouteHandler routeHandler;

private void setUpMapIfNeeded() <
if (mMap == null) <
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
>
routeHandler = new RouteHandler( this );
routeHandler.calculateRoute(55.772935, 37.594272, 55.88459, 37.4263165, RouteHandler.FINE_ROUTE);
>

Код полной активити:

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
import com.google.android.gms.maps.LocationSource;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolylineOptions;

import java.sql.Time;
import java.util.ArrayList;

import android.graphics.Color;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;

public class MainActivity extends FragmentActivity implements LocationSource, LocationListener, OnRouteCalcCompleted < private final static String TAG = «MainActivity»;

private OnLocationChangedListener mListener;
private LocationManager lManager;

private GoogleMap mMap;

private static double mLatitude;
private static double mLongitude;

private RouteHandler routeHandler;

@Override
protected void onCreate(Bundle savedInstanceState) <
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
//mMap.setTrafficEnabled(true);
mMap.setMyLocationEnabled(true);
mMap.setOnMapLongClickListener(this);
lManager = (LocationManager) getSystemService(LOCATION_SERVICE);
lManager.requestLocationUpdates( lManager.getBestProvider(new Criteria(), true), 1, 1000, this);
setUpMapIfNeeded();
>

@Override
protected void onResume() <
super.onResume();
setUpMapIfNeeded();
if( lManager != null ) <
lManager.requestLocationUpdates(lManager.getBestProvider(new Criteria(), true), 1, 1000, this);
>
>

private void setUpMapIfNeeded() <
if (mMap == null) <
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.mapView)).getMap();
>
routeHandler = new RouteHandler( this );
routeHandler.calculateRoute(55.772935, 37.594272, 55.88459, 37.4263165, AppSettings.FINE_ROUTE);
>

protected void onPause() <
if( lManager != null ) <
lManager.removeUpdates(this);
>
super.onPause();
>

@Override
public void onLocationChanged(Location location) <
if( mListener != null ) <
mListener.onLocationChanged( location );
>
>

@Override
public void activate(OnLocationChangedListener listener) <
mListener = listener;

>

@Override
public void deactivate() <
mListener = null;
>

@Override
public void onProviderDisabled(String provider) <
// TODO Auto-generated method stub

>

@Override
public void onProviderEnabled(String provider) <
// TODO Auto-generated method stub

>

@Override
public void onStatusChanged(String provider, int status, Bundle extras) <
// TODO Auto-generated method stub

>

@Override
public void onRouteCalcBegin() <
// TODO Auto-generated method stub

>

Источник

Оцените статью