1

There is a project in which the Google Maps API is used to build the route.

Now the task for me is the following: inside the web application using the Google Maps API to create a route, after changing the route by moving the route on the map (which is in the web application) and after generating a link with which you can open google maps (https://www.google.com.ua/maps) where the route should be displayed taking into account the user's shift in the application.

In other words: through the Google Maps API generate a link that displays the changed route in Google maps.

I watched the documentation, analyzed the queries and answers of the Google Maps API and Google maps. So I did not find a solution.

I want to clarify whether it is possible to realize this task?

xomena
  • 31,125
  • 6
  • 88
  • 117
Yaros
  • 31
  • 1
  • 5

1 Answers1

7

I understand you would like to generate a draggable route using Google Maps JavaScript API and open the same route in Google Maps website. Once the user dragged the way point and changed the route, you would like to update the website URL and open updated route on Google Maps website. Is it correct?

You can implement this using the following ideas:

  • The google.maps.DirectionsRenderer can listen to the directions_changed event. Each time the route changed the API will trigger this event, so you can analyze the DirectionsResult object to extract waypoints used during the route generation. https://developers.google.com/maps/documentation/javascript/reference#DirectionsRenderer

  • You can create universal cross-platform link for Google Maps website or native app using the Google Maps URLs. There is a directions mode where you can specify origin, destination and way points and also corresponding place IDs. Please refer to the documentation for more details.

  • You can extract place IDs from DirectionsResult, to get corresponding formatted addresses that are required in Google Maps URLs you can use the Geocoder service of Maps JavaScript API. Note that geocoder service is asynchronous, you might need use promises to implement this part.

I have created a small example that shows this idea. Please have a look, but be aware that this code is not production ready, it's just a proof of concept. Each time the route is changed the link 'Open in Google Maps' is updated. Also note that I used promises, so this code will work only in modern browsers.

var origin = 'Barcelona, Spain';
var destin = 'Madrid, Spain';
var geocoder;
var hashPlaces = {};

function initMap() {
  var map = new google.maps.Map(document.getElementById('map'), {
    zoom: 6,
    center: {lat: 41.557922, lng: -0.895386},  //Spain.
    gestureHandling: 'greedy'
  });

  var directionsService = new google.maps.DirectionsService;
  var directionsDisplay = new google.maps.DirectionsRenderer({
    draggable: true,
    map: map,
    panel: document.getElementById('right-panel')
  });
  geocoder = new google.maps.Geocoder();

  directionsDisplay.addListener('directions_changed', function() {
    computeTotalDistance(directionsDisplay.getDirections());
    prepareMapLink(directionsDisplay.getDirections());
  });

  displayRoute(origin, destin, directionsService,
      directionsDisplay);
}

function displayRoute(origin, destination, service, display) {
  service.route({
    origin: origin,
    destination: destination,
    waypoints: [{location: 'Lleida, Spain'}, {location: 'Zaragoza, Spain'}],
    travelMode: 'DRIVING',
    avoidTolls: true
  }, function(response, status) {
    if (status === 'OK') {
      display.setDirections(response);
    } else {
      alert('Could not display directions due to: ' + status);
    }
  });
}

function computeTotalDistance(result) {
  var total = 0;
  var myroute = result.routes[0];
  for (var i = 0; i < myroute.legs.length; i++) {
    total += myroute.legs[i].distance.value;
  }
  total = total / 1000;
  document.getElementById('total').innerHTML = total + ' km';
}

function geocodePlaceId (placeId) {
  return new Promise(function(resolve, reject) {
      geocoder.geocode({'placeId': placeId}, function(results, status) {
         if (status === 'OK') {
            var r = Object.create(null);
            r[placeId] = results[0].formatted_address
            resolve(r);
         } else {
            reject('Geocode was not successful for the following reason: ' + status);
         }
      });
  });
}

function prepareMapLink(result) {
  var arrWp = [];
  result.geocoded_waypoints.forEach(function (wp) {
      arrWp.push(wp.place_id);
  });

  var oplaceId = arrWp.shift();
  var dplaceId = arrWp.pop();  

  var arrProm = [];
  arrWp.forEach( function (pId) {
    if (!hashPlaces[pId]) {
      arrProm.push(geocodePlaceId(pId));
    }
  });  

  if (arrProm.length) {
      Promise.all(arrProm).then( function (values) {
          values.forEach(function (val) {
             for (key in val) {
                hashPlaces[key] = val[key];
             }
          });
          constructMapsUrl(oplaceId, dplaceId, arrWp);
      }).catch(reason => { 
          console.log(reason)
      });
  } else {
      constructMapsUrl(oplaceId, dplaceId, arrWp);
  }

}

function constructMapsUrl(originId, destinationId, waypoints) {
    var res = "https://www.google.com/maps/dir/?api=1&";
    res += "origin="+encodeURIComponent(origin)+"&origin_place_id="+originId;
    res += "&destination="+encodeURIComponent(destin)+"&destination_place_id="+destinationId;

    var wpAddr = [];
    waypoints.forEach( function (wp) {
        wpAddr.push(hashPlaces[wp]);
    });

    var waypointsStr = encodeURIComponent(wpAddr.join('|'));
    var waypointsIds = waypoints.join('|');

    res += "&waypoints="+waypointsStr+"&waypoint_place_ids="+waypointsIds+"&travelmode=driving";

    var aElem = document.getElementById("mapLink");
    aElem.setAttribute("href", res);
}
#right-panel {
  font-family: 'Roboto','sans-serif';
  line-height: 30px;
  padding-left: 10px;
}

#right-panel select, #right-panel input {
  font-size: 15px;
}

#right-panel select {
  width: 100%;
}

#right-panel i {
  font-size: 12px;
}
html, body {
  height: 100%;
  margin: 0;
  padding: 0;
}
#map {
  height: 100%;
  float: left;
  width: 63%;
  height: 100%;
}
#right-panel {
  float: right;
  width: 34%;
  height: 100%;
}
.panel {
  height: 100%;
  overflow: auto;
}
<div id="map"></div>
<div id="right-panel">
  <p>Total Distance: <span id="total"></span></p>
  <p><a id="mapLink" href="#" title="Open in Google Maps" target="_blank">Open in Google Maps</a></p>
</div>
<script async defer
    src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDztlrk_3CnzGHo7CFvLFqE_2bUKEq1JEU&callback=initMap"></script>

You can find this example on jsbin as well: http://jsbin.com/sazelo/edit?html,output

I hope this helps!

xomena
  • 31,125
  • 6
  • 88
  • 117
  • Thank you very much, you have understood me one for a long time. Yes, I was looking for this answer. – Yaros Aug 24 '17 at 11:48
  • Happy to help, and welcome to Stack Overflow. If this answer solved your issue, please mark it as accepted. – xomena Aug 24 '17 at 11:56
  • Forgive me for the impudence, I wanted to ask more clarifying question, but I can not post here in the comments because the link is very large from Google maps and exceeds the number of characters allowed. How can I ask you an additional question? – Yaros Aug 24 '17 at 12:15
  • Post a question and leave a link here. I will have a look. – xomena Aug 24 '17 at 15:38
  • You helped me a lot. But still this problem is not completely solved. ... – Yaros Aug 25 '17 at 06:10
  • Tell me please, but you can make sure that I get this result https://www.google.com/maps/dir/Barcelona,+Spain/%D0%A1%D0%B0%D1%80%D0%B0%D0%B3%D0%BE%D1%81%D0%B0,+%D0%98%D1%81%D0%BF%D0%B0%D0%BD%D0%B8%D1%8F/Madrid,+Spain/@41.0195274,-3.0050421,7z/data=!4m25!4m24!1m10!1m1!1s0x12a49816718e30e5:0x44b0fb3d4f47660a!2m2!1d2.1734035!2d41.3850639!3m4!1m2!1d-0.4100324!2d42.1440675!3s0xd584432ffed5995:0x1e37d2865bf18b25!1m5!1m1!1s0xd5914dd5e618e91:0x49df13f1158489a8!2m2!1d-0.8890853!2d41.6488226!1m5!1m1!1s0xd422997800a3c81:0xc436dec1618c2269!2m2!1d-3.7037902!2d40.4167754!3e0 (note the items on the left).... – Yaros Aug 25 '17 at 06:11
  • If I edit intermediate points in my Google Maps API not like this: [{Location: 'Huesca, Spain'}, {location: 'Zaragoza, Spain'}], but like this: [{Location: 'Huesca, Spain', stopover: false}, {location: 'Zaragoza, Spain'}]. That is, add "stopover: false". What do you think? – Yaros Aug 25 '17 at 06:12
  • stopover: false means that you will not split a route at this point and additionally there are certain restrictions in Directions API in this case, for example U-turns are not allowed, you have to go straight forward this point. If this is not possible the API will return ZERO_RESULTS. – xomena Aug 25 '17 at 09:05
  • I'll think about it. I can ask you about the Google Maps API in the future? If so, how can I contact you? – Yaros Aug 25 '17 at 10:31
  • How to create link without origin or destination. To have this created to one direction and have to specify another as: https://www.google.com/maps/dir//Barcelona,+Spain/ – Adam Michalski Feb 19 '18 at 11:41
  • I don't think Google Maps URLs supports this. – xomena Feb 19 '18 at 12:00