[processing] base class for network analysis algorithms

This commit is contained in:
Alexander Bruy 2018-07-27 10:15:38 +03:00
parent a6901edb9b
commit be3b2c8ff8
5 changed files with 249 additions and 109 deletions

View File

@ -95,6 +95,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmvectorize.cpp
processing/qgsalgorithmwedgebuffers.cpp
processing/qgsalgorithmzonalhistogram.cpp
processing/qgsalgorithmnetworkanalysisbase.cpp
processing/qgsnativealgorithms.cpp
processing/qgsoverlayutils.cpp

View File

@ -0,0 +1,166 @@
/***************************************************************************
qgsalgorithmnetworkanalysisbase.cpp
---------------------
begin : July 2018
copyright : (C) 2018 by Alexander Bruy
email : alexander dot bruy at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsalgorithmnetworkanalysisbase.h"
#include "qgsgraphanalyzer.h"
#include "qgsnetworkspeedstrategy.h"
#include "qgsnetworkdistancestrategy.h"
///@cond PRIVATE
//
// QgsNetworkAnalysisAlgorithmBase
//
QString QgsNetworkAnalysisAlgorithmBase::group() const
{
return QObject::tr( "Network analysis" );
}
QString QgsNetworkAnalysisAlgorithmBase::groupId() const
{
return QStringLiteral( "networkanalysis" );
}
void QgsNetworkAnalysisAlgorithmBase::addCommonParams()
{
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Vector layer representing network" ), QList< int >() << QgsProcessing::TypeVectorLine ) );
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "STRATEGY" ), QObject::tr( "Path type to calculate" ), QStringList() << QObject::tr( "Shortest" ) << QObject::tr( "Fastest" ), false, 0 ) );
std::unique_ptr< QgsProcessingParameterField > directionField = qgis::make_unique< QgsProcessingParameterField >( QStringLiteral( "DIRECTION_FIELD" ),
QObject::tr( "Direction field" ), QVariant(), QStringLiteral( "INPUT" ), QgsProcessingParameterField::Any, false, true );
directionField->setFlags( directionField->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( directionField.release() );
std::unique_ptr< QgsProcessingParameterString > forwardValue = qgis::make_unique< QgsProcessingParameterString >( QStringLiteral( "VALUE_FORWARD" ),
QObject::tr( "Value for forward direction" ), QVariant(), false, true );
forwardValue->setFlags( forwardValue->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( forwardValue.release() );
std::unique_ptr< QgsProcessingParameterString > backwardValue = qgis::make_unique< QgsProcessingParameterString >( QStringLiteral( "VALUE_BACKWARD" ),
QObject::tr( "Value for backward direction" ), QVariant(), false, true );
backwardValue->setFlags( backwardValue->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( backwardValue.release() );
std::unique_ptr< QgsProcessingParameterString > bothValue = qgis::make_unique< QgsProcessingParameterString >( QStringLiteral( "VALUE_BOTH" ),
QObject::tr( "Value for both directions" ), QVariant(), false, true );
bothValue->setFlags( bothValue->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( bothValue.release() );
std::unique_ptr< QgsProcessingParameterEnum > directionValue = qgis::make_unique< QgsProcessingParameterEnum >( QStringLiteral( "DEFAULT_DIRECTION" ),
QObject::tr( "Default direction" ), QStringList() << QObject::tr( "Forward direction" ) << QObject::tr( "Backward direction" ) << QObject::tr( "Both directions" ), false, 2 );
directionValue->setFlags( directionValue->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( directionValue.release() );
std::unique_ptr< QgsProcessingParameterField > speedField = qgis::make_unique< QgsProcessingParameterField >( QStringLiteral( "SPEED_FIELD" ),
QObject::tr( "Speed field" ), QVariant(), QStringLiteral( "INPUT" ), QgsProcessingParameterField::Numeric, false, true );
speedField->setFlags( speedField->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( speedField.release() );
std::unique_ptr< QgsProcessingParameterNumber > speed = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "DEFAULT_SPEED" ), QObject::tr( "Default speed (km/h)" ), QgsProcessingParameterNumber::Double, 50, false, 0 );
speed->setFlags( speed->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( speed.release() );
std::unique_ptr< QgsProcessingParameterNumber > tolerance = qgis::make_unique < QgsProcessingParameterDistance >( QStringLiteral( "TOLERANCE" ), QObject::tr( "Topology tolerance" ), 0, QStringLiteral( "INPUT" ), false, 0 );
tolerance->setFlags( tolerance->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( tolerance.release() );
}
void QgsNetworkAnalysisAlgorithmBase::loadCommonParams( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
Q_UNUSED( feedback );
mNetwork.reset( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !mNetwork )
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
int strategy = parameterAsInt( parameters, QStringLiteral( "STRATEGY" ), context );
QString directionFieldName = parameterAsString( parameters, QStringLiteral( "DIRECTION_FIELD" ), context );
QString forwardValue = parameterAsString( parameters, QStringLiteral( "VALUE_FORWARD" ), context );
QString backwardValue = parameterAsString( parameters, QStringLiteral( "VALUE_BACKWARD" ), context );
QString bothValue = parameterAsString( parameters, QStringLiteral( "VALUE_BOTH" ), context );
QgsVectorLayerDirector::Direction defaultDirection = static_cast< QgsVectorLayerDirector::Direction>( parameterAsInt( parameters, QStringLiteral( "DEFAULT_DIRECTION" ), context ) );
QString speedFieldName = parameterAsString( parameters, QStringLiteral( "SPEED_FIELD" ), context );
double defaultSpeed = parameterAsDouble( parameters, QStringLiteral( "DEFAULT_SPEED" ), context );
double tolerance = parameterAsDouble( parameters, QStringLiteral( "TOLERANCE" ), context );
int directionField = -1;
if ( !directionFieldName.isEmpty() )
{
directionField = mNetwork->fields().lookupField( directionFieldName );
}
int speedField = -1;
if ( !speedFieldName.isEmpty() )
{
speedField = mNetwork->fields().lookupField( speedFieldName );
}
mDirector = new QgsVectorLayerDirector( mNetwork.get(), directionField, forwardValue, backwardValue, bothValue, defaultDirection );
QgsUnitTypes::DistanceUnit distanceUnits = context.project()->crs().mapUnits();
mMultiplier = QgsUnitTypes::fromUnitToUnitFactor( distanceUnits, QgsUnitTypes::DistanceMeters );
if ( strategy )
{
mDirector->addStrategy( new QgsNetworkSpeedStrategy( speedField, defaultSpeed, mMultiplier * 1000.0 / 3600.0 ) );
mMultiplier = 3600;
}
else
{
mDirector->addStrategy( new QgsNetworkDistanceStrategy() );
}
mBuilder = qgis::make_unique< QgsGraphBuilder >( mNetwork->sourceCrs(), true, tolerance );
}
//~ QVector< QgsPointXY > QgsNetworkAnalysisAlgorithmBase::loadPoints( QgsFeatureSource *source, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
//~ {
//~ feedback.pushInfo( QObject::tr( "Loading points…" ) );
//~ QVector< QgsPointXY > points;
//~ QgsFeature feat;
//~ int i = 0;
//~ double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 0;
//~ QgsFeatureIterator features = source->getFeatures( QgsFeatureRequest().setDestinationCrs( mNetwork->sourceCrs(), context.transformContext() ) );
//~ while ( features.nextFeature( feat ) )
//~ {
//~ i++;
//~ if ( feedback->isCanceled() )
//~ {
//~ break;
//~ }
//~ feedback->setProgress( i * step );
//~ if ( !feat.hasGeometry() )
//~ continue;
//~ QgsGeometry geom = feat.geometry();
//~ QgsAbstractGeometry::vertex_iterator it = geom.vertices_begin();
//~ while ( it != geom.vertices_end() )
//~ {
//~ points.push_back( QgsPointXY( it ) )
//~ }
//~ }
//~ return points
//~ }
///@endcond

View File

@ -0,0 +1,71 @@
/***************************************************************************
qgsalgorithmnetworkanalysisbase.h
---------------------
begin : July 2018
copyright : (C) 2018 by Alexander Bruy
email : alexander dot bruy at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSALGORITHMNETWORKANALYSISBASE_H
#define QGSALGORITHMNETWORKANALYSISBASE_H
#define SIP_NO_FILE
#include "qgis.h"
#include "qgsprocessingalgorithm.h"
#include "qgsgraph.h"
#include "qgsgraphbuilder.h"
#include "qgsvectorlayerdirector.h"
///@cond PRIVATE
/**
* Base class for networkanalysis algorithms.
*/
class QgsNetworkAnalysisAlgorithmBase : public QgsProcessingAlgorithm
{
public:
QString group() const final;
QString groupId() const final;
QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmNetworkAnalysis.svg" ) ); }
QString svgIconPath() const override { return QgsApplication::iconPath( QStringLiteral( "/algorithms/mAlgorithmNetworkAnalysis.svg" ) ); }
protected:
/**
* Adds common algorithm parameters.
*/
void addCommonParams();
/**
* Populates common parameters with values.
*/
void loadCommonParams( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback );
/**
* Loads point from the feature source for further processing.
*/
//~ void loadPoints( QgsFeatureSource *source, QgsProcessingContext &context, QgsProcessingFeedback *feedback );
std::unique_ptr< QgsFeatureSource > mNetwork;
QgsVectorLayerDirector *mDirector = nullptr;
std::unique_ptr< QgsGraphBuilder > mBuilder;
std::unique_ptr< QgsGraph > mGraph;
double mMultiplier = 1;
};
///@endcond PRIVATE
#endif // QGSALGORITHMNETWORKANALYSISBASE_H

View File

@ -17,12 +17,7 @@
#include "qgsalgorithmshortestpathpointtopoint.h"
#include "qgsgraph.h"
#include "qgsgraphbuilder.h"
#include "qgsgraphanalyzer.h"
#include "qgsvectorlayerdirector.h"
#include "qgsnetworkspeedstrategy.h"
#include "qgsnetworkdistancestrategy.h"
///@cond PRIVATE
@ -41,16 +36,6 @@ QStringList QgsShortestPathPointToPointAlgorithm::tags() const
return QObject::tr( "network,path,shortest,fastest" ).split( ',' );
}
QString QgsShortestPathPointToPointAlgorithm::group() const
{
return QObject::tr( "Network analysis" );
}
QString QgsShortestPathPointToPointAlgorithm::groupId() const
{
return QStringLiteral( "networkanalysis" );
}
QString QgsShortestPathPointToPointAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm computes optimal (shortest or fastest) route between given start and end points." );
@ -63,48 +48,9 @@ QgsShortestPathPointToPointAlgorithm *QgsShortestPathPointToPointAlgorithm::crea
void QgsShortestPathPointToPointAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Vector layer representing network" ), QList< int >() << QgsProcessing::TypeVectorLine ) );
addCommonParams();
addParameter( new QgsProcessingParameterPoint( QStringLiteral( "START_POINT" ), QObject::tr( "Start point" ) ) );
addParameter( new QgsProcessingParameterPoint( QStringLiteral( "END_POINT" ), QObject::tr( "End point" ) ) );
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "STRATEGY" ), QObject::tr( "Path type to calculate" ), QStringList() << QObject::tr( "Shortest" ) << QObject::tr( "Fastest" ), false, 0 ) );
std::unique_ptr< QgsProcessingParameterField > directionField = qgis::make_unique< QgsProcessingParameterField >( QStringLiteral( "DIRECTION_FIELD" ),
QObject::tr( "Direction field" ), QVariant(), QStringLiteral( "INPUT" ), QgsProcessingParameterField::Any, false, true );
directionField->setFlags( directionField->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( directionField.release() );
std::unique_ptr< QgsProcessingParameterString > forwardValue = qgis::make_unique< QgsProcessingParameterString >( QStringLiteral( "VALUE_FORWARD" ),
QObject::tr( "Value for forward direction" ), QVariant(), false, true );
forwardValue->setFlags( forwardValue->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( forwardValue.release() );
std::unique_ptr< QgsProcessingParameterString > backwardValue = qgis::make_unique< QgsProcessingParameterString >( QStringLiteral( "VALUE_BACKWARD" ),
QObject::tr( "Value for backward direction" ), QVariant(), false, true );
backwardValue->setFlags( backwardValue->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( backwardValue.release() );
std::unique_ptr< QgsProcessingParameterString > bothValue = qgis::make_unique< QgsProcessingParameterString >( QStringLiteral( "VALUE_BOTH" ),
QObject::tr( "Value for both directions" ), QVariant(), false, true );
bothValue->setFlags( bothValue->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( bothValue.release() );
std::unique_ptr< QgsProcessingParameterEnum > directionValue = qgis::make_unique< QgsProcessingParameterEnum >( QStringLiteral( "DEFAULT_DIRECTION" ),
QObject::tr( "Default direction" ), QStringList() << QObject::tr( "Forward direction" ) << QObject::tr( "Backward direction" ) << QObject::tr( "Both directions" ), false, 2 );
directionValue->setFlags( directionValue->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( directionValue.release() );
std::unique_ptr< QgsProcessingParameterField > speedField = qgis::make_unique< QgsProcessingParameterField >( QStringLiteral( "SPEED_FIELD" ),
QObject::tr( "Speed field" ), QVariant(), QStringLiteral( "INPUT" ), QgsProcessingParameterField::Numeric, false, true );
speedField->setFlags( speedField->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( speedField.release() );
std::unique_ptr< QgsProcessingParameterNumber > speed = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "DEFAULT_SPEED" ), QObject::tr( "Default speed (km/h)" ), QgsProcessingParameterNumber::Double, 50, false, 0 );
speed->setFlags( speed->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( speed.release() );
std::unique_ptr< QgsProcessingParameterNumber > tolerance = qgis::make_unique < QgsProcessingParameterDistance >( QStringLiteral( "TOLERANCE" ), QObject::tr( "Topology tolerance" ), 0, QStringLiteral( "INPUT" ), false, 0 );
tolerance->setFlags( tolerance->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
addParameter( tolerance.release() );
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Shortest path" ), QgsProcessing::TypeVectorLine ) );
addOutput( new QgsProcessingOutputNumber( QStringLiteral( "TRAVEL_COST" ), QObject::tr( "Travel cost" ) ) );
@ -112,9 +58,7 @@ void QgsShortestPathPointToPointAlgorithm::initAlgorithm( const QVariantMap & )
QVariantMap QgsShortestPathPointToPointAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !source )
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
loadCommonParams( parameters, context, feedback );
QgsFields fields;
fields.append( QgsField( QStringLiteral( "start" ), QVariant::String ) );
@ -122,59 +66,21 @@ QVariantMap QgsShortestPathPointToPointAlgorithm::processAlgorithm( const QVaria
fields.append( QgsField( QStringLiteral( "cost" ), QVariant::Double ) );
QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, QgsWkbTypes::LineString, source->sourceCrs() ) );
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, QgsWkbTypes::LineString, mNetwork->sourceCrs() ) );
if ( !sink )
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
QgsPointXY startPoint = parameterAsPoint( parameters, QStringLiteral( "START_POINT" ), context, source->sourceCrs() );
QgsPointXY endPoint = parameterAsPoint( parameters, QStringLiteral( "END_POINT" ), context, source->sourceCrs() );
int strategy = parameterAsInt( parameters, QStringLiteral( "STRATEGY" ), context );
QString directionFieldName = parameterAsString( parameters, QStringLiteral( "DIRECTION_FIELD" ), context );
QString forwardValue = parameterAsString( parameters, QStringLiteral( "VALUE_FORWARD" ), context );
QString backwardValue = parameterAsString( parameters, QStringLiteral( "VALUE_BACKWARD" ), context );
QString bothValue = parameterAsString( parameters, QStringLiteral( "VALUE_BOTH" ), context );
QgsVectorLayerDirector::Direction defaultDirection = static_cast< QgsVectorLayerDirector::Direction>( parameterAsInt( parameters, QStringLiteral( "DEFAULT_DIRECTION" ), context ) );
QString speedFieldName = parameterAsString( parameters, QStringLiteral( "SPEED_FIELD" ), context );
double defaultSpeed = parameterAsDouble( parameters, QStringLiteral( "DEFAULT_SPEED" ), context );
double tolerance = parameterAsDouble( parameters, QStringLiteral( "TOLERANCE" ), context );
int directionIndex = -1;
if ( !directionFieldName.isEmpty() )
{
directionIndex = source->fields().lookupField( directionFieldName );
}
int speedIndex = -1;
if ( !speedFieldName.isEmpty() )
{
speedIndex = source->fields().lookupField( speedFieldName );
}
QgsVectorLayerDirector *director = new QgsVectorLayerDirector( source.get(), directionIndex, forwardValue, backwardValue, bothValue, defaultDirection );
QgsUnitTypes::DistanceUnit distanceUnits = context.project()->crs().mapUnits();
double multiplier = QgsUnitTypes::fromUnitToUnitFactor( distanceUnits, QgsUnitTypes::DistanceMeters );
if ( strategy )
{
director->addStrategy( new QgsNetworkSpeedStrategy( speedIndex, defaultSpeed, multiplier * 1000.0 / 3600.0 ) );
multiplier = 3600;
}
else
{
director->addStrategy( new QgsNetworkDistanceStrategy() );
}
QgsGraphBuilder builder( source->sourceCrs(), true, tolerance );
QgsPointXY startPoint = parameterAsPoint( parameters, QStringLiteral( "START_POINT" ), context, mNetwork->sourceCrs() );
QgsPointXY endPoint = parameterAsPoint( parameters, QStringLiteral( "END_POINT" ), context, mNetwork->sourceCrs() );
feedback->pushInfo( QObject::tr( "Building graph…" ) );
QVector< QgsPointXY > points;
points << startPoint << endPoint;
QVector< QgsPointXY > snappedPoints;
director->makeGraph( &builder, points, snappedPoints, feedback );
mDirector->makeGraph( mBuilder.get(), points, snappedPoints, feedback );
feedback->pushInfo( QObject::tr( "Calculating shortest path…" ) );
QgsGraph *graph = builder.graph();
QgsGraph *graph = mBuilder->graph();
int idxStart = graph->findVertex( snappedPoints[0] );
int idxEnd = graph->findVertex( snappedPoints[1] );
@ -201,14 +107,14 @@ QVariantMap QgsShortestPathPointToPointAlgorithm::processAlgorithm( const QVaria
QgsFeature feat;
feat.setFields( fields );
QgsAttributes attributes;
attributes << startPoint.toString() << endPoint.toString() << cost / multiplier;
attributes << startPoint.toString() << endPoint.toString() << cost / mMultiplier;
feat.setGeometry( geom );
feat.setAttributes( attributes );
sink->addFeature( feat, QgsFeatureSink::FastInsert );
QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
outputs.insert( QStringLiteral( "TRAVEL_COST" ), cost / multiplier );
outputs.insert( QStringLiteral( "TRAVEL_COST" ), cost / mMultiplier );
return outputs;
}

View File

@ -21,27 +21,23 @@
#define SIP_NO_FILE
#include "qgis.h"
#include "qgsprocessingalgorithm.h"
#include "qgsalgorithmnetworkanalysisbase.h"
///@cond PRIVATE
/**
* Native shortest path (point to point) algorithm.
*/
class QgsShortestPathPointToPointAlgorithm : public QgsProcessingAlgorithm
class QgsShortestPathPointToPointAlgorithm : public QgsNetworkAnalysisAlgorithmBase
{
public:
QgsShortestPathPointToPointAlgorithm() = default;
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmNetworkAnalysis.svg" ) ); }
QString svgIconPath() const override { return QgsApplication::iconPath( QStringLiteral( "/algorithms/mAlgorithmNetworkAnalysis.svg" ) ); }
QString name() const override;
QString displayName() const override;
QStringList tags() const override;
QString group() const override;
QString groupId() const override;
QString shortHelpString() const override;
QgsShortestPathPointToPointAlgorithm *createInstance() const override SIP_FACTORY;