Solve the routing problem of finding service areas for one or more facilities. Barriers in the form of points, lines, and polygons can be placed across the line network to affect the calculation of optimal service areas.
Service area (online)
// Copyright 2015 ESRI
//
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
//
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
//
// See the Sample code usage restrictions document for further information.
//
#ifndef SERVICE_AREA_SAMPLE_H
#define SERVICE_AREA_SAMPLE_H
#include "rtsample.h"
#include "ServiceAreaTask.h"
#include "SimpleFillSymbol.h"
#include "GraphicsLayer.h"
#include "Polyline.h"
#include "Polygon.h"
#include <QGraphicsItem>
namespace Ui {
class offset_sample;
}
class service_area_sample_mouse_helper;
class service_area_sample : public RTSample
{
Q_OBJECT
public:
explicit service_area_sample(const QString& title, const QString& categoryList, const QString& shortDesc, const QString& longDesc, const QString& thumbnail);
virtual ~service_area_sample();
virtual void run();
void doubleClickUpdate();
void hoverMoveUpdate(QGraphicsSceneHoverEvent *event);
public slots:
void onMapReady();
void onMousePress(QMouseEvent& event);
void onMouseRelease(QMouseEvent& event);
void onAddFacilities();
void onAddPointBarriers();
void onAddPolylineBarriers();
void onAddPolygonBarriers();
void onSolve();
void onServiceAreaTaskComplete(EsriRuntimeQt::ServiceAreaResult* result);
void onReset();
private:
void reset();
int randInt(int low, int high);
void drawPoint(const EsriRuntimeQt::Point& p);
void drawLine(const EsriRuntimeQt::Point& p);
void drawPolygon(const EsriRuntimeQt::Point& p);
void addFacilities(const EsriRuntimeQt::Point& mapPoint) ;
EsriRuntimeQt::SimpleFillSymbol createBarrierSymbol();
EsriRuntimeQt::GraphicsLayer* m_graphicsLayer;
EsriRuntimeQt::Point m_points;
EsriRuntimeQt::Polygon m_polygon;
EsriRuntimeQt::Polyline m_polyline;
QList<EsriRuntimeQt::Graphic*> m_facilities;
QList<EsriRuntimeQt::Graphic*> m_pointBarriers;
QList<EsriRuntimeQt::Graphic*> m_polylineBarriers;
QList<EsriRuntimeQt::Graphic*> m_polygonBarriers;
service_area_sample_mouse_helper *m_mouseHelper;
EsriRuntimeQt::ServiceAreaTask* m_task;
QPushButton* m_addFacilitiesBtn;
QPushButton* m_addPointBarriersBtn;
QPushButton* m_addPolylineBarriersBtn;
QPushButton* m_addPolygonBarriersBtn;
QPushButton* m_solveBtn;
QPushButton* m_resetBtn;
QPointF m_lastMouseCoord;
// Bools to track user input
bool m_addingFacilities;
bool m_addingPointBarriers;
bool m_addingPolylineBarriers;
bool m_addingPolygonBarriers;
bool m_polygonStarted;
bool m_polylinestarted;
bool m_polygonEnded;
bool m_polylineEnded;
int m_polygonGraphicId;
int m_polylineGraphicId;
};
class service_area_sample_mouse_helper : public QGraphicsItem
{
Q_INTERFACES(QGraphicsItem)
public:
QRectF boundingRect() const
{
return QRect(0, 0, m_sample->map()->width(), m_sample->map()->height());
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{ // No need to paint
Q_UNUSED(painter)
Q_UNUSED(option)
Q_UNUSED(widget)
}
service_area_sample_mouse_helper(service_area_sample *sa)
{
m_sample = sa;
setAcceptHoverEvents(true);
}
private:
service_area_sample_mouse_helper() {}
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
service_area_sample *m_sample;
};
#endif // SERVICE_AREA_SAMPLE_H
// Copyright 2015 ESRI
//
// All rights reserved under the copyright laws of the United States
// and applicable international laws, treaties, and conventions.
//
// You may freely redistribute and use this sample code, with or
// without modification, provided you include the original copyright
// notice and use restrictions.
//
// See the Sample code usage restrictions document for further information.
//
#include "service_area_sample.h"
#include "SimpleMarkerSymbol.h"
#include "ArcGISTiledMapServiceLayer.h"
#include "GeometryEngine.h"
#include "TextSymbol.h"
#include "widgetpanel.h"
static const QString URL_WORLD_STREET = "http://services.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer";
static const QString URL_NA_SANDIEGO = "http://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/ServiceArea";
service_area_sample::service_area_sample(const QString& title, const QString& categoryList, const QString& shortDesc, const QString& longDesc, const QString& thumbnail) :
RTSample(title, categoryList, shortDesc, longDesc, thumbnail),
m_mouseHelper(nullptr),
m_addFacilitiesBtn(nullptr),
m_addPointBarriersBtn(nullptr),
m_addPolylineBarriersBtn(nullptr),
m_addPolygonBarriersBtn(nullptr),
m_solveBtn(nullptr),
m_resetBtn(nullptr),
m_addingFacilities(false),
m_addingPointBarriers(false),
m_addingPolylineBarriers(false),
m_addingPolygonBarriers(false),
m_polygonStarted(false),
m_polylinestarted(false),
m_polygonEnded(false),
m_polylineEnded(false),
m_polygonGraphicId(-1),
m_polylineGraphicId(-1)
{
}
service_area_sample::~service_area_sample()
{
}
void service_area_sample::run()
{
this->setCursor(Qt::WaitCursor);
setRunning();
m_map = new EsriRuntimeQt::Map(this);
m_mapGraphicsView = EsriRuntimeQt::MapGraphicsView::create(m_map, this);
displaySampleWidget(m_mapGraphicsView);
// add the layers
EsriRuntimeQt::ArcGISTiledMapServiceLayer* tiledLayer = new EsriRuntimeQt::ArcGISTiledMapServiceLayer(URL_WORLD_STREET, this);
m_map->addLayer(tiledLayer);
m_graphicsLayer = new EsriRuntimeQt::GraphicsLayer(this);
m_map->addLayer(m_graphicsLayer);
connect(m_map, SIGNAL(mapReady()), this, SLOT(onMapReady()));
connect(m_map, SIGNAL(mousePress(QMouseEvent&)), this, SLOT(onMousePress(QMouseEvent&)));
connect(m_map, SIGNAL(mouseRelease(QMouseEvent&)), this, SLOT(onMouseRelease(QMouseEvent&)));
// create UI elements
WidgetPanel* widget = new WidgetPanel();
QLabel* instructionsLabel = new QLabel(
QString("To use the sample, click on the map to place a facility graphic. ") +
QString("Click the Solve button to generate results and display them in the map. ") +
QString("\n") +
QString("The task will be run using the facility points to find the service area ") +
QString("polygons with the breaks set at 1, 2, and 3 minutes."));
instructionsLabel->setWordWrap(true);
// set dark gray background
QPalette pal(palette());
pal.setColor(QPalette::Background, Qt::black);
pal.setColor(QPalette::WindowText, Qt::white);
widget->setAutoFillBackground(true);
widget->setPalette(pal);
QFont font;
font.setPixelSize(11);
instructionsLabel->setFont(font);
instructionsLabel->setMargin(0);
m_addFacilitiesBtn = new QPushButton("Add Facilities");
connect(m_addFacilitiesBtn, SIGNAL(clicked()), this, SLOT(onAddFacilities()));
QGroupBox* grpBox = new QGroupBox("Add Barriers");
grpBox->setTitle("Add Barriers");
grpBox->setObjectName("barriersBox");
grpBox->setStyleSheet("QGroupBox#barriersBox { border: 1px solid white; border-radius: 9px; margin: 0.35em; font-weight: bold; } QGroupBox::title {color: white; subcontrol-origin: margin;left: 10px; padding: 0 3px 0 3px;}");
m_addPointBarriersBtn = new QPushButton("Point");
connect(m_addPointBarriersBtn, SIGNAL(clicked()), this, SLOT(onAddPointBarriers()));
m_addPolylineBarriersBtn = new QPushButton("Polyline");
connect(m_addPolylineBarriersBtn, SIGNAL(clicked()), this, SLOT(onAddPolylineBarriers()));
m_addPolygonBarriersBtn = new QPushButton("Polygon");
connect(m_addPolygonBarriersBtn, SIGNAL(clicked()), this, SLOT(onAddPolygonBarriers()));
QHBoxLayout *hbox = new QHBoxLayout;
hbox->addWidget(m_addPointBarriersBtn);
hbox->addWidget(m_addPolylineBarriersBtn);
hbox->addWidget(m_addPolygonBarriersBtn);
hbox->addStretch(1);
grpBox->setLayout(hbox);
m_solveBtn = new QPushButton("Solve");
connect(m_solveBtn, SIGNAL(clicked()), this, SLOT(onSolve()));
m_resetBtn = new QPushButton("Reset");
connect(m_resetBtn, SIGNAL(clicked()), this, SLOT(onReset()));
// Initialize the ServiceAreaTask with the url
m_task = new EsriRuntimeQt::ServiceAreaTask(URL_NA_SANDIEGO, this);
//Connect to serviceAreaTaskComplete signal
connect(m_task, SIGNAL(serviceAreaTaskComplete(EsriRuntimeQt::ServiceAreaResult*)), this, SLOT(onServiceAreaTaskComplete(EsriRuntimeQt::ServiceAreaResult*)));
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(instructionsLabel);
layout->addWidget(m_addFacilitiesBtn);
layout->addWidget(grpBox);
layout->addWidget(m_solveBtn);
layout->addWidget(m_resetBtn);
widget->setFixedSize(225, 350);
widget->setLayout(layout);
// create helper object for drawing polygons and polylines
m_mouseHelper = new service_area_sample_mouse_helper(this);
m_mapGraphicsView->scene()->addItem(m_mouseHelper);
QGraphicsProxyWidget *proxy = m_mapGraphicsView->scene()->addWidget(widget);
proxy->setPos(7, 7);
proxy->setAcceptedMouseButtons(Qt::LeftButton);
proxy->setFlag(QGraphicsItem::ItemIsSelectable, false);
proxy->setOpacity(0.75);
this->setCursor(Qt::ArrowCursor);
}
void service_area_sample::doubleClickUpdate()
{
QMap<QString, QVariant> attributes;
attributes.insert("type", "Barrier");
// Add a thicker red line symbol to the polygon
if (m_addingPolygonBarriers)
{
m_polygonEnded = true;
m_polygon.closeAllPaths();
QColor red;
red.setRedF(0.5);
red.setAlphaF(0.45);
EsriRuntimeQt::SimpleFillSymbol fillSymbol(red,
EsriRuntimeQt::SimpleFillSymbolStyle::Solid,
EsriRuntimeQt::SimpleLineSymbol(Qt::red, 2));
EsriRuntimeQt::Graphic* graphic = new EsriRuntimeQt::Graphic(EsriRuntimeQt::Polygon(m_polygon), fillSymbol, attributes);
m_graphicsLayer->updateGraphic(m_polygonGraphicId, graphic);
m_graphicsLayer->sendToBack(m_polygonGraphicId);
m_polygonBarriers.append(graphic);
}
else if (m_addingPolylineBarriers)
{
m_polylineEnded = true;
EsriRuntimeQt::Graphic* graphic = m_graphicsLayer->graphic(m_polylineGraphicId);
m_polylineBarriers.append(graphic);
}
}
void service_area_sample::hoverMoveUpdate(QGraphicsSceneHoverEvent *event)
{
EsriRuntimeQt::Point mapPoint = m_map->toMapPoint(event->scenePos().x(), event->scenePos().y());
if (m_addingPolygonBarriers && m_polygonStarted && !m_polygonEnded)
{
// drawing the polygon
m_polygon.setPoint(m_polygon.pointCount()-1,mapPoint);
EsriRuntimeQt::Graphic* graphic = new EsriRuntimeQt::Graphic(EsriRuntimeQt::Polygon(m_polygon), createBarrierSymbol());
m_graphicsLayer->updateGraphic(m_polygonGraphicId, graphic);
}
if (m_addingPolylineBarriers && m_polylinestarted && !m_polylineEnded)
{
//drawing the polyline
m_polyline.setPoint(m_polyline.pointCount()-1,mapPoint);
EsriRuntimeQt::Graphic* graphic = new EsriRuntimeQt::Graphic(EsriRuntimeQt::Polyline(m_polyline),
EsriRuntimeQt::SimpleLineSymbol(Qt::red, 1));
m_graphicsLayer->updateGraphic(m_polylineGraphicId, graphic);
}
}
void service_area_sample::onMapReady()
{
EsriRuntimeQt::SpatialReference sr = m_map->spatialReference();
qDebug() << "MapReady, Spatial Reference = " << sr.wkid();
EsriRuntimeQt::Geometry projectedGeom = EsriRuntimeQt::GeometryEngine::project(
EsriRuntimeQt::Envelope(-117.20,32.68,-117.10,32.74, EsriRuntimeQt::SpatialReference(4326)), sr);
m_map->setExtent(EsriRuntimeQt::Envelope(projectedGeom));
m_map->zoom(0.75);
}
void service_area_sample::onMousePress(QMouseEvent& event)
{
// store the location of the mouse click
m_lastMouseCoord = QPointF(event.pos().x(), event.pos().y());
}
void service_area_sample::onMouseRelease(QMouseEvent& event)
{
if (event.button() != Qt::LeftButton)
return;
QPoint 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;
QGraphicsItem * graphicsItem = nullptr;
graphicsItem = itemFromQGraphicsScene(m_mapGraphicsView->scene(), scenePosition);
if (dynamic_cast<QGraphicsProxyWidget*>(graphicsItem))
return; // Clicked item box
if (event.button() == Qt::LeftButton)
{
EsriRuntimeQt::Point pt = m_map->toMapPoint(scenePosition.x(), scenePosition.y());
if (m_addingFacilities)
{
addFacilities(pt);
}
else if (m_addingPointBarriers)
{
drawPoint(pt);
}
else if (m_addingPolylineBarriers && !m_polylineEnded)
{
drawLine(pt);
}
else if (m_addingPolygonBarriers && !m_polygonEnded)
{
drawPolygon(pt);
}
}
}
void service_area_sample::onAddFacilities()
{
m_addingFacilities = true;
m_addingPointBarriers = false;
m_addingPolylineBarriers = false;
m_addingPolygonBarriers = false;
}
void service_area_sample::onAddPointBarriers()
{
m_addingFacilities = false;
m_addingPointBarriers = true;
m_addingPolylineBarriers = false;
m_addingPolygonBarriers = false;
}
void service_area_sample::onAddPolylineBarriers()
{
m_addingFacilities = false;
m_addingPointBarriers = false;
m_addingPolylineBarriers = true;
m_addingPolygonBarriers = false;
}
void service_area_sample::onAddPolygonBarriers()
{
m_addingFacilities = false;
m_addingPointBarriers = false;
m_addingPolylineBarriers = false;
m_addingPolygonBarriers = true;
}
void service_area_sample::onSolve()
{
// Reset the drawing bools
m_addingFacilities = false;
m_addingPointBarriers = false;
m_addingPolygonBarriers = false;
m_addingPolylineBarriers = false;
// Create the facilities features
EsriRuntimeQt::NAFeaturesAsFeature* facilitiesFeatures = new EsriRuntimeQt::NAFeaturesAsFeature(this);
facilitiesFeatures->setFeatures(m_facilities);
// Create the point barriers features
EsriRuntimeQt::NAFeaturesAsFeature* pointBarrierFeatures = new EsriRuntimeQt::NAFeaturesAsFeature(this);
pointBarrierFeatures->setFeatures(m_pointBarriers);
// Create the polyline barriers features
EsriRuntimeQt::NAFeaturesAsFeature* polylineBarrierFeatures = new EsriRuntimeQt::NAFeaturesAsFeature(this);
polylineBarrierFeatures->setFeatures(m_polylineBarriers);
// Create the polygon barriers features
EsriRuntimeQt::NAFeaturesAsFeature* polygonBarrierFeatures = new EsriRuntimeQt::NAFeaturesAsFeature(this);
polygonBarrierFeatures->setFeatures(m_polygonBarriers);
// Set up the service area task parameters
EsriRuntimeQt::ServiceAreaTaskParameters* parameters = new EsriRuntimeQt::ServiceAreaTaskParameters(this);
// add the features
parameters->setFacilities(facilitiesFeatures);
// add the breaks at 1, 2 and 3 minutes
parameters->setDefaultBreaks(QList<double>() << 1.0 << 2.0 << 3.0);
// add the barriers
if (m_pointBarriers.size() > 0)
parameters->setPointBarriers(pointBarrierFeatures);
if (m_polylineBarriers.size() > 0)
parameters->setPolylineBarriers(polylineBarrierFeatures);
if (m_polygonBarriers.size() > 0)
parameters->setPolygonBarriers(polygonBarrierFeatures);
parameters->setOutSpatialReference(m_map->spatialReference());
// Asynchronously solve the service area task
m_task->solve(parameters);
}
void service_area_sample::onServiceAreaTaskComplete(EsriRuntimeQt::ServiceAreaResult* result)
{
if (!result)
{
QString messageBox("There was an error computing the service area.");
QMessageBox::warning(this, "Warning", messageBox, QMessageBox::Ok);
return;
}
EsriRuntimeQt::FeatureSet* polygons = result->serviceAreaPolygons();
int drawOrder = m_graphicsLayer->minDrawOrder() - 1;
// no routing result returned
if (!polygons || polygons->graphics().size() == 0)
{
QString messageBox("There was an error computing the service area. ServiceArea result is empty.");
QMessageBox::warning(this, "Warning", messageBox, QMessageBox::Ok);
return;
}
foreach (EsriRuntimeQt::Graphic* graphic, polygons->graphics())
{
if (!graphic)
continue;
int polygonID = m_graphicsLayer->addGraphic(new EsriRuntimeQt::Graphic(graphic->geometry(),
EsriRuntimeQt::SimpleFillSymbol(QColor(randInt(0,255), randInt(0,255), randInt(0,255), 150))));
m_graphicsLayer->updateGraphic(polygonID, drawOrder);
}
result->deleteLater();
}
void service_area_sample::onReset()
{
reset();
}
void service_area_sample::reset()
{
m_polygon = EsriRuntimeQt::Polygon();
m_polyline = EsriRuntimeQt::Polyline();
m_polygonStarted = false;
m_polygonEnded = false;
m_polylinestarted = false;
m_polylineEnded = false;
m_addingFacilities = false;
m_addingPointBarriers = false;
m_addingPolygonBarriers = false;
m_addingPolylineBarriers = false;
m_facilities.clear();
m_pointBarriers.clear();
m_polygonBarriers.clear();
m_polylineBarriers.clear();
m_graphicsLayer->removeAll();
m_polygonGraphicId = -1;
m_polylineGraphicId= -1;
}
int service_area_sample::randInt(int low, int high)
{
// Random number between low and high
return qrand() % ((high + 1) - low) + low;
}
void service_area_sample::drawPoint(const EsriRuntimeQt::Point& point)
{
// draw the current point
EsriRuntimeQt::Graphic* graphic = new EsriRuntimeQt::Graphic(point, EsriRuntimeQt::SimpleMarkerSymbol(Qt::red, 7));
m_graphicsLayer->addGraphic(graphic);
if (m_addingPointBarriers)
m_pointBarriers.append(graphic);
}
void service_area_sample::drawLine(const EsriRuntimeQt::Point& point)
{ // draws the line connected from the previously added point
if (!m_polylinestarted)
{
m_polylinestarted = true;
m_polyline.startPath(point);
m_polyline.lineTo(point);
m_polylineGraphicId = m_graphicsLayer->addGraphic(new EsriRuntimeQt::Graphic(EsriRuntimeQt::Polyline(m_polyline),
EsriRuntimeQt::SimpleLineSymbol(Qt::red, 1)));
}
else
{
m_polyline.lineTo(point);
m_graphicsLayer->updateGraphic(m_polylineGraphicId, new EsriRuntimeQt::Graphic(EsriRuntimeQt::Polyline(m_polyline),
EsriRuntimeQt::SimpleLineSymbol(Qt::red, 1)));
}
}
void service_area_sample::drawPolygon(const EsriRuntimeQt::Point& point)
{
//draws the line connected from the previously added point
if (!m_polygonStarted)
{
m_polygon.startPath(point);
m_polygon.lineTo(point);
m_polygonGraphicId = m_graphicsLayer->addGraphic(new EsriRuntimeQt::Graphic(EsriRuntimeQt::Polygon(m_polygon), createBarrierSymbol()));
m_polygonStarted = true;
return;
}
m_polygon.lineTo(point);
m_graphicsLayer->updateGraphic(m_polygonGraphicId, new EsriRuntimeQt::Graphic(EsriRuntimeQt::Polygon(m_polygon), createBarrierSymbol()));
}
void service_area_sample::addFacilities(const EsriRuntimeQt::Point& mapPoint)
{
if (EsriRuntimeQt::GeometryEngine::within(mapPoint, m_map->extent()))
{
QMap<QString, QVariant> attributes;
attributes.insert("type", QVariant("Facility"));
EsriRuntimeQt::SimpleMarkerSymbol facilitiesSymbol(QColor(64, 224, 208), 18,
EsriRuntimeQt::SimpleMarkerSymbolStyle::Square);
facilitiesSymbol.setOutline(EsriRuntimeQt::SimpleLineSymbol(QColor("black"), 0.5,
EsriRuntimeQt::SimpleLineSymbolStyle::Solid));
EsriRuntimeQt::Graphic* graphic = new EsriRuntimeQt::Graphic(mapPoint, facilitiesSymbol, attributes);
m_graphicsLayer->addGraphic(graphic);
m_graphicsLayer->addGraphic(new EsriRuntimeQt::Graphic(mapPoint, EsriRuntimeQt::TextSymbol(QColor("black"), 8, QString::number(m_facilities.size()+1))));
m_facilities.append(graphic);
}
}
EsriRuntimeQt::SimpleFillSymbol service_area_sample::createBarrierSymbol()
{
QColor red;
red.setRedF(0.5);
red.setAlphaF(0.45);
EsriRuntimeQt::SimpleFillSymbol barrierSymbol;
barrierSymbol.setColor(red);
barrierSymbol.setOutline(EsriRuntimeQt::SimpleLineSymbol(Qt::red, 1));
return barrierSymbol;
}
void service_area_sample_mouse_helper::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
Q_UNUSED(event)
m_sample->doubleClickUpdate();
}
void service_area_sample_mouse_helper::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
m_sample->hoverMoveUpdate(event);
}