Android studio google maps построить маршрут

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? Потому что он построил маршрут для автомобилей, который идет по умолчанию, но мы можем это изменить. Чтобы построить маршрут для пешеходов, меняем код на:

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

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

Читайте также:  Что удаляется при hard reset android

Источник

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

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

Я опущу процесс интеграции Google карт в приложение, интересующиеся могут найти всю информацию в подробном руководстве от Google. Весь процесс отображения маршрутов состоит из нескольких этапов:

  • Получение информации о маршруте;
  • Обработка полученного ответа;
  • Рисование маршрута на карте.

Рассмотрим эти этапы.

Получение маршрута

Для получения информации о маршруте мы должны выполнить запрос к службе маршрутов Google. Полное описание запросов и возвращаемых запросов доступны на сайте Google. Я лишь отмечу, что для получения маршрута мы должны выполнить запрос вида:

В качестве output мы можем выбрать XML, либо (в нашем случае) JSON. К числу обязательных параметров относятся origin и destination — их можно указать в виде текстового представления адреса, либо в виде значений широты и долготы, разделенных запятой. Третий обязательный параметр sensor указывает на то, исходит ли запрос от устройства с датчиком положения, либо нет — в нашем случае всегда будет равен true.

После того, как мы разобрались с форматом запроса, необходимо выбрать способ, которым мы будем выполнять наш запрос к службе маршрутов Google и получать ответ. Я использую библиотеку Retrofit, которая позволят выполнять формировать запросы к REST-сервисам буквально в пару строк.

Для использования Retrofit необходимо подключить библиотеку к вашему проекту. С использованием gradle это делается добавлением одной строки зависимости в ваш gradle-файл:

Далее нам необходимо описать API службы маршрутов Google. Для этого мы создаем новый Java-интерфейс, где мы создаем некоторое количество методов и при помощи аннотаций Retrofit сопоставляем их с различными методами на сервере. Так как мы будем получать только информацию, нам нужно описать только один метод для GET-запроса:

Аннотация GET в качестве аргумента принимает директорию на сервере, к которой должен выполняться запрос, а уже в самом методе аннотируем каждый его параметр аннотацией Query. Как аргумент для каждой аннотации имя параметра, который мы включаем в запрос. В данном случае для параметров origin и destination я устанавливаю свои значения для флага encodeValue, при помощи которого я сообщаю Retrofit, чтобы он не кодировал запятую, которая разделяет значения широты и долготы в моем запросе. Также я добавляю еще один параметр language для того, что бы ответ от сервера приходил на русском языке. Наш REST-метод должен возвращать некий объект, назовем его RouteResponse. Его мы опишем позже, а пока просто создадим еще один класс с именем RouteResponse.

После того, как мы описали API нашей службы, мы можем выполнить запрос. Для этого нам надо создать RestAdapter, создать сервис, представляющий удаленную службу и вызвать у него метод нашего API:

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

Обработка полученного ответа

В результате выполнения запроса мы получим объект RouteResponse. На самом деле, поскольку от сервера мы запросили JSON, то и ответ от сервера придет в JSON-формате. Retrofit, получив ответ от сервера, самостоятельно запускает парсинг JSON с использованием парсера от Google GSON, а уж тот парсит JSON в объект RouteResponse. При желании можно выбрать другой парсер — Jackson, либо JSON-парсер от Instagram, но я предпочитаю пользоваться GSON. GSON идет в комплекте с Retrofit, поэтому никаких дополнительных зависимостей для его использования нам в проект включать не надо.

Для того, чтобы доставать какие-то данные из JSON-ответа, нам нужно создать класс, описывающий эти данные. Мы уже создали класс RouteResponse, осталось наполнить его каким-то содержимым. Общая структура ответа от сервера службы маршрутов Google такова:

Как видим, в ответе нам приходит массив маршрутов Routes, который содержит массив отрезков Legs, состоящий из шагов Steps, составляющих отрезок маршрута, и информации об отрезке. В ранних примерах маршруты строились на основе информации о каждом шаге отрезка, однако уже в объекте Route содержится объект Overview_polyline — это объект с массивом закодированных элементов points, которые представляют приблизительный (сглаженный) путь результирующего маршрута. В большинстве случаев этого сглаженного маршрута будет достаточно. Поэтому для рисования я буду использовать именно его.

Читайте также:  You are empty android

На основе этой информации мы пишем наш класс модели для GSON:

Выполнив запрос и получив объект RouteResponse мы можем получить из него строку points. В своем исходном состоянии она нам мало что дает. Для того, чтобы добыть из нее какую-то информацию, нам нужно расшифровать ее. Здесь нам придет на помощь класс PolyUtil из библиотеки Google Maps Android API utility library. Чтобы им воспользоваться, нужно включить следующую зависимость в ваш проект:

PolyUtil содержит метод decode(), принимающий строку Points и возвращающий набор объектов LatLng, узлов нашего маршрута. Этого нам достаточно для того, чтобы нарисовать наш маршрут на карте.

Рисование маршрута на карте

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

Для начала мы создаем экземпляр класса PolylineOptions и устанавливаем толщину и цвет линии. Затем получаем экземпляр LatLngBuilder для построения ограничивающего прямоугольника, который будет использоваться для того, чтобы масштабировать карту. Дальше мы проходим по списку объектов LatLng, полученному в результате расшифровки ответа от API маршрутов Google и добавляем каждую точку на линию в и LatLngBuilder. Для первого и последнего объекта в списке, которые представляют собой координаты начальной и конечной точки соответственно, мы создаем маркеры и добавляем их на карту. После завершения перебора элементов списка мы добавляем построенную линию на карту вызовом метода addPolyline().

Затем нам нужно масштабировать карту таким образом, чтобы отобразить весь маршрут. Перемещение по карте выполняется при помощи метода moveCamera() класса Camera, который принимает на вход настройки камеры в объекте UpdateCamera. Объект CameraUpdate мы создаем вызовом метода newLatLngBoudns у класса UpdateCameraFactory. Мы пеередаем ему созданный нами объект LatLngBounds, который содержит все точки нашего маршрута и передаем ему ширину нашего экрана и добавляем отступ от краев. После этого мы вызываем метод для передвижения камеры. И всё, маршрут нарисован.

Источник

Склад знаний

В данный момент я 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) <

Читайте также:  Galaxy on fire 2 thd android
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;
>

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

>

Источник

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