Find a route that reaches all stops without crossing any barriers.

Use case
You can define barriers to avoid unsafe areas, for example flooded roads, when planning the most efficient route to evacuate a hurricane zone. When solving a route, barriers allow you to define portions of the road network that cannot be traversed. You could also use this functionality to plan routes when you know an area will be inaccessible due to a community activity like an organized race or a market night.
In some situations, it is further beneficial to find the most efficient route that reaches all stops, reordering them to reduce travel time. For example, a delivery service may target a number of drop-off addresses, specifically looking to avoid congested areas or closed roads, arranging the stops in the most time-effective order.
How to use the sample
Click ‘Add stop’ to add stops to the route. Click ‘Add barrier’ to add areas that can’t be crossed by the route. Select ‘Find best sequence’ to allow stops to be re-ordered in order to find an optimal route. Select ‘Preserve first stop’ to preserve the first stop. Select ‘Preserve last stop’ to preserve the last stop.
How it works
- Construct a
RouteTaskwith the URL to a Network Analysis route service. - Get the default
RouteParametersfor the service, and create the desiredStops andPolygonBarriers. - Add the stops and barriers to the route’s parameters,
routeParameters.setStops(routeStops)androuteParameters.setPolygonBarriers(routeBarriers). - Set the
returnStopsandreturnDirectionstotrue. - If the user will accept routes with the stops in any order, set
findBestSequencetotrueto find the most optimal route. - If the user has a definite start point, set
preserveFirstStoptotrue. - If the user has a definite final destination, set
preserveLastStoptotrue. - Call
routeTask.solveRouteAsync(routeParameters)to get aRouteResult. - Get the first returned route by calling
routeResult.routes()[0]. - Get the geometry from the route to display the route to the map.
Relevant API
- DirectionManeuverListModel
- PolygonBarrier
- Route
- RouteParameters
- RouteResult
- RouteTask
- Stop
About the data
This sample uses an Esri-hosted sample street network for San Diego.
Tags
barriers, best sequence, directions, maneuver, network analysis, routing, sequence, stop order, stops
Sample Code
// [WriteFile Name=RouteAroundBarriers, Category=Routing]// [Legal]// Copyright 2020 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifdef PCH_BUILD#include "pch.hpp"#endif // PCH_BUILD
// sample headers#include "RouteAroundBarriers.h"
// ArcGIS Maps SDK headers#include "CompositeSymbol.h"#include "DirectionManeuverListModel.h"#include "Error.h"#include "GeometryEngine.h"#include "Graphic.h"#include "GraphicListModel.h"#include "GraphicsOverlay.h"#include "GraphicsOverlayListModel.h"#include "Map.h"#include "MapQuickView.h"#include "MapTypes.h"#include "PictureMarkerSymbol.h"#include "Point.h"#include "Polygon.h"#include "PolygonBarrier.h"#include "Polyline.h"#include "Route.h"#include "RouteResult.h"#include "RouteTask.h"#include "SimpleFillSymbol.h"#include "SimpleLineSymbol.h"#include "SimpleRenderer.h"#include "Stop.h"#include "SymbolTypes.h"#include "TextSymbol.h"#include "Viewpoint.h"
// Qt headers#include <QDir>#include <QFuture>#include <QUuid>
using namespace Esri::ArcGISRuntime;
namespace{const QUrl pinUrl("qrc:/Samples/Routing/RouteAroundBarriers/orange_symbol.png");const QUrl routeTaskUrl("https://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/Route");}
RouteAroundBarriers::RouteAroundBarriers(QObject* parent /* = nullptr */): QObject(parent), m_map(new Map(BasemapStyle::ArcGISStreets, this)), m_routeOverlay(new GraphicsOverlay(this)), m_stopsOverlay(new GraphicsOverlay(this)), m_barriersOverlay(new GraphicsOverlay(this)){ // create symbols for displaying the barriers and the route line m_barrierSymbol = new SimpleFillSymbol(SimpleFillSymbolStyle::DiagonalCross, Qt::red, this); SimpleLineSymbol* routeLineSymbol = new SimpleLineSymbol(SimpleLineSymbolStyle::Solid, Qt::blue, 3, this);
SimpleRenderer* routeRenderer = new SimpleRenderer(routeLineSymbol, this); m_routeOverlay->setRenderer(routeRenderer);
m_pinSymbol = new PictureMarkerSymbol(pinUrl, this); m_pinSymbol->setHeight(50); m_pinSymbol->setWidth(50); m_pinSymbol->setOffsetY(m_pinSymbol->height() / 2);
// create route task m_routeTask = new RouteTask(routeTaskUrl, this);}
RouteAroundBarriers::~RouteAroundBarriers() = default;
void RouteAroundBarriers::init(){ // Register the map view for QML qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView"); qmlRegisterType<RouteAroundBarriers>("Esri.Samples", 1, 0, "RouteAroundBarriersSample");}
MapQuickView* RouteAroundBarriers::mapView() const{ return m_mapView;}
// Set the view (created in QML)void RouteAroundBarriers::setMapView(MapQuickView* mapView){ if (!mapView || mapView == m_mapView) return;
m_mapView = mapView; m_mapView->setMap(m_map);
m_mapView->setViewpointAsync(Viewpoint(32.727, -117.1750, 40000));
// add the graphics overlays to the MapView m_mapView->graphicsOverlays()->append(m_routeOverlay); m_mapView->graphicsOverlays()->append(m_stopsOverlay); m_mapView->graphicsOverlays()->append(m_barriersOverlay);
connectRouteSignals(); m_routeTask->load(); emit mapViewChanged();}
void RouteAroundBarriers::connectRouteSignals(){ connect(m_routeTask, &RouteTask::doneLoading, this, [this](const Error& loadError) { if (!loadError.isEmpty()) { qDebug() << loadError.message() << loadError.additionalMessage(); } m_routeTask->createDefaultParametersAsync().then(this, [this](const RouteParameters& defaultParameters) { m_routeParameters = defaultParameters;
// set flags to return stops and directions m_routeParameters.setReturnStops(true); m_routeParameters.setReturnDirections(true); }); });
connect(m_mapView, &MapQuickView::mouseClicked, this, [this](QMouseEvent& e) { const Point clickedPoint = m_mapView->screenToLocation(e.position().x(), e.position().y()); if (m_addStops) { // add stop to list of stops const Stop stopPoint(clickedPoint); m_stopsList << stopPoint;
// create a marker symbol and graphics, and add the graphics to the graphics overlay TextSymbol* textSymbol = new TextSymbol(QString::number(m_stopsList.size()), Qt::white, 16, HorizontalAlignment::Center, VerticalAlignment::Bottom, this); textSymbol->setOffsetY(m_pinSymbol->height() / 2); CompositeSymbol* newStopSymbol = new CompositeSymbol(QList<Symbol*>{m_pinSymbol, textSymbol}, this);
Graphic* stopGraphic = new Graphic(clickedPoint, newStopSymbol, this); m_stopsOverlay->graphics()->append(stopGraphic);
createAndDisplayRoute(); } else if (m_addBarriers) { // add barrier to list const Polygon barrierPolygon = GeometryEngine::buffer(clickedPoint, 500); const PolygonBarrier barrier(barrierPolygon); m_barriersList << barrier;
Graphic* barrierGraphic = new Graphic(barrierPolygon, m_barrierSymbol, this); m_barriersOverlay->graphics()->append(barrierGraphic);
createAndDisplayRoute(); } });}
void RouteAroundBarriers::createAndDisplayRoute(){ if (m_stopsList.size() > 1) { // clear the previous route, if it exists if (m_routeOverlay) m_routeOverlay->graphics()->clear();
// clear the directions list if (m_directions) { delete m_directions; m_directions = nullptr; }
m_routeParameters.setStops(m_stopsList); m_routeParameters.setPolygonBarriers(m_barriersList); m_routeParameters.setFindBestSequence(m_findBestSequence); m_routeParameters.setPreserveFirstStop(m_preserveFirstStop); m_routeParameters.setPreserveLastStop(m_preserveLastStop);
m_routeTask->solveRouteAsync(m_routeParameters).then(this, [this](const RouteResult& routeResult) { if (routeResult.isEmpty()) return;
const Route route = std::as_const(routeResult).routes()[0]; const Geometry routeGeometry = route.routeGeometry(); Graphic* routeGraphic = new Graphic(routeGeometry, this); m_routeOverlay->graphics()->append(routeGraphic);
m_directions = route.directionManeuvers(this); emit directionsChanged(); }); }}
void RouteAroundBarriers::clearRouteAndGraphics(){ // clear stops from route parameters and stops list m_routeParameters.clearStops(); m_stopsList.clear();
// clear barriers m_routeParameters.clearPolygonBarriers(); m_barriersList.clear();
// clear directions list if (m_directions) { delete m_directions; m_directions = nullptr; emit directionsChanged(); }
// delete graphics from overlay, then clear graphics overlays for (GraphicsOverlay* overlay : *m_mapView->graphicsOverlays()) { if (overlay) { for (Graphic* graphic : *overlay->graphics()) { delete graphic; } overlay->graphics()->clear(); } }}
void RouteAroundBarriers::clearDirections(){ if (m_directions) { delete m_directions; m_directions = nullptr; emit directionsChanged(); }}// [WriteFile Name=RouteAroundBarriers, Category=Routing]// [Legal]// Copyright 2020 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifndef ROUTEAROUNDBARRIERS_H#define ROUTEAROUNDBARRIERS_H
// ArcGIS Maps SDK headers#include "PolygonBarrier.h"#include "RouteParameters.h"#include "Stop.h"
// Qt headers#include <QObject>
namespace Esri::ArcGISRuntime{class GraphicsOverlay;class Map;class MapQuickView;class PictureMarkerSymbol;class RouteTask;class SimpleFillSymbol;}
class QAbstractListModel;
Q_MOC_INCLUDE("MapQuickView.h")Q_MOC_INCLUDE("QAbstractListModel")
class RouteAroundBarriers : public QObject{ Q_OBJECT
Q_PROPERTY(Esri::ArcGISRuntime::MapQuickView* mapView READ mapView WRITE setMapView NOTIFY mapViewChanged) Q_PROPERTY(bool addStops MEMBER m_addStops NOTIFY addStopsChanged) Q_PROPERTY(bool addBarriers MEMBER m_addBarriers NOTIFY addBarriersChanged) Q_PROPERTY(bool findBestSequence MEMBER m_findBestSequence NOTIFY findBestSequenceChanged) Q_PROPERTY(bool preserveFirstStop MEMBER m_preserveFirstStop NOTIFY preserveFirstStopChanged) Q_PROPERTY(bool preserveLastStop MEMBER m_preserveLastStop NOTIFY preserveLastStopChanged) Q_PROPERTY(QAbstractListModel* directions MEMBER m_directions NOTIFY directionsChanged)
public: explicit RouteAroundBarriers(QObject* parent = nullptr); ~RouteAroundBarriers();
static void init();
Q_INVOKABLE void createAndDisplayRoute(); Q_INVOKABLE void clearRouteAndGraphics(); Q_INVOKABLE void clearDirections();
signals: void mapViewChanged(); void addStopsChanged(); void addBarriersChanged(); void findBestSequenceChanged(); void preserveFirstStopChanged(); void preserveLastStopChanged(); void directionsChanged();
private: Esri::ArcGISRuntime::MapQuickView* mapView() const; void setMapView(Esri::ArcGISRuntime::MapQuickView* mapView); void connectRouteSignals();
Esri::ArcGISRuntime::Map* m_map = nullptr; Esri::ArcGISRuntime::MapQuickView* m_mapView = nullptr; Esri::ArcGISRuntime::GraphicsOverlay* m_routeOverlay = nullptr; Esri::ArcGISRuntime::GraphicsOverlay* m_stopsOverlay = nullptr; Esri::ArcGISRuntime::GraphicsOverlay* m_barriersOverlay = nullptr; Esri::ArcGISRuntime::PictureMarkerSymbol* m_pinSymbol = nullptr; Esri::ArcGISRuntime::RouteParameters m_routeParameters; Esri::ArcGISRuntime::RouteTask* m_routeTask = nullptr; Esri::ArcGISRuntime::SimpleFillSymbol* m_barrierSymbol = nullptr;
QList<Esri::ArcGISRuntime::Stop> m_stopsList; QList<Esri::ArcGISRuntime::PolygonBarrier> m_barriersList; QAbstractListModel* m_directions = nullptr; bool m_addStops = true; bool m_addBarriers = false; bool m_findBestSequence = false; bool m_preserveFirstStop = false; bool m_preserveLastStop = false;};
#endif // ROUTEAROUNDBARRIERS_H// [WriteFile Name=RouteAroundBarriers, Category=Routing]// [Legal]// Copyright 2020 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
import QtQuickimport QtQuick.Controlsimport Esri.Samplesimport QtQuick.Layouts
Item { id: rootItem readonly property int checkBoxPadding: 20
ButtonGroup { buttons: buttonsRow.children }
// add a mapView component MapView { id: view anchors.fill: parent
Component.onCompleted: { // Set the focus on MapView to initially enable keyboard navigation forceActiveFocus(); }
ColumnLayout { spacing: 0 Layout.alignment: Qt.AlignTop
Rectangle { id: backBox z: 1 Layout.alignment: Qt.AlignLeft Layout.margins: 3 Layout.bottomMargin: 0 implicitWidth: grid.implicitWidth height: childrenRect.height color: "lightgrey"
GridLayout { id: grid anchors.horizontalCenter: parent.horizontalCenter rows: 4 columns: 1 rowSpacing: 10 columnSpacing: 2 Row { id: buttonsRow Layout.alignment: Qt.AlignHCenter spacing: 5 padding: 5 Button { id: stopButton text: "Add stop" checked: true highlighted: checked onClicked: { checked = true; } onCheckedChanged: { sampleModel.addStops = checked; } } Button { id: barrierButton text: "Add barrier" highlighted: checked; onClicked: { checked = true; } onCheckedChanged: { sampleModel.addBarriers = checked; } } }
Column { spacing: 2
CheckBox { id: bestSequenceBox text: "Find best sequence" onCheckedChanged: { sampleModel.findBestSequence = checked; sampleModel.createAndDisplayRoute(); } } CheckBox { id: firstStopBox text: "Preserve first stop" leftPadding: checkBoxPadding enabled: bestSequenceBox.checked onCheckedChanged: { sampleModel.preserveFirstStop = checked; sampleModel.createAndDisplayRoute(); } } CheckBox { id: lastStopBox text: "Preserve last stop" leftPadding: checkBoxPadding enabled: bestSequenceBox.checked onCheckedChanged: { sampleModel.preserveLastStop = checked; sampleModel.createAndDisplayRoute(); } } }
Row { Layout.alignment: Qt.AlignHCenter Button { id: resetButton text: "Reset" onClicked: { sampleModel.clearRouteAndGraphics(); sampleModel.clearDirections(); } } }
Row { Layout.alignment: Qt.AlignHCenter Button { text: "Hide directions" onClicked: { if (text === "Hide directions") { directionsView.delegate = blankDelegate; text = "Show directions"; } else { directionsView.delegate = directionDelegate; text = "Hide directions"; } } } } } }
// Create window for displaying the route directions Rectangle { id: directionWindow Layout.alignment: Qt.AlignBottom Layout.topMargin: 0 visible: true Layout.preferredWidth: backBox.width Layout.preferredHeight: 300 Layout.margins: 3 color: "lightgrey"
ScrollView { anchors.fill: parent
ListView { id: directionsView anchors { fill: parent margins: 5 } header: Text { text: "Directions:" font.pixelSize: 22 bottomPadding: 8 }
// set the model to the DirectionManeuverListModel returned from the route model: sampleModel.directions delegate: directionDelegate } } } } }
Component { id: blankDelegate Rectangle { width: parent.width height: 35 color: directionWindow.color } }
Component { id: directionDelegate Rectangle { id: rect width: parent ? parent.width : 0 height: 35 color: directionWindow.color
Rectangle { anchors { top: parent.top; left: parent.left; right: parent.right; topMargin: -8 leftMargin: 20 rightMargin: 20 } color: "darkgrey" height: 1 }
Text { text: directionText anchors { fill: parent leftMargin: 5 } elide: Text.ElideRight font.pixelSize: 14 } } }
// Declare the C++ instance which creates the map etc. and supply the view RouteAroundBarriersSample { id: sampleModel mapView: view }}// [Legal]// Copyright 2020 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
// sample headers#include "RouteAroundBarriers.h"
// ArcGIS Maps SDK headers#include "ArcGISRuntimeEnvironment.h"
// Qt headers#include <QDir>#include <QGuiApplication>#include <QQmlApplicationEngine>
// Platform specific headers#ifdef Q_OS_WIN#include <Windows.h>#endif
int main(int argc, char *argv[]){ Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setUseLegacyAuthentication(false); QGuiApplication app(argc, argv); app.setApplicationName(QString("RouteAroundBarriers"));
// Use of ArcGIS location services, such as basemap styles, geocoding, and routing services, // requires an access token. For more information see // https://links.esri.com/arcgis-runtime-security-auth.
// The following methods grant an access token:
// 1. User authentication: Grants a temporary access token associated with a user's ArcGIS account. // To generate a token, a user logs in to the app with an ArcGIS account that is part of an // organization in ArcGIS Online or ArcGIS Enterprise.
// 2. API key authentication: Get a long-lived access token that gives your application access to // ArcGIS location services. Go to the tutorial at https://links.esri.com/create-an-api-key. // Copy the API Key access token.
const QString accessToken = QString("");
if (accessToken.isEmpty()) { qWarning() << "Use of ArcGIS location services, such as the basemap styles service, requires" << "you to authenticate with an ArcGIS account or set the API Key property."; } else { Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setApiKey(accessToken); }
// Initialize the sample RouteAroundBarriers::init();
// Initialize application view QQmlApplicationEngine engine; // Add the import Path engine.addImportPath(QDir(QCoreApplication::applicationDirPath()).filePath("qml"));
#ifdef ARCGIS_RUNTIME_IMPORT_PATH_2 engine.addImportPath(ARCGIS_RUNTIME_IMPORT_PATH_2);#endif
// Set the source engine.load(QUrl("qrc:/Samples/Routing/RouteAroundBarriers/main.qml"));
return app.exec();}// Copyright 2020 Esri.
// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.
import QtQuick.Controlsimport Esri.Samples
ApplicationWindow { visible: true width: 800 height: 600
RouteAroundBarriers { anchors.fill: parent }}