Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[feature][processing] Add Line denisty algorithm
- Loading branch information
1 parent
5765cab
commit ce342f7
Showing
8 changed files
with
337 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<?xml version="1.0" encoding="utf-8" ?> | ||
<ogr:FeatureCollection | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://ogr.maptools.org/ linedensity.xsd" | ||
xmlns:ogr="http://ogr.maptools.org/" | ||
xmlns:gml="http://www.opengis.net/gml"> | ||
<gml:boundedBy> | ||
<gml:Box> | ||
<gml:coord><gml:X>0.3605633802816897</gml:X><gml:Y>-3.816901408450707</gml:Y></gml:coord> | ||
<gml:coord><gml:X>9.930985915492959</gml:X><gml:Y>5.512676056338028</gml:Y></gml:coord> | ||
</gml:Box> | ||
</gml:boundedBy> | ||
|
||
<gml:featureMember> | ||
<ogr:linedensity fid="1"> | ||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>0.753521126760563,2.11549295774648 1.65352112676056,4.04225352112676 3.1112676056338,3.96619718309859 5.19014084507042,5.37323943661972 6.66056338028169,4.86619718309859 7.7887323943662,4.94225352112676 8.70140845070423,5.51267605633803 9.5887323943662,5.03098591549296</gml:coordinates></gml:LineString></ogr:geometryProperty> | ||
<ogr:weight>1.000</ogr:weight> | ||
</ogr:linedensity> | ||
</gml:featureMember> | ||
<gml:featureMember> | ||
<ogr:linedensity fid="2"> | ||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>3.66901408450704,4.16901408450704 4.55633802816901,3.31971830985915 3.6056338028169,2.45774647887324 4.78450704225352,1.78591549295775</gml:coordinates></gml:LineString></ogr:geometryProperty> | ||
<ogr:weight>1.000</ogr:weight> | ||
</ogr:linedensity> | ||
</gml:featureMember> | ||
<gml:featureMember> | ||
<ogr:linedensity fid="3"> | ||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>3.45352112676056,-1.01549295774648 5.67183098591549,0.239436619718309 6.05211267605634,1.59577464788732 6.58450704225352,3.49718309859155 7.62394366197183,3.40845070422535 8.67605633802817,4.71408450704225 9.22112676056338,4.37183098591549 9.93098591549296,4.15633802816901</gml:coordinates></gml:LineString></ogr:geometryProperty> | ||
<ogr:weight>2.000</ogr:weight> | ||
</ogr:linedensity> | ||
</gml:featureMember> | ||
<gml:featureMember> | ||
<ogr:linedensity fid="4"> | ||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>1.20985915492958,1.03802816901408 3.45352112676056,0.797183098591549</gml:coordinates></gml:LineString></ogr:geometryProperty> | ||
<ogr:weight>1.000</ogr:weight> | ||
</ogr:linedensity> | ||
</gml:featureMember> | ||
<gml:featureMember> | ||
<ogr:linedensity fid="5"> | ||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>0.36056338028169,4.38450704225352 1.60281690140845,2.36901408450704 2.05915492957746,2.49577464788732 2.05915492957746,3.59859154929577 2.26197183098592,3.94084507042253 2.83239436619718,2.72394366197183 2.62957746478873,4.57464788732394 2.8830985915493,5.29718309859155 3.5169014084507,4.65070422535211 3.75774647887324,3.91549295774648 3.37746478873239,3.58591549295775 3.04788732394366,3.09154929577465 3.16197183098592,2.48309859154929</gml:coordinates></gml:LineString></ogr:geometryProperty> | ||
<ogr:weight>3.000</ogr:weight> | ||
</ogr:linedensity> | ||
</gml:featureMember> | ||
<gml:featureMember> | ||
<ogr:linedensity fid="6"> | ||
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>1.07042253521127,-2.52394366197183 3.33943661971831,-3.81690140845071 6.34366197183099,-2.09295774647887 8.29577464788733,-2.14366197183099 9.32253521126761,-0.736619718309861</gml:coordinates></gml:LineString></ogr:geometryProperty> | ||
<ogr:weight>1.000</ogr:weight> | ||
</ogr:linedensity> | ||
</gml:featureMember> | ||
</ogr:FeatureCollection> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
/*************************************************************************** | ||
qgsalgorithmlinedensity.cpp | ||
--------------------- | ||
begin : December 2019 | ||
copyright : (C) 2019 by Clemens Raffler | ||
email : clemens dot raffler 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 "qgsalgorithmlinedensity.h" | ||
#include "qgsgeometryengine.h" | ||
#include "qgsrasterfilewriter.h" | ||
|
||
///@cond PRIVATE | ||
|
||
QString QgsLineDensityAlgorithm::name() const | ||
{ | ||
return QStringLiteral( "linedensity" ); | ||
} | ||
|
||
QString QgsLineDensityAlgorithm::displayName() const | ||
{ | ||
return QObject::tr( "Line density" ); | ||
} | ||
|
||
QStringList QgsLineDensityAlgorithm::tags() const | ||
{ | ||
return QObject::tr( "density,kernel,line,line density,interpolation,weight" ).split( ',' ); | ||
} | ||
|
||
QString QgsLineDensityAlgorithm::group() const | ||
{ | ||
return QObject::tr( "Interpolation" ); | ||
} | ||
|
||
QString QgsLineDensityAlgorithm::groupId() const | ||
{ | ||
return QStringLiteral( "interpolation" ); | ||
} | ||
|
||
void QgsLineDensityAlgorithm::initAlgorithm( const QVariantMap & ) | ||
{ | ||
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input line layer" ), QList<int>() << QgsProcessing::TypeVectorLine ) ); | ||
addParameter( new QgsProcessingParameterField( QStringLiteral( "WEIGHT" ), QObject::tr( "Weight field "), QVariant(), QStringLiteral( "INPUT" ), QgsProcessingParameterField::Numeric ) ); | ||
addParameter( new QgsProcessingParameterDistance( QStringLiteral( "RADIUS" ), QObject::tr( "Search radius" ), 10, QStringLiteral( "INPUT" ), false, 0) ); | ||
addParameter( new QgsProcessingParameterDistance( QStringLiteral( "PIXEL_SIZE" ), QObject::tr( "Pixel size" ), 10, QStringLiteral( "INPUT" ), false) ); | ||
|
||
addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Line density raster" ) ) ); | ||
} | ||
|
||
QString QgsLineDensityAlgorithm::shortHelpString() const | ||
{ | ||
return QObject::tr( "The line density interpolation algorithm calculates a density measure of linear features " | ||
"which is obtained in a circular neighborhood within each raster cell. " | ||
"First, the length of the segment of each line that is intersected by the circular neighborhood " | ||
"is multiplied with the lines weight factor. In a second step, all length values are summed and " | ||
"divided by the area of the circular neighborhood. This process is repeated for all raster cells." | ||
); | ||
} | ||
|
||
QgsLineDensityAlgorithm *QgsLineDensityAlgorithm::createInstance() const | ||
{ | ||
return new QgsLineDensityAlgorithm(); | ||
} | ||
|
||
bool QgsLineDensityAlgorithm::prepareAlgorithm(const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback) | ||
{ | ||
Q_UNUSED(feedback); | ||
mSource.reset( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); | ||
if ( !mSource ) | ||
throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT" ) ) ); | ||
mWeight = parameterAsString( parameters, QStringLiteral( "WEIGHT" ), context ); | ||
mSearchRadius = parameterAsDouble(parameters, QStringLiteral("RADIUS"), context); | ||
mPixelSize = parameterAsDouble(parameters, QStringLiteral("PIXEL_SIZE"), context); | ||
|
||
mExtent = mSource->sourceExtent(); | ||
mCrs = mSource->sourceCrs(); | ||
|
||
if ( context.project() ) | ||
{ | ||
mDa.setEllipsoid( context.project()->ellipsoid() ); | ||
} | ||
mDa.setSourceCrs( mCrs, context.transformContext() ); | ||
|
||
|
||
//get cell midpoint from top left cell | ||
QgsGeometry firstCellMidpoint = QgsGeometry(new QgsPoint(mExtent.xMinimum() + (mPixelSize/2), mExtent.yMaximum() - (mPixelSize/2))); | ||
mSearchGeometry = firstCellMidpoint.buffer(mSearchRadius, 5); | ||
mSearchGeometryArea = mDa.measureArea(mSearchGeometry); | ||
|
||
mIndex = QgsSpatialIndex( *mSource, nullptr, QgsSpatialIndex::FlagStoreFeatureGeometries ); | ||
|
||
|
||
return true; | ||
} | ||
|
||
QVariantMap QgsLineDensityAlgorithm::processAlgorithm(const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback) | ||
{ | ||
const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context ); | ||
QFileInfo fi( outputFile ); | ||
const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() ); | ||
|
||
int rows = std::max( std::ceil( mExtent.height() / mPixelSize ), 1.0 ); | ||
int cols = std::max( std::ceil( mExtent.width() / mPixelSize ), 1.0 ); | ||
|
||
//build new raster extent based on number of columns and cellsize | ||
//this prevents output cellsize being calculated too small | ||
QgsRectangle rasterExtent = QgsRectangle(mExtent.xMinimum(), mExtent.yMaximum() - (rows * mPixelSize), mExtent.xMinimum() + (cols * mPixelSize), mExtent.yMaximum()); | ||
|
||
std::unique_ptr< QgsRasterFileWriter > writer = qgis::make_unique< QgsRasterFileWriter >( outputFile ); | ||
writer->setOutputProviderKey( QStringLiteral( "gdal" ) ); | ||
writer->setOutputFormat( outputFormat ); | ||
std::unique_ptr<QgsRasterDataProvider > provider( writer->createOneBandRaster( Qgis::Float32, cols, rows, rasterExtent, mCrs ) ); | ||
if ( !provider ) | ||
throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) ); | ||
if ( !provider->isValid() ) | ||
throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) ); | ||
|
||
provider->setNoDataValue( 1, -9999 ); | ||
|
||
int totalCellcnt = rows * cols; | ||
int cellcnt = 0; | ||
|
||
for (int row = 0; row < rows; row++) | ||
{ | ||
std::unique_ptr< QgsRasterBlock > rasterDataLine = qgis::make_unique< QgsRasterBlock >( Qgis::Float32, cols, 1 ); | ||
for (int col = 0; col < cols; col++) | ||
{ | ||
if ( feedback->isCanceled() ) | ||
{ | ||
break; | ||
} | ||
|
||
if(col > 0) | ||
mSearchGeometry.translate( mPixelSize, 0 ); | ||
|
||
QList<QgsFeatureId> fids = mIndex.intersects(mSearchGeometry.boundingBox()); | ||
|
||
std::unique_ptr< QgsGeometryEngine > engine( QgsGeometry::createGeometryEngine( mSearchGeometry.constGet() ) ); | ||
engine->prepareGeometry(); | ||
|
||
//remove non-intersecting fids | ||
int i = 0; | ||
while( i < fids.size() ) | ||
{ | ||
QgsFeatureId fid = fids.at(i); | ||
QgsGeometry lineGeom = mIndex.geometry(fid); | ||
|
||
if(engine->intersects(lineGeom.constGet()) == false) | ||
{ | ||
fids.removeAt(i); | ||
} | ||
i++; | ||
} | ||
|
||
QgsFeatureIds isectingFids = QSet< QgsFeatureId >::fromList( fids ); | ||
QgsFeatureIterator fit = mSource->getFeatures(QgsFeatureRequest().setFilterFids(isectingFids)); | ||
QgsFeature f; | ||
double abs_density = 0; | ||
while (fit.nextFeature( f )) | ||
{ | ||
double analysisLineLength = mDa.measureLength(QgsGeometry(engine->intersection(f.geometry().constGet()))); | ||
double analysisWeight = f.attribute( mWeight ).toDouble(); | ||
abs_density += ( analysisLineLength * analysisWeight ); | ||
} | ||
|
||
double lineDensity = abs_density/mSearchGeometryArea; | ||
rasterDataLine->setValue(0, col, lineDensity); | ||
|
||
feedback->setProgress(static_cast<double>(cellcnt)/static_cast<double>(totalCellcnt) * 100); | ||
cellcnt++; | ||
} | ||
provider->writeBlock( rasterDataLine.get(), 1, 0, row); | ||
|
||
//'carriage return and newline' for search geometry | ||
mSearchGeometry.translate((cols - 1) * -mPixelSize, -mPixelSize); | ||
} | ||
|
||
QVariantMap outputs; | ||
outputs.insert( QStringLiteral( "OUTPUT" ), outputFile); | ||
return outputs; | ||
} | ||
|
||
|
||
///@endcond |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/*************************************************************************** | ||
qgsalgorithmlinedensity.h | ||
--------------------- | ||
begin : December 2019 | ||
copyright : (C) 2019 by Clemens Raffler | ||
email : clemens dot raffler 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 QGSALGORITHMLINEDENSITY_H | ||
#define QGSALGORITHMLINEDENSITY_H | ||
|
||
#define SIP_NO_FILE | ||
|
||
#include "qgis_sip.h" | ||
#include "qgsprocessingalgorithm.h" | ||
#include "qgsapplication.h" | ||
|
||
///@cond PRIVATE | ||
|
||
/** | ||
* Line Density Algorithm as implemented in ESRI ArcGIS Spatial Analyst | ||
* | ||
* Literature: | ||
* Silverman, B.w. Density Estimation for Statistics and Data Analsis. | ||
* New York: Chapman and Hall, 1986 | ||
* | ||
*/ | ||
class QgsLineDensityAlgorithm : public QgsProcessingAlgorithm | ||
{ | ||
public: | ||
|
||
QgsLineDensityAlgorithm() = default; | ||
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override; | ||
QIcon icon() const override { return QgsApplication::getThemeIcon( QStringLiteral( "/algorithms/mAlgorithmLineDensity.svg" ) ); } | ||
QString svgIconPath() const override { return QgsApplication::iconPath( QStringLiteral( "/algorithms/mAlgorithmLineDensity.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; | ||
QgsLineDensityAlgorithm *createInstance() const override SIP_FACTORY; | ||
|
||
protected: | ||
bool prepareAlgorithm(const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback); | ||
QVariantMap processAlgorithm( const QVariantMap ¶meters, | ||
QgsProcessingContext &context, | ||
QgsProcessingFeedback *feedback ) override; | ||
|
||
private: | ||
std::unique_ptr< QgsFeatureSource > mSource; | ||
QString mWeight; | ||
double mSearchRadius; | ||
double mSearchGeometryArea; | ||
double mPixelSize; | ||
QgsGeometry mSearchGeometry; | ||
QgsRectangle mExtent; | ||
QgsCoordinateReferenceSystem mCrs; | ||
QgsDistanceArea mDa; | ||
QgsSpatialIndex mIndex; | ||
|
||
}; | ||
|
||
///@endcond PRIVATE | ||
|
||
#endif // QGSALGORITHMLINEDENSITY_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters