Search code examples
google-mapsgpx

How to extract route GPX from a Google Maps route?


My objective is to extract a route which some user would have created on Google Maps into a GPX file containing latitude longitudes for further use in other apps.

The route will be shared via a link like this - https://goo.gl/maps/HcikiDXFwN2coeFN8

Interestingly, this app is already doing what I want to do - https://mapstogpx.com/ and https://www.gpsvisualizer.com/convert_input?convert_format=gpx. I just want to know how they are doing it so that I can emulate it for my own needs.

Since both the tools I've mentioned also require providing a Google Maps API key with the directions API enabled, my initial guess is that these tools first parse the webpage for the waypoints and then use those waypoints in a Directions API call to get all the route trackpoints.

Thanks


Solution

  • The solution lies in the "data" param in the expanded url for the google maps route link.

    Lets take this URL for example - https://goo.gl/maps/zrcP5gL1cd2AHGoq8

    This expands to https://www.google.com/maps/dir/Eiffel+Tower,+Paris,+France/Palais+Garnier,+Pl.+de+l'Op%C3%A9ra,+75009+Paris,+France/@48.8606129,2.2961092,14z/am=t/data=!4m29!4m28!1m20!1m1!1s0x47e6701f7e8337b5:0xa2cb58dd28914524!2m2!1d2.2930458!2d48.8560934!3m4!1m2!1d2.3122286!2d48.8490916!3s0x47e6702fa62d0bc5:0xd2d94ed604f2e5a0!3m4!1m2!1d2.3035972!2d48.8729816!3s0x47e66fc1755cf609:0x3c5040f902b41a5f!3m4!1m2!1d2.3235117!2d48.8581242!3s0x47e66e2ac3bca3ed:0x1c289763e3096e61!1m5!1m1!1s0x47e66e30d4668339:0xa9abf21c286d0767!2m2!1d2.3316014!2d48.8719697!3e0

    On closer examination, you will see that the data param has lat-lng coordinates embedded in it. For example this !1d2.2930458!2d48.8560934 or !1d2.3122286!2d48.8490916. These are longitude latitude pairs representing various waypoints on the route. The longitude always starts with !1d and ends with !2d and the latitude starts with !2d and ends with !3.

    Here is a small kotlin method to extract all the coordinates :

    //https://stackoverflow.com/a/62191093/3090120
    fun String?.indexesOf(pat: String, ignoreCase: Boolean = true): List<Int> =
            pat.toRegex(if (ignoreCase) setOf(RegexOption.IGNORE_CASE) else emptySet())
                    .findAll(this ?: "")
                    .map { it.range.first }
                    .toList()
    
    fun extractCoordinates(expandedUrl: String): List<LatLng> {
        val latLngList = arrayListOf<LatLng>()
        val indexes = expandedUrl.indexesOf("!1d")
    
        indexes.forEach {
            val coordinatesStr = expandedUrl.substring(it + 3).substringBefore("!3").split("!2d")
            latLngList.add(LatLng(coordinatesStr[1].toDouble(), coordinatesStr[0].toDouble()))
        }
    
        return latLngList
    }
    

    The result with this string would be [lat/lng: (48.8560934,2.2930458), lat/lng: (48.8490916,2.3122286), lat/lng: (48.8729816,2.3035972), lat/lng: (48.8581242,2.3235117), lat/lng: (48.8719697,2.3316014)]

    Once we have all the lat-lng pairs we can simply fire off a Google Maps Directions API call and recreate the route in the link and export it as a GPX file.