Skip to content
Permalink
Browse files
Use a QgsFeatureSink instead of path to shapefile in QgsTinInterpolator
Instead of just forcing writing the triangulation to a shapefile (boo!)
change the parameter to use a QgsFeatureSink, so that anything
which implements the QgsFeatureSink interface can be used for
storing the triangulation.
  • Loading branch information
nyalldawson committed Aug 29, 2017
1 parent d5e63bc commit 9ca57bd
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 61 deletions.
@@ -2338,6 +2338,7 @@ QgsTINInterpolator {#qgis_api_break_3_0_QgsTINInterpolator}
------------------

- The constructor takes a QgsFeedback argument instead of using a QProgressDialog.
- setExportTriangulationToFile() and setTriangulationFilePath() were removed. Use setTriangulationSink() instead.

QgsTolerance {#qgis_api_break_3_0_QgsTolerance}
------------
@@ -45,8 +45,26 @@ class QgsTINInterpolator: QgsInterpolator
:rtype: int
%End

void setExportTriangulationToFile( bool e );
void setTriangulationFilePath( const QString &filepath );
static QgsFields triangulationFields();
%Docstring
Returns the fields output by features when saving the triangulation.
These fields should be used when creating
a suitable feature sink for setTriangulationSink()
.. seealso:: setTriangulationSink()
.. versionadded:: 3.0
:rtype: QgsFields
%End

void setTriangulationSink( QgsFeatureSink *sink );
%Docstring
Sets the optional ``sink`` for saving the triangulation features.

The sink must be setup to accept LineString features, with fields matching
those returned by triangulationFields().

.. seealso:: triangulationFields()
.. versionadded:: 3.0
%End

};

@@ -30,13 +30,16 @@
from qgis.PyQt.QtGui import QIcon

from qgis.core import (QgsProcessingUtils,
QgsProcessing,
QgsProcessingParameterDefinition,
QgsProcessingParameterEnum,
QgsProcessingParameterNumber,
QgsProcessingParameterExtent,
QgsProcessingParameterRasterDestination,
QgsProcessingParameterFileDestination,
QgsProcessingException)
QgsWkbTypes,
QgsProcessingParameterFeatureSink,
QgsProcessingException,
QgsCoordinateReferenceSystem)
from qgis.analysis import (QgsInterpolator,
QgsTINInterpolator,
QgsGridFileWriter)
@@ -85,7 +88,6 @@ def dataToString(data):


class TinInterpolation(QgisAlgorithm):

INTERPOLATION_DATA = 'INTERPOLATION_DATA'
METHOD = 'METHOD'
COLUMNS = 'COLUMNS'
@@ -94,7 +96,7 @@ class TinInterpolation(QgisAlgorithm):
CELLSIZE_Y = 'CELLSIZE_Y'
EXTENT = 'EXTENT'
OUTPUT = 'OUTPUT'
TRIANGULATION_FILE = 'TRIANGULATION_FILE'
TRIANGULATION = 'TRIANGULATION'

def icon(self):
return QIcon(os.path.join(pluginPath, 'images', 'interpolation.png'))
@@ -134,10 +136,10 @@ def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT,
self.tr('Interpolated')))

triangulation_file_param = QgsProcessingParameterFileDestination(self.TRIANGULATION_FILE,
self.tr('Triangulation'),
self.tr('SHP files (*.shp)'),
optional=True)
triangulation_file_param = QgsProcessingParameterFeatureSink(self.TRIANGULATION,
self.tr('Triangulation'),
type=QgsProcessing.TypeVectorLine,
optional=True)
triangulation_file_param.setCreateByDefault(False)
self.addParameter(triangulation_file_param)

@@ -156,7 +158,6 @@ def processAlgorithm(self, parameters, context, feedback):
cellsizeY = self.parameterAsDouble(parameters, self.CELLSIZE_Y, context)
bbox = self.parameterAsExtent(parameters, self.EXTENT, context)
output = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)
triangulation = self.parameterAsFileOutput(parameters, self.TRIANGULATION_FILE, context)

if interpolationData is None:
raise QgsProcessingException(
@@ -168,6 +169,7 @@ def processAlgorithm(self, parameters, context, feedback):

layerData = []
layers = []
crs = QgsCoordinateReferenceSystem()
for row in interpolationData.split(';'):
v = row.split(',')
data = QgsInterpolator.LayerData()
@@ -176,6 +178,8 @@ def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(v[0], context)
data.vectorLayer = layer
layers.append(layer)
if not crs.isValid():
crs = layer.crs()

data.zCoordInterpolation = bool(v[1])
data.interpolationAttribute = int(v[2])
@@ -192,10 +196,12 @@ def processAlgorithm(self, parameters, context, feedback):
else:
interpolationMethod = QgsTINInterpolator.CloughTocher

(triangulation_sink, triangulation_dest_id) = self.parameterAsSink(parameters, self.TRIANGULATION, context,
QgsTINInterpolator.triangulationFields(), QgsWkbTypes.LineString, crs)

interpolator = QgsTINInterpolator(layerData, interpolationMethod, feedback)
if triangulation is not None and triangulation != '':
interpolator.setExportTriangulationToFile(True)
interpolator.setTriangulationFilePath(triangulation)
if triangulation_sink is not None:
interpolator.setTriangulationSink(triangulation_sink)

writer = QgsGridFileWriter(interpolator,
output,
@@ -206,4 +212,4 @@ def processAlgorithm(self, parameters, context, feedback):
cellsizeY)

writer.writeFile(feedback)
return {self.OUTPUT: output}
return {self.OUTPUT: output, self.TRIANGULATION: triangulation_dest_id}
@@ -3081,30 +3081,9 @@ QList<int> *DualEdgeTriangulation::getPointsAroundEdge( double x, double y )
}
}

bool DualEdgeTriangulation::saveAsShapefile( const QString &fileName ) const
bool DualEdgeTriangulation::saveTriangulation( QgsFeatureSink *sink, QgsFeedback *feedback ) const
{
QString shapeFileName = fileName;

QgsFields fields;
fields.append( QgsField( QStringLiteral( "type" ), QVariant::String, QStringLiteral( "String" ) ) );

// add the extension if not present
if ( shapeFileName.indexOf( QLatin1String( ".shp" ) ) == -1 )
{
shapeFileName += QLatin1String( ".shp" );
}

//delete already existing files
if ( QFile::exists( shapeFileName ) )
{
if ( !QgsVectorFileWriter::deleteShapeFile( shapeFileName ) )
{
return false;
}
}

QgsVectorFileWriter writer( shapeFileName, QStringLiteral( "Utf-8" ), fields, QgsWkbTypes::LineString );
if ( writer.hasError() != QgsVectorFileWriter::NoError )
if ( !sink )
{
return false;
}
@@ -3123,6 +3102,9 @@ bool DualEdgeTriangulation::saveAsShapefile( const QString &fileName ) const

for ( int i = 0; i < mHalfEdge.size(); ++i )
{
if ( feedback && feedback->isCanceled() )
break;

HalfEdge *currentEdge = mHalfEdge[i];
if ( currentEdge->getPoint() != -1 && mHalfEdge[currentEdge->getDual()]->getPoint() != -1 && !alreadyVisitedEdges[currentEdge->getDual()] )
{
@@ -3152,14 +3134,14 @@ bool DualEdgeTriangulation::saveAsShapefile( const QString &fileName ) const
}
edgeLineFeature.setAttribute( 0, attributeString );

writer.addFeature( edgeLineFeature );
sink->addFeature( edgeLineFeature, QgsFeatureSink::FastInsert );
}
alreadyVisitedEdges[i] = true;
}

delete [] alreadyVisitedEdges;

return true;
return !feedback || !feedback->isCanceled();
}

double DualEdgeTriangulation::swapMinAngle( int edge ) const
@@ -103,9 +103,7 @@ class ANALYSIS_EXPORT DualEdgeTriangulation: public Triangulation
//! Returns a value list with the numbers of the four points, which would be affected by an edge swap. This function is e.g. needed by NormVecDecorator to know the points, for which the normals have to be recalculated. The returned ValueList has to be deleted by the code which calls the method
virtual QList<int> *getPointsAroundEdge( double x, double y ) override;

/** Saves the triangulation as a (line) shapefile
\returns true in case of success*/
virtual bool saveAsShapefile( const QString &fileName ) const override;
virtual bool saveTriangulation( QgsFeatureSink *sink, QgsFeedback *feedback = nullptr ) const override;

protected:
//! X-coordinate of the upper right corner of the bounding box
@@ -17,6 +17,7 @@
#include "NormVecDecorator.h"
#include "qgsfeedback.h"
#include "qgslogger.h"
#include "qgsfields.h"
#include <QApplication>

NormVecDecorator::~NormVecDecorator()
@@ -590,13 +591,12 @@ bool NormVecDecorator::swapEdge( double x, double y )
}
}

bool NormVecDecorator::saveAsShapefile( const QString &fileName ) const
bool NormVecDecorator::saveTriangulation( QgsFeatureSink *sink, QgsFeedback *feedback ) const
{
if ( !mTIN )
{
return false;
}
return mTIN->saveAsShapefile( fileName );
return mTIN->saveTriangulation( sink, feedback );
}


@@ -68,9 +68,7 @@ class ANALYSIS_EXPORT NormVecDecorator: public TriDecorator
//! Swaps the edge which is closest to the point with x and y coordinates (if this is possible) and forces recalculation of the concerned normals (if alreadyestimated is true)
virtual bool swapEdge( double x, double y ) override;

/** Saves the triangulation as a (line) shapefile
\returns true in case of success*/
virtual bool saveAsShapefile( const QString &fileName ) const override;
virtual bool saveTriangulation( QgsFeatureSink *sink, QgsFeedback *feedback = nullptr ) const override;

protected:
//! Is true, if the normals already have been estimated
@@ -13,5 +13,11 @@
* *
***************************************************************************/
#include "Triangulation.h"
#include "qgsfields.h"

//empty file (abstract class)
QgsFields Triangulation::triangulationFields()
{
QgsFields fields;
fields.append( QgsField( QStringLiteral( "type" ), QVariant::String, QStringLiteral( "String" ) ) );
return fields;
}
@@ -23,7 +23,10 @@
#include "TriangleInterpolator.h"
#include "qgis_analysis.h"

class QgsFeatureSink;
class Line3D;
class QgsFields;
class QgsFeedback;

#define SIP_NO_FILE

@@ -152,10 +155,26 @@ class ANALYSIS_EXPORT Triangulation
virtual bool swapEdge( double x, double y ) = 0;

/**
* Saves the triangulation as a (line) shapefile
* Returns the fields output by features when calling
* saveTriangulation(). These fields should be used when creating
* a suitable feature sink for saveTriangulation()
* \see saveTriangulation()
* \since QGIS 3.0
*/
static QgsFields triangulationFields();

/**
* Saves the triangulation features to a feature \a sink.
*
* The sink must be setup to accept LineString features, with fields matching
* those returned by triangulationFields().
*
* \returns true in case of success
*
* \see triangulationFields()
* \since QGIS 3.0
*/
virtual bool saveAsShapefile( const QString &fileName ) const = 0;
virtual bool saveTriangulation( QgsFeatureSink *sink, QgsFeedback *feedback = nullptr ) const = 0;
};

#ifndef SIP_RUN
@@ -35,7 +35,6 @@ QgsTINInterpolator::QgsTINInterpolator( const QList<LayerData> &inputData, TINIn
, mTriangleInterpolator( nullptr )
, mIsInitialized( false )
, mFeedback( feedback )
, mExportTriangulationToFile( false )
, mInterpolation( interpolation )
{
}
@@ -67,6 +66,16 @@ int QgsTINInterpolator::interpolatePoint( double x, double y, double &result )
return 0;
}

QgsFields QgsTINInterpolator::triangulationFields()
{
return Triangulation::triangulationFields();
}

void QgsTINInterpolator::setTriangulationSink( QgsFeatureSink *sink )
{
mTriangulationSink = sink;
}

void QgsTINInterpolator::initialize()
{
DualEdgeTriangulation *dualEdgeTriangulation = new DualEdgeTriangulation( 100000, nullptr );
@@ -143,9 +152,9 @@ void QgsTINInterpolator::initialize()
mIsInitialized = true;

//debug
if ( mExportTriangulationToFile )
if ( mTriangulationSink )
{
dualEdgeTriangulation->saveAsShapefile( mTriangulationFilePath );
dualEdgeTriangulation->saveTriangulation( mTriangulationSink, mFeedback );
}
}

@@ -22,10 +22,12 @@
#include <QString>
#include "qgis_analysis.h"

class QgsFeatureSink;
class Triangulation;
class TriangleInterpolator;
class QgsFeature;
class QgsFeedback;
class QgsFields;

/** \ingroup analysis
* Interpolation in a triangular irregular network*/
@@ -54,18 +56,34 @@ class ANALYSIS_EXPORT QgsTINInterpolator: public QgsInterpolator
\returns 0 in case of success*/
int interpolatePoint( double x, double y, double &result ) override;

void setExportTriangulationToFile( bool e ) {mExportTriangulationToFile = e;}
void setTriangulationFilePath( const QString &filepath ) {mTriangulationFilePath = filepath;}
/**
* Returns the fields output by features when saving the triangulation.
* These fields should be used when creating
* a suitable feature sink for setTriangulationSink()
* \see setTriangulationSink()
* \since QGIS 3.0
*/
static QgsFields triangulationFields();

/**
* Sets the optional \a sink for saving the triangulation features.
*
* The sink must be setup to accept LineString features, with fields matching
* those returned by triangulationFields().
*
* \see triangulationFields()
* \since QGIS 3.0
*/
void setTriangulationSink( QgsFeatureSink *sink );

private:
Triangulation *mTriangulation = nullptr;
TriangleInterpolator *mTriangleInterpolator = nullptr;
bool mIsInitialized;
QgsFeedback *mFeedback = nullptr;
//! If true: export triangulation to shapefile after initialization
bool mExportTriangulationToFile;
//! File path to export the triangulation
QString mTriangulationFilePath;

//! Feature sink for triangulation
QgsFeatureSink *mTriangulationSink = nullptr;
//! Type of interpolation
TINInterpolation mInterpolation;

0 comments on commit 9ca57bd

Please sign in to comment.