Skip to content

Low-End CPU Navigation

Render a route on the interactive map by drawing it, tracing with a finger.

Low-End CPU Navigation - 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

Given a GPX route and a corresponding NMEA pre-recorded navigation, compute a route and replay navigation along that route with low-end CPU hardware settings activated.

How to use the sample

The .gpx and the .nmea input files should be copied from the directory of this example to the Data/Res/ directory within the SDK install path. Example: C:\MAGICLANE\Data\Res\ where C:\MAGICLANE\ is the SDK install path.

When you open the sample, you’ll be viewing the scene from above. The route is calculated based on the GPX input file and playback navigation along the route starts based on the pre-recorded NMEA input file.

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, "NavigationLowEndHW", &pTouchEventListener));
5if ( !mapView )
6{
7   GEM_LOGE( "Error creating gem::MapView: %d", GEM_GET_API_ERROR() );
8}
  1. The usual CTouchEventListener standard touch event handler class is instantiated, which makes the map view interactive. Next, the low-end CPU and hardware settings are activated:

 1auto mapPrefs = mapView.get()->preferences();
 2mapPrefs.enableCursorRender(false);
 3auto positionArrow = gem::MapSceneObject::getDefPositionTracker().first;
 4positionArrow->setVisibility(true);
 5mapPrefs.setBuildingsVisibility(gem::EBuildingsVisibility::BV_2D);
 6mapPrefs.setMapDetailsQualityLevel(gem::EMapDetailsQualityLevel::MDQL_Low);
 7mapPrefs.setDrawFPS(true, gem::Xy(120, 60));
 8mapPrefs.setViewAngle(0);
 9mapView.get()->setZoomLevel(60);
10mapView.get()->extensions().setNavigationRouteLowRateUpdate(true);
11mapView.get()->extensions().setLowEndCPUOptimizations(true);
12mapPrefs.setMapLabelsFading(false);
13mapPrefs.followPositionPreferences().setPerspective(gem::EMapViewPerspective::MVP_2D);
14mapPrefs.followPositionPreferences().setViewAngle(0);
  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 .gpx and .nmea input files 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;
 4
 5std::string inputGPXlogFile = projectAbsolutePath +
 6    "strasbourg_points.gpx";
 7std::string inputNMEAlogFile = projectAbsolutePath +
 8    "strasbourg.nmea";
 9
10// The GPSLogs/ directory should be created within the SDK install path.
11// Example: C:\MAGICLANE\GPSLogs\
12
13gem::String logsdir(sdkResrcPath + "GPSLogs");
  1. Read the waypoints required to build a route from the input GPX file. A route requires a departure point, a destination point, and zero or more intermediate waypoints.

 1gem::DataBuffer mydatabuf;
 2
 3// Get size of file to know how much memory to allocate
 4std::uintmax_t gpxBytes = std::filesystem::file_size(inputGPXlogFile);
 5
 6if (gpxBytes > 0)
 7{
 8   // Allocate buffer to hold file
 9   char* readbuf = new char[gpxBytes];
10   if (readbuf)
11   {
12       std::ifstream fin(inputGPXlogFile, std::ios::binary);
13       fin.read(readbuf, gpxBytes);
14       if (fin)
15       {
16           mydatabuf.reserve(gpxBytes);
17           mydatabuf.setData(readbuf, gpxBytes);
18       }
19       fin.close();
20       delete readbuf;
21   }
22}
  1. Calculate and render a route based on the GPX input waypoints, then start a playback of a NMEA pre-recorded navigation along this route.

 1gem::Path path(mydatabuf, gem::PFF_Gpx);
 2if (!path.getWayPoints().empty() && !path.getCoordinates().empty())
 3{
 4   gem::RouteList routes;
 5   ProgressListener routeListener;
 6   gem::Landmark lmk;
 7   gem::LandmarkList waypoints;
 8   waypoints.emplace_back();
 9   gem::RouteBookmarks::setWaypointTrackData( waypoints.back_nc(), path );
10   gem::RoutingService().calculateRoute(routes, waypoints,
11      gem::RoutePreferences().setTransportMode(gem::RTM_Car).setRouteType(gem::RT_Fastest).setAlternativesSchema(gem::AS_Never), &routeListener);
12   // Wait until route calculation finished & check success
13   if (WAIT_UNTIL(std::bind(&ProgressListener::IsFinished, &routeListener), 30000) && routeListener.GetError() == gem::KNoError
14      && !routes.empty())
15   {
16      // Draw first resulting route (at index 0) on map view
17      mapView->preferences().routes().add(routes[0]);
18      mapView->centerOnRoute(routes[0], gem::Rect(), gem::Animation(gem::AnimationLinear, gem::ProgressListener(), 2000));
19      // Instantiate a navigation event listener for either navigation (real or playback) or simulation
20      auto navListener = gem::StrongPointerFactory<MyNavigationListener>();
21      // Start playback navigation along the route - first create and set the previously recorded navigation playback datasource
22      std::pair<gem::sense::DataSourcePtr, int> dataSource = gem::sense::produceLogDataSource(inputNMEAlogFile.c_str());
23      bool isNavigationPlayback = true;
24      if (isNavigationPlayback) // pre-recorded navigation playback
25      {
26          gem::PositionService().setDataSource(dataSource.first);
27          gem::NavigationService().startNavigation(routes[0], navListener, gem::ProgressListener());
28      }
29      else // simulation
30      {
31          gem::NavigationService().startSimulation(routes[0], navListener, gem::ProgressListener());
32      }
33      // Start follow GPS positions ( generated by the simulation ) - camera follows the position along the route
34      mapView->startFollowingPosition();
35   }
36}
37std::thread thread1(navThread, "navThread", mapView);
  1. A thread is created above, and the MapView instance is passed into it, so that during navigation playback, statistics can be queried, and debug can be printed to the debug console output. In this example, the green position arrow is queried to determine whether it is inside or outside the viewport. This can happen if the user pans the map, thus stopping the follow position mode, where the camera follows the position arrow, and then the position arrow can exit the viewport, or the user can pan the map, causing the position arrow to go outside the viewport. A log message is printed in the debug console output window every second, indicating whether the green position arrow is inside or outside the viewport.

 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 ? "WITHIN": "OUTSIDE");
 9      std::this_thread::sleep_for(std::chrono::milliseconds(1000));
10   }
11}
  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