Planificación de rutas con Map Kit.
Published: 2020-09-24 18:14:59

Recientemente tuvimos la oportunidad de experimentar con el kit de mapas. Pero, más allá de simplemente crear el mapa y agregar un par de marcadores, queríamos explorar un poco más con la planificación de rutas. La intención con este artículo es llevarlos a la inicialización y construcción de un proyecto que haga un uso adecuado de esta función.

Antes de comenzar, se recomienda que hayas tenido un primer acercamiento con el kit de mapas (Usando SupportMapFragment), te sugiero que tengas en cuenta el proceso de desarrollo o al menos el codelab.

El proyecto que les presentamos como referencia ha implementado una solución con flavors, es decir, tenemos la posibilidad de seleccionar una versión para dispositivos HMS o GMS. En este punto debes estar preguntándote lo mismo que nos preguntamos, ¿Cómo se comportará la planificación de Huawei en un teléfono con Google Maps? No es el objetivo principal del artículo, pero podemos incluirlo solo como excedente.

En primer lugar, te recomendamos consultar la documentación del API de planificación de rutas.
• API de indicaciones: planificación de rutas para caminar, andar en bicicleta y conducir.
• API Matrix: Planificación de múltiples rutas calculando distancias y tiempos entre múltiples puntos de inicio y finalización. Del mismo modo, se aplica a caminar, andar en bicicleta y conducir.
Para este caso en particular, usaremos la API de Directions y consumiremos este servicio usando Retrofit (personalmente es la biblioteca que más uso en mis proyectos) junto con Moshi. Puedes verificar las dependencias del proyecto para asegurarse de cómo incluirlas.
```
//Retrofit
implementation "com.squareup.retrofit2:retrofit: $version_retrofit"
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"
implementation "com.squareup.moshi:moshi:$version_moshi"
implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"
implementation "com.squareup.retrofit2:retrofit:$version_retrofit"
implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"
implementation "com.squareup.okhttp3:logging-interceptor:$okhttpLoggingInterceptorVersion"
```
Comenzaremos analizando las URL para ver cómo podemos hacer uso de un servicio para múltiples propósitos.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/6wjlwh4y144x8ej5fzri.png)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/u9ev70bblj0r4oezitbu.png)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/swm7yjh8i4j5cmddy7tw.png)

Lo que podemos extraer como nuestra URL principal para Retrofit se puede ver en el archivo Networ.kt: "https://mapapi.cloud.huawei.com/mapApi/v1/"

```
retrofit = Retrofit.Builder()
    .baseUrl("https://mapapi.cloud.huawei.com/mapApi/v1/")
    .client(client)
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .build()
```
En consecuencia, podemos organizar nuestro servicio según lo especificado por la interfaz en el archivo MapService.kt

```
interface MapsService {
@POST("routeService/{type}")
    suspend fun getRoute(
        @Path("type") path:String,
        @Query ("key")key:String,
        @Body route: RouteQuery
    ): RouteResponse
}
```
En el que usaremos los siguientes parámetros:
La etiqueta @POST permite especificar la URL del API que queremos consumir como POST.
La etiqueta @Path nos permitirá enviar la variable de ruta y ser reemplazada en la url descrita por la etiqueta anterior como "tipo".
Además, enviaremos la clave API como Consulta de la URL. Puedes comprobar de dónde se extrae en el siguiente enlace.
El modelo de solicitud para las API, llamado RouteQuery, consta de coordenadas de origen y destino (se pueden agregar más parámetros de acuerdo con la documentación). Para este caso particular, decidimos separar las clases de Origen y Destino para una explicación, pero ambas podrían ser la misma clase.

```
data class Origin(
    @Json(name = "lat") val lat: Double,
    @Json(name = "lng") val lng: Double
)
data class Destination(
    @Json(name = "lat") val lat: Double,
    @Json(name = "lng") val lng: Double
)
data class RouteQuery(
    @Json(name = "origin") val origin: Origin,
    @Json(name = "destination") val destination: Destination
)
```
El modelo de respuesta del servicio se puede consultar en cada API. Por ahora, te recomendamos que revises la clase RouteResponse ya que tiene varias subclases dentro de la carpeta del modelo del proyecto.

Con esto en mente, podemos crear nuestra instancia de actualización para hacer uso del servicio MapService con su método getRoute.
```
private val _mapService = retrofit.create(MapsService::class.java)
```
El siguiente paso en la agenda es hacer uso del servicio dentro de la clase Network.kt. Para las solicitudes el proyecto utiliza corrutinas, si no estás familiarizado con él, te aconsejo que investigues un poco más porque esa es la razón por la que usamos suspender en el método getRoute que presento a continuación.

```
suspend fun getRoute(
    route: RouteQuery,
    type:String,
    onSuccess: (RouteResponse) -> Unit = {},
    onError: (String) -> Unit = {}
) {
    _networkCurrentState.postValue(NetworkState.LOADING)
    var routeResponse: RouteResponse?
    try {
        routeResponse = _mapService.getRoute(type, BuildConfig.API_KEY,route)
        onSuccess(routeResponse)
        _networkCurrentState.postValue(NetworkState.SUCCESS)
} catch (e: Throwable) {
        onError(e.message.toString())
        _networkCurrentState.postValue(NetworkState.ERROR)
    }
}
```
En el ejemplo, después de seleccionar dos marcadores, te pedirá que seleccione el cálculo de la ruta. Una vez seleccionado, podrá ver la planificación que devuelve la API de mapas.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/n5yj5e4czq81td1gc7et.png)

Cuando se recibe la respuesta del servicio en el archivo MapsActivity.kt a través de una suscripción con la clase LiveData. Si la respuesta de la planificación de la ruta es exitosa, continúa dibujando las polilíneas que están en los pasos de la ruta.

```
viewModel.responseRoute.observe(this, Observer {result->
when(result){
        is Result.Success->{
            val route=result.data!!.routes[0].paths[0].steps.forEach {step->
                mMap.addPolyline(step.polyline,color?:Color.BLUE)
            }
        }
        is Result.Error->{
            Toast.makeText(this, result.exception, Toast.LENGTH_SHORT).show()
        }
    }
})
```
Si queremos probar el mismo efecto dentro de los mapas de Google, también se puede visualizar. Dentro del proyecto, simplemente selecciona la versión de googleDebug para cargar un dispositivo con GMS y verá un resultado similar.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/m2ym8m2mvkq6vybuddd9.png)

Github: https://github.com/juangaines/FlavoredGmsHmsMaps