Editing a Link's Permitted Speed |
The Telogis.GeoBase.Routing.LinkEdit namespace classes LinkEdit, LinkEditDatabase, LatLonSelection, PolygonSelection and CompiledLinkEdits are used to generate a database of link edits; select links (the base units of which streets are composed); edit maximum speeds permitted when traveling along selected links (whether selected individually by location or as a collection within a polygon), and compile databases containing link edits.
Databases containing these edits, called LinkEditDatabases, can then be assigned to a routing Strategy. The tutorial presented below demonstrates each of these steps.
Note |
---|
See the Modifying the Routing Strategy tutorial for more information on route strategies. |
We will create a small application containing a route with and without a link edit applied to a routing strategy.
Without the link edit, the routing engine will use the StreetLink's native ImpliedSpeedLimit of 25 MPH (for our particular street) to calculate an estimated route duration using GetTotalTime.
With the link edit applied, the StreetLink speed obtained from the GBFS database will be overridden by the LinkEditDatabase during route calculation.
In our example, we will calculate the total routing time using an unaltered link (one extending the full length of Sierra Noche Road in Irvine, Ca) with a speed of 25 MPH, and the same route with this link's permitted speed reduced to only 5 MPH. We will display the time taken to traverse the route using two strategies; one containing our link edit and one without.
Tip |
---|
Other GeoBase features that may be used to modify navigation strategies include Delivery Time Windows, Geofences and 'KeepOut' route strategies. |
First, create a Visual Studio project called 'RoutingStrategy' with a reference to the geobase.net binary and containing a MapCtrl object named mapMain.
Note |
---|
See the Creating the Basic Project tutorial for more information about creating a basic map project. |
Your project should look similar to the following:
Add the following using directives to the top of the project form:
using Telogis.GeoBase; using Telogis.GeoBase.Routing; using Telogis.GeoBase.Navigation; using Telogis.GeoBase.Routing.LinkEdit;
Then add the following to the top of the Form1 class:
RendererList renderList = new RendererList(); private LabelBox lBox = new LabelBox(); private LabelBox lBox2 = new LabelBox(); readonly private LatLon StartLocation = new LatLon(33.631591, -117.816194); readonly private LatLon StopLocation = new LatLon(33.632024, -117.815720); readonly private LatLon DestinationLocation = new LatLon(33.632512, -117.815271);
Below the InitializeComponent constructor create a route.
// create a route Route myRoute = new Route(); // set the start location myRoute.Start = new RouteStop(StartLocation); // set the end location myRoute.End = new RouteStop(DestinationLocation); // create a stop RouteStop Stop1 = new RouteStop(StopLocation); // add our one stop to the route myRoute.AddStops(new RouteStop[] { Stop1 }); // add some pushpins to make our location easier to see BalloonPushPin bpp1 = new BalloonPushPin(StartLocation); bpp1.Name = "Starting Point"; bpp1.Information = "Route starts here"; BalloonPushPin bpp2 = new BalloonPushPin(DestinationLocation); bpp2.Name = "Destination"; bpp2.Information = "Route ends here";
Then determine the speed of the selected StreetLink (at Stop1) and specify the dimensions and locations of the LabelBoxes. The LabelBoxes will be used to display the length and duration of our route with differing strategies applied.
// work out the speed limit of the link Stop 1 is on (from GBFS). StreetLink currentLink = GeoCoder.ReverseGeoCodeFull(StopLocation).StreetLink; double impliedSpeed = Math.Floor(MathUtil.ConvertUnits(currentLink.Flags.ImpliedSpeedLimit, currentLink.Flags.SPEED_UNITS, SpeedUnit.MilesPerHour)); BalloonPushPin bpp3 = new BalloonPushPin(StopLocation); bpp3.Name = "Stop 1 on Edited Link"; bpp3.Information = "GBFS database link speed is: \n" + impliedSpeed + " " + currentLink.Flags.SPEED_UNITS; // configure the size and location of our labelboxes lBox.Size = new System.Drawing.Size(220, 80); lBox.Top = mapMain.Top + 40; lBox.Left = mapMain.Right - lBox.Width - 380; lBox2.Size = new System.Drawing.Size(220, 80); lBox2.Top = mapMain.Top - lBox2.Height + 500; lBox2.Left = mapMain.Right - lBox2.Width - 100;
Get the estimated distance (GetTotalDistance) and time (GetTotalTime) of the route using GetDirections and compile and format the text content for our LabelBoxes.
// get the route directions Directions myDirections = myRoute.GetDirections(); // get the route distance and time double totalDistanceLong = myDirections.GetTotalDistance(DistanceUnit.MILES); decimal totalDistance = (decimal)(Math.Truncate((double)totalDistanceLong * 100.0) / 100.0); TimeSpan time = myDirections.GetTotalTime(); // add the route time and distance to a labelbox lBox.MajorText = "Unedited Route Time: \n" + time; lBox.MinorText = "Route Distance: " + totalDistance + " " + DistanceUnit.MILES; // specify the route color myDirections.RenderColor = Color.Red; // add the route to the render list renderList.Add(myDirections);
Now we will create a LinkEditDatabase containing a LatLonSelection selecting a StreetLink on our route.
Note the sequence when editing a link:
Create the LinkEditDatabase
Select the link(s) using either LatLonSelection or PolygonSelection
Create the link edit using CreateLinkEdit, specifying the selection, speed value, and speed unit
Compile the LinkEditDatabase using Compile
Apply the compiled database to a strategy using the RoutingStrategy property LinkEdits
Note |
---|
When selecting a StreetLink using the LatLonSelection constructor, the edit will apply in both directions if the link permits travel in both directions. If you want to edit the permitted speed in only one direction, use LatLonSelection with a LatLon and a Heading. If the link at the LatLon allows travel in two directions, and the Heading specified in the constructor is within 20 degrees of one of these directions, then the edit will only be applied to that direction. For example: C# // select only the heading if within 20 degrees of a 30 degree heading // -- that is, heading North East between 20 and 40 degrees LatLonSelection myLatLonSelection = new LatLonSelection(new LatLon(33.632024, -117.815720), 30); |
Note |
---|
Selection locations should include only one link. For this reason, avoid selecting links near street intersections, or any intersections of more than one link. |
using (myRoute) { // create a LinkEditDatabase LinkEditDatabase _myLinkEditDatabase = new LinkEditDatabase(); // create a selection (either a LatLon or polygon) // This is a LatLon selection LatLonSelection myLatLonSelection = new LatLonSelection(new LatLon(33.632024, -117.815720)); // create our LinkEdit to adjust the permitted speed on the link. // We will set the link speed to 5 MPH. _myLinkEditDatabase.CreateLinkEdit(myLatLonSelection, 5, SpeedUnit.MilesPerHour); // compile the LinkEdit Database CompiledLinkEdits _myCompiledLinks = _myLinkEditDatabase.Compile(); // apply the compiled Link Edits to the routing strategy myRoute.Strategy.LinkEdits = _myCompiledLinks; // recalculate the route with our edits myRoute.ForceRecalculate(); // get new directions Directions editdirs = myRoute.GetDirections(); // get the updated route distance and time double totalDistanceLongEdit = editdirs.GetTotalDistance(DistanceUnit.MILES); decimal totalDistanceEdit = (decimal)(Math.Truncate((double)totalDistanceLongEdit * 100.0) / 100.0); TimeSpan timeEdit = editdirs.GetTotalTime(); // add the route time and distance to a second labelbox lBox2.MajorText = "Link Edit Route Time: \n" + timeEdit; lBox2.MinorText = "Link Edit Route Distance: " + totalDistanceEdit + " " + DistanceUnit.MILES; // specify the route color editdirs.RenderColor = Color.Green; // add the route to the render list renderList.Add(editdirs); }
Note |
---|
The LinkEditDatabase created above uses an empty constructor. The database will therefore be stored only temporarily in local memory. To create as a GBTX (GeoBase Transactional) database file, or to use a previously created GBTX LinkEditDatabase, use LinkEditDatabase and specify a file location. For example: C# LinkEditDatabase _myLinkEditDatabase = new LinkEditDatabase(@"C:\LinkEdits\LinkEditDatabase.gbtx"); If the specified file does not exist, it will be created. |
Finally, we add the LabelBoxes and BalloonPushPins to the render list, center the map on the route stop and zoom the map.
// add our label boxes to the renderlist renderList.Add(lBox); renderList.Add(lBox2); // add our balloon pushpins to the renderlist renderList.Add(bpp1); renderList.Add(bpp2); renderList.Add(bpp3); mapMain.Renderer = renderList; // center the map on stop1 mapMain.Center = new LatLon(StopLocation); // set the map anchor to allow the map size to be adjusted mapMain.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right; // zoom close mapMain.Zoom = 0.1;
Run the application. The window will show the route starting at 5949 Sierra Siena Road, turning left onto Sierra Noche Road, then right onto Sierra Bravo Road before reaching our destination at 5690 Sierra Bravo Road. The route is short, traveling only one block (around 1/10th of a mile in this case).
Our application has edited the permitted speed of the link extending the full length of Sierra Noche Road, in both directions, reducing the allowed speed from 25 MPH to 5 MPH. The labelbox to the left shows that the speed of the route without this link edit applied to the strategy (all streets with a speed limit of 25 MPH) is just over 26 seconds.
The labelbox to the right indicates that with the link edit applied to the strategy (reducing the permitted speed on Sierra Noche Road to 5 MPH), the duration of the route has increased to nearly 70 seconds.
Increase or decrease the duration of the route by adjusting the speed value specified by the following code snippet in your project.
_myLinkEditDatabase.CreateLinkEdit(myLatLonSelection, 5, SpeedUnit.MilesPerHour);
In the code example above, we used LatLonSelection to choose a single link at a specific LatLon. If you wanted to select an area containing a number of links, you would use PolygonSelection.
For example, to select the entire city block containing all of the links traversed in the example route, you would create and select a polygon using LatLons to represent the four corners of a polygon rectangle as follows:
Telogis.GeoBase.Geometry.Polygon myPol = new Telogis.GeoBase.Geometry.Polygon( new Telogis.GeoBase.Geometry.LineString[] { new Telogis.GeoBase.Geometry.LineString( new LatLon(33.632714, -117.816371), new LatLon(33.631749, -117.816903), new LatLon(33.631190, -117.815253), new LatLon(33.632504, -117.814716), new LatLon(33.632714, -117.816371) ) } ); PolygonSelection myPolSelection = new PolygonSelection(myPol); _myLinkEditDatabase.CreateLinkEdit(myPolSelection, 5, SpeedUnit.MilesPerHour);
Note |
---|
Note that there are five locations specified. The first and last locations should be the same, 'closing' the polygon. A polygon must use a minimum of four locations: this number allowing the creation of a closed triangle. |
It is also possible to create a polygon using WKT (Well Known Text). The polygon example above, generated using locations represented in WKT format rather than LatLons, would be:
Telogis.GeoBase.Geometry.Polygon myPolWkt = new Telogis.GeoBase.Geometry.Polygon( "POLYGON((-117.816371 33.632714,-117.816903 33.631749,-117.815253 33.631190,-117.814716 33.632504,-117.816371 33.632714))"); PolygonSelection myPolSelection = new PolygonSelection(myPolWkt); _myLinkEditDatabase.CreateLinkEdit(myPolSelection, 5, SpeedUnit.MilesPerHour);
Note |
---|
Note that WKT uses 'x y' coordinates, reversing the values of a LatLon. |
These examples create a polygon covering an area shown in blue in the image below. This PolygonSelection modifies every link within, or touching, it. The street links modified by this polygon are shown in orange, while the route is shown in green.
The complete code for this example project is included below:
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.Navigation; using Telogis.GeoBase.Routing.LinkEdit; namespace RoutingStrategy { public partial class Form1 : Form { RendererList renderList = new RendererList(); private LabelBox lBox = new LabelBox(); private LabelBox lBox2 = new LabelBox(); readonly private LatLon StartLocation = new LatLon(33.631591, -117.816194); readonly private LatLon StopLocation = new LatLon(33.632024, -117.815720); readonly private LatLon DestinationLocation = new LatLon(33.632512, -117.815271); public Form1() { InitializeComponent(); // create a route Route myRoute = new Route(); // set the start location myRoute.Start = new RouteStop(StartLocation); // set the end location myRoute.End = new RouteStop(DestinationLocation); // create a stop RouteStop Stop1 = new RouteStop(StopLocation); // add our one stop to the route myRoute.AddStops(new RouteStop[] { Stop1 }); // add some pushpins to make our location easier to see BalloonPushPin bpp1 = new BalloonPushPin(StartLocation); bpp1.Name = "Starting Point"; bpp1.Information = "Route starts here"; BalloonPushPin bpp2 = new BalloonPushPin(DestinationLocation); bpp2.Name = "Destination"; bpp2.Information = "Route ends here"; // work out the speed limit of the link Stop 1 is on (from GBFS). StreetLink currentLink = GeoCoder.ReverseGeoCodeFull(StopLocation).StreetLink; double impliedSpeed = Math.Floor(MathUtil.ConvertUnits(currentLink.Flags.ImpliedSpeedLimit, currentLink.Flags.SPEED_UNITS, SpeedUnit.MilesPerHour)); BalloonPushPin bpp3 = new BalloonPushPin(StopLocation); bpp3.Name = "Stop 1 on Edited Link"; bpp3.Information = "GBFS database link speed is: \n" + impliedSpeed + " " + currentLink.Flags.SPEED_UNITS; // configure the size and location of our labelboxes lBox.Size = new System.Drawing.Size(220, 80); lBox.Top = mapMain.Top + 40; lBox.Left = mapMain.Right - lBox.Width - 380; lBox2.Size = new System.Drawing.Size(220, 80); lBox2.Top = mapMain.Top - lBox2.Height + 500; lBox2.Left = mapMain.Right - lBox2.Width - 100; // get the route directions Directions myDirections = myRoute.GetDirections(); // get the route distance and time double totalDistanceLong = myDirections.GetTotalDistance(DistanceUnit.MILES); decimal totalDistance = (decimal)(Math.Truncate((double)totalDistanceLong * 100.0) / 100.0); TimeSpan time = myDirections.GetTotalTime(); // add the route time and distance to a labelbox lBox.MajorText = "Unedited Route Time: \n" + time; lBox.MinorText = "Route Distance: " + totalDistance + " " + DistanceUnit.MILES; // specify the route color myDirections.RenderColor = Color.Red; // add the route to the render list renderList.Add(myDirections); using (myRoute) { // create a LinkEditDatabase LinkEditDatabase _myLinkEditDatabase = new LinkEditDatabase(); // create a selection (either a LatLon or polygon) // This is a LatLon selection LatLonSelection myLatLonSelection = new LatLonSelection(new LatLon(33.632024, -117.815720)); // create our LinkEdit to adjust the permitted speed on the link. // We will set the link speed to 5 MPH. _myLinkEditDatabase.CreateLinkEdit(myLatLonSelection, 5, SpeedUnit.MilesPerHour); // compile the LinkEdit Database CompiledLinkEdits _myCompiledLinks = _myLinkEditDatabase.Compile(); // apply the compiled Link Edits to the routing strategy myRoute.Strategy.LinkEdits = _myCompiledLinks; // recalculate the route with our edits myRoute.ForceRecalculate(); // get new directions Directions editdirs = myRoute.GetDirections(); // get the updated route distance and time double totalDistanceLongEdit = editdirs.GetTotalDistance(DistanceUnit.MILES); decimal totalDistanceEdit = (decimal)(Math.Truncate((double)totalDistanceLongEdit * 100.0) / 100.0); TimeSpan timeEdit = editdirs.GetTotalTime(); // add the route time and distance to a second labelbox lBox2.MajorText = "Link Edit Route Time: \n" + timeEdit; lBox2.MinorText = "Link Edit Route Distance: " + totalDistanceEdit + " " + DistanceUnit.MILES; // specify the route color editdirs.RenderColor = Color.Green; // add the route to the render list renderList.Add(editdirs); } // add our label boxes to the renderlist renderList.Add(lBox); renderList.Add(lBox2); // add our balloon pushpins to the renderlist renderList.Add(bpp1); renderList.Add(bpp2); renderList.Add(bpp3); mapMain.Renderer = renderList; // center the map on stop1 mapMain.Center = new LatLon(StopLocation); // set the map anchor to allow the map size to be adjusted mapMain.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right; // zoom close mapMain.Zoom = 0.1; } } }