Introduction
Both Google and Bing maps are incredibly useful tools. The API, which they have opened to developers offer an amazing ability to add value to the solutions we provide. We don't have to use all the things, as sometimes just a snippet here and there can make a big difference to our users. This article shows how I used some of Google Map APIs to help the office staff work out how to best route the mobile staff during their working day.
Background
When you have a team of staff on the road, working from cars and vans, they generally fit into one of two categories. The ones that are heavily pre-planned (say commercial transport delivery vehicles) and those that have an aspect of planning but need to adjust during the day as needs changes, as demands require. It's this last one we are interested in for this article. Take for example plumbers - sure, they have planned the jobs during the day. However, in a big city and a busy service company, there may be multiple emergencies, burst pipes, water leaks etc. during the day that need to be prioritized, and transport routes must be carefully, but quickly, planned out. One of the methods used to decide, where to send the personnel is a triage system that asks three questions, which are given below.
- How important/urgent is the task?
- How close is the location of the job to one of our mobile staff?
- What's the soonest one of our mobile staff can get from where they are to the new location?
This understandably can get very complex very quickly, but by combining different technologies such as GPS/Geo-location on mobile phones to monitor the current location of the remote workers, with the co-ordinates of the different places, you potentially need them to go, you can put some very useful solutions together for your users.
The code that I am going to describe allows the user to input the current location of a remote worker and a list of different 'emergency call out' destinations. Using Google maps, we will be able to display to the user; the distance the worker is from each location and the time it will take them to get to each. Using this knowledge, they can make better decisions about what call-out job to prioritize with what remote worker.
In the production version of this sample code, I combined numerous additional pieces of information to get to 'your best choice/route is...' algorithm. This comprises of the following.
- A mobile app that assisted in tracking the workers and updating their position after every 15 minutes in a central database (this was done, using an app written, using Visual Studio Cordova - highly recommended).
- A scheduler/diary application using fullcalander with MVC that holds the information about the appointments; the remote workers were scheduled for (time expected Vs actual time taken etc.)
- A client specific back-end Application for the customer management, which stores customer location data.
You can see that using all of the information above, combined with the data we are able to get from a mapping API, can allow us to do some very useful and time saving things in a solution.
Before we start into the code, as a quick aside, I discussed using the Google Map API before in an article I wrote describing how to implement custom information window popups and markers for the Google map API in an ASP.net MVC solution. You may find that article useful to give you other ideas for functionality.
Google API Key
In order to get started, you will need to set yourself up with a Google API key. The signup process is very simple and unless you are doing this in large volumes, there is no charge. At the time of writing this article, you can get 25,000 map loads free per every 24 hours of usage - pretty generous and if you need more than this, I'm sure someone somewhere is paying, so you can get a commercial license.
Here are the basics of how to get yourself; a license key. Note that in my sample code, you must replace your own key.
Getting a key is really simple, as you have to first go to the Google Map API page, click 'Get API' button, give a project name and in a few seconds, you are ready to go.
Keep the key safe, we'll use it later on...
Settings things up
I have written the code for the article in ASP.NET MVC targeted at .NET v4.5 in Visual Studio 2015. To get started, we create a Web project and in this case, I cleaned up the _Layout.cshtml, as I didn't want any of the standard 'Application login' functionality etc. floating at the top of the demo app, as it leaves us with something resembling what is shown below.
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8" />
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>@ViewBag.Title - My ASP.NET Application</title>
- @Styles.Render("~/Content/css")
- @Scripts.Render("~/bundles/modernizr")
- </head>
- <body>
- <div class="container body-content">
- @RenderBody()
- <hr />
- <footer>
- <p>© @DateTime.Now.Year - My ASP.NET Application</p>
- </footer>
- </div>
-
- @Scripts.Render("~/bundles/jquery")
- @Scripts.Render("~/bundles/bootstrap")
- @RenderSection("scripts", required: false)
- </body>
- </html>
In the index.cshtml page, I have HTML that allows the user to switch between the two different code demos. For this article, I am using it with the corresponding controllers.
- <div class="row">
- <div class="col-md-12">
- <h2><a href="demo1">Example 1</a> - simple distance between two points</h2>
- <p>
- This demonstrates getting and displaying the distance between two points
- </p>
-
-
- </div>
- </div>
-
- <div class="row">
- <div class="col-md-12">
- <h2><a href="demo2">Example 2</a> - distance between one origin and multiple target destinations</h2>
- <p>
- This demonstrates getting and displaying the distance between multiple points
- </p>
- </div>
- </div>
- public ActionResult Demo1()
- {
- return View();
- }
-
- public ActionResult Demo2()
- {
- return View();
- }
Distance and route between two points
To understand the basics, let's go to demo 1. Here, we will look at setting up a Google map and using API to request a route and a distance between the two points on a map.
The first thing we will do is to setup HTML and layout the page. The page has a map and two input boxes, which allows the user to enter the location, where they are starting and the location or destination they want to go to. To save typing in testing (ever lazy!), I have defaulted these input values to locations in UK.
- <div class="row">
- <div class="col-md-12">
- <h2>Example 1 - simple distance between two points</h2>
- <p>
- This demonstrates getting and displaying the distance between two points
- </p>
-
- <div>
- <div>
- Travel From : <input id="travelfrom" type="text" name="name" value="Chichester, UK" />
- To : <input id="travelto" type="text" name="name" value="Goodwood aerodrone, UK" />
- <input type="button" value="Get Route" onclick="GetRoute()" />
-
- </div>
- <br />
- <div>
- <div id="dvDistance">
- </div>
- </div>
-
- </div>
-
- <div id="dvMap" style="min-height:500px"></div>
-
- </div>
- </div>
The key controls given above above are the inputs 'travelfrom', 'travelto' and the button that calls the 'GetRoute()' function.
The rest of the code is all in the script section. Here, when we start to use that API key, we generated the code given below earlier.
- @section scripts{
-
-
- <script src="https://maps.googleapis.com/maps/api/js?libraries=places&key=YOUR_KEY">
- </script>
** Important ** - You Need to Replace 'Your_key' Above, With the Key That You Generate Yourself.
The first thing that we need to do is set up some JavaScript variables. These will contain the values, which we use when calling the Map API.
- var source, destination;
- var directionsDisplay;
- var directionsService = new google.maps.DirectionsService();
Next, we take the div element 'dvMap', we specified in the HTML Markup earlier and tell the Map API to use this as a container in which to draw the map. I am also using the latitude and longitude of Chichester in England to center the map. In production, I have some script that changes this center view between the main office, the remote worker and the customer location - making it easier for the user to orient their planning.
-
- var map = new google.maps.Map(document.getElementById('dvMap'), {
- center: { lat: 50.834697, lng: -0.773792 },
- zoom: 13,
- mapTypeId: 'roadmap'
- });
The map API is vast and has many little buggets, which really assists in coding solutions. For example, there is an API, which you can use to connect an input box with a 'type ahead' location lookup. This makes it easier for the user to find the locations. In this next snippet of code, we call the 'Searchbox' method to link our 'travelfrom' and 'travelto' input boxes to this type ahead Service. There is also some code to allow the user to move the map around by clicking/dragging with his mouse.
- google.maps.event.addDomListener(window, 'load', function () {
- new google.maps.places.SearchBox(document.getElementById('travelfrom'));
- new google.maps.places.SearchBox(document.getElementById('travelto'));
- directionsDisplay = new google.maps.DirectionsRenderer({ 'draggable': true });
- });
Example 1 - Main code
We have a core function called 'GetRoute()'. This takes as an input the 'travelfrom' and 'travelto' locations, sends these to the API and then draws the result from the API onto the visual map for the user.
The first thing, which we need to do is set up the request parameters to send into the API. We then call the 'directionsService.route' API method to draw the response for the user ('directionsDisplay.setDirections').
- directionsDisplay.setMap(map);
-
- source = document.getElementById("travelfrom").value;
- destination = document.getElementById("travelto").value;
-
- var request = {
- origin: source,
- destination: destination,
- travelMode: google.maps.TravelMode.DRIVING
- };
-
- directionsService.route(request, function (response, status) {
- if (status == google.maps.DirectionsStatus.OK) {
- directionsDisplay.setDirections(response);
- }
- });
It's one thing to show a pretty map to the user, but for our purposes, we also want to get specific details on the route such as the distance between the two points and the time, it would take to travel (in this case, assuming the remote worker was travelling in a car/van). To work this out, we are going to call the 'Distance matrix Service'.
Step one sets things up, giving the origin and destination locations, setting the travel mode to DRIVING
and the measurement system to METRIC
.
- var service = new google.maps.DistanceMatrixService();
- service.getDistanceMatrix({
- origins: [source],
- destinations: [destination],
- travelMode: google.maps.TravelMode.DRIVING,
- unitSystem: google.maps.UnitSystem.METRIC,
- avoidHighways: false,
- avoidTolls: false
Step two handles the response form the API, extracting the values for the distance and the duration and displays them for the user into the HTML element 'dvDistance'.
- }, function (response, status) {
-
- if (status == google.maps.DistanceMatrixStatus.OK &&
- response.rows[0].elements[0].status != "ZERO_RESULTS") {
- var distance = response.rows[0].elements[0].distance.text;
- var duration = response.rows[0].elements[0].duration.value;
- var dvDistance = document.getElementById("dvDistance");
- duration = parseFloat(duration / 60).toFixed(2);
- dvDistance.innerHTML = "";
- dvDistance.innerHTML += "Distance: " + distance + "<br />";
- dvDistance.innerHTML += "Time:" + duration + " min";
Here is how the output looks once rendered, showing the the metrics the user requires.
Distance and route between multiple points
To give the user more flexibility and allow them to plan a block of time and travel, we are now going to see how we can use the 'waypoint' API in Google maps to work out the distance between the multiple points and give a visual representation of this to the user on the map view.
The first thing which we are going to do is set up the UI, so that the user can enter a series of the possible destinations. In my production version, this is all fed automatically from a database of the existing customer addresses/unassigned but pending diary appointments.
As you can see, laziness strikes again. In the testing, I have hard-coded in a starting point (Chichester) and some possible destinations (Tagmere and Bosham).
In our HTML layout, the main difference from the first version is that we add few things, which are given below.
- We have an array of possible destinations in Javascript, so there is a '
PushDestination()
' method to allow us to add destinations to that array - My laziness gives us a quick way to test some locations with '
setDestination()
'. - The 'destinations' div stores the list of destinations *before* we calculate our distances and routes.
- Finally, we have a table, which displays the results from the API in a nice formatted manner.
- <div> Add Destination</div>
- <div>
- <input id="travelto" type="text" name="name" value="Oving, UK" />
- <input type="button" value="Add" onclick="PushDestination()" />
- <a href="#" onclick="setDestination('Tagmere, UK')">Tagmere, UK. </a>
- <a href="#" onclick="setDestination('Bosham, UK')">Bosham, UK</a>
- </div>
- <div id="destinations"></div><br />
- Source : <input id="travelfrom" type="text" name="name" value="Chichester, UK" />
-
- <input type="button" value="Calculate" onclick="GetRoute()" />
- <p></p>
- <br />
- <div id="dvDistance">
- <table id="tblResults" border="1" cellpadding="10">
- <tr>
- <th> Start </th>
- <th> End </th>
- <th> Distance </th>
- <th> Duration </th>
- </tr>
- </table>
-
- </div>
-
- <div id="dvMap" style="min-height:500px"></div>
JavaScript builds on the previous code, which we have with the same setup as before. The difference is the addition of a 'locations' array.
- var source, destination;
- var locations = [];
- var directionsDisplay;
- var directionsService = new google.maps.DirectionsService();
-
-
- var map = new google.maps.Map(document.getElementById('dvMap'), {
- center: { lat: 50.834697, lng: -0.773792 },
- zoom: 13,
- mapTypeId: 'roadmap'
- });
-
- google.maps.event.addDomListener(window, 'load', function () {
- new google.maps.places.SearchBox(document.getElementById('travelfrom'));
- new google.maps.places.SearchBox(document.getElementById('travelto'));
- directionsDisplay = new google.maps.DirectionsRenderer({ 'draggable': true });
- });
Our array of the destination locations is managed by the 'PushDestination()
' method. This takes whatever value is in the 'travelto' input control and adds it to the array. It also updates the screen by adding the new item to the div 'destinations'.
- function PushDestination() {
- destination = document.getElementById("travelto").value;
- locations.push(destination);
- document.getElementById("travelto").value = "";
- destinationArray = document.getElementById("destinations");
- destinationArray.innerHTML += destination + "<br />";
- }
My helper method adds the items to the input box and calls the PushDestination()
method to keep things moving along.
- function setDestination(dest)
- {
- document.getElementById('travelto').value = dest;
- PushDestination();
- }
The GetRoute()
method starts out the same.
- function GetRoute() {
-
- directionsDisplay.setMap(map);
-
- source = document.getElementById("travelfrom").value;
- destination = document.getElementById("travelto").value;
It adds an array of 'Waypoints'.
- var waypoints = [];
- for (var i = 0; i < locations.length; i++) {
- var address = locations[i];
- if (address !== "") {
- waypoints.push({
- location: address,
- stopover: true
- });
- }
- }
The request param is tweaked slightly, as there is more than one destination involved. We add in our array of waypoints and tell the API to optimize for the waypoints.
- var request = {
- origin: source,
- destination: waypoints[0].location,
- waypoints: waypoints,
- optimizeWaypoints: true,
-
- travelMode: google.maps.DirectionsTravelMode.DRIVING
- };
Finally, we send in the request and parse the waypoint results, which we get back, displays the output into the table, which are ready in HTML. As you can see, the key here is to look into each 'LEG' of the response routes that comes back and extract the information, which we want from there.
- directionsService.route(request, function (response, status) {
- if (status == google.maps.DirectionsStatus.OK) {
- var dvDistance = document.getElementById("dvDistance");
- var distance = 0;
- var minute = 0.00;
- response.routes[0].legs.forEach(function (item, index) {
- if (index < response.routes[0].legs.length - 1) {
- distance = distance + parseInt(item.distance.text);
- minute = parseFloat(minute) + parseFloat(item.duration.value / 60);
-
- tbl = document.getElementById("tblResults");
- var row = tbl.insertRow(1);
- var cell = row.insertCell(0);
- cell.innerText = source;
- var cell = row.insertCell(1);
- cell.innerText = item.end_address;
- var cell = row.insertCell(2);
- cell.innerText = distance;
- var cell = row.insertCell(3);
- cell.innerText = minute.toFixed(2) + " min";
- }
- });
- directionsDisplay.setDirections(response);
- }
- else {
-
- }
- })
Here, the final output is just as we want it.
This is one way of implementing this kind of solution. I hope you found it useful. APIs keep changing, so if you get an error, it's best to look at the Browser console for any feedback from the API.
I am attaching a sample project for you to play with, don't forget to get your own API key and insert it, where needed.
Points of Interest
In a previous article, I wrote about using the Google Map API. There was no need for an API key to use the code. Indeed, if you try out the code on, for example, JSFiddle, it seems you don't need a key. Using it outside of such a testing environment however causes the API to throw a Browser console error, which states you need an API ... so beware, always look to the console, when things don't work as expected :)
I am attaching some sample code - happy mapping.