Skip To Content ArcGIS for Developers Sign In Dashboard

ArcGIS Runtime SDK for Qt

Stretch renderer

Sample Viewer View Sample on GitHub

Use a stretch renderer to enhance the visual contrast of raster data for analysis.

Use case

An appropriate stretch renderer can enhance the contrast of raster imagery, allowing the user to control how their data is displayed for efficient imagery analysis.

How to use the sample

Choose one of the stretch parameter types:

  • Standard deviation - a linear stretch defined by the standard deviation of the pixel values
  • Min-max - a linear stretch based on minimum and maximum pixel values
  • Percent clip - a linear stretch between the defined percent clip minimum and percent clip maximum pixel values

Then configure the parameters and click 'Edit renderer'.

How it works

To apply a StretchRenderer to a RasterLayer:

  1. Create a Raster from a raster file.
  2. Create a RasterLayer from the raster.
  3. Create a Basemap from the raster layer and set it to the map.
  4. Create a StretchRenderer, specifying the StretchParameters and other properties.
  5. Set the renderer on the raster layer with rasterLayer.renderer = renderer.

Relevant API

  • ColorRamp
  • MinMaxStretchParameters
  • PercentClipStretchParameters
  • Raster
  • RasterLayer
  • StandardDeviationStretchParameters
  • StretchParameters

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

About the data

This sample uses a raster imagery tile of an area of forested mountainous terrain and rivers.

Additional information

See Stretch function in the ArcMap documentation for more information about the types of stretches that can be performed.

Tags

analysis, deviation, histogram, imagery, interpretation, min-max, percent clip, pixel, raster, stretch, symbology, visualization

Sample Code

import QtQuick 2.6
import QtQuick.Controls 2.2
import Esri.ArcGISRuntime 100.9
import Esri.ArcGISExtras 1.1

Rectangle {
    id: rootRectangle
    clip: true
    width: 800
    height: 600

    readonly property string dataPath: System.userHomePath + "/ArcGIS/Runtime/Data/raster"
    readonly property string minMax: "Min Max"
    readonly property string percentClip: "Percent Clip"
    readonly property string stdDeviation: "Standard Deviation"
    readonly property var stretchTypes: [minMax, percentClip, stdDeviation]
    property bool editingRenderer: false

    MapView {
        anchors.fill: parent

        Map {
            Basemap {
                RasterLayer {
                    id: rasterLayer

                    Raster {
                        path: dataPath + "/Shasta.tif"
                    }
                }
            }
        }
    }

    MinMaxStretchParameters {
        id: minMaxParams
    }

    PercentClipStretchParameters {
        id: percentClipParams
    }

    StandardDeviationStretchParameters {
        id: stdDevParams
    }

    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

        Column {
            anchors {
                top: parent.top
                bottom: parent.bottom
                margins: 24
            }
            width: parent.width
            spacing: 16

            ComboBox {
                id: stretchTypeCombo
                anchors.horizontalCenter: parent.horizontalCenter
                property int modelWidth: 0
                width: modelWidth + leftPadding + rightPadding + indicator.width
                model: stretchTypes
                Component.onCompleted : {
                    for (let i = 0; i < model.length; ++i) {
                        metrics.text = model[i];
                        modelWidth = Math.max(modelWidth, metrics.width);
                    }
                }
                TextMetrics {
                    id: metrics
                    font: stretchTypeCombo.font
                }
            }

            InputWithLabel {
                id: minMaxMin
                visible: stretchTypeCombo.currentText === minMax
                spacing: 8
                label: "min value"
                maxRange: 255
                value: 0
            }

            InputWithLabel {
                id: minMaxMax
                visible: stretchTypeCombo.currentText === minMax
                spacing: 8
                label: "max value"
                maxRange: 255
                value: 255
            }

            InputWithLabel {
                id: percentClipMin
                visible: stretchTypeCombo.currentText === percentClip
                spacing: 8
                label: "min value"
                maxRange: 100
                value: 0
            }

            InputWithLabel {
                id: percentClipMax
                visible: stretchTypeCombo.currentText === percentClip
                spacing: 8
                label: "max value"
                maxRange: 100
                value: 100
            }

            InputWithLabel {
                id: sdFactor
                visible: stretchTypeCombo.currentText === stdDeviation
                spacing: 8
                label: "factor"
                maxRange: 25
                value: 0
            }

            Button {
                text: "Render"
                anchors.horizontalCenter: parent.horizontalCenter
                onClicked: {
                    editingRenderer = false;
                    applyRendererSettings();
                }
            }
        }

        Behavior on width { PropertyAnimation { duration: 500 } }
    }

    //! [RasterStretchRenderer qml set renderers]
    function applyRendererSettings(){
        const rgbRenderer = ArcGISRuntimeEnvironment.createObject("StretchRenderer");

        if (stretchTypeCombo.currentText === minMax){
            minMaxParams.minValues = [minMaxMin.value];
            minMaxParams.maxValues = [minMaxMax.value];
            rgbRenderer.stretchParameters = minMaxParams;
        }
        else if (stretchTypeCombo.currentText === percentClip){
            percentClipParams.min =  percentClipMin.value;
            percentClipParams.max = 100 - percentClipMax.value;
            rgbRenderer.stretchParameters = percentClipParams;
        }
        else if (stretchTypeCombo.currentText === stdDeviation){
            stdDevParams.factor =  sdFactor.value;
            rgbRenderer.stretchParameters = stdDevParams;
        }

        rasterLayer.renderer = rgbRenderer;
    }
    //! [RasterStretchRenderer qml set renderers]
}
import QtQuick 2.6
import QtQuick.Controls 2.2

Row {
    anchors.horizontalCenter: parent.horizontalCenter

    property alias maxRange : slider.to
    property alias value: slider.value
    property alias label: text.text

    Text {
        id: text
    }

    Slider {
        id: slider
        width: 100
        from: 0
        to: maxRange

        onValueChanged: {
            if (spinBox.value !== value)
                spinBox.value = value;
        }
    }

    SpinBox {
        id: spinBox
        from: 0
        to: slider.to

        onValueChanged: {
            if (slider.value !== value)
                slider.value = value;
        }
    }
}