Skip to content

Finger Route

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

Polygon Marker - 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

Draw a route on the map using a finger on a touch screen, or a mouse, instead of typing the names of the departure and destination, and selecting from a list of results.

How to use the sample

When you open the sample, you’ll be viewing the scene from above. Tap or click once on the map, then click and hold down in the same location, which is the departure position(starting point), and then drag the finger or mouse to the destination, then lift the finger or mouse, and a route is computed, going through the points traced on the map.

Alternatively, double-tap or double-click on the map, then click and hold and drag, to compute the shortest route between the departure and destination positions, as usual, without going along the path traced on the map.

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
 2MyTouchEventListener pTouchEventListener;
 3gem::StrongPointer<gem::MapView> mapView = gem::MapView::produce(session.produceOpenGLContext(
 4   Environment::WindowFrameworks::Available, "FingerRoute", &pTouchEventListener));
 5if ( !mapView )
 6{
 7   GEM_LOGE( "Error creating gem::MapView: %d", GEM_GET_API_ERROR() );
 8}
 9// Coordinates are specified as lat,lon in degrees
10mapView->centerOnCoordinates({ 42.63134, 3.02693 }, 60);
  1. Note that instead of the usual CTouchEventListener standard touch event handler class instance, which makes the map view interactive, this time we create an instance of MyTouchEventListener which inherits from and extends the former, specifically overriding the handleTouchEvent() method, to add the special processing to start/stop drawing a multi-waypoint route or a standard 2-waypoint (departure and destination position) route, using single click or double click, respectively, followed by a drag and pointer-up/release. While draw route mode, or route tracing/drawing, is active, touch events are not forwarded to the MapView instance, so the map does not pan, zoom or rotate while the route is being drawn. This state is indicated by the isDrawing() function of the CTouchDrawRoute class.

 1class MyTouchEventListener : public CTouchEventListener
 2{
 3public:
 4void handleTouchEvent(int eventType,int pointerId,int x,int y)
 5{
 6   auto mapView = getMapViewPointer();
 7   setCursorPosition(x, y);
 8   gem::Xy mousePos(x,y);
 9   if ( mapView.get()!=nullptr )
10   {
11      drawRoute.addWaypoint(mousePos, (gem::ETouchEvent)eventType, mapView);
12      if ( !drawRoute.isDrawing() )
13      {
14         mapView->getScreen()->handleTouchEvent((gem::ETouchEvent)eventType,pointerId,mousePos);
15      }
16   }
17   else
18   {
19      fprintf(stderr,"null mapView!\n");
20   }
21   auto coord = mapView->transformScreenToWgs(mousePos);
22   fprintf(stderr,"xy %d %d mapcoord %f %f %s\n",x,y,
23      coord.getLongitude(),coord.getLatitude(),drawRoute.isDrawing()?"DRAWING":"VIEW");
24}
25private:
26   CTouchDrawRoute drawRoute;
27};
  1. The CTouchDrawRoute class implements the interactive drawing of a route on the map. Drawing a route is done by entering the draw route mode, as opposed to the regular interactive map mode, which supports pan, zoom and rotate. Draw route mode is activated by a single click followed by a click and hold, for a multi-waypoint route; or a double click followed by a click and hold for a regular 2 waypoint route, with only departure and destination. Once in draw mode, tracing with a finger on the map, or dragging the mouse, causes waypoints along the path traced on the map to be generated and added to a list of waypoints, where the first one is the departure location and the last one is the destination, in the order in which they were added. This is the same as adding waypoints to a route manually, except in this case they are drawn. A route has at least 2 waypoints, one for the departure and one for the destination. When drawing the route is completed by lifting the pointer, the list of waypoints m_waypoints is used to compute a route as usual: gem::RoutingService().calculateRoute()

  1class CTouchDrawRoute
  2{
  3public:
  4
  5CTouchDrawRoute()
  6{
  7   m_isDrawing = false;
  8   m_isRouteRendered = false;
  9   m_is2WaypointRoute = false;
 10   m_touchDownTime = 0;
 11   m_lastXyAdded.x = -1;
 12   m_lastXyAdded.y = -1;
 13}
 14void addWaypoint(gem::Xy xy, gem::ETouchEvent state, std::shared_ptr<gem::MapView> mapView)
 15{
 16   long long int inputTime = gem::Time::getUniversalTime().asInt();
 17   if ( m_isDrawing )
 18   {
 19      if ( state == gem::ETouchEvent::TE_Down )
 20      {
 21         m_isDrawingStarted = true;
 22         m_touchDownTime = inputTime;
 23      }
 24      if ( m_isDrawingStarted )
 25      {
 26         // do not add the same waypoint more than once
 27         if ( xy.x != m_lastXyAdded.x || xy.y != m_lastXyAdded.y )
 28         {
 29            auto coord = mapView->transformScreenToWgs(xy);
 30            if ( !m_is2WaypointRoute
 31            || (m_is2WaypointRoute && (m_waypoints.size() < 1 || state == gem::ETouchEvent::TE_Up)))
 32            {
 33               m_waypoints.push_back(gem::Landmark("waypoint", { coord.getLatitude(), coord.getLongitude() }));
 34               fprintf(stderr,"added waypoint %d\n",(int)m_waypoints.size());
 35               m_lastXyAdded.x = xy.x;
 36               m_lastXyAdded.y = xy.y;
 37            }
 38         }
 39      }
 40      if ( m_isDrawingStarted && state == gem::ETouchEvent::TE_Up )
 41      {
 42         m_isDrawing = false;
 43         m_isDrawingStarted = false;
 44         if ( m_waypoints.size() > 1 )
 45         {
 46            ProgressListener calculateRouteListener;
 47            gem::RoutingService().calculateRoute(m_routes, m_waypoints, m_preferences, &calculateRouteListener);
 48            auto ret = WAIT_UNTIL(std::bind(&ProgressListener::IsFinished, &calculateRouteListener), 15000);
 49            if ( m_routes.size() > 0 )
 50            {
 51               m_isRouteRendered = true;
 52               m_is2WaypointRoute = false;
 53               mapView->centerOnRoute(m_routes[0], gem::Rect(), gem::Animation(gem::AnimationLinear, gem::ProgressListener(), 3000));
 54               mapView->preferences().routes().add(m_routes[0], true);
 55            }
 56         }
 57         else
 58         {
 59            int tdiff = m_touchDownTime > 0 ? (int)(inputTime - m_touchDownTime) : -1;
 60            if ( tdiff < MSEC_TOUCH_TRIGGER_DRAW && tdiff >= 0 )
 61            {
 62               //fast double click, activate 2-waypoint route, only departure and destination
 63               m_is2WaypointRoute = true;
 64               m_isDrawing = true;
 65               m_isDrawingStarted = true;
 66            }
 67         }
 68         m_touchDownTime = 0;
 69      }
 70   }
 71   else
 72   {
 73      // not drawing - detect a touch less than MSEC_TOUCH_TRIGGER_DRAW
 74      // to start drawing route
 75      if ( state == gem::ETouchEvent::TE_Down )
 76      {
 77         m_touchDownTime = inputTime;
 78      }
 79      else if ( state == gem::ETouchEvent::TE_Up )
 80      {
 81         int tdiff = m_touchDownTime > 0 ? (int)(inputTime - m_touchDownTime) : -1;
 82         if ( tdiff < MSEC_TOUCH_TRIGGER_DRAW && tdiff >= 0 )
 83         {
 84            //fast single click, activate DRAWING multi-waypoint route
 85            m_isDrawing = true;
 86            m_isDrawingStarted = false;
 87            m_lastXyAdded.x = -1;
 88            m_lastXyAdded.y = -1;
 89            m_waypoints.clear();
 90            m_routes.clear();
 91            m_isRouteRendered = false;
 92         }
 93         m_touchDownTime = 0;
 94      }
 95   }
 96}
 97bool isDrawing() const { return m_isDrawingStarted; }
 98bool isRouteRendered() const { return m_isRouteRendered; }
 99
100private:
101
102gem::Xy m_lastXyAdded;
103bool m_isDrawing;
104bool m_isDrawingStarted;
105bool m_isRouteRendered;
106bool m_is2WaypointRoute;
107long long int m_touchDownTime;
108gem::LandmarkList m_waypoints;
109gem::RouteList m_routes;
110gem::RoutePreferences m_preferences;
111};
  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