Low-End CPU Navigation¶
Render a route on the interactive map by drawing it, tracing with a finger.
Setup¶
Prerequisites¶
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¶
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] : "" });
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}
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);
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 theData/Res/
subdirectory within the SDK install path. Example:C:/MAGICLANE/Data/Res/
whereC:/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");
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}
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);
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}
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}