mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-04 00:04:03 -04:00
refactor(processing): add QgsAlgorithmTransectBase used for fixed distance and 'normal' one
This commit is contained in:
parent
31a20c0cad
commit
c08ce4c652
@ -289,6 +289,7 @@ set(QGIS_ANALYSIS_SRCS
|
||||
processing/qgsalgorithmtaperedbuffer.cpp
|
||||
processing/qgsalgorithmtinmeshcreation.cpp
|
||||
processing/qgsalgorithmtransect.cpp
|
||||
processing/qgsalgorithmtransectbase.cpp
|
||||
processing/qgsalgorithmtransectfixeddistance.cpp
|
||||
processing/qgsalgorithmtransform.cpp
|
||||
processing/qgsalgorithmtranslate.cpp
|
||||
|
@ -31,40 +31,6 @@ QString QgsTransectAlgorithm::displayName() const
|
||||
return QObject::tr( "Transect" );
|
||||
}
|
||||
|
||||
QStringList QgsTransectAlgorithm::tags() const
|
||||
{
|
||||
return QObject::tr( "transect,station,lines,extend," ).split( ',' );
|
||||
}
|
||||
|
||||
QString QgsTransectAlgorithm::group() const
|
||||
{
|
||||
return QObject::tr( "Vector geometry" );
|
||||
}
|
||||
|
||||
QString QgsTransectAlgorithm::groupId() const
|
||||
{
|
||||
return QStringLiteral( "vectorgeometry" );
|
||||
}
|
||||
|
||||
void QgsTransectAlgorithm::initAlgorithm( const QVariantMap & )
|
||||
{
|
||||
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) ) );
|
||||
auto length = std::make_unique<QgsProcessingParameterDistance>( QStringLiteral( "LENGTH" ), QObject::tr( "Length of the transect" ), 5.0, QStringLiteral( "INPUT" ), false, 0 );
|
||||
length->setIsDynamic( true );
|
||||
length->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "LENGTH" ), QObject::tr( "Length of the transect" ), QgsPropertyDefinition::DoublePositive ) );
|
||||
length->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
|
||||
addParameter( length.release() );
|
||||
|
||||
auto angle = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "ANGLE" ), QObject::tr( "Angle in degrees from the original line at the vertices" ), Qgis::ProcessingNumberParameterType::Double, 90.0, false, 0, 360 );
|
||||
angle->setIsDynamic( true );
|
||||
angle->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "ANGLE" ), QObject::tr( "Angle in degrees" ), QgsPropertyDefinition::Double ) );
|
||||
angle->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
|
||||
addParameter( angle.release() );
|
||||
|
||||
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "SIDE" ), QObject::tr( "Side to create the transects" ), QStringList() << QObject::tr( "Left" ) << QObject::tr( "Right" ) << QObject::tr( "Both" ), false ) );
|
||||
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Transect" ), Qgis::ProcessingSourceType::VectorLine ) );
|
||||
}
|
||||
|
||||
QString QgsTransectAlgorithm::shortHelpString() const
|
||||
{
|
||||
return QObject::tr( "This algorithm creates transects on vertices for (multi)linestrings.\n" )
|
||||
@ -79,156 +45,37 @@ QString QgsTransectAlgorithm::shortHelpString() const
|
||||
+ QObject::tr( "- TR_ORIENT: Side of the transect (only on the left or right of the line, or both side)\n" );
|
||||
}
|
||||
|
||||
QString QgsTransectAlgorithm::shortDescription() const
|
||||
{
|
||||
return QObject::tr( "Creates transects on vertices for (multi)linestrings." );
|
||||
}
|
||||
|
||||
Qgis::ProcessingAlgorithmDocumentationFlags QgsTransectAlgorithm::documentationFlags() const
|
||||
{
|
||||
return Qgis::ProcessingAlgorithmDocumentationFlag::RegeneratesPrimaryKey;
|
||||
}
|
||||
|
||||
QgsTransectAlgorithm *QgsTransectAlgorithm::createInstance() const
|
||||
{
|
||||
return new QgsTransectAlgorithm();
|
||||
}
|
||||
|
||||
QVariantMap QgsTransectAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
|
||||
void QgsTransectAlgorithm::addAlgorithmParams()
|
||||
{
|
||||
const Side orientation = static_cast<QgsTransectAlgorithm::Side>( parameterAsInt( parameters, QStringLiteral( "SIDE" ), context ) );
|
||||
const double angle = fabs( parameterAsDouble( parameters, QStringLiteral( "ANGLE" ), context ) );
|
||||
const bool dynamicAngle = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "ANGLE" ) );
|
||||
QgsProperty angleProperty;
|
||||
if ( dynamicAngle )
|
||||
angleProperty = parameters.value( QStringLiteral( "ANGLE" ) ).value<QgsProperty>();
|
||||
|
||||
double length = parameterAsDouble( parameters, QStringLiteral( "LENGTH" ), context );
|
||||
const bool dynamicLength = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "LENGTH" ) );
|
||||
QgsProperty lengthProperty;
|
||||
if ( dynamicLength )
|
||||
lengthProperty = parameters.value( QStringLiteral( "LENGTH" ) ).value<QgsProperty>();
|
||||
|
||||
if ( orientation == QgsTransectAlgorithm::Both )
|
||||
length /= 2.0;
|
||||
|
||||
std::unique_ptr<QgsFeatureSource> source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
|
||||
if ( !source )
|
||||
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
|
||||
|
||||
QgsExpressionContext expressionContext = createExpressionContext( parameters, context, dynamic_cast<QgsProcessingFeatureSource *>( source.get() ) );
|
||||
|
||||
QgsFields newFields;
|
||||
newFields.append( QgsField( QStringLiteral( "TR_FID" ), QMetaType::Type::Int, QString(), 20 ) );
|
||||
newFields.append( QgsField( QStringLiteral( "TR_ID" ), QMetaType::Type::Int, QString(), 20 ) );
|
||||
newFields.append( QgsField( QStringLiteral( "TR_SEGMENT" ), QMetaType::Type::Int, QString(), 20 ) );
|
||||
newFields.append( QgsField( QStringLiteral( "TR_ANGLE" ), QMetaType::Type::Double, QString(), 5, 2 ) );
|
||||
newFields.append( QgsField( QStringLiteral( "TR_LENGTH" ), QMetaType::Type::Double, QString(), 20, 6 ) );
|
||||
newFields.append( QgsField( QStringLiteral( "TR_ORIENT" ), QMetaType::Type::Int, QString(), 1 ) );
|
||||
QgsFields fields = QgsProcessingUtils::combineFields( source->fields(), newFields );
|
||||
|
||||
Qgis::WkbType outputWkb = Qgis::WkbType::LineString;
|
||||
if ( QgsWkbTypes::hasZ( source->wkbType() ) )
|
||||
outputWkb = QgsWkbTypes::addZ( outputWkb );
|
||||
if ( QgsWkbTypes::hasM( source->wkbType() ) )
|
||||
outputWkb = QgsWkbTypes::addM( outputWkb );
|
||||
|
||||
QString dest;
|
||||
std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, outputWkb, source->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) );
|
||||
if ( !sink )
|
||||
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
|
||||
|
||||
QgsFeatureIterator features = source->getFeatures();
|
||||
|
||||
int current = -1;
|
||||
int number = 0;
|
||||
const double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
|
||||
QgsFeature feat;
|
||||
|
||||
|
||||
while ( features.nextFeature( feat ) )
|
||||
{
|
||||
current++;
|
||||
if ( feedback->isCanceled() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
feedback->setProgress( current * step );
|
||||
if ( !feat.hasGeometry() )
|
||||
continue;
|
||||
|
||||
QgsGeometry inputGeometry = feat.geometry();
|
||||
|
||||
if ( dynamicLength || dynamicAngle )
|
||||
{
|
||||
expressionContext.setFeature( feat );
|
||||
}
|
||||
|
||||
double evaluatedLength = length;
|
||||
if ( dynamicLength )
|
||||
evaluatedLength = lengthProperty.valueAsDouble( context.expressionContext(), length );
|
||||
double evaluatedAngle = angle;
|
||||
if ( dynamicAngle )
|
||||
evaluatedAngle = angleProperty.valueAsDouble( context.expressionContext(), angle );
|
||||
|
||||
inputGeometry.convertToMultiType();
|
||||
const QgsMultiLineString *multiLine = static_cast<const QgsMultiLineString *>( inputGeometry.constGet() );
|
||||
for ( int id = 0; id < multiLine->numGeometries(); ++id )
|
||||
{
|
||||
const QgsLineString *line = multiLine->lineStringN( id );
|
||||
QgsAbstractGeometry::vertex_iterator it = line->vertices_begin();
|
||||
while ( it != line->vertices_end() )
|
||||
{
|
||||
const QgsVertexId vertexId = it.vertexId();
|
||||
const int i = vertexId.vertex;
|
||||
QgsFeature outFeat;
|
||||
QgsAttributes attrs = feat.attributes();
|
||||
attrs << current << number << i + 1 << evaluatedAngle << ( ( orientation == QgsTransectAlgorithm::Both ) ? evaluatedLength * 2 : evaluatedLength ) << orientation;
|
||||
outFeat.setAttributes( attrs );
|
||||
const double angleAtVertex = line->vertexAngle( vertexId );
|
||||
outFeat.setGeometry( calcTransect( *it, angleAtVertex, evaluatedLength, orientation, evaluatedAngle ) );
|
||||
if ( !sink->addFeature( outFeat, QgsFeatureSink::FastInsert ) )
|
||||
throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
|
||||
number++;
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sink->finalize();
|
||||
|
||||
QVariantMap outputs;
|
||||
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
|
||||
return outputs;
|
||||
// No additional parameters for the basic transect algorithm (vertex-based only)
|
||||
}
|
||||
|
||||
|
||||
QgsGeometry QgsTransectAlgorithm::calcTransect( const QgsPoint &point, const double angleAtVertex, const double length, const QgsTransectAlgorithm::Side orientation, const double angle )
|
||||
bool QgsTransectAlgorithm::prepareAlgorithmTransectParameters( const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * )
|
||||
{
|
||||
QgsPoint pLeft; // left point of the line
|
||||
QgsPoint pRight; // right point of the line
|
||||
|
||||
QgsPolyline line;
|
||||
|
||||
if ( ( orientation == QgsTransectAlgorithm::Right ) || ( orientation == QgsTransectAlgorithm::Both ) )
|
||||
{
|
||||
pLeft = point.project( length, angle + 180.0 / M_PI * angleAtVertex );
|
||||
if ( orientation != QgsTransectAlgorithm::Both )
|
||||
pRight = point;
|
||||
}
|
||||
|
||||
if ( ( orientation == QgsTransectAlgorithm::Left ) || ( orientation == QgsTransectAlgorithm::Both ) )
|
||||
{
|
||||
pRight = point.project( -length, angle + 180.0 / M_PI * angleAtVertex );
|
||||
if ( orientation != QgsTransectAlgorithm::Both )
|
||||
pLeft = point;
|
||||
}
|
||||
|
||||
line.append( pLeft );
|
||||
line.append( pRight );
|
||||
|
||||
return QgsGeometry::fromPolyline( line );
|
||||
// No additional preparation needed for basic transect algorithm
|
||||
return true;
|
||||
}
|
||||
|
||||
///@endcond
|
||||
std::vector<QgsPoint> QgsTransectAlgorithm::generateSamplingPoints( const QgsLineString &line, const QVariantMap &, QgsProcessingContext & )
|
||||
{
|
||||
std::vector<QgsPoint> samplingPoints;
|
||||
|
||||
// Vertex-based sampling only (like original master algorithm)
|
||||
for ( auto it = line.vertices_begin(); it != line.vertices_end(); ++it )
|
||||
samplingPoints.push_back( *it );
|
||||
|
||||
return samplingPoints;
|
||||
}
|
||||
|
||||
double QgsTransectAlgorithm::calculateAzimuth( const QgsLineString &line, const QgsPoint &, int pointIndex )
|
||||
{
|
||||
// For vertex-based sampling, use vertex angle directly (like original master algorithm)
|
||||
return line.vertexAngle( QgsVertexId( 0, 0, pointIndex ) );
|
||||
}
|
||||
|
||||
///@endcond
|
@ -21,50 +21,27 @@
|
||||
#define SIP_NO_FILE
|
||||
|
||||
#include "qgis_sip.h"
|
||||
#include "qgsprocessingalgorithm.h"
|
||||
#include "qgsalgorithmtransectbase.h"
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
/**
|
||||
* Native transect algorithm.
|
||||
*/
|
||||
class QgsTransectAlgorithm : public QgsProcessingAlgorithm
|
||||
class QgsTransectAlgorithm : public QgsTransectAlgorithmBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Draw the transect on which side of the line
|
||||
*/
|
||||
enum Side
|
||||
{
|
||||
Left,
|
||||
Right,
|
||||
Both
|
||||
};
|
||||
QgsTransectAlgorithm() = default;
|
||||
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
|
||||
QString name() const override;
|
||||
QString displayName() const override;
|
||||
QStringList tags() const override;
|
||||
QString group() const override;
|
||||
QString groupId() const override;
|
||||
QString shortHelpString() const override;
|
||||
QString shortDescription() const override;
|
||||
Qgis::ProcessingAlgorithmDocumentationFlags documentationFlags() const override;
|
||||
QgsTransectAlgorithm *createInstance() const override SIP_FACTORY;
|
||||
|
||||
protected:
|
||||
QVariantMap processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns the transect of the point \a point with \a length, \a orientation and \a angle.
|
||||
* \param point The vertex
|
||||
* \param angleAtVertex Angle at the vertex
|
||||
* \param length Length of the transect Distance to extend line from input feature
|
||||
* \param orientation Orientation of the transect
|
||||
* \param angle Angle of the transect relative to the segment [\a p1 - \a p2] (degrees clockwise)
|
||||
*/
|
||||
QgsGeometry calcTransect( const QgsPoint &point, double angleAtVertex, double length, Side orientation, double angle );
|
||||
void addAlgorithmParams() override;
|
||||
bool prepareAlgorithmTransectParameters( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
|
||||
std::vector<QgsPoint> generateSamplingPoints( const QgsLineString &line, const QVariantMap ¶meters, QgsProcessingContext &context ) override;
|
||||
double calculateAzimuth( const QgsLineString &line, const QgsPoint &point, int pointIndex ) override;
|
||||
};
|
||||
|
||||
///@endcond PRIVATE
|
||||
|
221
src/analysis/processing/qgsalgorithmtransectbase.cpp
Normal file
221
src/analysis/processing/qgsalgorithmtransectbase.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
/***************************************************************************
|
||||
qgsalgorithmtransectbase.cpp
|
||||
----------------------------
|
||||
begin : September 2025
|
||||
copyright : (C) 2025 by Loïc Bartoletti
|
||||
email : loic dot bartoletti at oslandia 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 "qgsalgorithmtransectbase.h"
|
||||
#include "qgsmultilinestring.h"
|
||||
#include "qgslinestring.h"
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
QString QgsTransectAlgorithmBase::group() const
|
||||
{
|
||||
return QObject::tr( "Vector geometry" );
|
||||
}
|
||||
|
||||
QString QgsTransectAlgorithmBase::groupId() const
|
||||
{
|
||||
return QStringLiteral( "vectorgeometry" );
|
||||
}
|
||||
|
||||
QStringList QgsTransectAlgorithmBase::tags() const
|
||||
{
|
||||
return QObject::tr( "transect,station,lines,extend" ).split( ',' );
|
||||
}
|
||||
|
||||
QString QgsTransectAlgorithmBase::shortDescription() const
|
||||
{
|
||||
return QObject::tr( "Creates transects for (multi)linestrings." );
|
||||
}
|
||||
|
||||
Qgis::ProcessingAlgorithmDocumentationFlags QgsTransectAlgorithmBase::documentationFlags() const
|
||||
{
|
||||
return Qgis::ProcessingAlgorithmDocumentationFlag::RegeneratesPrimaryKey;
|
||||
}
|
||||
|
||||
void QgsTransectAlgorithmBase::initAlgorithm( const QVariantMap & )
|
||||
{
|
||||
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) ) );
|
||||
|
||||
auto length = std::make_unique<QgsProcessingParameterDistance>( QStringLiteral( "LENGTH" ), QObject::tr( "Length of the transect" ), 5.0, QStringLiteral( "INPUT" ), false, 0 );
|
||||
length->setIsDynamic( true );
|
||||
length->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "LENGTH" ), QObject::tr( "Length of the transect" ), QgsPropertyDefinition::DoublePositive ) );
|
||||
length->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
|
||||
addParameter( length.release() );
|
||||
|
||||
auto angle = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "ANGLE" ), QObject::tr( "Angle in degrees from the original line at the vertices" ), Qgis::ProcessingNumberParameterType::Double, 90.0, false, 0, 360 );
|
||||
angle->setIsDynamic( true );
|
||||
angle->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "ANGLE" ), QObject::tr( "Angle in degrees" ), QgsPropertyDefinition::Double ) );
|
||||
angle->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
|
||||
addParameter( angle.release() );
|
||||
|
||||
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "SIDE" ), QObject::tr( "Side to create the transects" ), QStringList() << QObject::tr( "Left" ) << QObject::tr( "Right" ) << QObject::tr( "Both" ), false ) );
|
||||
|
||||
// Allow subclasses to add their specific parameters
|
||||
addAlgorithmParams();
|
||||
|
||||
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Transect" ), Qgis::ProcessingSourceType::VectorLine ) );
|
||||
}
|
||||
|
||||
QVariantMap QgsTransectAlgorithmBase::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
|
||||
{
|
||||
mOrientation = static_cast<QgsTransectAlgorithmBase::Side>( parameterAsInt( parameters, QStringLiteral( "SIDE" ), context ) );
|
||||
mAngle = fabs( parameterAsDouble( parameters, QStringLiteral( "ANGLE" ), context ) );
|
||||
mDynamicAngle = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "ANGLE" ) );
|
||||
if ( mDynamicAngle )
|
||||
mAngleProperty = parameters.value( QStringLiteral( "ANGLE" ) ).value<QgsProperty>();
|
||||
|
||||
mLength = parameterAsDouble( parameters, QStringLiteral( "LENGTH" ), context );
|
||||
mDynamicLength = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "LENGTH" ) );
|
||||
if ( mDynamicLength )
|
||||
mLengthProperty = parameters.value( QStringLiteral( "LENGTH" ) ).value<QgsProperty>();
|
||||
|
||||
if ( mOrientation == QgsTransectAlgorithmBase::Both )
|
||||
mLength /= 2.0;
|
||||
|
||||
// Let subclass prepare their specific parameters
|
||||
if ( !prepareAlgorithmTransectParameters( parameters, context, feedback ) )
|
||||
return QVariantMap();
|
||||
|
||||
std::unique_ptr<QgsFeatureSource> source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
|
||||
if ( !source )
|
||||
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
|
||||
|
||||
QgsExpressionContext expressionContext = createExpressionContext( parameters, context, dynamic_cast<QgsProcessingFeatureSource *>( source.get() ) );
|
||||
|
||||
QgsFields newFields;
|
||||
newFields.append( QgsField( QStringLiteral( "TR_FID" ), QMetaType::Type::Int, QString(), 20 ) );
|
||||
newFields.append( QgsField( QStringLiteral( "TR_ID" ), QMetaType::Type::Int, QString(), 20 ) );
|
||||
newFields.append( QgsField( QStringLiteral( "TR_SEGMENT" ), QMetaType::Type::Int, QString(), 20 ) );
|
||||
newFields.append( QgsField( QStringLiteral( "TR_ANGLE" ), QMetaType::Type::Double, QString(), 5, 2 ) );
|
||||
newFields.append( QgsField( QStringLiteral( "TR_LENGTH" ), QMetaType::Type::Double, QString(), 20, 6 ) );
|
||||
newFields.append( QgsField( QStringLiteral( "TR_ORIENT" ), QMetaType::Type::Int, QString(), 1 ) );
|
||||
QgsFields fields = QgsProcessingUtils::combineFields( source->fields(), newFields );
|
||||
|
||||
Qgis::WkbType outputWkb = Qgis::WkbType::LineString;
|
||||
if ( QgsWkbTypes::hasZ( source->wkbType() ) )
|
||||
outputWkb = QgsWkbTypes::addZ( outputWkb );
|
||||
if ( QgsWkbTypes::hasM( source->wkbType() ) )
|
||||
outputWkb = QgsWkbTypes::addM( outputWkb );
|
||||
|
||||
QString dest;
|
||||
std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, outputWkb, source->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) );
|
||||
if ( !sink )
|
||||
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
|
||||
|
||||
QgsFeatureIterator features = source->getFeatures();
|
||||
|
||||
int current = -1;
|
||||
int number = 0;
|
||||
const double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
|
||||
QgsFeature feat;
|
||||
|
||||
while ( features.nextFeature( feat ) )
|
||||
{
|
||||
current++;
|
||||
if ( feedback->isCanceled() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
feedback->setProgress( current * step );
|
||||
if ( !feat.hasGeometry() )
|
||||
continue;
|
||||
|
||||
QgsGeometry inputGeometry = feat.geometry();
|
||||
|
||||
if ( mDynamicLength || mDynamicAngle )
|
||||
{
|
||||
expressionContext.setFeature( feat );
|
||||
}
|
||||
|
||||
double evaluatedLength = mLength;
|
||||
if ( mDynamicLength )
|
||||
evaluatedLength = mLengthProperty.valueAsDouble( context.expressionContext(), mLength );
|
||||
double evaluatedAngle = mAngle;
|
||||
if ( mDynamicAngle )
|
||||
evaluatedAngle = mAngleProperty.valueAsDouble( context.expressionContext(), mAngle );
|
||||
|
||||
inputGeometry.convertToMultiType();
|
||||
const QgsMultiLineString *multiLine = static_cast<const QgsMultiLineString *>( inputGeometry.constGet() );
|
||||
|
||||
for ( int part = 0; part < multiLine->numGeometries(); ++part )
|
||||
{
|
||||
const QgsLineString *lineString = multiLine->lineStringN( part );
|
||||
if ( !lineString )
|
||||
continue;
|
||||
|
||||
QgsLineString line = *lineString;
|
||||
|
||||
// Let subclass generate sampling points using their specific strategy
|
||||
std::vector<QgsPoint> samplingPoints = generateSamplingPoints( line, parameters, context );
|
||||
|
||||
for ( int i = 0; i < static_cast<int>( samplingPoints.size() ); ++i )
|
||||
{
|
||||
const QgsPoint &pt = samplingPoints[i];
|
||||
|
||||
// Let subclass calculate azimuth using their specific method
|
||||
double azimuth = calculateAzimuth( line, pt, i );
|
||||
|
||||
QgsFeature outFeat;
|
||||
QgsAttributes attrs = feat.attributes();
|
||||
attrs << current << number << i + 1 << evaluatedAngle
|
||||
<< ( ( mOrientation == QgsTransectAlgorithmBase::Both ) ? evaluatedLength * 2 : evaluatedLength )
|
||||
<< static_cast<int>( mOrientation );
|
||||
outFeat.setAttributes( attrs );
|
||||
outFeat.setGeometry( calcTransect( pt, azimuth, evaluatedLength, mOrientation, evaluatedAngle ) );
|
||||
if ( !sink->addFeature( outFeat, QgsFeatureSink::FastInsert ) )
|
||||
throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
|
||||
number++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sink->finalize();
|
||||
|
||||
QVariantMap outputs;
|
||||
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
|
||||
return outputs;
|
||||
}
|
||||
|
||||
QgsGeometry QgsTransectAlgorithmBase::calcTransect( const QgsPoint &point, const double angleAtVertex, const double length, const QgsTransectAlgorithmBase::Side orientation, const double angle )
|
||||
{
|
||||
QgsPoint pLeft; // left point of the line
|
||||
QgsPoint pRight; // right point of the line
|
||||
|
||||
QgsPolyline line;
|
||||
|
||||
if ( ( orientation == QgsTransectAlgorithmBase::Right ) || ( orientation == QgsTransectAlgorithmBase::Both ) )
|
||||
{
|
||||
pLeft = point.project( length, angle + 180.0 / M_PI * angleAtVertex );
|
||||
if ( orientation != QgsTransectAlgorithmBase::Both )
|
||||
pRight = point;
|
||||
}
|
||||
|
||||
if ( ( orientation == QgsTransectAlgorithmBase::Left ) || ( orientation == QgsTransectAlgorithmBase::Both ) )
|
||||
{
|
||||
pRight = point.project( -length, angle + 180.0 / M_PI * angleAtVertex );
|
||||
if ( orientation != QgsTransectAlgorithmBase::Both )
|
||||
pLeft = point;
|
||||
}
|
||||
|
||||
line.append( pLeft );
|
||||
line.append( pRight );
|
||||
|
||||
return QgsGeometry::fromPolyline( line );
|
||||
}
|
||||
|
||||
///@endcond
|
97
src/analysis/processing/qgsalgorithmtransectbase.h
Normal file
97
src/analysis/processing/qgsalgorithmtransectbase.h
Normal file
@ -0,0 +1,97 @@
|
||||
/***************************************************************************
|
||||
qgsalgorithmtransectbase.h
|
||||
-------------------------
|
||||
begin : September 2025
|
||||
copyright : (C) 2025 by Loïc Bartoletti
|
||||
email : loic dot bartoletti at oslandia 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 QGSALGORITHMTRANSECTBASE_H
|
||||
#define QGSALGORITHMTRANSECTBASE_H
|
||||
|
||||
#define SIP_NO_FILE
|
||||
|
||||
#include "qgis_sip.h"
|
||||
#include "qgsprocessingalgorithm.h"
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
/**
|
||||
* Base class for transect algorithms.
|
||||
*/
|
||||
class QgsTransectAlgorithmBase : public QgsProcessingAlgorithm
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Draw the transect on which side of the line
|
||||
*/
|
||||
enum Side
|
||||
{
|
||||
Left,
|
||||
Right,
|
||||
Both
|
||||
};
|
||||
|
||||
QString group() const final;
|
||||
QString groupId() const final;
|
||||
QStringList tags() const override;
|
||||
QString shortDescription() const override;
|
||||
Qgis::ProcessingAlgorithmDocumentationFlags documentationFlags() const final;
|
||||
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) final;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Adds specific subclass algorithm parameters. The common parameters (INPUT, LENGTH, ANGLE, SIDE, OUTPUT)
|
||||
* are automatically added by the base class.
|
||||
*/
|
||||
virtual void addAlgorithmParams() = 0;
|
||||
|
||||
/**
|
||||
* Prepares the transect algorithm subclass for execution.
|
||||
*/
|
||||
virtual bool prepareAlgorithmTransectParameters( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) = 0;
|
||||
|
||||
/**
|
||||
* Processes a line geometry using the specific sampling strategy implemented in subclasses.
|
||||
*/
|
||||
QVariantMap processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) final;
|
||||
|
||||
/**
|
||||
* Pure virtual method that generates sampling points along a line geometry.
|
||||
* Subclasses implement their specific sampling strategy here.
|
||||
*/
|
||||
virtual std::vector<QgsPoint> generateSamplingPoints( const QgsLineString &line, const QVariantMap ¶meters, QgsProcessingContext &context ) = 0;
|
||||
|
||||
/**
|
||||
* Calculate the azimuth at a given point for transect orientation.
|
||||
* Subclasses can override this if they need different azimuth calculation.
|
||||
*/
|
||||
virtual double calculateAzimuth( const QgsLineString &line, const QgsPoint &point, int pointIndex ) = 0;
|
||||
|
||||
/**
|
||||
* Returns the transect geometry at the specified point.
|
||||
*/
|
||||
static QgsGeometry calcTransect( const QgsPoint &point, double angleAtVertex, double length, Side orientation, double angle );
|
||||
|
||||
// Shared member variables accessible to subclasses
|
||||
Side mOrientation = Both;
|
||||
double mAngle = 90.0;
|
||||
double mLength = 5.0;
|
||||
bool mDynamicAngle = false;
|
||||
bool mDynamicLength = false;
|
||||
QgsProperty mAngleProperty;
|
||||
QgsProperty mLengthProperty;
|
||||
};
|
||||
|
||||
///@endcond PRIVATE
|
||||
|
||||
#endif // QGSALGORITHMTRANSECTBASE_H
|
@ -1,9 +1,9 @@
|
||||
/***************************************************************************
|
||||
qgsalgorithmtransectfixeddistance.cpp
|
||||
-------------------------------------
|
||||
begin : September 2024
|
||||
copyright : (C) 2024 by Loïc Bartoletti
|
||||
email : lbartoletti at tuxfamily dot org
|
||||
begin : September 2025
|
||||
copyright : (C) 2025 by Loïc Bartoletti
|
||||
email : loic dot bartoletti at oslandia dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
@ -36,38 +36,6 @@ QStringList QgsTransectFixedDistanceAlgorithm::tags() const
|
||||
return QObject::tr( "transect,station,lines,extend,fixed,interval,distance" ).split( ',' );
|
||||
}
|
||||
|
||||
QString QgsTransectFixedDistanceAlgorithm::group() const
|
||||
{
|
||||
return QObject::tr( "Vector geometry" );
|
||||
}
|
||||
|
||||
QString QgsTransectFixedDistanceAlgorithm::groupId() const
|
||||
{
|
||||
return QStringLiteral( "vectorgeometry" );
|
||||
}
|
||||
|
||||
void QgsTransectFixedDistanceAlgorithm::initAlgorithm( const QVariantMap & )
|
||||
{
|
||||
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ), QList<int>() << static_cast<int>( Qgis::ProcessingSourceType::VectorLine ) ) );
|
||||
auto length = std::make_unique<QgsProcessingParameterDistance>( QStringLiteral( "LENGTH" ), QObject::tr( "Length of the transect" ), 5.0, QStringLiteral( "INPUT" ), false, 0 );
|
||||
length->setIsDynamic( true );
|
||||
length->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "LENGTH" ), QObject::tr( "Length of the transect" ), QgsPropertyDefinition::DoublePositive ) );
|
||||
length->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
|
||||
addParameter( length.release() );
|
||||
|
||||
auto angle = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "ANGLE" ), QObject::tr( "Angle in degrees from the original line at the vertices" ), Qgis::ProcessingNumberParameterType::Double, 90.0, false, 0, 360 );
|
||||
angle->setIsDynamic( true );
|
||||
angle->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "ANGLE" ), QObject::tr( "Angle in degrees" ), QgsPropertyDefinition::Double ) );
|
||||
angle->setDynamicLayerParameterName( QStringLiteral( "INPUT" ) );
|
||||
addParameter( angle.release() );
|
||||
|
||||
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "SIDE" ), QObject::tr( "Side to create the transects" ), QStringList() << QObject::tr( "Left" ) << QObject::tr( "Right" ) << QObject::tr( "Both" ), false ) );
|
||||
|
||||
addParameter( new QgsProcessingParameterNumber( QStringLiteral( "INTERVAL" ), QObject::tr( "Fixed sampling interval" ), Qgis::ProcessingNumberParameterType::Double, 10.0, false, 0 ) );
|
||||
|
||||
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Transect" ), Qgis::ProcessingSourceType::VectorLine ) );
|
||||
}
|
||||
|
||||
QString QgsTransectFixedDistanceAlgorithm::shortHelpString() const
|
||||
{
|
||||
return QObject::tr( "This algorithm creates transects at fixed distance intervals along (multi)linestrings.\n" )
|
||||
@ -87,172 +55,45 @@ QString QgsTransectFixedDistanceAlgorithm::shortDescription() const
|
||||
return QObject::tr( "Creates transects at fixed distance intervals along (multi)linestrings." );
|
||||
}
|
||||
|
||||
Qgis::ProcessingAlgorithmDocumentationFlags QgsTransectFixedDistanceAlgorithm::documentationFlags() const
|
||||
{
|
||||
return Qgis::ProcessingAlgorithmDocumentationFlag::RegeneratesPrimaryKey;
|
||||
}
|
||||
|
||||
QgsTransectFixedDistanceAlgorithm *QgsTransectFixedDistanceAlgorithm::createInstance() const
|
||||
{
|
||||
return new QgsTransectFixedDistanceAlgorithm();
|
||||
}
|
||||
|
||||
QVariantMap QgsTransectFixedDistanceAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
|
||||
void QgsTransectFixedDistanceAlgorithm::addAlgorithmParams()
|
||||
{
|
||||
const Side orientation = static_cast<QgsTransectFixedDistanceAlgorithm::Side>( parameterAsInt( parameters, QStringLiteral( "SIDE" ), context ) );
|
||||
const double angle = fabs( parameterAsDouble( parameters, QStringLiteral( "ANGLE" ), context ) );
|
||||
const bool dynamicAngle = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "ANGLE" ) );
|
||||
QgsProperty angleProperty;
|
||||
if ( dynamicAngle )
|
||||
angleProperty = parameters.value( QStringLiteral( "ANGLE" ) ).value<QgsProperty>();
|
||||
|
||||
double length = parameterAsDouble( parameters, QStringLiteral( "LENGTH" ), context );
|
||||
const bool dynamicLength = QgsProcessingParameters::isDynamic( parameters, QStringLiteral( "LENGTH" ) );
|
||||
QgsProperty lengthProperty;
|
||||
if ( dynamicLength )
|
||||
lengthProperty = parameters.value( QStringLiteral( "LENGTH" ) ).value<QgsProperty>();
|
||||
|
||||
const double interval = parameterAsDouble( parameters, QStringLiteral( "INTERVAL" ), context );
|
||||
|
||||
if ( orientation == QgsTransectFixedDistanceAlgorithm::Both )
|
||||
length /= 2.0;
|
||||
|
||||
std::unique_ptr<QgsFeatureSource> source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
|
||||
if ( !source )
|
||||
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
|
||||
|
||||
QgsExpressionContext expressionContext = createExpressionContext( parameters, context, dynamic_cast<QgsProcessingFeatureSource *>( source.get() ) );
|
||||
|
||||
QgsFields newFields;
|
||||
newFields.append( QgsField( QStringLiteral( "TR_FID" ), QMetaType::Type::Int, QString(), 20 ) );
|
||||
newFields.append( QgsField( QStringLiteral( "TR_ID" ), QMetaType::Type::Int, QString(), 20 ) );
|
||||
newFields.append( QgsField( QStringLiteral( "TR_SEGMENT" ), QMetaType::Type::Int, QString(), 20 ) );
|
||||
newFields.append( QgsField( QStringLiteral( "TR_ANGLE" ), QMetaType::Type::Double, QString(), 5, 2 ) );
|
||||
newFields.append( QgsField( QStringLiteral( "TR_LENGTH" ), QMetaType::Type::Double, QString(), 20, 6 ) );
|
||||
newFields.append( QgsField( QStringLiteral( "TR_ORIENT" ), QMetaType::Type::Int, QString(), 1 ) );
|
||||
QgsFields fields = QgsProcessingUtils::combineFields( source->fields(), newFields );
|
||||
|
||||
Qgis::WkbType outputWkb = Qgis::WkbType::LineString;
|
||||
if ( QgsWkbTypes::hasZ( source->wkbType() ) )
|
||||
outputWkb = QgsWkbTypes::addZ( outputWkb );
|
||||
if ( QgsWkbTypes::hasM( source->wkbType() ) )
|
||||
outputWkb = QgsWkbTypes::addM( outputWkb );
|
||||
|
||||
QString dest;
|
||||
std::unique_ptr<QgsFeatureSink> sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, outputWkb, source->sourceCrs(), QgsFeatureSink::RegeneratePrimaryKey ) );
|
||||
if ( !sink )
|
||||
throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) );
|
||||
|
||||
QgsFeatureIterator features = source->getFeatures();
|
||||
|
||||
int current = -1;
|
||||
int number = 0;
|
||||
const double step = source->featureCount() > 0 ? 100.0 / source->featureCount() : 1;
|
||||
QgsFeature feat;
|
||||
|
||||
while ( features.nextFeature( feat ) )
|
||||
{
|
||||
current++;
|
||||
if ( feedback->isCanceled() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
feedback->setProgress( current * step );
|
||||
if ( !feat.hasGeometry() )
|
||||
continue;
|
||||
|
||||
QgsGeometry inputGeometry = feat.geometry();
|
||||
|
||||
if ( dynamicLength || dynamicAngle )
|
||||
{
|
||||
expressionContext.setFeature( feat );
|
||||
}
|
||||
|
||||
double evaluatedLength = length;
|
||||
if ( dynamicLength )
|
||||
evaluatedLength = lengthProperty.valueAsDouble( context.expressionContext(), length );
|
||||
double evaluatedAngle = angle;
|
||||
if ( dynamicAngle )
|
||||
evaluatedAngle = angleProperty.valueAsDouble( context.expressionContext(), angle );
|
||||
|
||||
inputGeometry.convertToMultiType();
|
||||
const QgsMultiLineString *multiLine = static_cast<const QgsMultiLineString *>( inputGeometry.constGet() );
|
||||
|
||||
for ( int part = 0; part < multiLine->numGeometries(); ++part )
|
||||
{
|
||||
const QgsLineString *lineString = multiLine->lineStringN( part );
|
||||
if ( !lineString )
|
||||
continue;
|
||||
|
||||
QgsLineString line = *lineString;
|
||||
std::vector<QgsPoint> samplingPoints;
|
||||
|
||||
// Sample points at fixed intervals
|
||||
double totalLength = line.length();
|
||||
for ( double d = 0; d <= totalLength; d += interval )
|
||||
{
|
||||
QgsPoint *pt = line.interpolatePoint( d );
|
||||
samplingPoints.push_back( *pt );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < static_cast<int>( samplingPoints.size() ); ++i )
|
||||
{
|
||||
const QgsPoint &pt = samplingPoints[i];
|
||||
double azimuth = 0;
|
||||
|
||||
QgsPoint segPt;
|
||||
QgsVertexId vid;
|
||||
line.closestSegment( pt, segPt, vid, nullptr, Qgis::DEFAULT_SEGMENT_EPSILON );
|
||||
QgsVertexId prev( vid.part, vid.ring, vid.vertex - 1 );
|
||||
azimuth = line.vertexAt( prev ).azimuth( line.vertexAt( vid ) ) * M_PI / 180.0;
|
||||
|
||||
QgsFeature outFeat;
|
||||
QgsAttributes attrs = feat.attributes();
|
||||
attrs << current << number << i + 1 << evaluatedAngle
|
||||
<< ( ( orientation == QgsTransectFixedDistanceAlgorithm::Both ) ? evaluatedLength * 2 : evaluatedLength )
|
||||
<< static_cast<int>( orientation );
|
||||
outFeat.setAttributes( attrs );
|
||||
outFeat.setGeometry( calcTransect( pt, azimuth, evaluatedLength, orientation, evaluatedAngle ) );
|
||||
if ( !sink->addFeature( outFeat, QgsFeatureSink::FastInsert ) )
|
||||
throw QgsProcessingException( writeFeatureError( sink.get(), parameters, QStringLiteral( "OUTPUT" ) ) );
|
||||
number++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sink->finalize();
|
||||
|
||||
QVariantMap outputs;
|
||||
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
|
||||
return outputs;
|
||||
addParameter( new QgsProcessingParameterNumber( QStringLiteral( "INTERVAL" ), QObject::tr( "Fixed sampling interval" ), Qgis::ProcessingNumberParameterType::Double, 10.0, false, 0 ) );
|
||||
}
|
||||
|
||||
QgsGeometry QgsTransectFixedDistanceAlgorithm::calcTransect( const QgsPoint &point, const double angleAtVertex, const double length, const QgsTransectFixedDistanceAlgorithm::Side orientation, const double angle )
|
||||
bool QgsTransectFixedDistanceAlgorithm::prepareAlgorithmTransectParameters( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback * )
|
||||
{
|
||||
QgsPoint pLeft; // left point of the line
|
||||
QgsPoint pRight; // right point of the line
|
||||
|
||||
QgsPolyline line;
|
||||
|
||||
if ( ( orientation == QgsTransectFixedDistanceAlgorithm::Right ) || ( orientation == QgsTransectFixedDistanceAlgorithm::Both ) )
|
||||
{
|
||||
pLeft = point.project( length, angle + 180.0 / M_PI * angleAtVertex );
|
||||
if ( orientation != QgsTransectFixedDistanceAlgorithm::Both )
|
||||
pRight = point;
|
||||
}
|
||||
|
||||
if ( ( orientation == QgsTransectFixedDistanceAlgorithm::Left ) || ( orientation == QgsTransectFixedDistanceAlgorithm::Both ) )
|
||||
{
|
||||
pRight = point.project( -length, angle + 180.0 / M_PI * angleAtVertex );
|
||||
if ( orientation != QgsTransectFixedDistanceAlgorithm::Both )
|
||||
pLeft = point;
|
||||
}
|
||||
|
||||
line.append( pLeft );
|
||||
line.append( pRight );
|
||||
|
||||
return QgsGeometry::fromPolyline( line );
|
||||
mInterval = parameterAsDouble( parameters, QStringLiteral( "INTERVAL" ), context );
|
||||
return true;
|
||||
}
|
||||
|
||||
///@endcond
|
||||
std::vector<QgsPoint> QgsTransectFixedDistanceAlgorithm::generateSamplingPoints( const QgsLineString &line, const QVariantMap &, QgsProcessingContext & )
|
||||
{
|
||||
std::vector<QgsPoint> samplingPoints;
|
||||
|
||||
// Sample points at fixed intervals
|
||||
double totalLength = line.length();
|
||||
for ( double d = 0; d <= totalLength; d += mInterval )
|
||||
{
|
||||
QgsPoint *pt = line.interpolatePoint( d );
|
||||
samplingPoints.push_back( *pt );
|
||||
}
|
||||
|
||||
return samplingPoints;
|
||||
}
|
||||
|
||||
double QgsTransectFixedDistanceAlgorithm::calculateAzimuth( const QgsLineString &line, const QgsPoint &point, int )
|
||||
{
|
||||
// For fixed distance sampling, find closest segment
|
||||
QgsPoint segPt;
|
||||
QgsVertexId vid;
|
||||
line.closestSegment( point, segPt, vid, nullptr, Qgis::DEFAULT_SEGMENT_EPSILON );
|
||||
QgsVertexId prev( vid.part, vid.ring, vid.vertex - 1 );
|
||||
return line.vertexAt( prev ).azimuth( line.vertexAt( vid ) ) * M_PI / 180.0;
|
||||
}
|
||||
|
||||
///@endcond
|
||||
|
@ -1,9 +1,9 @@
|
||||
/***************************************************************************
|
||||
qgsalgorithmtransectfixeddistance.h
|
||||
------------------------------------
|
||||
begin : September 2024
|
||||
copyright : (C) 2024 by Loïc Bartoletti
|
||||
email : lbartoletti at tuxfamily dot org
|
||||
begin : September 2025
|
||||
copyright : (C) 2025 by Loïc Bartoletti
|
||||
email : loic dot bartoletti at oslandia dot com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
@ -21,52 +21,34 @@
|
||||
#define SIP_NO_FILE
|
||||
|
||||
#include "qgis_sip.h"
|
||||
#include "qgsprocessingalgorithm.h"
|
||||
#include "qgsalgorithmtransectbase.h"
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
/**
|
||||
* Native transect (fixed distance) algorithm.
|
||||
*/
|
||||
class QgsTransectFixedDistanceAlgorithm : public QgsProcessingAlgorithm
|
||||
class QgsTransectFixedDistanceAlgorithm : public QgsTransectAlgorithmBase
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Draw the transect on which side of the line
|
||||
*/
|
||||
enum Side
|
||||
{
|
||||
Left,
|
||||
Right,
|
||||
Both
|
||||
};
|
||||
QgsTransectFixedDistanceAlgorithm() = default;
|
||||
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
|
||||
QString name() const override;
|
||||
QString displayName() const override;
|
||||
QStringList tags() const override;
|
||||
QString group() const override;
|
||||
QString groupId() const override;
|
||||
QString shortHelpString() const override;
|
||||
QString shortDescription() const override;
|
||||
Qgis::ProcessingAlgorithmDocumentationFlags documentationFlags() const override;
|
||||
QgsTransectFixedDistanceAlgorithm *createInstance() const override SIP_FACTORY;
|
||||
|
||||
protected:
|
||||
QVariantMap processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
|
||||
void addAlgorithmParams() override;
|
||||
bool prepareAlgorithmTransectParameters( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
|
||||
std::vector<QgsPoint> generateSamplingPoints( const QgsLineString &line, const QVariantMap ¶meters, QgsProcessingContext &context ) override;
|
||||
double calculateAzimuth( const QgsLineString &line, const QgsPoint &point, int pointIndex ) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns the transect of the point \a point with \a length, \a orientation and \a angle.
|
||||
* \param point The vertex
|
||||
* \param angleAtVertex Angle at the vertex
|
||||
* \param length Length of the transect Distance to extend line from input feature
|
||||
* \param orientation Orientation of the transect
|
||||
* \param angle Angle of the transect relative to the segment [\a p1 - \a p2] (degrees clockwise)
|
||||
*/
|
||||
QgsGeometry calcTransect( const QgsPoint &point, double angleAtVertex, double length, Side orientation, double angle );
|
||||
double mInterval = 10.0;
|
||||
};
|
||||
|
||||
///@endcond PRIVATE
|
||||
|
||||
#endif // QGSALGORITHMTRANSECTFIXEDDISTANCE_H
|
||||
#endif // QGSALGORITHMTRANSECTFIXEDDISTANCE_H
|
||||
|
Loading…
x
Reference in New Issue
Block a user