Basic Route Calculator Tutorial - Programmatically

I want to start simple. I want to set markers for 2 points, draw a route between, and interact with that route.

I try something like this:

var
  Route : TTMSFNCRouteCalculatorRoute;
  Step1, Step2 : TTMSFNCDirectionsStep;
  Segment1 : TTMSFNCRouteCalculatorSegment;
begin
  Route := RouteCalculator.Routes.Add;
  Route.ID := 'TheID';
  Route.Active := True;
  Route.RouteName := 'TheRouteName';
  RouteCalculator.Options.Polyline.StrokeWidth := 2;
  Route.DataString := 'SomeDataStr';

  Step1 := Route.Steps.Add;
  Step1.StartLocation.Latitude := 40.391537;
  Step1.StartLocation.Longitude := -104.681168;
  Step1.EndLocation.Latitude := 32.253460;
  Step1.EndLocation.Longitude := -110.911789;

  Step2 := Route.Steps.Add;
  Step2.StartLocation.Latitude := 41.391537;
  Step2.StartLocation.Longitude := -106.681168;
  Step2.EndLocation.Latitude := 33.253460;
  Step2.EndLocation.Longitude := -111.911789;

  TMSFNCMaps1.RouteCalculatorPlotRoute(Route);

No errors, but nothing happens.

How can I programmatically add start and end markers, draw a route between, and let the user interact with the route?

Hi,

The Routes collection is filled up automatically, by calling RouteCalculator.CalculateRoute. You can check out the demo how the connection is made with the route calculator to plot routes via interaction.

RouteCalculator.zip (93.4 KB)

Alternatively, if you want to plot routes manually, you can call:

unit Unit21;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.TMSFNCTypes, FMX.TMSFNCUtils, FMX.TMSFNCGraphics, FMX.TMSFNCGraphicsTypes,
  FMX.TMSFNCMapsCommonTypes, FMX.TMSFNCCustomComponent,
  FMX.TMSFNCRouteCalculator, FMX.Controls.Presentation, FMX.StdCtrls,
  FMX.TMSFNCCustomControl, FMX.TMSFNCWebBrowser, FMX.TMSFNCMaps;

type
  TForm21 = class(TForm)
    TMSFNCMaps1: TTMSFNCMaps;
    Button1: TButton;
    TMSFNCRouteCalculator1: TTMSFNCRouteCalculator;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form21: TForm21;

implementation

{$R *.fmx}

type
  TTMSFNCMapsOpen = class(TTMSFNCMaps);
  TTMSFNCRouteCalculatorOpen = class(TTMSFNCRouteCalculator);

procedure TForm21.Button1Click(Sender: TObject);
begin
  TMSFNCRouteCalculator1.CalculateRoute(CreateCoordinate(40.391537, -104.681168), CreateCoordinate(32.253460, -110.911789),
  procedure(const ARoute: TTMSFNCRouteCalculatorRoute)
  begin
    TMSFNCMaps1.ZoomToBounds(ARoute.Polyline);
  end);
end;

procedure TForm21.FormCreate(Sender: TObject);
begin
  TMSFNCRouteCalculator1.APIKey := 'MyAPIKey';
  TMSFNCRouteCalculator1.Active := True;
  TMSFNCMaps1.RouteCalculator := TMSFNCRouteCalculator1;
  TTMSFNCRouteCalculatorOpen(TMSFNCRouteCalculator1).OnCalculateRouteInternal := TTMSFNCMapsOpen(TMSFNCMaps1).RouteCalculatorDoCalculateRoute;
  TMSFNCMaps1.APIKey := TMSFNCRouteCalculator1.APIKey;
  TMSFNCMaps1.Service := msGoogleMaps;
end;

end.

Note the assignment of the OnCalculatRouteInternal event, which actually should not be necessary, we have fixed this issue here internally, but it needs to be released. When the next version comes out, you can remove this line. When starting the application, clicking on the button, you will be able to calculate a route, and then manipulate it afterwards.

Thank you, that worked perfect. Here's my findings on the next phase.

We need to be able to allow the user to interact with the route and output the new directions. So I decided to implement the Map RouteCalculatorWayPointUpdated and RouteCalculatorWayPointAdded events.

procedure TForm1.TMSFNCMaps1RouteCalculatorWayPointAdded(Sender: TObject;
  AMarker: TTMSFNCMapsMarker; ASegment: TTMSFNCRouteCalculatorSegment);
var
  I : Integer;
begin
  Memo1.Lines.Clear;
  for I := 0 to TMSFNCRouteCalculator1.Routes.Items[0].Steps.Count - 1 do
  begin
    Memo1.Lines.Add(TMSFNCRouteCalculator1.Routes.Items[0].Steps.Items[i].Instructions);
  end;
end;

procedure TForm1.TMSFNCMaps1RouteCalculatorWayPointUpdated(Sender: TObject;
  AMarker: TTMSFNCMapsMarker; ASegment: TTMSFNCRouteCalculatorSegment);
var
  I : Integer;
begin
  Memo1.Lines.Clear;
  for I := 0 to TMSFNCRouteCalculator1.Routes.Items[0].Steps.Count - 1 do
  begin
    Memo1.Lines.Add(TMSFNCRouteCalculator1.Routes.Items[0].Steps.Items[i].Instructions);
  end;
end;
  1. I don't see an event for StartLocationAdded/Updated or EndLocationAdded/Updated. We'll need to be able to take action if the user changes the start and end markers.

  2. The directions seem to get longer and longer at each interaction. For example the below route has 44 Steps when first added.

var
  WayPointsArray : TTMSFNCMapsCoordinateRecArray;
begin
  SetLength(WayPointsArray, 1);
  WayPointsArray[0].Latitude := 33.576698;
  WayPointsArray[0].Longitude := -101.855072;

  TMSFNCRouteCalculator1.CalculateRoute(CreateCoordinate(40.391537, -104.681168), CreateCoordinate(32.253460, -110.911789),
  MyOnCalculateRouteCallback, WayPointsArray, 'SomeID', nil);

But when I interact with the waypoints, the next Step count is 93, and then 190, 297, etc. I only dragged the waypoint a few miles out each time. The instructions seem to be adding onto themselves.

In order to overcome this, I implemented OnGetRouteDirections and cleared the Steps. What are your thoughts on this one?

procedure TForm1.TMSFNCRouteCalculator1GetRouteDirections(Sender: TObject;
  const ARequest: TTMSFNCDirectionsRequest;
  const ARequestResult: TTMSFNCCloudBaseRequestResult);
begin
  TMSFNCRouteCalculator1.Routes.Items[0].Steps.Clear;
end;
  1. Not mission critical yet, but the RouteID does not seem to be coming through in the callback.
procedure TForm1.MyOnCalculateRouteCallback(const ARoute: TTMSFNCRouteCalculatorRoute);
begin
    TMSFNCMaps1.ZoomToBounds(ARoute.Polyline);
  
    for I := 0 to ARoute.Steps.Count - 1 do
    begin
      Memo1.Lines.Add(ARoute.Steps.Items[i].Instructions);
    end;

    showmessage(ARoute.ID);  //doesn't work
end;

Hi Dwayne,

Thank you for your feedback.

  1. You can manually determine if the start/end location was updated by using the ASegment.Index parameter value. For example if the ASegment.Index value is 0 this means the start location was updated.
    We'll consider adding the extra Start/EndLocationAdded/Updated events you suggested in a future version.
    Also, while investigating this, I noticed the OnRouteCalculatorWayPointUpdated event was not triggered correctly for the end location. This issue has now been resolved and the update will be available with the next TMS FNC Maps release.

  2. Thank you for notifying. This issue has been resolved and the update will be available with the next TMS FNC Maps release.

  3. I have not been able to reproduce this issue. Please note that the Route ID is not generated automatically, but has to be set manually first.

Sound good for 1 and 2. We'll anxiously await the next release. Thanks again for the great development.

For number 3, here's the exact code. Delphi Sydney, FNC Maps V 1.2.2.3

procedure TForm1.Button3Click(Sender: TObject);
var
  WayPointsArray : TTMSFNCMapsCoordinateRecArray;
begin
  SetLength(WayPointsArray, 1);
  WayPointsArray[0].Latitude := 33.576698;
  WayPointsArray[0].Longitude := -101.855072;

  TMSFNCRouteCalculator1.CalculateRoute(CreateCoordinate(40.391537, -104.681168), CreateCoordinate(32.253460, -110.911789),
  MyOnCalculateRouteCallback, WayPointsArray, 'SomeID', nil);
end;

procedure TForm1.MyOnCalculateRouteCallback(const ARoute: TTMSFNCRouteCalculatorRoute);
var
  I: Integer;
begin
  TMSFNCMaps1.ZoomToBounds(ARoute.Polyline);

  for I := 0 to ARoute.Steps.Count - 1 do
  begin
    Memo1.Lines.Add(ARoute.Steps.Items[i].Instructions);
  end;
  showmessage(ARoute.ID);  //doesn't work
end;

The issue with the empty Route ID has been fixed and the update will be available with the next release of TMS FNC Maps.