Поскольку я являюсь начинающим разработчиком в данной области, то на первом этапе пользуюсь готовыми идеями, чтобы понять суть решения типовых задач. В данный момент мне стало необходимо проложить схематический маршрут между двумя точками на картах Google. Самым интересным аналогом для решения поставленной задачи, обнаруженным в сети Интернет, оказался следующий: "Маршруты на картах Google в Android-приложении". Однако, при его дальнейшем рассмотрении и реализации появились некоторые подводные камни, о которых я и хочу рассказать.
Во первых, возникла проблема постановки двух маркеров для указания отправной и конечной точек. Может это сделано и криво, но я выбрал следующее решение — контроль за их наличием с передачей данных при постановке. Получилось примерно так:
// Установка слушателя кликов по карте map.setOnMapClickListener(new GoogleMap.OnMapClickListener() { // обработка кликов @Override public void onMapClick(LatLng latlng) { if ((fromMarker == false) && (toMarker == false)) { MarkerOptions markerOptions = new MarkerOptions(); markerOptions.position(latlng); markerOptions.title("" + latlng.latitude + " " + latlng.longitude); BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory. fromResource(R.drawable.a_marker); markerOptions.icon(bitmapDescriptor); //параметры "from - из" и "to - в" - задаются, как строки, и будут передаваться как строки from=""+latlng.latitude+","+latlng.longitude; map.addMarker(markerOptions); fromMarker = true; } else { if ((fromMarker == true) && (toMarker == false)) { MarkerOptions markerOptions = new MarkerOptions(); markerOptions.position(latlng); markerOptions.title("" + latlng.latitude + " " + latlng.longitude); BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory. fromResource(R.drawable.b_marker); markerOptions.icon(bitmapDescriptor); to=""+latlng.latitude+","+latlng.longitude; map.addMarker(markerOptions); toMarker = true; } else { if ((fromMarker == true) && (toMarker == true)) { map.clear(); fromMarker = false; toMarker = false; } } } } });
После проверки установки маркеров и запоминания исходных и конечных координат, стал вопрос о реализации отображения маршрута. Предложенный в указанной выше статье «Маршруты на картах Google в Android-приложении» вариант реализации оказался не совсем работоспособным.
Во-первых, возникала ошибка при обращении с запросом к сайту maps.googleapis.com. Ошибка выражалась в невозможности выполнения запроса GET с передачей в главный поток. Проверка выполнялась на устройстве с Android 5,0. По описанию, автор примера делает синхронный GET запрос, который имеет вид:
public interface RouteApi { @GET("/maps/api/directions/json") RouteResponse getRoute( @Query(value = "origin", encodeValue = false) String position, @Query(value = "destination", encodeValue = false) String destination, @Query("sensor") boolean sensor, @Query("language") String language); }
Поскольку ошибку давал именно Retrofit, на базе которого и построены все запросы и парсинг JSON-ответа, то решено было разобраться в нем. Для получения информации воспользовался статьей — «Retrofit – библиотека для работы с REST API» по ссылке java-help.ru/retrofit-library.
Помня, что ранее в статьях писалось о нежелательности работы в синхронном режиме при обращении к сети, было решено переделать запрос в асинхронную версию. В результате получилось так:
//Интерфейс для запроса маршрута public interface RouteApi { @GET("/maps/api/directions/json") void getRoute( @Query(value = "origin", encodeValue = false) String position, @Query(value = "destination", encodeValue = false) String destination, @Query("sensor") boolean sensor, @Query("language") String language, Callback<RouteResponse> cb ); }
Соответственно произошли изменения в дальнейшем коде. Вместо:
RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://maps.googleapis.com") .build(); RouteApi routeService = restAdapter.create(RouteApi.class); RouteResponse routeResponse = routeService.getRoute(position, destination, true, "ru");
Получилось:
//Переход от интерфейса к API RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://maps.googleapis.com") .setLogLevel(RestAdapter.LogLevel.FULL) .build(); RouteApi routeService = restAdapter.create(RouteApi.class); //Вызов запроса на маршрут (асинхрон) routeService.getRoute(from, to, true, "ru", new Callback<RouteResponse>() { public void success(RouteResponse arg0, retrofit.client.Response arg1) { } public void failure(RetrofitError arg0) { } });
После чего, по запросу начал возвращаться JSON-ответ без ошибок.
В дальнейшем осталось только выделить из всего кода маршрут и отразить его на карте. Автор цитируемой статьи упоминает, что для получения точек маршрута необходимо воспользоваться классом PolyUtil. Цитирую: "…PolyUtil содержит метод decode(), принимающий строку Points и возвращающий набор объектов LatLng, узлов нашего маршрута. Этого нам достаточно для того, чтобы нарисовать наш маршрут на карте." Однако, образца применения метода в статье нет. В моей реализации это выглядит вот так:
//Вызов запроса на маршрут (асинхрон) routeService.getRoute(from, to, true, "ru", new Callback<RouteResponse>() { public void success(RouteResponse arg0, retrofit.client.Response arg1) { //Если прошло успешно, то декодируем маршрут в точки LatLng List<LatLng> mPoints = PolyUtil.decode(arg0.getPoints());
Далее уже можно строить полилинию методом, описанным у автора.
Итоговый код приложения будет иметь следующий вид:
package com.example.gpstest; import android.location.LocationManager; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.view.View; import com.google.android.gms.location.LocationListener; import com.google.android.gms.maps.CameraUpdate; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.model.BitmapDescriptor; import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.LatLngBounds; import com.google.android.gms.maps.model.Marker; import com.google.android.gms.maps.model.MarkerOptions; import com.google.android.gms.maps.model.PolylineOptions; import com.google.maps.android.PolyUtil; import java.util.List; import retrofit.Callback; import retrofit.RestAdapter; import retrofit.RetrofitError; import retrofit.http.GET; import retrofit.http.Query; public class MainActivity extends FragmentActivity { SupportMapFragment mapFragment; GoogleMap map; private LocationManager locationManager; private LocationListener locationListener; Marker label; boolean fromMarker, toMarker; String from, to,result; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); fromMarker = false; toMarker = false; //запрашиваем карту на вывод mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); map = mapFragment.getMap(); map.getUiSettings().setZoomControlsEnabled(true); map.setMyLocationEnabled(true); if (map == null) { return; } //Обработка клика на карту //Если нет маркеров, то ставим А, если есть А - ставим B, если есть оба - сбрасываем и вводим заново map.setOnMapClickListener(new GoogleMap.OnMapClickListener() { @Override public void onMapClick(LatLng latlng) { if ((fromMarker == false) && (toMarker == false)) { MarkerOptions markerOptions = new MarkerOptions(); markerOptions.position(latlng); markerOptions.title("" + latlng.latitude + " " + latlng.longitude); BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromResource(R.drawable.a_marker); markerOptions.icon(bitmapDescriptor); from=""+latlng.latitude+","+latlng.longitude; map.addMarker(markerOptions); fromMarker = true; } else { if ((fromMarker == true) && (toMarker == false)) { MarkerOptions markerOptions = new MarkerOptions(); markerOptions.position(latlng); markerOptions.title("" + latlng.latitude + " " + latlng.longitude); BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromResource(R.drawable.b_marker); markerOptions.icon(bitmapDescriptor); to=""+latlng.latitude+","+latlng.longitude; map.addMarker(markerOptions); toMarker = true; } else { if ((fromMarker == true) && (toMarker == true)) { map.clear(); fromMarker = false; toMarker = false; } } } } }); } //Класс точки маршрута движения public class RouteResponse { public List<Route> routes; public String getPoints() { return this.routes.get(0).overview_polyline.points; } class Route { OverviewPolyline overview_polyline; } class OverviewPolyline { String points; } } //Интерфейс для запросак маршрута public interface RouteApi { @GET("/maps/api/directions/json") void getRoute( @Query(value = "origin", encodeValue = false) String position, @Query(value = "destination", encodeValue = false) String destination, @Query("sensor") boolean sensor, @Query("language") String language, Callback<RouteResponse> cb ); } // метод показа маршрута public void showRoute(View view ) { //Переход от интерфейса к API RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://maps.googleapis.com") .setLogLevel(RestAdapter.LogLevel.FULL) .build(); RouteApi routeService = restAdapter.create(RouteApi.class); //Вызов запроса на маршрут (асинхрон) routeService.getRoute(from, to, true, "ru", new Callback<RouteResponse>() { public void success(RouteResponse arg0, retrofit.client.Response arg1) { //Если прошло успешно, то декодируем маршрут в точки LatLng List<LatLng> mPoints = PolyUtil.decode(arg0.getPoints()); //Строим полилинию PolylineOptions line = new PolylineOptions(); line.width(4f).color(R.color.colorPrimary); LatLngBounds.Builder latLngBuilder = new LatLngBounds.Builder(); for (int i = 0; i < mPoints.size(); i++) { line.add((LatLng) mPoints.get(i)); latLngBuilder.include((LatLng) mPoints.get(i)); } map.addPolyline(line); int size = getResources().getDisplayMetrics().widthPixels; LatLngBounds latLngBounds = latLngBuilder.build(); CameraUpdate track = CameraUpdateFactory.newLatLngBounds(latLngBounds, size, size, 25); map.moveCamera(track); } //Если запрос прошел неудачно public void failure(RetrofitError arg0) { } }); } }
Результат работы ниже на скрине:
Ссылки на материалы, которыми воспользовался при решении задачи:
Retrofit – библиотека для работы с REST API
A type-safe HTTP client for Android and Java
Маршруты на картах Google в Android-приложении
ссылка на оригинал статьи http://habrahabr.ru/post/275019/
Добавить комментарий