Skip to content
Permalink
Browse files

[renderer] Live hillshade renderer for raster layers

Thanks to Asger Skovbo Petersen (@AsgerPetersen) for the idea and fixes
Thanks to Nyall for reviews and bug fixes
  • Loading branch information
NathanW2 committed May 25, 2016
1 parent d8ccec0 commit 17b48563a502140a3fe735a53692dbe2662a2ffb
@@ -284,6 +284,7 @@
%Include raster/qgssinglebandcolordatarenderer.sip
%Include raster/qgssinglebandgrayrenderer.sip
%Include raster/qgssinglebandpseudocolorrenderer.sip
%Include raster/qgshillshaderenderer.sip

%Include symbology-ng/qgscolorbrewerpalette.sip
%Include symbology-ng/qgscptcityarchive.sip
@@ -0,0 +1,67 @@
class QgsHillshadeRenderer : QgsRasterRenderer
{
%TypeHeaderCode
#include "qgshillshaderenderer.h"
%End
public:
/** Renderer owns color array*/
QgsHillshadeRenderer( QgsRasterInterface* input, int band , double lightAzimuth, double lightAngle );

~QgsHillshadeRenderer();

virtual QgsHillshadeRenderer * clone() const /Factory/;

static QgsRasterRenderer* create( const QDomElement& elem, QgsRasterInterface* input ) /Factory/;

QgsRasterBlock *block( int bandNo, const QgsRectangle & extent, int width, int height ) /Factory/;

void writeXML( QDomDocument& doc, QDomElement& parentElem ) const;

QList<int> usesBands() const;

/** Returns the band used by the renderer
*/
int band() const;

/** Sets the band used by the renderer.
* @see band
*/
void setBand( int bandNo );

/**
* @brief The direction of the light over the raster between 0-360
* @return The direction of the light over the raster
*/
double azimuth() const;

/**
* @brief The angle of the light source over the raster
* @return The angle of the light source over the raster
*/
double altitude() const;

/**
* @brief Z Factor
* @return Z Factor
*/
double zFactor() const;


/**
* @brief Set the azimith of the light source.
* @param azimuth The azimuth of the light source.
*/
void setAzimuth( double azimuth );

/**
* @brief Set the altitude of the light source
* @param altitude The altitude
*/
void setAltitude( double angle );

/**
* @brief Set the Z factor of the result image.
* @param zfactor The z factor.
*/
void setZFactor( double zfactor );
};
@@ -203,6 +203,7 @@
%Include raster/qgssinglebandpseudocolorrendererwidget.sip
%Include raster/qgsrendererrasterpropertieswidget.sip
%Include raster/qgsrastertransparencywidget.sip
%Include raster/qgshillshaderendererwidget.sip

%Include symbology-ng/characterwidget.sip
%Include symbology-ng/qgs25drendererwidget.sip
@@ -0,0 +1,72 @@
/**
* @brief Renderer widget for the hill shade renderer.
*/
class QgsHillshadeRendererWidget: QgsRasterRendererWidget
{
%TypeHeaderCode
#include <qgshillshaderendererwidget.h>
%End
public:

/**
* @brief Renderer widget for the hill shade renderer.
* @param layer The layer attached for this widget.
* @param extent The current extent.
*/
QgsHillshadeRendererWidget( QgsRasterLayer* layer, const QgsRectangle &extent = QgsRectangle() );
~QgsHillshadeRendererWidget();

/**
* Factory method to create the renderer for this type.
*/
static QgsRasterRendererWidget* create( QgsRasterLayer* layer, const QgsRectangle &theExtent ) /Factory/;

/**
* @brief The renderer for the widget.
* @return A new renderer for the the config in the widget
*/
QgsRasterRenderer* renderer();

/**
* @brief Set the widget state from the given renderer.
* @param r The renderer to take the state from.
*/
void setFromRenderer( const QgsRasterRenderer* r );

/**
* @brief The direction of the light over the raster between 0-360
* @return The direction of the light over the raster
*/
double azimuth() const;

/**
* @brief The angle of the light source over the raster
* @return The angle of the light source over the raster
*/
double altitude() const;

/**
* @brief Z Factor
* @return Z Factor
*/
double zFactor() const;

public slots:
/**
* @brief Set the altitude of the light source
* @param altitude The altitude
*/
void setAltitude( double altitude );

/**
* @brief Set the azimith of the light source.
* @param azimuth The azimuth of the light source.
*/
void setAzimuth( double azimuth );

/**
* @brief Set the Z factor of the result image.
* @param zfactor The z factor.
*/
void setZFactor( double zfactor );
};
@@ -50,6 +50,7 @@
#include "qgssinglebandgrayrendererwidget.h"
#include "qgssinglebandpseudocolorrendererwidget.h"
#include "qgshuesaturationfilter.h"
#include "qgshillshaderendererwidget.h"

#include <QTableWidgetItem>
#include <QHeaderView>
@@ -366,6 +367,7 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer* lyr, QgsMapCanv
QgsRasterRendererRegistry::instance()->insertWidgetFunction( "multibandcolor", QgsMultiBandColorRendererWidget::create );
QgsRasterRendererRegistry::instance()->insertWidgetFunction( "singlebandpseudocolor", QgsSingleBandPseudoColorRendererWidget::create );
QgsRasterRendererRegistry::instance()->insertWidgetFunction( "singlebandgray", QgsSingleBandGrayRendererWidget::create );
QgsRasterRendererRegistry::instance()->insertWidgetFunction( "hillshade", QgsHillshadeRendererWidget::create );

//fill available renderers into combo box
QgsRasterRendererRegistryEntry entry;
@@ -329,6 +329,7 @@ SET(QGIS_CORE_SRCS
raster/qgssinglebandcolordatarenderer.cpp
raster/qgssinglebandgrayrenderer.cpp
raster/qgssinglebandpseudocolorrenderer.cpp
raster/qgshillshaderenderer.cpp

geometry/qgsabstractgeometryv2.cpp
geometry/qgscircularstringv2.cpp
@@ -801,6 +802,7 @@ SET(QGIS_CORE_HDRS
raster/qgssinglebandcolordatarenderer.h
raster/qgssinglebandgrayrenderer.h
raster/qgssinglebandpseudocolorrenderer.h
raster/qgshillshaderenderer.h

symbology-ng/qgs25drenderer.h
symbology-ng/qgscategorizedsymbolrendererv2.h

5 comments on commit 17b4856

@haubourg

This comment has been minimized.

Copy link
Contributor

@haubourg haubourg replied May 30, 2016

Hi guys,
when testing new live hillshader renderer, I got strange visual artifacts, that I don't get with gdal precalculated hillshade. I see random points and ghost grids. Grids seems to be cleaned by bilinear resampling, when points remain. See :
image

@AsgerPetersen

This comment has been minimized.

Copy link
Contributor

@AsgerPetersen AsgerPetersen replied May 30, 2016

@haubourg That looks strange indeed. Do you have a small sample dataset which can be used for reproducing?

@haubourg

This comment has been minimized.

Copy link
Contributor

@haubourg haubourg replied May 30, 2016

here is one extract:
dem.zip

when using nearest neighbor interpolation, it is even stranger:
image

@AsgerPetersen

This comment has been minimized.

Copy link
Contributor

@AsgerPetersen AsgerPetersen replied May 30, 2016

@haubourg The nearest neighbor artefact is QGIS behaving "as designed" as far as I can tell.

When QGIS wants to upsample (for instance render 100x100 DEM pixels to 500x500 screen pixels), you can choose between the three resampling options.

When choosing nearest neighbor, QGIS will resample the DEM to 500x500 pixels using nearest neighbor interpolation. This upsampled raster is then handed to the hillshade renderer. This resampling method results in "staircase" artefacts. And we will get steep gradients for every 5 pixels, and no gradient for the next 5 pixels like your screendump above.

If you choose "Bilinear" or "Cubic" QGIS deploys a completely different strategy. In these cases the 100x100 pixel DEM is handed to the renderer which will render a nice (but small) hillshade. This rendered image is then upsampled (or stretched) to 500x500 pixels by QGIS. This results in a hillshade which is more "blurry" than it had to be.

What would be optimal in the context of the hillshade renderer would be, if QGIS could upsample the DEM data using for instance bilinear resampling and then hand the properly upsampled DEM to the renderer.

@AsgerPetersen

This comment has been minimized.

Copy link
Contributor

@AsgerPetersen AsgerPetersen replied May 30, 2016

@haubourg @NathanW2 I fixed this in #3152

Please sign in to comment.
You can’t perform that action at this time.