Get driving directions

This topic walks you through the development of an application using the ArcGIS Runtime SDK for Qt. The application provides users with the ability to specify a route start point and end point on a basemap and determine an optimal route between the selected points on any device to which the application is deployed.

Developing a Qt map application

Complete the following steps to create an ArcGIS Runtime SDK for Qt mapping application using an application template:

  1. In a location of your choice, create a folder named RoutingExercise.
  2. Start Qt Creator and create a new project using the ArcGIS Runtime Qt Widgets application template.
  3. Specify the following project name to ensure consistency with relative paths in steps you will encounter later in this exercise: PointToPointRouting_Example.
  4. For the location, browse to the RoutingExercise folder you created earlier.
  5. Click Next, Next again, and Finish to create the project.

This topic shows how to use local server with a geoprocessing package. For routing, you also have the option to perform route tasks without using local server. See the samples Local Routing and Route (online) in the Qt Sample App for an example of that workflow.

Include a basemap as a local tiled layer

Complete the following steps to modify your application's code within Qt Creator to include a tile package as a basemap layer. When the runtime is initialized, a service is created locally on the device running your application to consume the contents of the tile package providing users with a basemap without requiring an on-line connection to an external server.

  1. In Qt Creator, double-click PointToPointRouting_Example.h to display the code.
  2. In the file PointToPointRouting_Example.h, uncomment the line that declares the member variable m_tiledServiceLayer.
  3. Double-click PointToPointRouting_Example.cpp to display the code file.
  4. In the file PointToPointRouting_Example.cpp, uncomment #include "ArcGISTiledMapServiceLayer.h".
  5. Locate the line of code starting with m_tiledLayer = EsriRuntimeQt::ArcGISLocalTiledLayer. The entire Local Tiled Basemap Layer code block is commented-out in the template code, as shown in the following sample code:
    //// Local Tiled Basemap Layer using: sdk/samples/data/tpks/Topographic.tpk
      ////QString tiledBaseMapLayer = pathSampleData + "tpks" + QDir::separator() + "Topographic.tpk";
      ////m_tiledLayer = new EsriRuntimeQt::ArcGISLocalTiledLayer(tiledBaseMapLayer, this);
      ////m_map->addLayer(m_tiledLayer);
  6. Change the system path in the code block to address your local copy of the SanFrancisco.tpk tile package, found with the sample data installed when you installed the software development kit (SDK).
  7. Uncomment the code block to include the basemap layer in your application as shown in the following sample code:
    // Local Tiled Basemap Layer using: sdk/samples/data/tpks/SanFrancisco.tpk
        QString tiledBaseMapLayer = pathSampleData + "tpks" + QDir::separator() + "SanFrancisco.tpk";
        m_tiledLayer = new EsriRuntimeQt::ArcGISLocalTiledLayer(tiledBaseMapLayer, this);
        m_map->addLayer(m_tiledLayer);
    Note:

    The previous code sample configures your application to use local data for its basemap. Using the fully-qualified path to the tile package in the ArcGIS Runtime SDK for Qt sample data will work for the purposes of this exercise; however, to license and deploy this application, you need to change this to a path relative to your application's executable and deploy the tile package along with your compiled application's executable.

Change the extent of the basemap

To make it easier for the user to select start and end points of thier route change the extent of the map to a smaller area of San Francisco.

  1. Find the onMapReady slot. The entire method is commented out, as is code to set the extent.
    ////void PointToPointRouting_Example::onMapReady()
    ////{
    ////    // set the map extent
    ////    m_map->setExtent(EsriRuntimeQt::Envelope(xmin, ymin, xmax, ymax, m_map->spatialReference()));
    ////}
  2. Uncomment the method, and then uncomment the line of code that sets the extent and change xmin, ymin, xmax, ymax to -122.520, 37.8365, -122.3023, 37.6985 as shown in the following sample code:
    void PointToPointRouting_Example::onMapReady()
    {
        // set the map extent
        m_map->setExtent(EsriRuntimeQt::Envelope(-122.520, 37.8365, -122.3023 , 37.6985, m_map->spatialReference()));
    }
  3. Uncomment the line of code that connects to the onMapReady slot
    connect(m_map, SIGNAL(mapReady()), this, SLOT(onMapReady()));
  4. In PointToPointRouting_Example.h uncomment the line of code that defines the onMapReady slot
    void onMapReady();

Building the application's user interface

The user interface (UI) for your point-to-point routing application will have three buttons. One will allow the user to specify the route's starting point; the second will be used to establish the route's end point, and the third button will execute the geoprocessing task to solve the problem and identify an optimal route between the designated points.

First you'll define the buttons. Each button will require an event handler that will perform an action when the button is clicked. After you define the buttons, you'll modify the code to define a click event binding and create a stub for each click event handler. You'll also set an initial enabled state for the Solve button within the button's declaration.

Complete the following steps to define the UI for your application:

  1. Open the PointToPointRouting_Example.h file and add member variables after the m_map member variable for the start point, end point and solve route buttons. Also add member variables to keep track of the user input for the start and end points. Be sure to add an include statement for QPushButton in the includes section of your code. Your code should look similar to the following:
    ...
    #include <QPushButton>
    ...
    private:
        EsriRuntimeQt::Map* m_map;
        EsriRuntimeQt::MapGraphicsView* m_mapGraphicsView;
       
        // UI elements
        QPushButton* m_startPointBtn;
        QPushButton* m_endPointBtn;
        QPushButton* m_solveBtn;
    
        // Bools to track user input
        bool m_addingStartPoint;
        bool m_addingEndPoint;
  2. Each of these buttons will be hooked up to slots. Create the slots for the buttons after the existing onMapReady slot. Your code should look similar to the following code snippet:
    public slots:
      void onMapReady();
      void onAddStartPoint();
      void onAddEndPoint();
      void onSolve();
  3. Open the PointToPointRouting_Example.cpp file and add the slots you just defined after the existing onMapReady slot. Add the Booleans to keep track of the user input to the onAddStartPoint and onAddEndPoint slots. Leave the onSolve slot empty for now. We will define this slot later.
    void PointToPointRouting_Example::onAddStartPoint()
    {
        m_addingStartPoint = true;
        m_addingEndPoint = false;
    }
    
    void PointToPointRouting_Example::onAddEndPoint()
    {
        m_addingStartPoint = false;
        m_addingEndPoint = true;
    }
    
    void PointToPointRouting_Example::onSolve()
    {
        // We will define this slot later...
    }
  4. You will need to initialize the variables to false when starting the application as shown in the code below:
    PointToPointRouting_Example::PointToPointRouting_Example(QWidget *parent)
        : QMainWindow(parent),
          m_addingStartPoint(false),
          m_addingEndPoint(false)
    {
  5. Add the code to create the start button and connect to the onAddStartPoint slot. Place this after the line of code m_map->setWrapAroundEnabled(false);. The code should look similar to the following:
    // create UI elements
        QWidget* widget = new QWidget();
    
        m_startPointBtn = new QPushButton("Start Point");
        m_startPointBtn->setObjectName("m_startPointBtn");
        m_startPointBtn->setStyleSheet("QPushButton#m_startPointBtn { background-color: white; color: #000; } QPushButton#m_startPointBtn:pressed {background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #dadbde, stop: 1 #f6f7fa);}");
    
        connect(m_startPointBtn, SIGNAL(clicked()), this, SLOT(onAddStartPoint()));
  6. Repeat this last step for the End Point and the Solve buttons. Your code should look similar to the following:
    m_endPointBtn = new QPushButton("End Point");
        m_endPointBtn->setObjectName("m_endPointBtn");
        m_endPointBtn->setStyleSheet("QPushButton#m_endPointBtn { background-color: white; color: #000; } QPushButton#m_endPointBtn:pressed {background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,stop: 0 #dadbde, stop: 1 #f6f7fa);}");
    
        connect(m_endPointBtn, SIGNAL(clicked()), this, SLOT(onAddEndPoint()));
    
        m_solveBtn = new QPushButton("Solve");
        m_solveBtn->setObjectName("m_solveBtn");
        m_solveBtn->setStyleSheet("QPushButton#m_solveBtn { background-color: white; color: #000; }");
    
        connect(m_solveBtn, SIGNAL(clicked()), this, SLOT(onSolve()));
  7. Set the start point and end point buttons to be enabled when the user starts the application. Set the solve button to be disabled. we'll enable this button when both a start point and an end point have been defined by the user. your code should look similar to the following;
    m_startPointBtn->setEnabled(true);
        m_endPointBtn->setEnabled(true);
        m_solveBtn->setEnabled(false);
  8. Create a layout and add your buttons to the application as in the following code. Be sure to add the include statements for QVBoxLayout and QGraphicsProxyWidget to the include section of your code.
    QVBoxLayout* layout = new QVBoxLayout();
        layout->addWidget(m_startPointBtn);
        layout->addWidget(m_endPointBtn);
        layout->addWidget(m_solveBtn);
        widget->setFixedSize(100,150);
        widget->setLayout(layout);
        widget->setPalette(QPalette(QPalette::Base));
    
        QGraphicsProxyWidget *proxy = m_mapGraphicsView->scene()->addWidget(widget);
        proxy->setPos(7, 7);
        proxy->setAcceptedMouseButtons(Qt::LeftButton);
        proxy->setFlag(QGraphicsItem::ItemIsSelectable, false);
        proxy->setOpacity(0.75);

Add the graphics for the start point, end point and route

A graphics layer provides your application's users with a set of simple vector graphics with which they can interact. The route start point, end point, and the route itself will all be rendered as simple vector graphics. Complete the following steps to modify your application's code within Qt Creator to include a graphics layer:

  1. Open the PointToPointRouting_Example.h. Create a member variable to keep track of the last mouse coordinate. Put this member variable after the member variable for the tiled layer.
    EsriRuntimeQt::ArcGISLocalTiledLayer* m_tiledLayer;
      QPointF m_lastMouseCoord;
  2. After either the start or the end buttons have been selected, a user can click on the map to define the start or end point. To do this in code we must set up the onMousePress and onMouseRelease slots. Add the public slots for onMousePress and onMouseRelease. Add these slots after the onSolve slot from the previous section. Your code will look similar to the following:
    ...  
        void onSolve();
        void onMousePress(QMouseEvent& event);
        void onMouseRelease(QMouseEvent& event);
  3. In the PointToPointRouting_Example.cpp add the connections to the slot at the end of the constructor's code block. The code should look similar to the following:
    // connect mouse press signals
        connect(m_map, SIGNAL(mousePress(QMouseEvent&)), this, SLOT(onMousePress(QMouseEvent&)));
        connect(m_map, SIGNAL(mouseRelease(QMouseEvent&)), this, SLOT(onMouseRelease(QMouseEvent&)));
    }
  4. Define these slots after the empty onSolve slot created in the last section. The code should look similar to the following:
    void PointToPointRouting_Example::onMousePress(QMouseEvent& event)
    { 
    }
    
    void PointToPointRouting_Example::onMouseRelease(QMouseEvent& event)
    {
    }
  5. On the MousePress event, store the location of the mouse click. Your code should look similar to the following:
    void PointToPointRouting_Example::onMousePress(QMouseEvent& event)
    {
        // store the location of the mouse click
        if (event.button() == Qt::LeftButton)
            m_lastMouseCoord = QPointF(event.pos().x(), event.pos().y());
    }
  6. On the MouseRelease event, ensure it was not a left mouse button release. Your code should look similar to the following:
    void PointToPointRouting_Example::onMouseRelease(QMouseEvent& event)
    {
        if (event.button() != Qt::LeftButton)
            return;
    }
  7. Ensure that the user wasn't trying to pan or was trying to click another button on your user interface. Your code should look similar to the following:
    QPointF scenePosition = event.pos();
    
        // make sure the mouse didn't move - if so it was a pan so do nothing
        if (m_lastMouseCoord.x() != scenePosition.x() || m_lastMouseCoord.y() != scenePosition.y())
            return;
    
        // Clicked in item box
        if (m_mapGraphicsView->scene()->itemAt(scenePosition, QTransform()))
            return;
  8. Get the point and pass it into the function that adds it to the display. Your code should look similar to the following:
    EsriRuntimeQt::Point pt = m_map->toMapPoint(scenePosition.x(), scenePosition.y());
        if (m_addingStartPoint)
        {
            addStartPoint(pt);
        }
        else if (m_addingEndPoint)
        {
            addEndPoint(pt);
        }
  9. Uncomment the include statement for point and simple marker symbol, and add the include statements for the geometry engine and simple line symbol.
    #include "Point.h"
    #include "GeometryEngine.h"
    #include "SimpleMarkerSymbol.h"
    #include "SimpleLineSymbol.h"
  10. In the PointToPointRouting_Example.h define the addStartPoint and addEndPoint functions. Add this before the declarion of the map member variable. Your code should look similar to below:
    void addStartPoint(EsriRuntimeQt::Point mapPoint);
        void addEndPoint(EsriRuntimeQt::Point mapPoint);
      private: 
        EsriRuntimeQt::Map* m_map;
  11. Define a member variable for a startID, endID and routeID after the Booleans to keep track of the start and end points. Your code should look similar to the following:
    bool m_addingStartPoint;
        bool m_addingEndPoint;
    
        int m_startID;
        int m_endID;
        int m_routeID;
  12. Define member variables for the graphics layer, start point, end point and the route. Place this after the member variable for the tiled layer and before the member variable for the last mouse coordinate. Don't forget to add the #include "GraphicsLayer.h" statement in the includes section of the code. Your code should look similar to the following:
    ...
    #include  "GraphicsLayer.h"
    ...
        EsriRuntimeQt::ArcGISLocalTiledLayer* m_tiledLayer;
        EsriRuntimeQt::GraphicsLayer* m_graphicsLayer;
        EsriRuntimeQt::Graphic* m_grStartPoint;
        EsriRuntimeQt::Graphic* m_grEndPoint;
        EsriRuntimeQt::Graphic* m_grRouteLine;
        QPointF m_lastMouseCoord;
  13. Back in the PointToPointRouting_Example.cpp, initialize the variables to -1 when starting the application as shown in the code below:
    PointToPointRouting_Example::PointToPointRouting_Example(QWidget *parent)
        : QMainWindow(parent),
          m_addingStartPoint(false),
          m_addingEndPoint(false),
          m_startID(-1),
          m_endID(-1),
          m_routeID(-1)
    {
  14. Define the addStartPoint and addEndPoint functions after the onMouseRelease slot. Don't forget to add the #include "GeometryEngine.h" statement in the includes section of the code.Your code should look as follows:
    void PointToPointRouting_Example::addStartPoint(EsriRuntimeQt::Point mapPoint)
    {
        if (EsriRuntimeQt::GeometryEngine::within(mapPoint, m_map->extent()))
        {
            EsriRuntimeQt::SimpleMarkerSymbol purpleDiamond(QColor(102, 0, 187), 15, EsriRuntimeQt::SimpleMarkerSymbolStyle::Diamond);
            purpleDiamond.setOutline(EsriRuntimeQt::SimpleLineSymbol(QColor("black"), 1.0, EsriRuntimeQt::SimpleLineSymbolStyle::Solid));
    
            if (m_startID != -1)
            {
                m_graphicsLayer->removeGraphic(m_startID);
            }
    
            m_grStartPoint = new EsriRuntimeQt::Graphic(mapPoint, purpleDiamond);
            m_startID = m_graphicsLayer->addGraphic(static_cast<EsriRuntimeQt::Graphic*>(m_grStartPoint->clone()));
        }
    }
    void PointToPointRouting_Example::addEndPoint(EsriRuntimeQt::Point mapPoint)
    {
        if (EsriRuntimeQt::GeometryEngine::within(mapPoint, m_map->extent()))
        {
            EsriRuntimeQt::SimpleMarkerSymbol purpleDiamond(QColor(0, 102, 187), 15, EsriRuntimeQt::SimpleMarkerSymbolStyle::Diamond);
            purpleDiamond.setOutline(EsriRuntimeQt::SimpleLineSymbol(QColor("black"), 1.0, EsriRuntimeQt::SimpleLineSymbolStyle::Solid));
    
            if (m_endID != -1)
            {
                m_graphicsLayer->removeGraphic(m_endID);
            }
            m_grEndPoint = new EsriRuntimeQt::Graphic(mapPoint, purpleDiamond);
            m_endID = m_graphicsLayer->addGraphic(static_cast<EsriRuntimeQt::Graphic*>(m_grEndPoint->clone()));
        }
    }
  15. Add the graphics layer to the map. Find the section of code in the constructor that defines a graphics layer. It should look similar to the following:
    // Graphics Layer
        /*
        EsriRuntimeQt::Point point1(0, 0);
        EsriRuntimeQt::SimpleMarkerSymbol redCircle(Qt::red, 8, EsriRuntimeQt::SimpleMarkerSymbolStyle::Circle);
        EsriRuntimeQt::Graphic graphic1(point1, redCircle);
        m_graphicsLayer = new EsriRuntimeQt::GraphicsLayer(this);
        m_graphicsLayer->addGraphic(graphic1);
        m_map->addLayer(m_graphicsLayer);
        */
  16. Uncomment the last two lines. We will not be using the other lines of commented code, so remove them. You can also slightly modify the comment for what this section of code is doing. Your code should now look simlar to the following:
    // Add the graphics layer to the map
        m_graphicsLayer = new EsriRuntimeQt::GraphicsLayer(this);
        m_map->addLayer(m_graphicsLayer);
  17. Enable the solve button when both a start point and end point have been added to the map. Do this at the end of the onMouseRelease slot. Your code should look similar to the following:
    if (m_startID != -1 && m_endID != -1)
        m_solveBtn->setEnabled(true);
  18. Run the application. Add the start and end points to the map. Notice that the application only allows you to have one start and one end point. Notice also that the Solve button becomes enabled once both the start point and end point have been added to the map.

Create a LocalGeoprocessingService

Applications you develop using the ArcGIS Runtime SDK for Qt use services to retrieve data from online sources or packages you prepare using ArcGIS for Desktop. Packages spin up services locally. These services are instantiated at run time on the device running your application.

Complete the following steps to add code to the project's PointToPointRouting_Example.cpp code file. The code you're adding declares a local geoprocessing service, provides a path to a local geoprocessing package containing an implemented point-to-point routing model, and starts the service asynchronously.

  1. Open up the PointToPointRouting_Example.h. Add the include files for the local geoprocessing service and the geoprocessor. Also add member variables for the local geoprocessing service, the geoprocessor and the spatial reference of the map. Place these below the member variable for route graphic and above the member variable for the last mouse coordinate. Also remember to add the include statements for the Geoprocessor and the LocalGeoprocessingService. The code should look similar to the following:
    #include "LocalGeoprocessingService.h"
        #include "Geoprocessor.h"
    
        ...
    
        EsriRuntimeQt::Graphic* m_grRouteLine;
        EsriRuntimeQt::LocalGeoprocessingService* m_routingService;
        EsriRuntimeQt::Geoprocessor* m_geoprocessor;
        EsriRuntimeQt::SpatialReference m_srMap;
    
        QPointF m_lastMouseCoord;
  2. Create a boolean to keep track of whether the routing service is active or not. Place this code in the Boolean section of the member variables after the m_addingendPoint member variable.The code should look similar to the following:
    // Bools to track user input
        bool m_addingStartPoint;
        bool m_addingEndPoint;
        bool m_bRouteActive;
  3. Create a slot for successful service creation and one for if the service fails. Also create slots for the completion and failure of the GP executions. Place this code after the slot for the onSolve. Your code should look similar to the following:
    void onServiceCreationSuccess(QString url, QString name);
        void onServiceCreationFailure(QString name);
        void onGpExecuteComplete(const QList<EsriRuntimeQt::GPParameter*> &result);
        void onGpError(EsriRuntimeQt::ServiceError error);
  4. In PointToPointRouting_Example.cpp, create the routing service that uses the geoprocessing package route.gpk included in the sample data installed with the SDK. Declare the LocalGeoprocessingService with a GPServiceType of Execute . This is the recommended service type for services that are known to execute quickly (for example, less than 10 seconds). This configuration will handle service requests synchronously blocking the application from executing further tasks. Place this code just after the code to set the variable pathSampleData. Your code should look similar to the following:
    m_routingService = new EsriRuntimeQt::LocalGeoprocessingService(
              pathSampleData + "gpks" + QDir::separator() + 
              "Routing" + QDir::separator() + "Route.gpk" );
        m_routingService->setServiceType(EsriRuntimeQt::GPServiceType::Execute);
  5. Create the slots for the service creation success. Point the geoprocessor to the geoprocessing URL and set the spatial reference. Connect to the geoprocessing Execute and Error slots. You might want to disconnect first to ensure you are making a clean connection. Once a successful connection has been created, set the active route member variable to true. Your code should look similar to the following code:
    void PointToPointRouting_Example::onServiceCreationSuccess(QString url, QString name)
    {
        Q_UNUSED(url);
        Q_UNUSED(name);
        // create a Geoprocessor that points to the geoprocessing service URL
        QString urlService(m_routingService->urlGeoprocessingService());
        m_geoprocessor = new EsriRuntimeQt::Geoprocessor( urlService + "/Route");
        m_geoprocessor->setProcessSR(m_srMap);
        m_geoprocessor->setOutSR(m_srMap);
    
        connect(m_geoprocessor, SIGNAL(gpExecuteComplete(const QList<EsriRuntimeQt::GPParameter*>&)), this, SLOT(onGpExecuteComplete(const QList<EsriRuntimeQt::GPParameter*>&)));
        connect(m_geoprocessor, SIGNAL(gpError(EsriRuntimeQt::ServiceError)), this, SLOT(onGpError(EsriRuntimeQt::ServiceError)));
    
        m_bRouteActive = true;
    }
  6. Create a slot for the onGpExecuteComplete. Grab all of the output parameters . Define the symbol for the resulting route and add the it to the map. You will define a Polyline, so add #include "Polyline.h" to the include statement section of your code. Your code should look similar to the following:
    void PointToPointRouting_Example::onGpExecuteComplete(const QList<EsriRuntimeQt::GPParameter*> &result)
    {
        foreach (EsriRuntimeQt::GPParameter* outputParameter, result)
        {
            EsriRuntimeQt::GPFeatureRecordSetLayer* gpLayer = qobject_cast<EsriRuntimeQt::GPFeatureRecordSetLayer*>(outputParameter);
            if (!gpLayer)
                continue;
    
            // get all the graphics and add them to the graphics layer.
            foreach (EsriRuntimeQt::Graphic* resultGraphic, gpLayer->graphics())
            {
                EsriRuntimeQt::Graphic* grRouteLine = new EsriRuntimeQt::Graphic(resultGraphic->geometry(), EsriRuntimeQt::SimpleLineSymbol(QColor("black"), 2.0, EsriRuntimeQt::SimpleLineSymbolStyle::Dash));
                // add to the graphics layer
                m_routeID = m_graphicsLayer->addGraphic(grRouteLine);
                m_graphicsLayer->sendToBack(m_routeID);
            }
        }
    }
  7. Create a slot for the onGpError. Bring up an error message saying that the execution failed. Your code should look similar to the following:
    void PointToPointRouting_Example::onGpError(EsriRuntimeQt::ServiceError error)
    {
        qWarning() << QString("Service Error, code: %1, message: %2, details: %3").arg(QString::number(error.code()), error.message(), error.details());
    }
  8. Create the slot for the service creation failure. Set the active route member variable to false and pop up a message saying that the service failed to start. Your code should look similar to the following:
    void PointToPointRouting_Example::onServiceCreationFailure(QString name)
    {
        m_bRouteActive = false;
    
        qWarning() << name + " failed to start";
        qWarning() << m_routingService->error().what();
    }
  9. Connect to the service creation and service failure slots you just created and start the service. Place this code after the connection to the onSolve slot. Your code should look similar to the following:
    connect(m_routingService, SIGNAL(serviceCreationSuccess(QString,QString)), this, SLOT(onServiceCreationSuccess(QString,QString)));
        connect(m_routingService, SIGNAL(serviceCreationFailure(QString)), this, SLOT(onServiceCreationFailure(QString)));
        m_routingService->start();
  10. Find the onSolve slot you created earlier in this exercise. First check to see if both a start point and end point have been added to the map. Verify that there is not already a route present on the map. If there is, remove it. Finally, execute the geoprocessing service by passing in the start and end point parameters and pass the result to the onGpExecuteComplete slot you created earlier. Your code uses QMessageBox, so add #include <QMessageBox> to the include statement section of your code. Your code should look similar to the following:
    #include <QMessageBox>
    ...
    void PointToPointRouting_Example::onSolve()
    {
      if ((m_endID == -1)|| (m_startID == -1))
      {
        QMessageBox::warning(this, "Warning", "You need to add at least one start point and one end point to solve a route.", QMessageBox::Ok);
        return;
      }
      // Reset the drawing bools
      m_addingStartPoint = false;
      m_addingEndPoint = false;
      if (m_routeID != -1)
      {
        m_graphicsLayer->removeGraphic(m_routeID);
      }
    
      QList<EsriRuntimeQt::GPParameter*> parameters;
    
      EsriRuntimeQt::GPFeatureRecordSetLayer* param = new EsriRuntimeQt::GPFeatureRecordSetLayer("Input_Locations", this);
      param->setGeometryType(EsriRuntimeQt::GeometryType::Point);
      param->setSpatialReference(m_srMap);
      param->addGraphic(m_grStartPoint);
      param->addGraphic(m_grEndPoint);
    
      parameters.append(param);
    
      m_geoprocessor->execute(parameters);
    }

Run your application and examine the task parameters

If you've completed each step in this exercise, you'll have a functional point-to-point application that can be licensed and deployed to any device supporting applications developed for Qt.

  1. Run your application from within Qt Creator.
  2. Zoom to the center of San Francisco and click the Start Point button.
  3. Click a point on the map display to designate a route start point.
  4. Click the End Point button and select a route end point on the map.
  5. Click the Solve button and wait a few seconds for the geoprocessing task to complete.

The geoprocessing task executes and the resulting route is added to the map.

Examine the LocalGeoprocessingService task parameters

Based on the development you completed in this exercise, your application is creating services and executing these services locally on the device on which your application is running. This is a fundamental aspect of the ArcGIS Runtime architecture.

Complete the following steps to examine some of the messages generated when your application is run:

  1. From within Qt Creator, run your application in the Qt Creator debugger.
  2. Open the Debugger's Output window and scan the output for a message similar to the following:

    Message : Server version : 10.n.n.

    Message : Server listening on : http://127.0.0.1:50001/XBMvCW/arcgis/rest

    Note:

    The token between the IP address and the port number (for example, nnn.nn.nn.nn:nnnnn) and the /arcgis/rest/services address will be different each time you run your application.

  3. Copy the URL associated with the running application and paste it into a browser window.

    A directory of the available REST services similar to the following screen shot appears:

  4. Click the route (GPServer) link.
  5. Click the Route task link to the left of Execute Job.
  6. Click the JSON link at the top of the services page and study the GPFeatureRecordSetLayer parameters.

    Notice that the Route service has two parameters. The input parameters are feature points with X and Y values in decimal degrees (consistent with the input record set's spatial reference). The output is also a feature record set, but its geometry is of type polyline and its fields include information on the first stop, last top, number of stops and total travel time. This information is computed by the model running within the geoprocessing package being consumed by the geoprocessing service instantiated by your application.

  7. Close your application, but leave your Internet browser open.
  8. At the top of the REST Directory page, click the Home link in the REST Directory path: Home >> services >> route (GPServer) >> Route
    Note:

    You cannot reach the REST Directory through the URL once the application is no longer running. Each instance of your application instantiates a very small, very efficient Web server that interacts with your application through the HTTP and REST protocols. When your application is terminated, so is its instance of the runtime, its dedicated Web server, and any services that were started to support the runtime application.