Click or drag to resize

Editing a Link's Permitted Speed

Verizon Connect Logo
Print this page
Learn more about Verizon Connect GeoBase.
Get information about the latest release
Overview

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 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 Tip

Other GeoBase features that may be used to modify navigation strategies include Delivery Time Windows, Geofences and 'KeepOut' route strategies.

Editing Link Speed

First, create a Visual Studio project called 'RoutingStrategy' with a reference to the geobase.net binary and containing a MapCtrl object named mapMain.

Note 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:

linkedit empty

Add the following using directives to the top of the project form:

C#
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:

C#
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.

C#
// 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.

C#
// 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.

C#
// 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:

  1. Create the LinkEditDatabase

  2. Select the link(s) using either LatLonSelection or PolygonSelection

  3. Create the link edit using CreateLinkEdit, specifying the selection, speed value, and speed unit

  4. Compile the LinkEditDatabase using Compile

  5. Apply the compiled database to a strategy using the RoutingStrategy property LinkEdits

Note 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 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.

C#
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 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.

C#
// 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;
Testing

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.

linkedit

Increase or decrease the duration of the route by adjusting the speed value specified by the following code snippet in your project.

C#
_myLinkEditDatabase.CreateLinkEdit(myLatLonSelection, 5, SpeedUnit.MilesPerHour);
Selecting Links

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:

C#
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

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:

C#
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

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.

linkedit polygon
Complete Code

The complete code for this example project is included below:

C#
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;
        }  
    }
}
Next Topic