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.
This commit is contained in:
Nyall Dawson 2017-08-26 22:38:12 +10:00
parent d5e63bc84f
commit 9ca57bd62c
11 changed files with 117 additions and 62 deletions

View File

@ -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}
------------

View File

@ -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
};

View File

@ -30,13 +30,16 @@ import os
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 @@ class ParameterInterpolationData(QgsProcessingParameterDefinition):
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 @@ class TinInterpolation(QgisAlgorithm):
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 @@ class TinInterpolation(QgisAlgorithm):
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 @@ class TinInterpolation(QgisAlgorithm):
layerData = []
layers = []
crs = QgsCoordinateReferenceSystem()
for row in interpolationData.split(';'):
v = row.split(',')
data = QgsInterpolator.LayerData()
@ -176,6 +178,8 @@ class TinInterpolation(QgisAlgorithm):
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 @@ class TinInterpolation(QgisAlgorithm):
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 @@ class TinInterpolation(QgisAlgorithm):
cellsizeY)
writer.writeFile(feedback)
return {self.OUTPUT: output}
return {self.OUTPUT: output, self.TRIANGULATION: triangulation_dest_id}

View File

@ -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

View File

@ -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

View File

@ -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 );
}

View File

@ -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

View File

@ -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;
}

View File

@ -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 true in case of success
* 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
*/
virtual bool saveAsShapefile( const QString &fileName ) const = 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 saveTriangulation( QgsFeatureSink *sink, QgsFeedback *feedback = nullptr ) const = 0;
};
#ifndef SIP_RUN

View File

@ -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 );
}
}

View File

@ -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;