QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgsalgorithmlinedensity.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsalgorithmlinedensity.cpp
3  ---------------------
4  begin : December 2019
5  copyright : (C) 2019 by Clemens Raffler
6  email : clemens dot raffler at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
19 #include "qgscircle.h"
20 #include "qgsgeometryengine.h"
21 #include "qgsrasterfilewriter.h"
22 
24 
25 QString QgsLineDensityAlgorithm::name() const
26 {
27  return QStringLiteral( "linedensity" );
28 }
29 
30 QString QgsLineDensityAlgorithm::displayName() const
31 {
32  return QObject::tr( "Line density" );
33 }
34 
35 QStringList QgsLineDensityAlgorithm::tags() const
36 {
37  return QObject::tr( "density,kernel,line,line density,interpolation,weight" ).split( ',' );
38 }
39 
40 QString QgsLineDensityAlgorithm::group() const
41 {
42  return QObject::tr( "Interpolation" );
43 }
44 
45 QString QgsLineDensityAlgorithm::groupId() const
46 {
47  return QStringLiteral( "interpolation" );
48 }
49 
50 void QgsLineDensityAlgorithm::initAlgorithm( const QVariantMap & )
51 {
52  addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input line layer" ), QList<int>() << QgsProcessing::TypeVectorLine ) );
53  addParameter( new QgsProcessingParameterField( QStringLiteral( "WEIGHT" ), QObject::tr( "Weight field " ), QVariant(), QStringLiteral( "INPUT" ), QgsProcessingParameterField::Numeric, false, true ) );
54  addParameter( new QgsProcessingParameterDistance( QStringLiteral( "RADIUS" ), QObject::tr( "Search radius" ), 10, QStringLiteral( "INPUT" ), false, 0 ) );
55  addParameter( new QgsProcessingParameterDistance( QStringLiteral( "PIXEL_SIZE" ), QObject::tr( "Pixel size" ), 10, QStringLiteral( "INPUT" ), false ) );
56 
57  addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Line density raster" ) ) );
58 }
59 
60 QString QgsLineDensityAlgorithm::shortHelpString() const
61 {
62  return QObject::tr( "The line density interpolation algorithm calculates a density measure of linear features "
63  "which is obtained in a circular neighborhood within each raster cell. "
64  "First, the length of the segment of each line that is intersected by the circular neighborhood "
65  "is multiplied with the lines weight factor. In a second step, all length values are summed and "
66  "divided by the area of the circular neighborhood. This process is repeated for all raster cells."
67  );
68 }
69 
70 QgsLineDensityAlgorithm *QgsLineDensityAlgorithm::createInstance() const
71 {
72  return new QgsLineDensityAlgorithm();
73 }
74 
75 bool QgsLineDensityAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
76 {
77  Q_UNUSED( feedback );
78  mSource.reset( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
79  if ( !mSource )
80  throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) );
81 
82  mWeightField = parameterAsString( parameters, QStringLiteral( "WEIGHT" ), context );
83 
84  mPixelSize = parameterAsDouble( parameters, QStringLiteral( "PIXEL_SIZE" ), context );
85 
86  mSearchRadius = parameterAsDouble( parameters, QStringLiteral( "RADIUS" ), context );
87  if ( mSearchRadius < 0.5 * mPixelSize * std::sqrt( 2 ) )
88  throw QgsProcessingException( QObject::tr( "Raster cells must be fully contained by the search circle. Therefore, "
89  "the search radius must not be smaller than half of the pixel diagonal." ) );
90 
91  mExtent = mSource->sourceExtent();
92  mCrs = mSource->sourceCrs();
93  mDa = QgsDistanceArea();
94 
95  if ( context.project() )
96  {
97  mDa.setEllipsoid( context.project()->ellipsoid() );
98  }
99  mDa.setSourceCrs( mCrs, context.transformContext() );
100 
101 
102  //get cell midpoint from top left cell
103  QgsPoint firstCellMidpoint = QgsPoint( mExtent.xMinimum() + ( mPixelSize / 2 ), mExtent.yMaximum() - ( mPixelSize / 2 ) );
104  QgsCircle searchCircle = QgsCircle( firstCellMidpoint, mSearchRadius );
105  mSearchGeometry = QgsGeometry( searchCircle.toPolygon() );
106 
107  return true;
108 }
109 
110 QVariantMap QgsLineDensityAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
111 {
113 
114  QStringList weightName = QStringList( mWeightField );
115  QgsFields attrFields = mSource->fields();
116 
118  r.setSubsetOfAttributes( weightName, attrFields );
119  QgsFeatureIterator fit = mSource->getFeatures( r );
120  QgsFeature f;
121 
122  while ( fit.nextFeature( f ) )
123  {
124  mIndex.addFeature( f, QgsFeatureSink::FastInsert );
125 
126  //only populate hash if weight field is given
127  if ( !mWeightField.isEmpty() )
128  {
129  double analysisWeight = f.attribute( mWeightField ).toDouble();
130  mFeatureWeights.insert( f.id(), analysisWeight );
131  }
132  }
133 
134  const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
135  QFileInfo fi( outputFile );
136  const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
137 
138  int rows = std::max( std::ceil( mExtent.height() / mPixelSize ), 1.0 );
139  int cols = std::max( std::ceil( mExtent.width() / mPixelSize ), 1.0 );
140 
141  //build new raster extent based on number of columns and cellsize
142  //this prevents output cellsize being calculated too small
143  QgsRectangle rasterExtent = QgsRectangle( mExtent.xMinimum(), mExtent.yMaximum() - ( rows * mPixelSize ), mExtent.xMinimum() + ( cols * mPixelSize ), mExtent.yMaximum() );
144 
145  QgsRasterFileWriter writer = QgsRasterFileWriter( outputFile );
146  writer.setOutputProviderKey( QStringLiteral( "gdal" ) );
147  writer.setOutputFormat( outputFormat );
148  std::unique_ptr<QgsRasterDataProvider > provider( writer.createOneBandRaster( Qgis::Float32, cols, rows, rasterExtent, mCrs ) );
149  if ( !provider )
150  throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
151  if ( !provider->isValid() )
152  throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
153 
154  provider->setNoDataValue( 1, -9999 );
155 
156  qgssize totalCellcnt = static_cast<qgssize>( rows ) * cols;
157  int cellcnt = 0;
158 
159  std::unique_ptr< QgsRasterBlock > rasterDataLine = qgis::make_unique< QgsRasterBlock >( Qgis::Float32, cols, 1 );
160 
161  for ( int row = 0; row < rows; row++ )
162  {
163  for ( int col = 0; col < cols; col++ )
164  {
165  if ( feedback->isCanceled() )
166  {
167  break;
168  }
169 
170  if ( col > 0 )
171  mSearchGeometry.translate( mPixelSize, 0 );
172 
173  const QList<QgsFeatureId> fids = mIndex.intersects( mSearchGeometry.boundingBox() );
174 
175  if ( !fids.isEmpty() )
176  {
177  std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( mSearchGeometry.constGet() ) );
178  engine->prepareGeometry();
179 
180  double absDensity = 0;
181  for ( QgsFeatureId id : fids )
182  {
183  QgsGeometry lineGeom = mIndex.geometry( id );
184 
185  if ( engine->intersects( lineGeom.constGet() ) )
186  {
187  double analysisLineLength = mDa.measureLength( QgsGeometry( engine->intersection( mIndex.geometry( id ).constGet() ) ) );
188  double weight = 1;
189 
190  if ( !mWeightField.isEmpty() )
191  {
192  weight = mFeatureWeights.value( id );
193  }
194 
195  absDensity += ( analysisLineLength * weight );
196  }
197  }
198 
199  double lineDensity = 0;
200  if ( absDensity > 0 )
201  {
202  //only calculate ellipsoidal area if abs density is greater 0
203  double analysisSearchGeometryArea = mDa.measureArea( mSearchGeometry );
204  lineDensity = absDensity / analysisSearchGeometryArea;
205  }
206  rasterDataLine->setValue( 0, col, lineDensity );
207  }
208  else
209  {
210  //no lines found in search radius
211  rasterDataLine->setValue( 0, col, 0.0 );
212  }
213 
214  feedback->setProgress( static_cast<double>( cellcnt ) / static_cast<double>( totalCellcnt ) * 100 );
215  cellcnt++;
216  }
217  provider->writeBlock( rasterDataLine.get(), 1, 0, row );
218 
219  //'carriage return and newline' for search geometry
220  mSearchGeometry.translate( ( cols - 1 ) * -mPixelSize, -mPixelSize );
221  }
222 
223  QVariantMap outputs;
224  outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
225  return outputs;
226 }
227 
228 
QgsFeedback::setProgress
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:75
Qgis::Float32
@ Float32
Thirty two bit floating point (float)
Definition: qgis.h:122
QgsProcessingParameterField::Numeric
@ Numeric
Accepts numeric fields.
Definition: qgsprocessingparameters.h:2503
QgsCircle
Circle geometry type.
Definition: qgscircle.h:43
QgsProcessingContext::project
QgsProject * project() const
Returns the project in which the algorithm is being executed.
Definition: qgsprocessingcontext.h:99
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
QgsProcessingFeedback
Definition: qgsprocessingfeedback.h:37
QgsRasterFileWriter::driverForExtension
static QString driverForExtension(const QString &extension)
Returns the GDAL driver name for a specified file extension.
Definition: qgsrasterfilewriter.cpp:1061
QgsProcessingParameterRasterDestination
Definition: qgsprocessingparameters.h:2944
QgsFields
Definition: qgsfields.h:44
qgsalgorithmlinedensity.h
QgsProcessing::TypeVectorLine
@ TypeVectorLine
Vector line layers.
Definition: qgsprocessing.h:49
QgsProcessingParameterFeatureSource
Definition: qgsprocessingparameters.h:2612
QgsFeatureRequest::setSubsetOfAttributes
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
Definition: qgsfeaturerequest.cpp:190
QgsRectangle
Definition: qgsrectangle.h:41
qgsrasterfilewriter.h
QgsFeature::id
QgsFeatureId id
Definition: qgsfeature.h:68
QgsFeatureRequest
Definition: qgsfeaturerequest.h:75
qgsgeometryengine.h
QgsProcessingContext
Definition: qgsprocessingcontext.h:43
qgscircle.h
QgsFeature::attribute
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:262
QgsSpatialIndex
Definition: qgsspatialindex.h:67
QgsProcessingContext::transformContext
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Definition: qgsprocessingcontext.h:135
QgsGeometry::constGet
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:128
QgsProcessingParameterDistance
Definition: qgsprocessingparameters.h:1941
QgsSpatialIndex::FlagStoreFeatureGeometries
@ FlagStoreFeatureGeometries
Indicates that the spatial index should also store feature geometries. This requires more memory,...
Definition: qgsspatialindex.h:77
QgsFeedback::isCanceled
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:66
QgsGeometry::createGeometryEngine
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine.
Definition: qgsgeometry.cpp:3659
QgsRasterFileWriter
Definition: qgsrasterfilewriter.h:39
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:373
QgsGeometry
Definition: qgsgeometry.h:122
QgsErrorMessage::Text
@ Text
Definition: qgserror.h:38
QgsRasterFileWriter::createOneBandRaster
QgsRasterDataProvider * createOneBandRaster(Qgis::DataType dataType, int width, int height, const QgsRectangle &extent, const QgsCoordinateReferenceSystem &crs)
Create a raster file with one band without initializing the pixel data.
Definition: qgsrasterfilewriter.cpp:38
QgsEllipse::toPolygon
virtual QgsPolygon * toPolygon(unsigned int segments=36) const
Returns a segmented polygon.
Definition: qgsellipse.cpp:224
QgsDistanceArea
Definition: qgsdistancearea.h:49
QgsFeature
Definition: qgsfeature.h:55
QgsFeatureIterator
Definition: qgsfeatureiterator.h:263
QgsProject::ellipsoid
QString ellipsoid
Definition: qgsproject.h:100
QgsProcessingException
Definition: qgsexception.h:82
QgsProcessingParameterField
Definition: qgsprocessingparameters.h:2495
QgsRasterFileWriter::setOutputProviderKey
void setOutputProviderKey(const QString &key)
Definition: qgsrasterfilewriter.h:137
QgsFeatureSink::FastInsert
@ FastInsert
Use faster inserts, at the cost of updating the passed features to reflect changes made at the provid...
Definition: qgsfeaturesink.h:70
QgsRasterFileWriter::setOutputFormat
void setOutputFormat(const QString &format)
Definition: qgsrasterfilewriter.h:134
QgsFeatureId
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
qgssize
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
Definition: qgis.h:723