📜 ⬆️ ⬇️

Routes on Google Maps in the Android application

Recently, I needed to display a route between two points on a Google map in my application. Habré already had publications on this topic. For example, "Routes on Google Maps in your Android application . " However, these materials are already satisfied with a lot of time and they do not use new features. I want to show another way of drawing routes, maybe it will be useful to someone.

I will omit the process of integrating Google maps into an application; interested users can find all the information in Google’s detailed guide. The whole process of displaying routes consists of several stages:


Consider these steps.

Getting a route


For route information, we must complete a request to the Google Route service. A full description of requests and return requests are available on Google . I just note that in order to get the route, we must execute a query of the form:
')
https://maps.googleapis.com/maps/api/directions/output?parameters 

We can choose XML as output , or (in our case) JSON. The required parameters include origin and destination - you can specify them as a textual representation of the address, or as latitude and longitude values, separated by a comma. The third required sensor parameter indicates whether the request comes from a device with a position sensor, or not - in our case it will always be true .

After we have dealt with the format of the request, you need to choose the way in which we will fulfill our request to the Google route service and receive a response. I use the library Retrofit , which will allow to form requests to REST services in just a couple of lines.

To use Retrofit, you must connect the library to your project. Using gradle, this is done by adding a single dependency line to your gradle file:

 dependencies { compile 'com.squareup.retrofit:retrofit:1.7.1' } 


Next, we need to describe the Google Route Services API. To do this, we create a new Java interface, where we create a number of methods and use Retrofit annotations to match them with various methods on the server. Since we will only receive information, we need to describe only one method for a GET request:

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


The GET annotation takes as an argument the directory on the server to which the request should be executed, and already in the method itself we annotate each of its parameters with the Query annotation. As an argument for each annotation, the name of the parameter that we include in the request. In this case, for the origin and destination parameters, I set my values ​​for the encodeValue flag, with which I tell Retrofit so that it does not encode a comma, which separates the values ​​of latitude and longitude in my request. I also add another language parameter for the answer from the server to come in Russian. Our REST method should return some object, let's call it RouteResponse. We will describe it later, but for now let's just create another class called RouteResponse.

After we describe our service API, we can execute the request. To do this, we need to create a RestAdapter, create a service representing the remote service and call its API method:

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


That's all you need to get a route from Google's route service. Adding a string to the RestAdapter constructor
 .setLogLevel(RestAdapter.LogLevel.FULL) 
, you can execute the request, and see the response from the server in your log. But we do not stop there.

Processing the response received


As a result of the query, we get the object RouteResponse. In fact, since we requested JSON from the server, the response from the server will come in JSON format. Retrofit, having received a response from the server, independently starts JSON parsing using a Google GSON parser , and that parsing JSON into the RouteResponse object. If you want, you can choose another parser - Jackson , or JSON-parser from Instagram , but I prefer to use GSON. GSON comes bundled with Retrofit, so we don’t need to include any additional dependencies to use it in the project.

In order to get some data from a JSON response, we need to create a class that describes this data. We have already created the class RouteResponse, it remains to fill it with some content. The general structure of the response from the Google route service server is as follows:

 { "routes" : [ { "bounds" : { "northeast" : { "lat" : 55.79283659999999, "lng" : 49.2216592 }, "southwest" : { "lat" : 55.73007759999999, "lng" : 49.1309371 } }, "copyrights" : "  © 2014 Google", "legs" : [ ], "overview_polyline" : { "points" : "qffsIk{zjHEwKpKcAvGo@bFk@bGg@vFg@hEIxFQHcTL{a@FkCF_AFm@L_@Zs@Pa@f@cB|@gDb@aBbAuDrByIrAqIhB{LTaDFoA?uAK_B]gEe@oEKk@]]}@u@AGCIEEkEsCgAy@o@o@mBwBmCyCyAaBSQiAg@iBq@aAWmGaA_AKUFm@MiACU@i@Jj@sAVW^YbAs@T_@Nq@?_@Eu@g@iCuBcHq@yCIy@Aq@Fq@He@nCmGhC{FnGcNbA}BNa@TeAPqAZmDzBiWJ}@Da@cA_CiFmLc@aAkBkEqBiEcP__@oHmPaE}IgD}HaCiFcGyM}H{PcFeLyKqV_BuDyA}CaCqF{HgQsCuGyAiDsAoCk@cAe@u@iAmAq@k@m@]aA_@oA]m@IuCK_C@yMGwUO_M@{B?yUSuEAqG?aD@cM@qFDoFEs@?iPGiDEgA?yAEoFAoDCo@?mGEmGE_JEsGAq@BaCHsAJKqAHcBn@HEsDBADEJ]FIPEZ?LJTB" }, "summary" : ". ", "warnings" : [], "waypoint_order" : [] } ], "status" : "OK" } 


As you can see, in the answer we get an array of Routes routes, which contains an array of Legs segments, consisting of Steps steps that make up a segment of the route, and information about the segment. In the earlier examples, the routes were based on information about each step of the segment, but already in the Route object there is an Overview_polyline object - this is an object with an array of encoded points that represent an approximate (smoothed) path of the resulting route. In most cases, this smoothed route will be sufficient. Therefore, I will use it for drawing.

Based on this information, we write our class model for GSON:

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


By executing the query and getting the RouteResponse object, we can get the string points from it. In its initial state, it gives us little. In order to extract some information from it, we need to decipher it. This is where the PolyUtil class from the Google Maps Android Android API utility library comes to the rescue. To use it, you need to include the following dependency in your project:

 dependencies { compile 'com.google.maps.android:android-maps-utils:0.3+' } 


PolyUtil contains a decode () method that accepts the Points string and returns a set of LatLng objects, nodes of our route. This is enough for us to draw our route on the map.

Drawing a route on the map


In the old examples, Overlay was used to draw the route, but we will manage with the Polyline class - in this case we don’t need to create an additional class inherited from Overlay and the amount of code we need to write is drastically reduced. Polyline is a list of points on the map and the line connecting them. Polyline can then be added to the map:

 PolylineOptions line = new PolylineOptions(); line.width(4f).color(R.color.indigo_900); LatLngBounds.Builder latLngBuilder = new LatLngBounds.Builder(); for (int i = 0; i < mPoints.size(); i++) { if (i == 0) { MarkerOptions startMarkerOptions = new MarkerOptions() .position(mPoints.get(i)) .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_marker_a)); mGoogleMap.addMarker(startMarkerOptions); } else if (i == mPoints.size() - 1) { MarkerOptions endMarkerOptions = new MarkerOptions() .position(mPoints.get(i)) .icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_marker_b)); mGoogleMap.addMarker(endMarkerOptions); } line.add(mPoints.get(i)); latLngBuilder.include(mPoints.get(i)); } mGoogleMap.addPolyline(line); int size = getResources().getDisplayMetrics().widthPixels; LatLngBounds latLngBounds = latLngBuilder.build(); CameraUpdate track = CameraUpdateFactory.newLatLngBounds(latLngBounds, size, size, 25); mGoogleMap.moveCamera(track); 


To begin with, we create an instance of the PolylineOptions class and set the thickness and color of the line. Then we get an instance of LatLngBuilder to build a bounding box that will be used to scale the map. Next we go through the list of LatLng objects obtained by decrypting the response from the Google Routes API and add each point to the line in LatLngBuilder . For the first and last objects in the list, which are the coordinates of the starting and ending points, respectively, we create markers and add them to the map. After completing the enumeration of the list items, we add the constructed line to the map by calling the addPolyline () method.

Then we need to scale the map so as to display the entire route. Moving around the map is done using the camera's moveCamera () method, which accepts camera settings in the UpdateCamera object. We create the CameraUpdate object by calling the newLatLngBoudns method of the UpdateCameraFactory class. We pass to it a LatLngBounds object created by us, which contains all points of our route and pass it the width of our screen and add indentation from the edges. After that we call the method to move the camera. And everything, the route is drawn.

image

In conclusion, once again I will cite all references to the materials used by me:

Source: https://habr.com/ru/post/244229/


All Articles