Skip to content

Route Direction Arrows

In this guide you will learn how to render arrows on the route, indicating the direction of travel, and also set their color. RoutingService is used to compute a route and NavigationService to start a real or simulated navigation on that route, with the route direction arrows layer activated. The Route Direction Arrows layer must be added to the map style used for rendering the map.

Route Direction Arrows - cpp example screenshot

Setup

First, get an API key token, see the Getting Started guide.

Prerequisites

It is required that you complete the Environment Setup - CPP Examples guide before starting this guide.

Setting your API key token

To set your API key token, define it like this:

#define API_TOKEN "YOUR_API_KEY_TOKEN"

replacing YOUR_API_KEY_TOKEN with your actual API key token text, within the quotes.

This can be done in the main() function, before the following line:

#if defined(API_TOKEN)

or outside the main() function, further up, or in a header file.

Although it is also possible to define your API key token as the text value of the GEM_TOKEN environment variable, this is not recommended, as it is not secure.

Use case

On a complicated route, it may be useful to render arrows on the route to indicate the direction of travel.

How to use the sample

When you open the sample, a preset route is computed, and a simulated navigation along that route starts, with the route direction arrows rendered on the route. This is made possible by also setting a map style which has the Route Direction Arrows layer.

Route Direction Arrows

Adding the Route Direction Arrows layer to a map style:

1) Go to the Styles tab in the online studio: https://developer.magiclane.com/api/studio

  1. Select your style or create a new one and then under Available Layers filter by arrow;

  2. Drag the Route Direction Arrows layer from Available Layers to Selected Layers in the Road arrows section;

  3. Click on it in the list, to set its Inner Color and Border Color in the Style tab on the right;

  4. Name the style in the upper left by clicking on its current name, such as Basic 1, and then typing the new name, such as Route Arrows;

  5. Click on Settings in the upper right and in the Resolution drop-down select Mobile then click Save;

  6. Click the left arrow in the upper left corner to go back to the list of styles;

  7. Click the 3 dots to the right of your style and select Download Style;

How it works

  1. Create an instance of Environment and set your API key token:

1int main( int argc, char** argv )
2{
3   std::string projectApiToken = "";
4#define API_TOKEN "YOUR_API_KEY_TOKEN"
5#if defined(API_TOKEN)
6   projectApiToken = std::string( API_TOKEN );
7#endif
8   // Sdk objects can be created & used below this line
9   Environment::SdkSession session(projectApiToken, { argc > 1 ? argv[1] : "" });
  1. The SDK is initialized with your API key token string and the log file path, where to write the application logs. Note that the log file path is not initialized by default, the empty string above, after the API key token, which means that no logs are written. The log file path is initialized with the first command line argument, if any. Create a gem::MapView interactive map object, using an OpenGL context for 3D graphics, the SDK environment created above, and a touch event listener for interactive user touch and mouse input, such as pan and zoom.

1// Create an interactive map view
2CTouchEventListener pTouchEventListener;
3gem::StrongPointer<gem::MapView> mapView = gem::MapView::produce(session.produceOpenGLContext(
4   Environment::WindowFrameworks::Available, "RouteDirectionArrows", &pTouchEventListener));
5if ( !mapView )
6{
7   GEM_LOGE( "Error creating gem::MapView: %d", GEM_GET_API_ERROR() );
8}
  1. The absolute resources path (SDK install path) is obtained, and the relative /Data/Res/ path is appended to it, thus building the path to the input data files. The .style input file(s) should be copied from the directory of this example to the Data/Res/ subdirectory within the SDK install path. Example: C:/MAGICLANE/Data/Res/ where C:/MAGICLANE is the SDK install path.

1std::string sdkResrcPath = Environment::GetInstance().GetResourcesPath();
2std::string projectRelativePath = "/Data/Res/";
3std::string projectAbsolutePath = sdkResrcPath + projectRelativePath;
  1. The map style with the Route Direction Arrows layer is set by path:

1std::string inputMapStyle = projectAbsolutePath +
2     "MobileRouteArrow.style";
3mapView->preferences().setMapStyleByPath(inputMapStyle);
  1. The color of the direction arrows can optionally be set to something other than they are set in the layer; then activate the route direction arrow rendering:

1gem::RouteRenderSettings routeSettings;
2routeSettings.setDirectionArrowInnerColor(gem::Rgba(255, 0, 255, 224));
3routeSettings.setDirectionArrowOuterColor(gem::Rgba(0, 255, 255, 224));
4routeSettings.setOption(gem::ERouteRenderOptions::RRS_ShowDirectionArrows);
  1. Calculate, render and simulate navigation on a route. At least 2 waypoints define the route: the first is the departure position, and the last is the destination. There can be zero or more intermediate waypoints through which the route passes, in the order they are listed. The coordinates are {latitude,longitude} in degrees; the landmark name is optional, and can be an empty string.

1gem::LandmarkList waypoints({
2   { "", { 48.526059 , 7.735456 } },
3   { "", { 48.525613 , 7.735448 } },
4   { "", { 48.525995 , 7.733916 } },
5   { "", { 48.526143 , 7.733535 } },
6   });
  1. The class MyNavigationListener implements the gem::INavigationListener interface, and is used to receive asynchronous navigation events, such as: onNavigationStarted(), onNavigationInstructionUpdated(), onWaypointReached(), onDestinationReached() and others. The first result route (at index 0) is added to the map: mapView->preferences().routes().add(routes[0]); The render settings defined above are set, so the route direction arrows are rendered: mapView->preferences().routes().setRenderSettings(routes[0], routeSettings);

 1// Compute route using these preferences: car / fastest / without alternatives in result
 2gem::RouteList routes;
 3ProgressListener routeListener;
 4auto navListener = gem::StrongPointerFactory<MyNavigationListener>();
 5gem::RoutingService().calculateRoute(routes, waypoints,
 6    gem::RoutePreferences().setTransportMode(gem::RTM_Car).setRouteType(
 7    gem::RT_Fastest).setAlternativesSchema(gem::AS_Never), &routeListener);
 8
 9// Wait until route calculation finished & check success
10if (WAIT_UNTIL(std::bind(&ProgressListener::IsFinished, &routeListener), 30000)
11&& routeListener.GetError() == gem::KNoError && !routes.empty())
12{
13    // Add the first resulting route (at index 0) to map view
14    mapView->preferences().routes().add(routes[0]);
15    mapView->preferences().routes().setRenderSettings(routes[0], routeSettings);
16    mapView->centerOnRoute(routes[0], gem::Rect(), gem::Animation(gem::AnimationLinear,
17       gem::ProgressListener(), 2000));
18    // Start simulated navigation along the route
19    gem::NavigationService().startSimulation(routes[0], navListener, gem::ProgressListener());
20    // Start follow GPS positions ( generated by simulation ) - camera follows position along route
21    mapView->startFollowingPosition();
22}
  1. A separate thread is used to demonstrate doing other things during navigation, such as, in this case, checking if the position indicator (green GPS arrow by default) has moved outside the viewport, if the user pans the map, which deactivates follow position mode - and the corresponding status is printed in the console debug output, once per second:

 1void navThread(std::string navThread, gem::StrongPointer<gem::MapView> mapView)
 2{
 3 auto positionArrow = gem::MapSceneObject::getDefPositionTracker().first;
 4 bool isPositionArrowVisible = true;
 5 while ( true )
 6 {
 7     isPositionArrowVisible = mapView.get()->checkObjectVisibility(*positionArrow);
 8     GEM_LOGE("POSITION ARROW IS %s THE VIEWPORT # # #", isPositionArrowVisible
 9     ? "WITHIN": "OUTSIDE");
10     std::this_thread::sleep_for(std::chrono::milliseconds(1000));
11 }
12}
1std::thread thread1(navThread, "navThread", mapView);
  1. Use the special provided macro to keep the interactive map and wait until the user closes the window.

1   WAIT_UNTIL_WINDOW_CLOSE();
2   return 0;
3}

C++ Examples

Maps SDK for C++ Examples can be downloaded or cloned with Git