Blend a hillshade with a raster by specifying the elevation data. The resulting raster looks similar to the original raster, but with some terrain shading, giving it a textured look.

Use case
BlendRenderer can be used to apply a color ramp to a hillshade to emphasize areas of high or low elevation. A BlendRenderer can also be used to add a hillshade effect to aerial or satellite imagery, thereby making changes in elevation more visible.
How to use the sample
Tap on “Edit Renderer” in the toolbar to change the settings for the blend renderer. Choose and adjust the altitude, azimuth, slope type and color ramp type settings to update the image.
How it works
- Create a
Rasterobject from a raster file. - Create a
RasterLayerobject from the raster. - Create a
Basemapobject from the raster layer and set it to the map. - Create another
Rasterobject for elevation from a grayscale raster file. - Create a
BlendRendererobject, specifying the elevation raster, color ramp, and other properties.- If you specify a non-null color ramp, use the elevation raster as the base raster in addition to the elevation raster parameter. That way, the color ramp is used instead of the satellite imagery.
- Set the blend renderer to the raster layer.
Relevant API
- BlendRenderer
- ColorRamp
- Raster
- RasterLayer
Offline Data
Read more about how to set up the sample’s offline data here.
| Link | Local Location |
|---|---|
| Shasta.tif raster | <userhome>/ArcGIS/Runtime/Data/raster/Shasta.tif |
| Shasta_Elevation.tif raster | <userhome>/ArcGIS/Runtime/Data/raster/Shasta_Elevation.tif |
Tags
color ramp, elevation, hillshade, image, raster, raster layer, visualization
Sample Code
// [WriteFile Name=BlendRasterLayer, Category=Layers]// [Legal]// Copyright 2017 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifdef PCH_BUILD#include "pch.hpp"#endif // PCH_BUILD
// sample headers#include "BlendRasterLayer.h"
// ArcGIS Maps SDK headers#include "Basemap.h"#include "BlendRenderer.h"#include "ColorRamp.h"#include "Map.h"#include "MapQuickView.h"#include "MapViewTypes.h"#include "Raster.h"#include "RasterLayer.h"#include "RasterTypes.h"
// Qt headers#include <QStandardPaths>#include <QtCore/qglobal.h>
using namespace Esri::ArcGISRuntime;
// helper method to get cross platform data pathnamespace{ QString defaultDataPath() { QString dataPath;
#ifdef Q_OS_IOS dataPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation); #else dataPath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); #endif
return dataPath; }} // namespace
BlendRasterLayer::BlendRasterLayer(QQuickItem* parent /* = nullptr */): QQuickItem(parent), m_dataPath(defaultDataPath() + "/ArcGIS/Runtime/Data/raster"){}
BlendRasterLayer::~BlendRasterLayer() = default;
void BlendRasterLayer::init(){ // Register the map view for QML qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView"); qmlRegisterType<BlendRasterLayer>("Esri.Samples", 1, 0, "BlendRasterLayerSample");}
void BlendRasterLayer::componentComplete(){ QQuickItem::componentComplete();
// find QML MapView component m_mapView = findChild<MapQuickView*>("mapView"); m_mapView->setWrapAroundMode(WrapAroundMode::Disabled);
// Create the raster and raster layer Raster* raster = new Raster(m_dataPath + "/Shasta.tif", this); m_rasterLayer = new RasterLayer(raster, this);
m_elevationRaster = new Raster(m_dataPath + "/Shasta_Elevation.tif", this); m_rasterLayerColorRamp = new RasterLayer(m_elevationRaster, this);
// Add the raster to the map m_basemap = new Basemap(m_rasterLayer, this); m_basemapColorRamp = new Basemap(m_rasterLayerColorRamp, this); m_map = new Map(m_basemap, this); m_mapView->setMap(m_map);}
void BlendRasterLayer::applyRenderSettings(double altitude, double azimuth, int slopeTypeVal, int colorRampTypeVal){ PresetColorRampType colorRampType = static_cast<PresetColorRampType>(colorRampTypeVal); RasterLayer* rasterLyr = rasterLayer(colorRampType != PresetColorRampType::None); if (!rasterLyr) return;
QList<double> outputMinValues{9}; QList<double> outputMaxValues{255}; QList<double> sourceMinValues; QList<double> sourceMaxValues; QList<double> noDataValues; QList<double> gammas; double zFactor = 1.; double pixelSizeFactor = 1.; double pixelSizePower = 1.; int outputBitDepth = 8; ColorRamp* colorRamp = ColorRamp::create(colorRampType, 800, this); SlopeType slopeType = static_cast<SlopeType>(slopeTypeVal);
BlendRenderer* renderer = new BlendRenderer(m_elevationRaster, outputMinValues, outputMaxValues, sourceMinValues, sourceMaxValues, noDataValues, gammas, colorRamp, altitude, azimuth, zFactor, slopeType, pixelSizeFactor, pixelSizePower, outputBitDepth, this);
rasterLyr->setRenderer(renderer);}
RasterLayer* BlendRasterLayer::rasterLayer(bool useColorRamp){ m_map->setBasemap(useColorRamp ? m_basemapColorRamp : m_basemap); return useColorRamp ? m_rasterLayerColorRamp : m_rasterLayer;}// [WriteFile Name=BlendRasterLayer, Category=Layers]// [Legal]// Copyright 2017 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifndef BLENDRASTERLAYER_H#define BLENDRASTERLAYER_H
// Qt headers#include <QQuickItem>
namespace Esri::ArcGISRuntime{ class Basemap; class Map; class MapQuickView; class Raster; class RasterLayer;}
class BlendRasterLayer : public QQuickItem{ Q_OBJECT
public: explicit BlendRasterLayer(QQuickItem* parent = nullptr); ~BlendRasterLayer() override;
static void init();
void componentComplete() override;
Q_INVOKABLE void applyRenderSettings(double altitude, double azimuth, int slopeType, int colorRampType);
private:
Esri::ArcGISRuntime::RasterLayer* rasterLayer(bool useColorRamp);
Esri::ArcGISRuntime::Map* m_map = nullptr; Esri::ArcGISRuntime::Basemap* m_basemap = nullptr; Esri::ArcGISRuntime::Basemap* m_basemapColorRamp = nullptr; Esri::ArcGISRuntime::MapQuickView* m_mapView = nullptr; Esri::ArcGISRuntime::RasterLayer* m_rasterLayer = nullptr; Esri::ArcGISRuntime::RasterLayer* m_rasterLayerColorRamp = nullptr; Esri::ArcGISRuntime::Raster* m_elevationRaster = nullptr; QString m_dataPath;};
#endif // BLENDRASTERLAYER_H// [WriteFile Name=BlendRasterLayer, Category=Layers]// [Legal]// Copyright 2017 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
import QtQuickimport QtQuick.Controlsimport QtQuick.Layoutsimport Esri.Samples
BlendRasterLayerSample { id: rootRectangle clip: true width: 800 height: 600
property bool editingRenderer: false
SlopeTypeModel { id: slopeTypeModel }
ColorRampModel { id: colorRampModel }
// add a mapView component MapView { anchors.fill: parent objectName: "mapView"
Component.onCompleted: { // Set the focus on MapView to initially enable keyboard navigation forceActiveFocus(); } }
Rectangle { visible: editButton.visible anchors.centerIn: editButton radius: 8 height: editButton.height + (16) width: editButton.width + (16) color: "lightgrey" border.color: "darkgrey" border.width: 2 opacity: 0.75 }
Button { id: editButton anchors { bottom: parent.bottom horizontalCenter: parent.horizontalCenter margins: 32 } visible: rendererBox.width === 0 text: "Edit Renderer" onClicked: editingRenderer = true; }
Rectangle { id: rendererBox clip: true anchors { right: parent.right top: parent.top bottom: parent.bottom }
color: "white" opacity: 0.75 width: editingRenderer ? parent.width : 0
GridLayout { anchors { centerIn: parent margins: 24 }
columns: 2
Text { text: "altitude" }
SpinBox { id: altSpinBox from: 0 to: 90 editable: true textFromValue: function(value) { return value.toFixed(0) + "\u00B0"; } valueFromText: function(text) { return parseInt(text); } }
Text { text: "azimuth" }
SpinBox { id: azimuthSpinBox from: 0 to: 360 editable: true textFromValue: function(value) { return value.toFixed(0) + "\u00B0"; } valueFromText: function(text) { return parseInt(text); } }
Text { text: "slope type" }
ComboBox { id: slopeCombo property int modelWidth: 0 Layout.minimumWidth: modelWidth + leftPadding + rightPadding + (indicator ? indicator.width : 10) Layout.fillWidth: true textRole: "name" model: slopeTypeModel Component.onCompleted : { for (let i = 0; i < model.count; ++i) { metrics.text = model.get(i).name; modelWidth = Math.max(modelWidth, metrics.width); } } TextMetrics { id: metrics font: slopeCombo.font } }
Text { text: "color ramp" }
ComboBox { id: colorCombo property int modelWidth: 0 Layout.minimumWidth: modelWidth + leftPadding + rightPadding + (indicator ? indicator.width : 10) Layout.fillWidth: true textRole: "name" model: colorRampModel Component.onCompleted : { for (let i = 0; i < model.count; ++i) { metrics2.text = model.get(i).name; modelWidth = Math.max(modelWidth, metrics2.width); } } TextMetrics { id: metrics2 font: colorCombo.font } }
Button { text: "Render" Layout.columnSpan: 2 Layout.alignment: Qt.AlignHCenter onClicked: { editingRenderer = false; applyRenderSettings(altSpinBox.value, azimuthSpinBox.value, slopeTypeModel.get(slopeCombo.currentIndex).value, colorRampModel.get(colorCombo.currentIndex).value); } } }
Behavior on width { PropertyAnimation { duration: 500 } } }}// [WriteFile Name=BlendRasterLayer, Category=Layers]// [Legal]// Copyright 2017 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
import QtQuick
ListModel { ListElement { name: "none" value: -1 } ListElement { name: "elevation" value: 0 } ListElement { name: "DEM screen" value: 1 } ListElement { name: "DEM light" value: 2 }}// [WriteFile Name=BlendRasterLayer, Category=Layers]// [Legal]// Copyright 2017 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
import QtQuick
ListModel { ListElement { name: "none" value: -1 } ListElement { name: "degree" value: 0 } ListElement { name: "percent rise" value: 1 } ListElement { name: "scaled" value: 2 }}// [Legal]// Copyright 2017 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
// sample headers#include "BlendRasterLayer.h"
// ArcGIS Maps SDK headers#include "ArcGISRuntimeEnvironment.h"
// Qt headers#include <QCommandLineParser>#include <QDir>#include <QGuiApplication>#include <QQmlEngine>#include <QQuickView>
// Platform specific headers#ifdef Q_OS_WIN#include <Windows.h>#endif
#define STRINGIZE(x) #x#define QUOTE(x) STRINGIZE(x)
using namespace Esri::ArcGISRuntime;
int main(int argc, char *argv[]){ Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setUseLegacyAuthentication(false); QGuiApplication app(argc, argv); app.setApplicationName(QString("BlendRasterLayer"));
// Use of ArcGIS location services, such as basemap styles, geocoding, and routing services, // requires an access token. For more information see // https://links.esri.com/arcgis-runtime-security-auth.
// The following methods grant an access token:
// 1. User authentication: Grants a temporary access token associated with a user's ArcGIS account. // To generate a token, a user logs in to the app with an ArcGIS account that is part of an // organization in ArcGIS Online or ArcGIS Enterprise.
// 2. API key authentication: Get a long-lived access token that gives your application access to // ArcGIS location services. Go to the tutorial at https://links.esri.com/create-an-api-key. // Copy the API Key access token.
const QString accessToken = QString("");
if (accessToken.isEmpty()) { qWarning() << "Use of ArcGIS location services, such as the basemap styles service, requires" << "you to authenticate with an ArcGIS account or set the API Key property."; } else { Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setApiKey(accessToken); }
// Initialize the sample BlendRasterLayer::init();
// Initialize application view QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView);
// Add the import Path view.engine()->addImportPath(QDir(QCoreApplication::applicationDirPath()).filePath("qml"));
QString arcGISRuntimeImportPath = QUOTE(ARCGIS_RUNTIME_IMPORT_PATH);
#if defined(LINUX_PLATFORM_REPLACEMENT) // on some linux platforms the string 'linux' is replaced with 1 // fix the replacement paths which were created QString replaceString = QUOTE(LINUX_PLATFORM_REPLACEMENT); arcGISRuntimeImportPath = arcGISRuntimeImportPath.replace(replaceString, "linux", Qt::CaseSensitive); #endif
// Add the Runtime and Extras path view.engine()->addImportPath(arcGISRuntimeImportPath);
// Set the source view.setSource(QUrl("qrc:/Samples/Layers/BlendRasterLayer/BlendRasterLayer.qml"));
view.show();
return app.exec();}