API Keys
TECNativeMap uses OpenStreetMap.
If you want to use these services of PTV or TomTom or MapBox you must register on their site to obtain a user key .
Offline mode
If you specify a directory in the LocalCache property, all datas will be cached and can be reused in offline mode.
TECRouting
Managing Routes and turn by turn navigation.
procedure Request(const StartAdress, EndAdress: string;const params: string = '';const FutureRoute:TECShapeline=nil); overload;
procedure Request(const dLatLngs: array of double;const params: string = '';const FutureRoute:TECShapeLine=nil); overload;
// get data road between Tarbes and Paris
map.Routing.Request('Tarbes','Paris');
// get data road between Tarbes and Lourdes via Aureilhan
map.Routing.Request('Tarbes','Aureilhan|Lourdes');
// you can also pass a array of coordinate pairs
map.Routing.Request([43.232858,0.0781021,43.2426749,0.0965226]);
Attention for OpenStreetMap and OSRM you must make sure to pass valid settings, see the documentation (identical for both because the engine is the same)
1property routeType: TMQRouteType
Only PTV, TomTom and Valhalla support rtTruck
2property OptimizeRoute : boolean
property GroupName : string
function FutureRoute:TECShapeLine;
We will directly pass a line to the routing engine
so that we can attach parameters to it
that will be used when the route is calculated,
here we store the speed of our mobile according to the type of route desired
*)
RouteLine := map.routing.futureRoute; // return a empty line
// fired when route is ready
map.Routing.OnAddRoute := doOnAddRoute;
if rbWalking.ischecked then
begin
map.Routing.RouteType := rtPedestrian ;
RouteLine.propertyValue['speed']:='6';
end
else if rbBicycling.ischecked then
begin
map.Routing.RouteType := rtBicycle ;
RouteLine.propertyValue['speed']:='20';
end
else
begin
map.Routing.RouteType := rtCar;
RouteLine.propertyValue['speed']:='50';
end;
// then empty string is for optionnal params, here none
map.Routing.Request(RouteStart.Text, RouteEnd.Text,'',RouteLine);
(* attention by default the request is asynchronous,
so your route is not yet ready just after the request,
connect to OnAddRoute to react when it is ready *)
procedure TForm.doOnAddRoute(sender: TECShapeLine;const params:string);
begin
// sender is the route (TECShapeLine)
// params this is the URL sent to the routing engine
end;
property Color : TColor
property Weight: integer
property StartMarkerFilename: string
property EndMarkerFilename: string
property RouteDrawingTime: integer
Default 0, the road appears only once.
property EditRoute: TECShapeLine
property EditOnClick : boolean
Routing engine
procedure Engine(value:TRoutingEngine;const ApiKey:string='');
Allows you to choose a routing engine, OpenStreetMap (default choice), PTV, Valhalla, TomTom, OSRM or MapBox
map.routing.Engine(reValhalla);
map.routing.Engine(reTomTom,'your_key');
map.routing.Engine(reOSRM);
map.routing.Engine(reMapBox,'your_token');
map.routing.Engine(rePTV,'your_key');
Use EngineValidUrl to change the url before is sent to the engine
This is recommended for Valhalla which by default is connected to a demo server.
1begin
// Here you can change the url
// ! your are not in main thread
sender.Url := ...;
end;
procedure TFDemoNativeRoute.FormCreate(Sender: TObject);
begin
// setup routing
map.routing.Engine(reOSRM);
// intercept url before engine
map.Routing.EngineValidUrl := doUrl;
end;
Define its own engine
Use EngineUrl and EngineExcute to edit your own engine.
property EngineUrl : TECThreadRoutingUrl
property EngineExecute : TECThreadRoutingExecute
Map.Routing.EngineName := yourRouting_Name;
Map.Routing.EngineUrl := yourRouting_URL;
Map.Routing.EngineExecute := yourRouting_Execute;
...
procedure yourRouting_URL(sender: TECThreadDataRoute);
begin
//
end;
procedure yourRouting_Execute(sender: TECThreadDataRoute; var Valid: boolean);
begin
//
end;
EngineUrl and EngineExecute are executed in another thread !
1property EngineName : string
Gives a name to the engine, used for local storage
property EngineKey : string
Key to use the engine, you get yours from TomTom or MapBox
property TurnByTurn: TECTurnByTurn
4 events are available
property OnAddRoute : TOnAddRoutemap.routing.OnErrorRoute := doOnErrorRoute ;
map.Routing.OnChangeRoute:= doOnChangeRoute;
procedure TFDemoNativeRoute.doOnAddRoute(sender: TECShapeLine;const params:string);
var i:integer;
PLine : TECPointLine;
begin
// sender is the route (TECShapeLine)
// params this is the URL sent to the routing engine
for i := 0 to sender.Count-1 do
begin
// roadside information points
PLine := sender[i];
PLine.Latitude;
PLine.Longitude;
PLine.Distance; // meters
PLine.Tex;
PLine.Time; // TDateTime
PLine.Maneuver;
end;
end;
procedure TFDemoNativeRoute.doOnErrorRoute(sender: TObject;const params:string);
begin
// sender is a TECThreadDataRoute
ShowMessage('Route not found !');
end;
procedure TFDemoNativeRoute.doOnChangeRoute(sender: TECShapeLine;const params:string);
begin
if not assigned(sender) then exit;
showMessage ( doubleToStrDigit(sender.Distance, 2) + 'km - ' +
doubleToStrDigit(sender.Duration / 3600, 2)+'h' ) ;
end;
Itinerary
This property makes it easier to build and manage the itinerary included in the route..
procedure ShowInstruction(const index: integer);
procedure ShowSegment(const index: integer);
property Count: integer
property Route: TECShapeLine
property SegmentRoute: TECShapeLine
property Segment[const index: integer]: TECItinerarySegment
TECItinerarySegment
property Instruction: stringproperty SegmentIndex: integer
property OnSelectedItinerarySegment: TOnSelectedItinerarySegment
property OnChangeRoute : TNotifyEvent
begin
Map.Routing.OnAddRoute := doOnAddRoute;
Map.Routing.itinerary.OnSelectedItinerarySegment :=
doOnSelectedItinerarySegment;
Map.Routing.Itinerary.OnChangeRoute := doChangeRoute;
end;
// request new route
procedure TItineraryForm.AddRouteClick(Sender: TObject);
begin
// select engine routing
case engine.ItemIndex of
0 : map.Routing.Engine(reOpenStreetMap);
1 : map.Routing.Engine(reValhalla);
end;
// select route type
case vehicle.ItemIndex of
0:
Map.Routing.routeType := rtPedestrian;
1:
Map.Routing.routeType := rtBicycle;
2:
Map.Routing.routeType := rtCar;
3:
Map.Routing.routeType := rtTruck;
end;
Map.Routing.Request(StartRoute.Text, EndRoute.Text);
end;
// event triggered when the route has been calculated and is available
procedure TItineraryForm.doOnAddRoute(Sender: TECShapeLine; const params: string);
begin
Sender.Focusable := false;
Sender.Color := GetRandomColor;
// show all road
Sender.fitBounds;
BuildItinerary(Sender);
end;
// build route planner
procedure TItineraryForm.BuildItinerary(const route: TECShapeLine);
var
i: integer;
d: double;
distance: string;
begin
// connect route
Map.Routing.itinerary.route := route;
itinerary.Items.Clear;
itinerary.Items.BeginUpdate;
for i := 0 to Map.Routing.itinerary.Count - 1 do
begin
// calculate distance
d := Map.Routing.itinerary[i].Distancekm;
if d < 1 then
distance := inttostr(round(d * 1000)) + 'm'
else
distance := DoubleToStrDigit(d, 2) + 'km' ;
itinerary.Items.Add(Map.Routing.itinerary[i].Instruction + ' (' +
distance + ')');
end;
itinerary.Items.EndUpdate;
itinerary.ItemIndex := Map.Routing.itinerary.SegmentIndex;
end;
// click on route planner
procedure TItineraryForm.itineraryClick(Sender: TObject);
begin
SelectSegment(itinerary.ItemIndex);
end;
// Triggered by Click on Map.Routing.itinerary.route
procedure TItineraryForm.doOnSelectedItinerarySegment(Sender: TECItinerary);
begin
SelectSegment(Sender.SegmentIndex);
end;
// Triggered by Click of a route <> Map.Routing.itinerary.route
procedure TItineraryForm.doChangeRoute(Sender: TObject);
begin
BuildItinerary(Sender as TECShapeLine)
end;
procedure TItineraryForm.SelectSegment(const index: integer);
var selected_segment: TECShapeLine;
begin
if index < 0 then
exit;
itinerary.ItemIndex := Index;
Map.Routing.itinerary.SegmentIndex := index;
selected_segment := Map.Routing.itinerary.SegmentRoute;
if not assigned(Map.Routing.itinerary.SegmentRoute) then
exit;
// move map for show segment
Map.Routing.itinerary.ShowSegment(index);
// you can also change color of selected_segment here
// Flash the segment 20 times per 250 ms period
// the animation will be automatically released when required
selected_segment.animation := TECAnimationShapeColor.create(20);
selected_segment.animation.Timing := 250;
// show infowindow with segment instruction
Map.Routing.itinerary.ShowInstruction(index);
end;
TECTurnByTurn
property line: TECShapeLine
function Position(const Lat, Lng: double):boolean
property AlertDistance: integer
property ExecutionDistance: integer
property ErrorDistance: integer
property NextInstruction:string
property NextManeuver: string ;
Available in the OnInstruction event
3property NextInstructionInKm: double ;
property NextInstructionPosition: TLatLng ;
available events
property OnAlert: TOnTurnByTurnAlert
property OnInstruction: TOnTurnByTurnInstruction
property OnArrival: TNotifyEvent
property OnError: TOnTurnByTurnError
property OnAfterInstruction: TOnTurnByTurnInstruction
property OnConnectRoute: TNotifyEvent
property OnDeconnectRoute: TNotifyEvent
The DemoNativeRoute demo has been rewritten to use the new feature.
1Old methods, use new features available since version 2.7
2Search for routes
You have at your disposal 4 routines (2 synchronous et 2 asynchronous) to calculate a route.
function GetRoutePathByAddress(const StartAdress, EndAdress: string;const routeType:TMQRouteType = rtFastest const params: string = '' ): TECRoutePath
function GetRoutePathFrom(const dLatLngs: array of double;const routeType:TMQRouteType = rtFastest ;const params: string = '' ): TECRoutePath
procedure GetASyncRoutePathByAddress(const StartAdress, EndAdress: string;const routeType:TMQRouteType = rtFastest ;const params: string = '' )
procedure GetASyncRoutePathFrom(const dLatLngs: array of double;const routeType:TMQRouteType = rtFastest ;const params: string = '' )
These functions allow to obtain information from a route without trace, asynchronous procedures will run in background and raise the OnRoutePath event when the data are available.
See open.mapquestapi.com/directions/ for the parameter Params
6If you do not release the TECMapRoutePath obtained, it will be automatically during the destruction of TECNativeMap
7Display the route
You can create a TECShapeLine from one TECMapRoutePath with the function AddRoute
var line : TECShapeLine;
...
// ge data road between Tarbes and Lourdes via Aureilhan
routePath := map.GetRoutePathByAddress('Tarbes','Aureilhan|Lourdes');
// draw polyline from routePath
if routePath<>nil then
begin
// change line 0 with new data
if map.shapes.lines.count>0 then
map.shapes.lines[0].setRoutePath(routePath);
// see the entire route
map.fitBounds(line.NorthEastLatitude,line.NorthEastLongitude,line.SouthWestLatitude,line.SouthWestLongitude);
routePath.free;
end;
You can also directly edit a TECShapeLine with the procedure setRoutePath
var line : TECShapeLine;
...
// ge data road between Tarbes and Lourdes via Aureilhan
routePath := map.GetRoutePathByAddress('Tarbes','Aureilhan|Lourdes');
// draw polyline from routePath
if routePath<>nil then
begin
// change line 0 with new data
if map.shapes.lines.count>0 then
map.shapes.lines[0].setRoutePath(routePath);
// see the entire route
map.fitBounds(line.NorthEastLatitude,line.NorthEastLongitude,line.SouthWestLatitude,line.SouthWestLongitude);
routePath.free;
end;
Edit the road with the mouse
For this you will need to add the unit uecEditNativeLine (FMX.uecEditNativeLine under FireMonkey)
You can then use the TecNativeLineToRoute class to be able to dynamically change the route.
property Line : TECShapeLineExample of use
var ecNativeLineToRoute : TecNativeLineToRoute;
...
ecNativeLineToRoute := TecNativeLineToRoute.create;
// edit route
ecNativeLineToRoute.Line := map.shapes.Lines[0];
// end edit route
ecNativeLineToRoute.Line := nil;
...
ecNativeLineToRoute.free;
DemoNativeRoute
A demonstration is available to understand the management of roads.
Draw a path with the mouse or finger
TECNativeMap has a wizard that allows you to draw your paths in a few clicks, the route will automatically follow the road between your points.
property Activate: booleanBy clicking directly in the center of the screen on the target you also trigger this action.
2property AddPointOnClick : boolean
map.DrawPath.AddPoint;
map.DrawPath.AddPointOnClick := true;
map.DrawPath.Undo;
var line : TECShapeLine;
line := map.shapes.AddLine;
map.DrawPath.GetPath(Line) ;
property PathType: TECDrawPathType
map.DrawPath.PathType := dptPedestrian;
map.DrawPath.PathType := dptBicycle;
map.DrawPath.PathType := dptCar;
property CursorData : string
Available only under Firemonkey.
3property Name: string
// here we will change the color
// add unit uecMapUtil for use ColorToHTML
Map.Styles.addRule('#' + map.DrawPath.Name + '.line {color:'+ColorToHTML(ColorPathLine.Color)+ '}');
property isReady: boolean
property OnPaintCursor: TECDrawPathPaintCursor
...
// We draw a simple cross in the center of the map,
// the same as in VCL mode
// add unit uecGraphics for TECCanvas
procedure TForm7.doPaintCursor(const canvas:TECCanvas);
var X, Y: integer;
begin
// convert center position to local X,Y
Map.FromLatLngToXY(Map.Center, X, Y);
canvas.PenWidth(2);
canvas.Polyline([Point(X, Y - 10), Point(X, Y + 10)]);
canvas.Polyline([Point(X - 10, Y), Point(X + 10, Y)]);
end;
property OnActivate : TECDrawPathActivate
property OnReady: TECDrawPathReady
map.DrawPath.OnReady := doOnReady;
// This event occurs before and after the calculation
// of the route in a thread,
// while the segment is being calculated
// you can neither validate the route
// nor cancel the last segment
procedure TForm7.doOnReady(const Ready:boolean);
begin
Validate.Enabled := Ready and map.DrawPath.activate;
Undo.Enabled := Ready and map.DrawPath.isUndo;
end;
// Activation / deactivation of route tracing
procedure TForm7.doOnActivate(const Activate:boolean);
begin
AddPoint.Enabled := Activate;
Guidance.Enabled := Activate;
if Activate then
begin
Active_deactive.Text := 'Deactivate' ;
Guidance.ItemIndex := integer(map.DrawPath.PathType);
end
else
Active_deactive.Text := 'Activate'
end;
property OnError : TNotifyEvent ;
IsoChrone
The isochrone service calculates the areas that are reachable within specified time or distance intervals from a location, and returns the reachable regions as polygon or line contours.
Only Valhalla and MapBox have this service, note that MapBox uses Valhalla and as it is an opensource software you can also build your own server.
8You can request up to 4 isochrones per call, and the maximum distance is 100km.
// To use your own Valhalla server set ServerURL
map.Routing.IsoChrone.ServerURL := 'xxx';
map.Routing.engine(reMapBox,map.MapBoxToken);
// 5, 10, 15 and 20 minutes by bicycle
map.routing.IsoChrone.Time(map.latitude,map.longitude,map.shapes.Polygones,[ 5,10,15,20],rtbicycle) ;
// delineate the 2 and 4.5 km walkable areas
map.routing.IsoChrone.Distance(map.latitude,map.longitude,map[ 'isoGroup'].lines,[2,4.5],rtPedestrian) ;
// other syntax by passing directly the polygons and lines
var P,P1:TECShapePolygone;
L:TECShapeLine;
...
P := map.addPolygone;
P1:= map.addPolygones;
map.routing.IsoChrone.Time(map.latitude,map.longitude,[P,P1],[ 5,10],rtCar) ;
L := map.addLine('your_group');
map.routing.IsoChrone.Distance(map.latitude,map.longitude,[L],[ 80],rtPedestrian) ;
The returned polygons and lines have isoTime and isoDistance properties that contain the requested values, you can use them to style your elements.
map.styles.addRule('.polygone{if:isotime>0;color:dark(grey)}');
map.styles.addRule('.polygone{if:isotime<21;fcolor:red}');
map.styles.addRule('.polygone{if:isotime<16;fcolor:#d97200}');
map.styles.addRule('.polygone{if:isotime<11;fcolor:yellow}');
map.styles.addRule('.polygone{if:isotime<6;fcolor:green}');