Introduction

A RouteMatrix allows the times and distances between a single stop and many other stops in a Route to be calculated simultaneously.

In this tutorial we will create a simple application that displays the driving times and distances between a central depot and four customer sites (locations), then automatically routes to the customer site can be driven to in the shortest time.

Prerequisites

This tutorial assumes familiarity with Microsoft Visual Studio. You will need a copy of Visual Studio 2005 or later and a moderately specified desktop computer. You will need a licensed copy of the Telogis GeoBase SDK. A 30-day free-of-charge trial may be downloaded from the GeoBase developer portal: http://dev.telogis.com/. Two versions of the trial SDK are available, one loaded with US (West Coast) map data, the other version loaded with map data for the UK. The locations used in this tutorial assume US data.

This tutorial also assumes that you are already familiar with the basics of creating a GeoBase-specific Visual Studio Project and adding a map control to a GeoBase project.

The Application

Create a new Visual Studio Project (a Windows Forms Application) called 'MyRouteMatrix'.

Add a reference to geobase.net.dll then, to simplify our code, add the following usings to the top of the project form (Form1.cs).

using Telogis.GeoBase;
using Telogis.GeoBase.Routing;
using Telogis.GeoBase.ImageUtils;

Return to the 'Design' view (Shift+F7) and add the following controls to the form:

  • • A GeoBase map control named mapMain
  • • A button named buttonCalculate with the text 'Calculate Routes to All Customers'

When run, your new application should appear similar to the screenshot below:

RouteMatrix Example

Next, add the following code to the top of the project such that the items are global (immediately above the Form1 constructor). Here we create a RenderList object to display map objects (several PushPins, a BalloonPushPin and a Route highlight), a list containing RouteStop objects, and an empty message string.

// Create rendererlist
RendererList renderList = new RendererList();

// Create route stops 
RouteStop depot = new RouteStop(new LatLon(33.607467, -117.722967));
List<RouteStop> customers = new List<RouteStop> {
	new RouteStop(new LatLon(33.647536, -117.774116)),
	new RouteStop(new LatLon(33.633131, -117.685001)),
	new RouteStop(new LatLon(33.593368, -117.721010)),
	new RouteStop(new LatLon(33.611624, -117.752008))
};

String msg;

Add the following code to the Form1 constructor, beneath InitializeComponent(). This will add PushPins to the map at the RouteStop locations specified above; then set the map's default renderer, center location and zoom level.

// Create a pushpin for the depot
PushPin base_location = new PushPin(depot.Location);
base_location.Icon = Icons.Building8;
renderList.Add(base_location);

// And pushpins for all customer locations
foreach (var customer in customers) {
	PushPin customerPin = new PushPin(customer.Location);
	customerPin.Icon = Icons.Number1 + customers.IndexOf(customer);
	renderList.Add(customerPin);
}

// Set the default renderer
mapMain.Renderer = renderList;

// Set the map center
mapMain.Center = new LatLon(33.6221, -117.7244);

// Set the map zoom
mapMain.Zoom = 7;

Now add a click event to the buttonCalculate button. This click event will be used to trigger a RouteMatrix calculation to provide information for a MessageBox, and to generate a new Route.

Update the buttonCalculate_Click event to match the following:

private void buttonCalculate_Click(object sender, EventArgs e) {
	// Clear the render list
	renderList.Clear();

	// Add the customers to our list of stops
	List<RouteStop> matrixStops = new List<RouteStop> { depot };
	matrixStops.AddRange(customers);

	// Create the RouteMatrix
	RouteMatrix myMatrix = new RouteMatrix(matrixStops.ToArray(),
		new RoutingStrategyFastest());

	// Get times and distances from RouteStop 0 (depot) to all customers
	List<double> myTimes = myMatrix.Times(0).ToList(); // seconds
	List<double> myDistances = myMatrix.Distances(0).ToList(); // meters

	// Get the shortest distance from the list
	var lowdistance = myDistances.OrderBy(num => num).ElementAt(1);

	// Get the lowest time from the list
	var lowtime = myTimes.OrderBy(num => num).ElementAt(1);

	// Get the routestop that is the shortest driving time away
	RouteStop nearestStop = myMatrix.Stops[myTimes.IndexOf(lowtime)];

	// Then route from the depot to this stop
	Route srte = new Route();
	srte.Start = depot;
	srte.AddStopAtEnd(nearestStop);
	srte.Strategy = new RoutingStrategyShortest();
	srte.ForceRecalculate();
	Directions dirs = srte.GetDirections();
	renderList.Add(dirs);

	// Loop through the times array
	for (int i = 1; i < myTimes.Count; i++) {
		// Values in the distances array are measured in meters
		string distance = (myDistances[i] / 1000).ToString(".00");

		// But we can convert to miles
		double miles = Math.Floor(MathUtil.ConvertUnits(myDistances[i],
		DistanceUnit.METERS, DistanceUnit.MILES));

		// the values in the times array are measured in seconds
		string time = (myTimes[i] / 60).ToString(".0");

		// Construct the message contents for this stop, then repeat
		msg += "Customer " + i + " is " + distance + 
			" km (around " + miles + " miles) " +
			time + " minutes away. \n";
	}

	// Add a note about the stop the shortest driving time away
	msg += "\nRouting to customer shortest driving time away (" + 
	(lowtime / 60).ToString(".0") + " minutes)...";

	// Make pushpins for the main depot
	PushPin base_location = new PushPin(depot.Location);
	base_location.Icon = Icons.Building8;
	renderList.Add(base_location);

	// Add a pushpin at the stop identified as the closest (by driving time)
	BalloonPushPin closest_stop = new BalloonPushPin(nearestStop.Location);
	closest_stop.Information = "This is the closest stop";
	renderList.Add(closest_stop);

	// And for each customer routestop
	foreach (var customer in customers) {
		PushPin customerPin = new PushPin(customer.Location);
		customerPin.Icon = Icons.Number1 + customers.IndexOf(customer);
		renderList.Add(customerPin);
	}

	// Force redraw
	mapMain.Invalidate();

	// Show the RouteMatrix details as a messagebox
	MessageBox.Show(msg, "Route Matrix to All Customers", 
		MessageBoxButtons.OK, 
		MessageBoxIcon.Information);

	// In case the UI button is pressed again...
	msg = null;
}

Testing

Run the application. After clicking the 'Calculate Routes to All Customers' button the map will be updated with a new route between the depot and the customer site that can be driven to in the shortest time. A messagebox will also list the times and distances between the depot and all stops in the RouteMatrix.

RouteMatrix Example Application

Complete Code


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Telogis.GeoBase;
using Telogis.GeoBase.Routing;
using Telogis.GeoBase.ImageUtils;

namespace MyRouteMatrix {
	public partial class Form1 : Form {
		// Create rendererlist
		RendererList renderList = new RendererList();

		// Create route stops 
		RouteStop depot = new RouteStop(new LatLon(33.607467, -117.722967));
		List<RouteStop> customers = new List<RouteStop> {
			new RouteStop(new LatLon(33.647536, -117.774116)),
			new RouteStop(new LatLon(33.633131, -117.685001)),
			new RouteStop(new LatLon(33.593368, -117.721010)),
			new RouteStop(new LatLon(33.611624, -117.752008))
		};

		String msg;

		public Form1() {
			InitializeComponent();

			// Create a pushpin for the depot
			PushPin base_location = new PushPin(depot.Location);
			base_location.Icon = Icons.Building8;
			renderList.Add(base_location);

			// And pushpins for all customer locations
			foreach (var customer in customers) {
			    PushPin customerPin = new PushPin(customer.Location);
			    customerPin.Icon = Icons.Number1 + customers.IndexOf(customer);
			    renderList.Add(customerPin);
			}

			// Set the default renderer
			mapMain.Renderer = renderList;

			// Set the map center
			mapMain.Center = new LatLon(33.6221, -117.7244);

			// Set the map zoom
			mapMain.Zoom = 7;
		}

		private void buttonCalculate_Click(object sender, EventArgs e) {
			// Clear the render list
			renderList.Clear();

			// Add the customers to our list of stops
			List<RouteStop> matrixStops = new List<RouteStop> { depot };
			matrixStops.AddRange(customers);

			// Create the RouteMatrix
			RouteMatrix myMatrix = new RouteMatrix(matrixStops.ToArray(),
				new RoutingStrategyFastest());

			// Get times and distances from RouteStop 0 (depot) to all customers
			List<double> myTimes = myMatrix.Times(0).ToList(); // seconds
			List<double> myDistances = myMatrix.Distances(0).ToList(); // meters

			// Get the shortest distance from the list
			var lowdistance = myDistances.OrderBy(num => num).ElementAt(1);

			// Get the lowest time from the list
			var lowtime = myTimes.OrderBy(num => num).ElementAt(1);

			// Get the routestop that is the shortest driving time away
			RouteStop nearestStop = myMatrix.Stops[myTimes.IndexOf(lowtime)];

			// Then route from the depot to this stop
			Route srte = new Route();
			srte.Start = depot;
			srte.AddStopAtEnd(nearestStop);
			srte.Strategy = new RoutingStrategyShortest();
			srte.ForceRecalculate();
			Directions dirs = srte.GetDirections();
			renderList.Add(dirs);

			// Loop through the times array
			for (int i = 1; i < myTimes.Count; i++) {
				// Values in the distances array are measured in meters
				string distance = (myDistances[i] / 1000).ToString(".00");

				// But we can convert to miles
				double miles = Math.Floor(MathUtil.ConvertUnits(myDistances[i],
				DistanceUnit.METERS, DistanceUnit.MILES));

				// the values in the times array are measured in seconds
				string time = (myTimes[i] / 60).ToString(".0");

				// Construct the message contents for this stop, then repeat
				msg += "Customer " + i + " is " + distance + 
					" km (around " + miles + " miles) " +
					time + " minutes away. \n";
			}

			// Add a note about the stop the shortest driving time away
			msg += "\nRouting to customer shortest driving time away (" + 
				(lowtime / 60).ToString(".0") + " minutes)...";

			// Make pushpins for the main depot
			PushPin base_location = new PushPin(depot.Location);
			base_location.Icon = Icons.Building8;
			renderList.Add(base_location);

			// Add a pushpin at the stop identified as the closest (by driving time)
			BalloonPushPin closest_stop = new BalloonPushPin(nearestStop.Location);
			closest_stop.Information = "This is the closest stop";
			renderList.Add(closest_stop);

			// And for each customer routestop
			foreach (var customer in customers) {
				PushPin customerPin = new PushPin(customer.Location);
				customerPin.Icon = Icons.Number1 + customers.IndexOf(customer);
				renderList.Add(customerPin);
			}

			// Force redraw
			mapMain.Invalidate();

			// Show the RouteMatrix details as a messagebox
			MessageBox.Show(msg, "Route Matrix to All Customers", 
				MessageBoxButtons.OK, 
				MessageBoxIcon.Information);

			// In case the UI button is pressed again...
			msg = null;
		}
	}
}

Published, Jul 8th 2016, 00:48

Tagged under: dotnet traffic geobase routing navigation maps