766 changes: 766 additions & 0 deletions src/analysis/raster/qgsrelief.cpp

Large diffs are not rendered by default.

101 changes: 101 additions & 0 deletions src/analysis/raster/qgsrelief.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#ifndef QGSRELIEF_H
#define QGSRELIEF_H

#include <QColor>
#include <QMap>
#include <QPair>
#include <QString>
#include "gdal.h"

class QgsAspectFilter;
class QgsSlopeFilter;
class QgsHillshadeFilter;
class QProgressDialog;

/**Produces coloured relief rasters from DEM*/
class ANALYSIS_EXPORT QgsRelief
{
public:
struct ReliefColor
{
ReliefColor( const QColor& c, double min, double max ): color( c ), minElevation( min ), maxElevation( max ) { }
QColor color;
double minElevation;
double maxElevation;
};

QgsRelief( const QString& inputFile, const QString& outputFile, const QString& outputFormat );
~QgsRelief();

/**Starts the calculation, reads from mInputFile and stores the result in mOutputFile
@param p progress dialog that receives update and that is checked for abort. 0 if no progress bar is needed.
@return 0 in case of success*/
int processRaster( QProgressDialog* p );

double zFactor() const { return mZFactor; }
void setZFactor( double factor ) { mZFactor = factor; }

void clearReliefColors();
void addReliefColorClass( const ReliefColor& color );
const QList< ReliefColor >& reliefColors() const { return mReliefColors; }
void setReliefColors( const QList< ReliefColor >& c ) { mReliefColors = c; }

/**Calculates class breaks according with the method of Buenzli (2011) using an iterative algorithm for segmented regression
@return true in case of success*/
QList< ReliefColor > calculateOptimizedReliefClasses();

/**Write frequency of elevation values to file for manual inspection*/
bool exportFrequencyDistributionToCsv( const QString& file );

private:

QString mInputFile;
QString mOutputFile;
QString mOutputFormat;

double mCellSizeX;
double mCellSizeY;
/**The nodata value of the input layer*/
float mInputNodataValue;
/**The nodata value of the output layer*/
float mOutputNodataValue;

double mZFactor;

QgsSlopeFilter* mSlopeFilter;
QgsAspectFilter* mAspectFilter;
QgsHillshadeFilter* mHillshadeFilter285;
QgsHillshadeFilter* mHillshadeFilter300;
QgsHillshadeFilter* mHillshadeFilter315;

//relief colors and corresponding elevations
QList< ReliefColor > mReliefColors;

bool processNineCellWindow( float* x1, float* x2, float* x3, float* x4, float* x5, float* x6, float* x7, float* x8, float* x9,
int* red, int* green, int* blue );

/**Opens the input file and returns the dataset handle and the number of pixels in x-/y- direction*/
GDALDatasetH openInputFile( int& nCellsX, int& nCellsY );
/**Opens the output driver and tests if it supports the creation of a new dataset
@return NULL on error and the driver handle on success*/
GDALDriverH openOutputDriver();
/**Opens the output file and sets the same geotransform and CRS as the input data
@return the output dataset or NULL in case of error*/
GDALDatasetH openOutputFile( GDALDatasetH inputDataset, GDALDriverH outputDriver );

/**Set elevation color*/
bool setElevationColor( double elevation, int* red, int* green, int* blue );

/**Sets relief colors*/
void setDefaultReliefColors();
/**Returns class (0-255) for an elevation value
@return elevation class or -1 in case of error*/
int frequencyClassForElevation( double elevation, double minElevation, double elevationClassRange );
/**Do one iteration of class break optimisation (algorithm from Garcia and Rodriguez)*/
void optimiseClassBreaks( QList<int>& breaks, double* frequencies );
/**Calculates coefficients a (slope) and b (y value for x=0)
@param input data points ( elevation class / frequency )*/
bool calculateRegression( const QList< QPair < int, double > >& input, double& a, double& b );
};

#endif // QGSRELIEF_H
2 changes: 1 addition & 1 deletion src/analysis/raster/qgsruggednessfilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ QgsRuggednessFilter::~QgsRuggednessFilter()

}

float QgsRuggednessFilter::processNineCellWindow( float* x11, float* x21, float* x31,
float QgsRuggednessFilter::processNineCellWindow( float* x11, float* x21, float* x31, \
float* x12, float* x22, float* x32, float* x13, float* x23, float* x33 )
{
//the formula would be that easy without nodata values...
Expand Down
2 changes: 1 addition & 1 deletion src/analysis/raster/qgsruggednessfilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class ANALYSIS_EXPORT QgsRuggednessFilter: public QgsNineCellFilter
~QgsRuggednessFilter();

protected:
/**Calculates output value from nine input values. The input values and the output value can be equal to the
/**Calculates output value from nine input values. The input values and the output value can be equal to the \
nodata value if not present or outside of the border. Must be implemented by subclasses*/
float processNineCellWindow( float* x11, float* x21, float* x31, \
float* x12, float* x22, float* x32, float* x13, float* x23, float* x33 );
Expand Down
6 changes: 3 additions & 3 deletions src/analysis/raster/qgsslopefilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

#include "qgsslopefilter.h"

QgsSlopeFilter::QgsSlopeFilter( const QString& inputFile, const QString& outputFile, const QString& outputFormat )
: QgsDerivativeFilter( inputFile, outputFile, outputFormat )
QgsSlopeFilter::QgsSlopeFilter( const QString& inputFile, const QString& outputFile, const QString& outputFormat ): \
QgsDerivativeFilter( inputFile, outputFile, outputFormat )
{

}
Expand All @@ -28,7 +28,7 @@ QgsSlopeFilter::~QgsSlopeFilter()

}

float QgsSlopeFilter::processNineCellWindow( float* x11, float* x21, float* x31,
float QgsSlopeFilter::processNineCellWindow( float* x11, float* x21, float* x31, \
float* x12, float* x22, float* x32, float* x13, float* x23, float* x33 )
{
float derX = calcFirstDerX( x11, x21, x31, x12, x22, x32, x13, x23, x33 );
Expand Down
8 changes: 3 additions & 5 deletions src/analysis/raster/qgsslopefilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,10 @@ class ANALYSIS_EXPORT QgsSlopeFilter: public QgsDerivativeFilter
QgsSlopeFilter( const QString& inputFile, const QString& outputFile, const QString& outputFormat );
~QgsSlopeFilter();

protected:
/**Calculates output value from nine input values. The input values and the output value can be equal to the
/**Calculates output value from nine input values. The input values and the output value can be equal to the \
nodata value if not present or outside of the border. Must be implemented by subclasses*/
float processNineCellWindow( float* x11, float* x21, float* x31,
float* x12, float* x22, float* x32,
float* x13, float* x23, float* x33 );
float processNineCellWindow( float* x11, float* x21, float* x31, \
float* x12, float* x22, float* x32, float* x13, float* x23, float* x33 );
};

#endif // QGSSLOPEFILTER_H
10 changes: 5 additions & 5 deletions src/analysis/raster/qgstotalcurvaturefilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

#include "qgstotalcurvaturefilter.h"

QgsTotalCurvatureFilter::QgsTotalCurvatureFilter( const QString& inputFile, const QString& outputFile, const QString& outputFormat )
: QgsNineCellFilter( inputFile, outputFile, outputFormat )
QgsTotalCurvatureFilter::QgsTotalCurvatureFilter( const QString& inputFile, const QString& outputFile, const QString& outputFormat ): \
QgsNineCellFilter( inputFile, outputFile, outputFormat )
{

}
Expand All @@ -28,12 +28,12 @@ QgsTotalCurvatureFilter::~QgsTotalCurvatureFilter()

}

float QgsTotalCurvatureFilter::processNineCellWindow( float* x11, float* x21, float* x31, float* x12,
float QgsTotalCurvatureFilter::processNineCellWindow( float* x11, float* x21, float* x31, float* x12, \
float* x22, float* x32, float* x13, float* x23, float* x33 )
{
//return nodata if one value is the nodata value
if ( *x11 == mInputNodataValue || *x21 == mInputNodataValue || *x31 == mInputNodataValue || *x12 == mInputNodataValue
|| *x22 == mInputNodataValue || *x32 == mInputNodataValue || *x13 == mInputNodataValue || *x23 == mInputNodataValue
if ( *x11 == mInputNodataValue || *x21 == mInputNodataValue || *x31 == mInputNodataValue || *x12 == mInputNodataValue \
|| *x22 == mInputNodataValue || *x32 == mInputNodataValue || *x13 == mInputNodataValue || *x23 == mInputNodataValue \
|| *x33 == mInputNodataValue )
{
return mOutputNodataValue;
Expand Down
7 changes: 3 additions & 4 deletions src/analysis/raster/qgstotalcurvaturefilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,10 @@ class ANALYSIS_EXPORT QgsTotalCurvatureFilter: public QgsNineCellFilter
~QgsTotalCurvatureFilter();

protected:
/**Calculates total curvature from nine input values. The input values and the output value can be equal to the
/**Calculates total curvature from nine input values. The input values and the output value can be equal to the \
nodata value if not present or outside of the border. Must be implemented by subclasses*/
float processNineCellWindow( float* x11, float* x21, float* x31,
float* x12, float* x22, float* x32,
float* x13, float* x23, float* x33 );
float processNineCellWindow( float* x11, float* x21, float* x31, \
float* x12, float* x22, float* x32, float* x13, float* x23, float* x33 );
};

#endif // QGSTOTALCURVATUREFILTER_H
333 changes: 262 additions & 71 deletions src/plugins/raster_terrain_analysis/qgsrasterterrainanalysisdialog.cpp

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,50 +1,51 @@
/***************************************************************************
qgsrasterterrainanalysisdialog.h - description
-----------------------------
begin : August 8th, 2009
copyright : (C) 2009 by Marco Hugentobler
email : marco dot hugentobler at karto dot baug dot ethz dot ch
***************************************************************************/

/***************************************************************************
* *
* 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 QGSRASTERTERRAINANALYSISDIALOG_H
#define QGSRASTERTERRAINANALYSISDIALOG_H

#include "ui_qgsrasterterrainanalysisdialogbase.h"

class QgisInterface;
#include "qgsrelief.h"

class QgsRasterTerrainAnalysisDialog: public QDialog, private Ui::QgsRasterTerrainAnalysisDialogBase
{
Q_OBJECT
public:
QgsRasterTerrainAnalysisDialog( QgisInterface* iface, QWidget* parent = 0 );

enum DisplayMode
{
NoParameter,
HillshadeInput,
ReliefInput
};

QgsRasterTerrainAnalysisDialog( DisplayMode mode = NoParameter, QWidget * parent = 0, Qt::WindowFlags f = 0 );
~QgsRasterTerrainAnalysisDialog();

QString selectedInputLayerId() const;
QString selectedDriverKey() const;
QString selectedOuputFilePath() const;
QString selectedAnalysisMethod() const;
bool addLayerToProject() const;
QList< QgsRelief::ReliefColor > reliefColors() const;
QString inputFile() const;
QString outputFile() const;
QString outputFormat() const;

bool addResultToProject() const;
double zFactor() const;
double lightAzimuth() const;
double lightAngle() const;

private slots:
void on_mOutputLayerLineEdit_textChanged( const QString& text );
void on_mOutputLayerPushButton_clicked();
void on_mAutomaticColorButton_clicked();
void on_mOutputLayerToolButton_clicked();
void on_mAddClassButton_clicked();
void on_mRemoveClassButton_clicked();
void on_mUpPushButton_clicked();
void on_mDownPushButton_clicked();
void on_mReliefClassTreeWidget_itemDoubleClicked( QTreeWidgetItem* item, int column );
void on_mExportToCsvButton_clicked();
void on_mExportColorsButton_clicked();
void on_mImportColorsButton_clicked();
void on_mButtonBox_accepted();

private:
QgisInterface* mIface;

/**Stores relation between driver name and extension*/
QMap<QString, QString> mDriverExtensionMap;
};

#endif // QGSRASTERTERRAINANALYSISDIALOG_H
#endif //QGSRASTERTERRAINANALYSISDIALOG_H
Original file line number Diff line number Diff line change
Expand Up @@ -6,100 +6,209 @@
<rect>
<x>0</x>
<y>0</y>
<width>355</width>
<height>176</height>
<width>492</width>
<height>473</height>
</rect>
</property>
<property name="windowTitle">
<string>Raster based terrain analysis</string>
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="mAnalysisLabel">
<widget class="QLabel" name="mElevationLayerLabel">
<property name="text">
<string>Analysis</string>
</property>
<property name="buddy">
<cstring>mAnalysisComboBox</cstring>
<string>Elevation layer</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="mAnalysisComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
<widget class="QComboBox" name="mElevationLayerComboBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="mInputLayerLabel">
<property name="text">
<string>Input layer</string>
</property>
<property name="buddy">
<cstring>mInputLayerComboBox</cstring>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="mInputLayerComboBox"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mOutputLayerLabel">
<property name="text">
<string>Output layer</string>
</property>
<property name="buddy">
<cstring>mOutputLayerPushButton</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="1" column="1">
<widget class="QLineEdit" name="mOutputLayerLineEdit"/>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="mOutputLayerPushButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<item row="1" column="2">
<widget class="QToolButton" name="mOutputLayerToolButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="3" column="0">
<item row="2" column="0">
<widget class="QLabel" name="mOutputFormatLabel">
<property name="text">
<string>Output format</string>
</property>
<property name="buddy">
<cstring>mOutputFormatComboBox</cstring>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QComboBox" name="mOutputFormatComboBox"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="mZFactorLabel">
<property name="text">
<string>Z factor</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QComboBox" name="mOutputFormatComboBox"/>
<widget class="QLineEdit" name="mZFactorLineEdit"/>
</item>
<item row="4" column="0" colspan="3">
<item row="4" column="0">
<widget class="QCheckBox" name="mAddResultToProjectCheckBox">
<property name="text">
<string>Add result to project</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="3">
<widget class="QGroupBox" name="mIlluminationGroupBox">
<property name="title">
<string>Illumination</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="mLightAzimuthLabel">
<property name="text">
<string>Azimuth (horizontal angle)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="mLightAzimuthAngleSpinBox">
<property name="maximum">
<double>360.000000000000000</double>
</property>
<property name="value">
<double>300.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="mLightVerticalAngleLabel">
<property name="text">
<string>Vertical angle</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="mLightVerticalAngleSpinBox">
<property name="maximum">
<double>90.000000000000000</double>
</property>
<property name="value">
<double>40.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="6" column="0" colspan="3">
<widget class="QGroupBox" name="mReliefColorsGroupBox">
<property name="title">
<string>Relief colors</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<widget class="QPushButton" name="mAutomaticColorButton">
<property name="text">
<string>Create automatically</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="mExportToCsvButton">
<property name="text">
<string>Export distribution...</string>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>268</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="4">
<widget class="QPushButton" name="mUpPushButton">
<property name="text">
<string>Up</string>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="QPushButton" name="mDownPushButton">
<property name="text">
<string>Down</string>
</property>
</widget>
</item>
<item row="0" column="6">
<widget class="QPushButton" name="mAddClassButton">
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item row="0" column="7">
<widget class="QPushButton" name="mRemoveClassButton">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="8">
<widget class="QTreeWidget" name="mReliefClassTreeWidget">
<column>
<property name="text">
<string>Lower bound</string>
</property>
</column>
<column>
<property name="text">
<string>Upper bound</string>
</property>
</column>
<column>
<property name="text">
<string>Color</string>
</property>
</column>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="mExportColorsButton">
<property name="text">
<string>Export colors...</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QPushButton" name="mImportColorsButton">
<property name="text">
<string>Import colors...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="QDialogButtonBox" name="mButtonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
Expand All @@ -111,15 +220,6 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>mAnalysisComboBox</tabstop>
<tabstop>mInputLayerComboBox</tabstop>
<tabstop>mOutputLayerLineEdit</tabstop>
<tabstop>mOutputLayerPushButton</tabstop>
<tabstop>mOutputFormatComboBox</tabstop>
<tabstop>mAddResultToProjectCheckBox</tabstop>
<tabstop>mButtonBox</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
Expand Down
164 changes: 124 additions & 40 deletions src/plugins/raster_terrain_analysis/qgsrasterterrainanalysisplugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,25 @@
#include "qgsmaplayer.h"
#include "qgsmaplayerregistry.h"
#include "qgsaspectfilter.h"
#include "qgshillshadefilter.h"
#include "qgsslopefilter.h"
#include "qgsruggednessfilter.h"
#include "qgstotalcurvaturefilter.h"
#include "qgsrelief.h"
#include "qgsrasterterrainanalysisdialog.h"
#include <QAction>
#include <QFileInfo>
#include <QMainWindow>
#include <QMenu>
#include <QMenuBar>
#include <QProgressDialog>

static const QString name_ = QObject::tr( "Raster Terrain Analysis plugin" );
static const QString description_ = QObject::tr( "A plugin for raster based terrain analysis" );
static const QString version_ = QObject::tr( "Version 0.1" );
static const QString icon_ = ":/raster/raster_terrain_icon.png";

QgsRasterTerrainAnalysisPlugin::QgsRasterTerrainAnalysisPlugin( QgisInterface* iface ): mIface( iface ), mAction( 0 )
QgsRasterTerrainAnalysisPlugin::QgsRasterTerrainAnalysisPlugin( QgisInterface* iface ): mIface( iface ), mTerrainAnalysisMenu( 0 )
{

}
Expand All @@ -49,69 +54,148 @@ void QgsRasterTerrainAnalysisPlugin::initGui()
//create Action
if ( mIface )
{
mAction = new QAction( QIcon( ":/raster/raster_terrain_icon.png" ), tr( "&Raster based terrain analysis..." ), 0 );
QObject::connect( mAction, SIGNAL( triggered() ), this, SLOT( run() ) );
mIface->addToolBarIcon( mAction );
mIface->addPluginToMenu( tr( "&Raster based terrain analysis..." ), mAction );
//find raster menu
QString rasterText = QCoreApplication::translate( "QgisApp", "&Raster" );
QMainWindow* mainWindow = qobject_cast<QMainWindow*>( mIface->mainWindow() );
if ( !mainWindow )
{
return;
}

QMenuBar* menuBar = mainWindow->menuBar();
if ( !menuBar )
{
return;
}

QMenu* rasterMenu = 0;
QList<QAction *> menuBarActions = menuBar->actions();
QList<QAction *>::iterator menuActionIt = menuBarActions.begin();
for ( ; menuActionIt != menuBarActions.end(); ++menuActionIt )
{
if (( *menuActionIt )->menu() && ( *menuActionIt )->menu()->title() == rasterText )
{
rasterMenu = ( *menuActionIt )->menu();
rasterMenu->addSeparator();
break;
}
}

if ( !rasterMenu )
{
return;
}

mTerrainAnalysisMenu = new QMenu( tr( "Terrain analysis" ) );
mTerrainAnalysisMenu->addAction( tr( "Slope" ), this, SLOT( slope() ) );
mTerrainAnalysisMenu->addAction( tr( "Aspect" ), this, SLOT( aspect() ) );
mTerrainAnalysisMenu->addAction( tr( "Hillshade" ), this, SLOT( hillshade() ) );
mTerrainAnalysisMenu->addAction( tr( "Relief" ), this, SLOT( relief() ) );
mTerrainAnalysisMenu->addAction( tr( "Ruggedness index" ), this, SLOT( ruggedness() ) );
rasterMenu->addMenu( mTerrainAnalysisMenu );
}
}

void QgsRasterTerrainAnalysisPlugin::unload()
{
if ( mIface )
{
mIface->removePluginMenu( tr( "&Raster based terrain analysis..." ), mAction );
mIface ->removeToolBarIcon( mAction );
delete mAction;
delete mTerrainAnalysisMenu;
}
}

void QgsRasterTerrainAnalysisPlugin::run()
void QgsRasterTerrainAnalysisPlugin::hillshade()
{
QgsRasterTerrainAnalysisDialog d( mIface );
QgsRasterTerrainAnalysisDialog d( QgsRasterTerrainAnalysisDialog::HillshadeInput );
d.setWindowTitle( tr( "Hillshade" ) );
if ( d.exec() == QDialog::Accepted )
{
//get input layer from id
QString inputLayerId = d.selectedInputLayerId();
QgsMapLayer* inputLayer = QgsMapLayerRegistry::instance()->mapLayer( inputLayerId );
if ( !inputLayer )
QString outputFile = d.outputFile();
QgsHillshadeFilter hillshade( d.inputFile(), outputFile, d.outputFormat(), d.lightAzimuth(), d.lightAngle() );
hillshade.setZFactor( d.zFactor() );
QProgressDialog p( tr( "Calculating hillshade..." ), tr( "Abort" ), 0, 0 );
p.setWindowModality( Qt::WindowModal );
hillshade.processRaster( &p );
if ( d.addResultToProject() )
{
return;
mIface->addRasterLayer( outputFile, QFileInfo( outputFile ).baseName() );
}
QString inputFilePath = inputLayer->source();

QString analysisMethod = d.selectedAnalysisMethod();
QString selectedFormat = d.selectedDriverKey();
QString outputFile = d.selectedOuputFilePath();
}
}

QgsNineCellFilter* filter = 0;
if ( d.selectedAnalysisMethod() == tr( "Slope" ) )
{
filter = new QgsSlopeFilter( inputFilePath, outputFile, selectedFormat );
}
else if ( d.selectedAnalysisMethod() == tr( "Aspect" ) )
void QgsRasterTerrainAnalysisPlugin::relief()
{
QgsRasterTerrainAnalysisDialog d( QgsRasterTerrainAnalysisDialog::ReliefInput );
d.setWindowTitle( tr( "Relief" ) );
if ( d.exec() == QDialog::Accepted )
{
QString outputFile = d.outputFile();
QgsRelief relief( d.inputFile(), outputFile, d.outputFormat() );
relief.setReliefColors( d.reliefColors() );
relief.setZFactor( d.zFactor() );
QProgressDialog p( tr( "Calculating relief..." ), tr( "Abort" ), 0, 0 );
p.setWindowModality( Qt::WindowModal );
relief.processRaster( &p );
if ( d.addResultToProject( ) )
{
filter = new QgsAspectFilter( inputFilePath, outputFile, selectedFormat );
mIface->addRasterLayer( outputFile, QFileInfo( outputFile ).baseName() );
}
else if ( d.selectedAnalysisMethod() == tr( "Ruggedness index" ) )
}
}

void QgsRasterTerrainAnalysisPlugin::slope()
{
QgsRasterTerrainAnalysisDialog d( QgsRasterTerrainAnalysisDialog::NoParameter );
d.setWindowTitle( tr( "Slope" ) );
if ( d.exec() == QDialog::Accepted )
{
QString outputFile = d.outputFile();
QgsSlopeFilter slope( d.inputFile(), outputFile, d.outputFormat() );
slope.setZFactor( d.zFactor() );
QProgressDialog p( tr( "Calculating slope..." ), tr( "Abort" ), 0, 0 );
p.setWindowModality( Qt::WindowModal );
slope.processRaster( &p );
if ( d.addResultToProject( ) )
{
filter = new QgsRuggednessFilter( inputFilePath, outputFile, selectedFormat );
mIface->addRasterLayer( outputFile, QFileInfo( outputFile ).baseName() );
}
else if ( d.selectedAnalysisMethod() == tr( "Total curvature" ) )
}
}

void QgsRasterTerrainAnalysisPlugin::aspect()
{
QgsRasterTerrainAnalysisDialog d( QgsRasterTerrainAnalysisDialog::NoParameter );
d.setWindowTitle( tr( "Aspect" ) );
if ( d.exec() == QDialog::Accepted )
{
QString outputFile = d.outputFile();
QgsAspectFilter aspect( d.inputFile(), outputFile, d.outputFormat() );
aspect.setZFactor( d.zFactor() );
QProgressDialog p( tr( "Calculating aspect..." ), tr( "Abort" ), 0, 0 );
p.setWindowModality( Qt::WindowModal );
aspect.processRaster( &p );
if ( d.addResultToProject( ) )
{
filter = new QgsTotalCurvatureFilter( inputFilePath, outputFile, selectedFormat );
mIface->addRasterLayer( outputFile, QFileInfo( outputFile ).baseName() );
}
}
}

if ( filter )
void QgsRasterTerrainAnalysisPlugin::ruggedness()
{
QgsRasterTerrainAnalysisDialog d( QgsRasterTerrainAnalysisDialog::NoParameter );
d.setWindowTitle( tr( "Ruggedness" ) );
if ( d.exec() == QDialog::Accepted )
{
QString outputFile = d.outputFile();
QgsRuggednessFilter ruggedness( d.inputFile(), outputFile, d.outputFormat() );
ruggedness.setZFactor( d.zFactor() );
QProgressDialog p( tr( "Calculating ruggedness..." ), tr( "Abort" ), 0, 0 );
p.setWindowModality( Qt::WindowModal );
ruggedness.processRaster( &p );
if ( d.addResultToProject( ) )
{
QProgressDialog p( tr( "Calculating " ) + d.selectedAnalysisMethod() + "...", tr( "Abort..." ), 0, 0 );
p.setWindowModality( Qt::WindowModal );
filter->processRaster( &p );
delete filter;
if ( d.addLayerToProject() )
{
mIface->addRasterLayer( outputFile, QFileInfo( outputFile ).baseName() );
}
mIface->addRasterLayer( outputFile, QFileInfo( outputFile ).baseName() );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

class QgsInterface;
class QAction;
class QMenu;

/**A plugin for raster based terrain analysis (e.g. slope, aspect, ruggedness)*/
class QgsRasterTerrainAnalysisPlugin: public QObject, public QgisPlugin
Expand All @@ -38,12 +39,15 @@ class QgsRasterTerrainAnalysisPlugin: public QObject, public QgisPlugin
void unload();

private slots:
/**Select input file, output file, format and analysis method*/
void run();
void hillshade();
void relief();
void slope();
void aspect();
void ruggedness();

private:
QgisInterface* mIface;
QAction* mAction;
QMenu* mTerrainAnalysisMenu;
};

#endif // QGSRASTERTERRAINANALYSISPLUGIN_H
270 changes: 270 additions & 0 deletions src/plugins/raster_terrain_analysis/qgsreliefdialog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
#include "qgsreliefdialog.h"
#include "qgsmaplayerregistry.h"
#include "qgsrasterlayer.h"
#include <QColorDialog>
#include <QDir>
#include <QFileDialog>
#include <QFileInfo>
#include <QInputDialog>
#include <QSettings>
#include "cpl_string.h"
#include "gdal.h"

QgsReliefDialog::QgsReliefDialog( DisplayMode mode, QWidget * parent, Qt::WindowFlags f ): QDialog( parent, f )
{
setupUi( this );

if ( mode == HillshadeInput )
{
mReliefColorsGroupBox->setVisible( false );
mLightAzimuthAngleSpinBox->setValue( 300 );
mLightVerticalAngleSpinBox->setValue( 40 );
}
else if ( mode == ReliefInput )
{
mIlluminationGroupBox->setVisible( false );
}
else //no parameters
{
mReliefColorsGroupBox->setVisible( false );
mIlluminationGroupBox->setVisible( false );
}

mZFactorLineEdit->setText( "1.0" );
mZFactorLineEdit->setValidator( new QDoubleValidator( this ) );

//insert available raster layers
//enter available layers into the combo box
QMap<QString, QgsMapLayer*> mapLayers = QgsMapLayerRegistry::instance()->mapLayers();
QMap<QString, QgsMapLayer*>::iterator layer_it = mapLayers.begin();

//insert available input layers
for ( ; layer_it != mapLayers.end(); ++layer_it )
{
QgsRasterLayer* rl = qobject_cast<QgsRasterLayer *>( layer_it.value() );
if ( rl )
{
mElevationLayerComboBox->addItem( rl->name(), QVariant( rl->id() ) );
}
}

//insert available drivers that support the create() operation
GDALAllRegister();

int nDrivers = GDALGetDriverCount();
for ( int i = 0; i < nDrivers; ++i )
{
GDALDriverH driver = GDALGetDriver( i );
if ( driver != NULL )
{
char** driverMetadata = GDALGetMetadata( driver, NULL );
if ( CSLFetchBoolean( driverMetadata, GDAL_DCAP_CREATE, false ) )
{
mOutputFormatComboBox->addItem( GDALGetDriverLongName( driver ), QVariant( GDALGetDriverShortName( driver ) ) );

//store the driver shortnames and the corresponding extensions
//(just in case the user does not give an extension for the output file name)
int index = 0;
while (( driverMetadata ) && driverMetadata[index] != 0 )
{
QStringList metadataTokens = QString( driverMetadata[index] ).split( "=", QString::SkipEmptyParts );
if ( metadataTokens.size() < 1 )
{
break;
}

if ( metadataTokens[0] == "DMD_EXTENSION" )
{
if ( metadataTokens.size() < 2 )
{
++index;
continue;
}
mDriverExtensionMap.insert( QString( GDALGetDriverShortName( driver ) ), metadataTokens[1] );
break;
}
++index;
}

}
}
}

//and set last used driver in combo box
QSettings s;
QString lastUsedDriver = s.value( "/RasterTerrainAnalysis/lastOutputFormat", "GeoTIFF" ).toString();
int lastDriverIndex = mOutputFormatComboBox->findText( lastUsedDriver );
if ( lastDriverIndex != -1 )
{
mOutputFormatComboBox->setCurrentIndex( lastDriverIndex );
}

mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( false );
}

QgsReliefDialog::~QgsReliefDialog()
{
}

QList< QgsRelief::ReliefColor > QgsReliefDialog::reliefColors() const
{
QList< QgsRelief::ReliefColor > reliefColorList;

for ( int i = 0; i < mReliefClassTreeWidget->topLevelItemCount(); ++i )
{
QTreeWidgetItem* reliefItem = mReliefClassTreeWidget->topLevelItem( i );
if ( reliefItem )
{
QgsRelief::ReliefColor rc( reliefItem->background( 2 ).color(), reliefItem->text( 0 ).toDouble(), reliefItem->text( 1 ).toDouble() );
reliefColorList.push_back( rc );
}
}

return reliefColorList;
}

QString QgsReliefDialog::inputFile() const
{
QgsMapLayer* inputLayer = QgsMapLayerRegistry::instance()->mapLayer( mElevationLayerComboBox->itemData( mElevationLayerComboBox->currentIndex() ).toString() );
if ( !inputLayer )
{
return "";
}

QString inputFilePath = inputLayer->source();
return inputFilePath;
}

QString QgsReliefDialog::outputFile() const
{
return mOutputLayerLineEdit->text();
}

QString QgsReliefDialog::outputFormat() const
{
int index = mOutputFormatComboBox->currentIndex();
if ( index == -1 )
{
return "";
}
return mOutputFormatComboBox->itemData( index ).toString();
}

bool QgsReliefDialog::addResultToProject() const
{
return mAddResultToProjectCheckBox->isChecked();
}

double QgsReliefDialog::zFactor() const
{
return mZFactorLineEdit->text().toDouble();
}

void QgsReliefDialog::on_mOutputLayerLineEdit_textChanged( const QString& text )
{
bool enabled = false;

QFileInfo fi( text );
if ( fi.absoluteDir().exists() && mElevationLayerComboBox->count() > 0 )
{
enabled = true;
}
mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( enabled );
}

void QgsReliefDialog::on_mAutomaticColorButton_clicked()
{
QgsRelief relief( inputFile(), outputFile(), outputFormat() );
QList< QgsRelief::ReliefColor > reliefColorList = relief.calculateOptimizedReliefClasses();
QList< QgsRelief::ReliefColor >::iterator it = reliefColorList.begin();

mReliefClassTreeWidget->clear();
for ( ; it != reliefColorList.end(); ++it )
{
QTreeWidgetItem* item = new QTreeWidgetItem();
item->setText( 0, QString::number( it->minElevation ) );
item->setText( 1, QString::number( it->maxElevation ) );
item->setBackground( 2, QBrush( it->color ) );
mReliefClassTreeWidget->addTopLevelItem( item );
}
}

void QgsReliefDialog::on_mExportToCsvButton_clicked()
{
QString file = QFileDialog::getSaveFileName( 0, tr("Export Frequency distribution as csv") );
if( file.isEmpty() )
{
return;
}

QgsRelief relief( inputFile(), outputFile(), outputFormat() );
relief.exportFrequencyDistributionToCsv( file );
}

void QgsReliefDialog::on_mOutputLayerToolButton_clicked()
{
QSettings s;
QString lastDir = s.value( "/RasterTerrainAnalysis/lastOutputDir" ).toString();
QString saveFileName = QFileDialog::getSaveFileName( 0, tr( "Enter result file" ), lastDir );
if ( !saveFileName.isNull() )
{
mOutputLayerLineEdit->setText( saveFileName );
}
}

double QgsReliefDialog::lightAzimuth() const
{
return mLightAzimuthAngleSpinBox->value();
}

double QgsReliefDialog::lightAngle() const
{
return mLightVerticalAngleSpinBox->value();
}

void QgsReliefDialog::on_mRemoveClassButton_clicked()
{
QList<QTreeWidgetItem*> selectedItems = mReliefClassTreeWidget->selectedItems();
QList<QTreeWidgetItem*>::iterator itemIt = selectedItems.begin();
for(; itemIt != selectedItems.end(); ++itemIt )
{
delete *itemIt;
}
}

void QgsReliefDialog::on_mReliefClassTreeWidget_itemDoubleClicked( QTreeWidgetItem* item, int column )
{
if( !item )
{
return;
}

if( column == 0 )
{
bool ok;
double d = QInputDialog::getDouble(0, tr("Enter lower elevation class bound"), tr("Elevation"), item->text( 0 ).toDouble(), -2147483647,
2147483647, 2, &ok );
if( ok )
{
item->setText( 0, QString::number( d ) );
}
}
else if( column == 1 )
{
bool ok;
double d = QInputDialog::getDouble(0, tr("Enter upper elevation class bound"), tr("Elevation"), item->text( 1 ).toDouble(), -2147483647,
2147483647, 2, &ok );
if( ok )
{
item->setText( 1, QString::number( d ) );
}
}
else if( column == 2 )
{
QColor c = QColorDialog::getColor( item->background( 2 ).color(), 0, tr("Select color for relief class") );
if( c.isValid() )
{
item->setBackground( 2, QBrush( c ) );
}
}
}
45 changes: 45 additions & 0 deletions src/plugins/raster_terrain_analysis/qgsreliefdialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#ifndef QGSRELIEFDIALOG_H
#define QGSRELIEFDIALOG_H

#include "ui_qgsreliefdialogbase.h"
#include "qgsrelief.h"

class QgsReliefDialog: public QDialog, private Ui::QgsReliefDialogBase
{
Q_OBJECT
public:

enum DisplayMode
{
NoParameter,
HillshadeInput,
ReliefInput
};

QgsReliefDialog( DisplayMode mode = NoParameter, QWidget * parent = 0, Qt::WindowFlags f = 0 );
~QgsReliefDialog();

QList< QgsRelief::ReliefColor > reliefColors() const;
QString inputFile() const;
QString outputFile() const;
QString outputFormat() const;

bool addResultToProject() const;
double zFactor() const;
double lightAzimuth() const;
double lightAngle() const;

private slots:
void on_mOutputLayerLineEdit_textChanged( const QString& text );
void on_mAutomaticColorButton_clicked();
void on_mOutputLayerToolButton_clicked();
void on_mRemoveClassButton_clicked();
void on_mReliefClassTreeWidget_itemDoubleClicked( QTreeWidgetItem* item, int column );
void on_mExportToCsvButton_clicked();

private:
/**Stores relation between driver name and extension*/
QMap<QString, QString> mDriverExtensionMap;
};

#endif // QGSRELIEFDIALOG_H
230 changes: 230 additions & 0 deletions src/plugins/raster_terrain_analysis/qgsreliefdialogbase.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsReliefDialogBase</class>
<widget class="QDialog" name="QgsReliefDialogBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>393</width>
<height>405</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="mElevationLayerLabel">
<property name="text">
<string>Elevation layer</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="mElevationLayerComboBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="mOutputLayerLabel">
<property name="text">
<string>Output layer</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="mOutputLayerLineEdit"/>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="mOutputLayerToolButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="mOutputFormatLabel">
<property name="text">
<string>Output format</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QComboBox" name="mOutputFormatComboBox"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="mZFactorLabel">
<property name="text">
<string>Z factor</string>
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QLineEdit" name="mZFactorLineEdit"/>
</item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="mAddResultToProjectCheckBox">
<property name="text">
<string>Add result to project</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="3">
<widget class="QGroupBox" name="mIlluminationGroupBox">
<property name="title">
<string>Illumination</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="mLightAzimuthLabel">
<property name="text">
<string>Azimuth (horizontal angle)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="mLightAzimuthAngleSpinBox">
<property name="maximum">
<double>360.000000000000000</double>
</property>
<property name="value">
<double>300.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="mLightVerticalAngleLabel">
<property name="text">
<string>Vertical angle</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="mLightVerticalAngleSpinBox">
<property name="maximum">
<double>90.000000000000000</double>
</property>
<property name="value">
<double>40.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="6" column="0" colspan="3">
<widget class="QGroupBox" name="mReliefColorsGroupBox">
<property name="title">
<string>Relief colors</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>268</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="mAddClassButton">
<property name="text">
<string>+</string>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QPushButton" name="mRemoveClassButton">
<property name="text">
<string>-</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="5">
<widget class="QTreeWidget" name="mReliefClassTreeWidget">
<column>
<property name="text">
<string>Lower bound</string>
</property>
</column>
<column>
<property name="text">
<string>Upper bound</string>
</property>
</column>
<column>
<property name="text">
<string>Color</string>
</property>
</column>
</widget>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="mAutomaticColorButton">
<property name="text">
<string>Create automatically</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="mExportToCsvButton">
<property name="text">
<string>Export distribution...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="7" column="0" colspan="2">
<widget class="QDialogButtonBox" name="mButtonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>mButtonBox</sender>
<signal>accepted()</signal>
<receiver>QgsReliefDialogBase</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>mButtonBox</sender>
<signal>rejected()</signal>
<receiver>QgsReliefDialogBase</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>