25using namespace Qt::StringLiterals;
29QString QgsServiceAreaFromLayerAlgorithm::name()
const
31 return u
"serviceareafromlayer"_s;
34QString QgsServiceAreaFromLayerAlgorithm::displayName()
const
36 return QObject::tr(
"Service area (from layer)" );
39QStringList QgsServiceAreaFromLayerAlgorithm::tags()
const
41 return QObject::tr(
"network,service,area,shortest,fastest" ).split(
',' );
44QString QgsServiceAreaFromLayerAlgorithm::shortHelpString()
const
46 return QObject::tr(
"This algorithm creates a new vector layer with all the edges or parts of "
47 "edges of a network line layer that can be reached within a distance "
48 "or a time, starting from features of a point layer. The distance and "
49 "the time (both referred to as \"travel cost\") must be specified "
50 "respectively in the network layer units or in hours." );
53QString QgsServiceAreaFromLayerAlgorithm::shortDescription()
const
55 return QObject::tr(
"Creates a vector layer with all the edges or parts of "
56 "edges of a network line layer that can be reached within a distance "
57 "or a time, starting from features of a point layer." );
60QgsServiceAreaFromLayerAlgorithm *QgsServiceAreaFromLayerAlgorithm::createInstance()
const
62 return new QgsServiceAreaFromLayerAlgorithm();
65void QgsServiceAreaFromLayerAlgorithm::initAlgorithm(
const QVariantMap & )
70 auto travelCost = std::make_unique<QgsProcessingParameterNumber>( u
"TRAVEL_COST"_s, QObject::tr(
"Travel cost (distance for 'Shortest', time for 'Fastest')" ),
Qgis::ProcessingNumberParameterType::Double, 0,
true, 0 );
72 addParameter( std::move( travelCost ) );
74 auto travelCost2 = std::make_unique<QgsProcessingParameterNumber>( u
"TRAVEL_COST2"_s, QObject::tr(
"Travel cost (distance for 'Shortest', time for 'Fastest')" ),
Qgis::ProcessingNumberParameterType::Double, 0,
false, 0 );
75 travelCost2->setIsDynamic(
true );
77 travelCost2->setDynamicLayerParameterName( u
"START_POINTS"_s );
78 addParameter( std::move( travelCost2 ) );
80 auto includeBounds = std::make_unique<QgsProcessingParameterBoolean>( u
"INCLUDE_BOUNDS"_s, QObject::tr(
"Include upper/lower bound points" ),
false,
true );
82 addParameter( includeBounds.release() );
84 std::unique_ptr<QgsProcessingParameterNumber> maxPointDistanceFromNetwork = std::make_unique<QgsProcessingParameterDistance>( u
"POINT_TOLERANCE"_s, QObject::tr(
"Maximum point distance from network" ), QVariant(), u
"INPUT"_s,
true, 0 );
86 maxPointDistanceFromNetwork->setHelp( QObject::tr(
"Specifies an optional limit on the distance from the points to the network layer. If a point is further from the network than this distance it will be treated as non-routable." ) );
87 addParameter( maxPointDistanceFromNetwork.release() );
89 auto outputLines = std::make_unique<QgsProcessingParameterFeatureSink>( u
"OUTPUT_LINES"_s, QObject::tr(
"Service area (lines)" ),
Qgis::ProcessingSourceType::VectorLine, QVariant(),
true );
90 outputLines->setCreateByDefault(
true );
91 addParameter( outputLines.release() );
93 auto outputPoints = std::make_unique<QgsProcessingParameterFeatureSink>( u
"OUTPUT"_s, QObject::tr(
"Service area (boundary nodes)" ),
Qgis::ProcessingSourceType::VectorPoint, QVariant(),
true );
94 outputPoints->setCreateByDefault(
false );
95 addParameter( outputPoints.release() );
97 auto outputNonRoutable = std::make_unique<QgsProcessingParameterFeatureSink>( u
"OUTPUT_NON_ROUTABLE"_s, QObject::tr(
"Non-routable features" ),
Qgis::ProcessingSourceType::VectorPoint, QVariant(),
true );
98 outputNonRoutable->setHelp( QObject::tr(
"An optional output which will be used to store any input features which could not be routed (e.g. those which are too far from the network layer)." ) );
99 outputNonRoutable->setCreateByDefault(
false );
100 addParameter( outputNonRoutable.release() );
105 loadCommonParams( parameters, context, feedback );
107 std::unique_ptr<QgsProcessingFeatureSource> startPoints( parameterAsSource( parameters, u
"START_POINTS"_s, context ) );
112 const bool useOldTravelCost = parameters.value( u
"TRAVEL_COST"_s ).isValid();
113 const double defaultTravelCost = parameterAsDouble( parameters, useOldTravelCost ? u
"TRAVEL_COST"_s : u
"TRAVEL_COST2"_s, context );
116 QgsExpressionContext expressionContext = createExpressionContext( parameters, context, startPoints.get() );
118 if ( dynamicTravelCost )
120 travelCostProperty = parameters.
value( u
"TRAVEL_COST2"_s ).value<
QgsProperty>();
123 const int strategy = parameterAsInt( parameters, u
"STRATEGY"_s, context );
124 const double multiplier = ( strategy && !useOldTravelCost ) ? mMultiplier : 1;
126 bool includeBounds =
true;
127 if ( parameters.contains( u
"INCLUDE_BOUNDS"_s ) )
129 includeBounds = parameterAsBool( parameters, u
"INCLUDE_BOUNDS"_s, context );
132 QVector<QgsPointXY> points;
133 QHash<int, QgsFeature> sourceFeatures;
134 loadPoints( startPoints.get(), &points,
nullptr, context, feedback, &sourceFeatures );
136 feedback->
pushInfo( QObject::tr(
"Building graph…" ) );
137 QVector<QgsPointXY> snappedPoints;
138 mDirector->makeGraph( mBuilder.get(), points, snappedPoints, feedback );
140 feedback->
pushInfo( QObject::tr(
"Calculating service areas…" ) );
141 std::unique_ptr<QgsGraph> graph( mBuilder->takeGraph() );
144 newFields.
append(
QgsField( u
"type"_s, QMetaType::Type::QString ) );
145 newFields.
append(
QgsField( u
"start"_s, QMetaType::Type::QString ) );
148 QString pointsSinkId;
149 std::unique_ptr<QgsFeatureSink> pointsSink( parameterAsSink( parameters, u
"OUTPUT"_s, context, pointsSinkId, fields,
Qgis::WkbType::MultiPoint, mNetwork->sourceCrs() ) );
152 std::unique_ptr<QgsFeatureSink> linesSink( parameterAsSink( parameters, u
"OUTPUT_LINES"_s, context, linesSinkId, fields,
Qgis::WkbType::MultiLineString, mNetwork->sourceCrs() ) );
154 QString nonRoutableSinkId;
155 std::unique_ptr<QgsFeatureSink> nonRoutableSink( parameterAsSink( parameters, u
"OUTPUT_NON_ROUTABLE"_s, context, nonRoutableSinkId, startPoints->fields(),
Qgis::WkbType::Point, mNetwork->sourceCrs() ) );
157 const double pointDistanceThreshold = parameters.value( u
"POINT_TOLERANCE"_s ).isValid() ? parameterAsDouble( parameters, u
"POINT_TOLERANCE"_s, context ) : -1;
161 QVector<double> costs;
163 int inboundEdgeIndex;
164 double startVertexCost, endVertexCost;
171 const double step = snappedPoints.size() > 0 ? 100.0 / snappedPoints.size() : 1;
172 for (
int i = 0; i < snappedPoints.size(); i++ )
179 double travelCost = defaultTravelCost;
180 if ( dynamicTravelCost )
182 expressionContext.
setFeature( sourceFeatures.value( i + 1 ) );
183 travelCost = travelCostProperty.
valueAsDouble( expressionContext, travelCost );
185 travelCost *= multiplier;
187 const QgsPointXY snappedPoint = snappedPoints.at( i );
188 const QgsPointXY originalPoint = points.at( i );
190 if ( pointDistanceThreshold >= 0 )
192 double distancePointToNetwork = 0;
195 distancePointToNetwork = mBuilder->distanceArea()->measureLine( originalPoint, snappedPoint );
202 if ( distancePointToNetwork > pointDistanceThreshold )
204 feedback->
pushWarning( QObject::tr(
"Point is too far from the network layer (%1, maximum permitted is %2)" ).arg( distancePointToNetwork ).arg( pointDistanceThreshold ) );
205 if ( nonRoutableSink )
208 attributes = sourceFeatures.value( i + 1 ).attributes();
211 throw QgsProcessingException( writeFeatureError( nonRoutableSink.get(), parameters, u
"OUTPUT_NON_ROUTABLE"_s ) );
219 const QString originalPointString = originalPoint.
toString();
221 idxStart = graph->findVertex( snappedPoint );
229 for (
int j = 0; j < costs.size(); j++ )
231 inboundEdgeIndex = tree.at( j );
233 if ( inboundEdgeIndex == -1 && j != idxStart )
239 startVertexCost = costs.at( j );
240 if ( startVertexCost > travelCost )
246 vertices.insert( j );
247 startPoint = graph->vertex( j ).point();
250 const QList<int> outgoingEdges = graph->vertex( j ).outgoingEdges();
251 for (
int edgeId : outgoingEdges )
253 edge = graph->edge( edgeId );
254 endVertexCost = startVertexCost + edge.
cost( 0 ).toDouble();
255 endPoint = graph->vertex( edge.
toVertex() ).point();
256 if ( endVertexCost <= travelCost )
260 lines.push_back(
QgsPolylineXY() << startPoint << endPoint );
267 areaPoints.push_back( interpolatedEndPoint );
268 lines.push_back(
QgsPolylineXY() << startPoint << interpolatedEndPoint );
274 QList<int> verticesList = qgis::setToList( vertices );
275 areaPoints.reserve( verticesList.size() );
276 std::sort( verticesList.begin(), verticesList.end() );
277 for (
int v : verticesList )
279 areaPoints.push_back( graph->vertex( v ).point() );
286 attributes = sourceFeatures.value( i + 1 ).attributes();
287 attributes << u
"within"_s << originalPointString;
296 nodes.reserve( costs.size() );
299 for (
int v = 0; v < costs.size(); v++ )
301 if ( costs.at( v ) > travelCost && tree.at( v ) != -1 )
303 vertexId = graph->edge( tree.at( v ) ).fromVertex();
304 if ( costs.at( vertexId ) <= travelCost )
306 nodes.push_back( v );
311 upperBoundary.reserve( nodes.size() );
312 lowerBoundary.reserve( nodes.size() );
313 for (
int n : std::as_const( nodes ) )
315 upperBoundary.push_back( graph->vertex( graph->edge( tree.at( n ) ).toVertex() ).point() );
316 lowerBoundary.push_back( graph->vertex( graph->edge( tree.at( n ) ).fromVertex() ).point() );
323 attributes = sourceFeatures.value( i + 1 ).attributes();
324 attributes << u
"upper"_s << originalPointString;
330 attributes = sourceFeatures.value( i + 1 ).attributes();
331 attributes << u
"lower"_s << originalPointString;
342 attributes = sourceFeatures.value( i + 1 ).attributes();
343 attributes << u
"lines"_s << originalPointString;
355 pointsSink->finalize();
356 outputs.insert( u
"OUTPUT"_s, pointsSinkId );
360 linesSink->finalize();
361 outputs.insert( u
"OUTPUT_LINES"_s, linesSinkId );
363 if ( nonRoutableSink )
365 nonRoutableSink->finalize();
366 outputs.insert( u
"OUTPUT_NON_ROUTABLE"_s, nonRoutableSinkId );
@ VectorPoint
Vector point layers.
@ VectorLine
Vector line layers.
@ MultiLineString
MultiLineString.
@ Hidden
Parameter is hidden and should not be shown to users.
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
@ Double
Double/float values.
Custom exception class for Coordinate Reference System related exceptions.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
bool isCanceled() const
Tells whether the operation has been canceled already.
void setProgress(double progress)
Sets the current progress for the feedback object.
Encapsulate a field in an attribute table or data source.
Container of fields for a vector layer.
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
static QgsPointXY interpolatePointOnLineByValue(double x1, double y1, double v1, double x2, double y2, double v2, double value)
Interpolates the position of a point along the line from (x1, y1) to (x2, y2).
A geometry is the spatial representation of a feature.
static QgsGeometry fromMultiPolylineXY(const QgsMultiPolylineXY &multiline)
Creates a new geometry from a QgsMultiPolylineXY object.
static QgsGeometry fromMultiPointXY(const QgsMultiPointXY &multipoint)
Creates a new geometry from a QgsMultiPointXY object.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
static void dijkstra(const QgsGraph *source, int startVertexIdx, int criterionNum, QVector< int > *resultTree=nullptr, QVector< double > *resultCost=nullptr)
Solve shortest path problem using Dijkstra algorithm.
Represents an edge in a graph.
int toVertex() const
Returns the index of the vertex at the end of this edge.
QVariant cost(int strategyIndex) const
Returns edge cost calculated using specified strategy.
QString toString(int precision=-1) const
Returns a string representation of the point (x, y) with a preset precision.
Contains information about the context in which a processing algorithm is executed.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
virtual void pushWarning(const QString &warning)
Pushes a warning informational message from the algorithm.
An input feature source (such as vector layers) parameter for processing algorithms.
static bool isDynamic(const QVariantMap ¶meters, const QString &name)
Returns true if the parameter with matching name is a dynamic parameter, and must be evaluated once f...
static QgsFields combineFields(const QgsFields &fieldsA, const QgsFields &fieldsB, const QString &fieldsBPrefix=QString())
Combines two field lists, avoiding duplicate field names (in a case-insensitive manner).
Definition for a property.
@ DoublePositive
Positive double value (including 0).
A store for object properties.
QVariant value(const QgsExpressionContext &context, const QVariant &defaultValue=QVariant(), bool *ok=nullptr) const
Calculates the current value of the property, including any transforms which are set for the property...
double valueAsDouble(const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property and interprets it as a double.
QVector< QgsPolylineXY > QgsMultiPolylineXY
A collection of QgsPolylines that share a common collection of attributes.
QVector< QgsPointXY > QgsMultiPointXY
A collection of QgsPoints that share a common collection of attributes.
QVector< QgsPointXY > QgsPolylineXY
Polyline as represented as a vector of two-dimensional points.