Stretch renderer

This sample demonstrates how to use stretch renderer on a raster layer.

Apply a stretch renderer to a raster layer

Instructions

Choose a stretch renderer type from the dropdown listbox to change the settings for the stretch renderer. The sample allows you to change the stretch type and the parameters for each type. Click/tap the 'Update Renderer' button to update the raster. Experiment with settings for the various types for stretch parameters. For example, setting the renderer to use stretch parameters: Min Max with a min value of 50 and a max value of 200 will stretch between these pixel values. A higher min value will remove more of the lighter pixels values whilst a lower max will remove more of the darker. Percent Clip with a min value of 2 and a max value of 98 will stretch from 2% to 98% of the pixel values histogram. A lower min and higher max percentage will render using more of the original raster histogram. Standard Deviation with a factor of 2.0 will stretch 2 standard deviations from the mean. A higher factor (further from the mean) will render using more of the original raster histogram.

Code

// Copyright 2018 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.

using Esri.ArcGISRuntime.Mapping;
using Esri.ArcGISRuntime.Rasters;
using Esri.ArcGISRuntime.UI.Controls;
using Foundation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using UIKit;

namespace ArcGISRuntimeXamarin.Samples.ChangeStretchRenderer
{

    [Register("ChangeStretchRenderer")]
    public class ChangeStretchRenderer : UIViewController
    {
        // Global constant holding offset where the MapView control should start
        private const int _yPageOffset = 60;

        // Global reference to the MapView used in the sample
        private MapView _myMapView = new MapView();

        // Global reference to table of stretch render choices the user can choose from
        private UITableView _myRenderChoiceType;

        // Global reference to a label that displays the 1st parameter used by the stretch renderer
        private UILabel _Label_Parameter1;

        // Global reference to the 1st parameter used by the stretch renderer that the user can modify 
        private UITextField _Input_Parameter1;

        // Global reference to a label that displays the 2nd parameter used by the stretch renderer
        private UILabel _Label_Parameter2;

        // Global reference to the 2nd parameter used by the stretch renderer that the user can modify 
        private UITextField _Input_Parameter2;

        // Global reference to button the user clicks to change the stretch renderer on the raster 
        private UIButton _UpdateRenderer;

        public ChangeStretchRenderer()
        {
            Title = "Stretch renderer";
        }

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            // Create the layout
            CreateLayout();

            // Initialize the app
            Initialize();
        }

        public override void ViewDidLayoutSubviews()
        {
            // Setup the visual frame for button the users clicks to change the stretch renderer on the raster
            _UpdateRenderer.Frame = new CoreGraphics.CGRect(0, _yPageOffset, View.Bounds.Width, 40);

            // Setup the visual frame for the list of stretch renderer choices the user can pick from
            _myRenderChoiceType.Frame = new CoreGraphics.CGRect(0, _yPageOffset + 40, View.Bounds.Width, 140);

            // Setup the visual frame for the label that displays the 1st parameter used by the stretch renderer
            _Label_Parameter1.Frame = new CoreGraphics.CGRect(0, _yPageOffset + 180, View.Bounds.Width, 40);

            // Setup the visual frame for the 1st parameter used by the stretch renderer that the user can modify 
            _Input_Parameter1.Frame = new CoreGraphics.CGRect(250, _yPageOffset + 180 , View.Bounds.Width, 40);

            // Setup the visual frame for the label that displays the 2nd parameter used by the stretch renderer
            _Label_Parameter2.Frame = new CoreGraphics.CGRect(0, _yPageOffset + 220, View.Bounds.Width, 40);

            // Setup the visual frame for the 2nd parameter used by the stretch renderer that the user can modify 
            _Input_Parameter2.Frame = new CoreGraphics.CGRect(250, _yPageOffset + 220, View.Bounds.Width, 40);

            // Setup the visual frame for the MapView
            _myMapView.Frame = new CoreGraphics.CGRect(0, _yPageOffset + 260, View.Bounds.Width, View.Bounds.Height - 300);
        }

        private async void Initialize()
        {
            // Add an imagery basemap
            Map myMap = new Map(Basemap.CreateImagery());

            // Wait for the map to load
            await myMap.LoadAsync();

            // Get the file name
            string filepath = await GetRasterPath();

            // Load the raster file
            Raster myRasterFile = new Raster(filepath);

            // Create the layer
            RasterLayer myRasterLayer = new RasterLayer(myRasterFile);

            // Add the layer to the map
            myMap.OperationalLayers.Add(myRasterLayer);

            // Wait for the layer to load
            await myRasterLayer.LoadAsync();

            // Set the viewpoint
            myMap.InitialViewpoint = new Viewpoint(myRasterLayer.FullExtent);

            // Add map to the mapview
            _myMapView.Map = myMap;
        }

        private void CreateLayout()
        {
            // This section creates the UI elements and adds them to the layout view of the GUI

            // Create button to change stretch renderer of the raster
            _UpdateRenderer = new UIButton();
            _UpdateRenderer.SetTitle("Update Renderer", UIControlState.Normal);
            _UpdateRenderer.SetTitleColor(UIColor.Blue, UIControlState.Normal);
            _UpdateRenderer.BackgroundColor = UIColor.White;
            // Hook to touch/click event of the button
            _UpdateRenderer.TouchUpInside += OnUpdateRendererClicked;

            // Create a list of stretch renderer choices the user can choose from
            _myRenderChoiceType = new UITableView();
            // The options of stretch renderer types the user can choose from
            string[] tableItems = new string[] { "Min Max", "Percent Clip", "Standard Deviation"};
            // Custom class to handle display/interaction of items in the UITableView
            _myRenderChoiceType.Source = new TableSource(tableItems,this); 
            _myRenderChoiceType.SeparatorColor = UIColor.Yellow;

            // Create label that displays the 1st parameter used by the stretch renderer
            _Label_Parameter1 = new UILabel();
            _Label_Parameter1.Text = "Minimum value (0 - 255):";
            _Label_Parameter1.AdjustsFontSizeToFitWidth = true;
            _Label_Parameter1.BackgroundColor = UIColor.White;

            // Create text field for 1st parameter used by the stretch renderer that the user can modify 
            _Input_Parameter1 = new UITextField();
            _Input_Parameter1.Text = "10";
            _Input_Parameter1.AdjustsFontSizeToFitWidth = true;
            _Input_Parameter1.BackgroundColor = UIColor.White;
            // Allow pressing 'return' to dismiss the keyboard
            _Input_Parameter1.ShouldReturn += (textField) => { textField.ResignFirstResponder(); return true; };

            // Create label that displays the 2nd parameter used by the stretch renderer
            _Label_Parameter2 = new UILabel();
            _Label_Parameter2.Text = "Maximum value (0 - 255):";
            _Label_Parameter2.AdjustsFontSizeToFitWidth = true;
            _Label_Parameter2.BackgroundColor = UIColor.White;

            // Create text field for 2nd parameter used by the stretch renderer that the user can modify 
            _Input_Parameter2 = new UITextField();
            _Input_Parameter2.Text = "150";
            _Input_Parameter2.AdjustsFontSizeToFitWidth = true;
            _Input_Parameter2.BackgroundColor = UIColor.White;
            // Allow pressing 'return' to dismiss the keyboard
            _Input_Parameter2.ShouldReturn += (textField) => { textField.ResignFirstResponder(); return true; };

            // Add all of the UI controls to the page
            View.AddSubviews(_UpdateRenderer, _myRenderChoiceType, _Label_Parameter1, _Input_Parameter1, _Label_Parameter2, _Input_Parameter2, _myMapView);
        }

        public void RenderChoiceSelectionChanged(int selectedIndex)
        {
            // This function modifies the UI parameter controls depending on which stretch 
            // renderer is chosen by the user when clicking the table view

            // Get the user choice for the raster stretch render
            UITableViewSource myUITableViewSource = _myRenderChoiceType.Source;
            TableSource myTableSource = (TableSource)myUITableViewSource;
            string myRendererTypeChoice = myTableSource.TableItems[selectedIndex];

            switch (myRendererTypeChoice)
            {
                case "Min Max":

                    // This section displays/resets the user choice options for MinMaxStretchParameters

                    // Make sure all the GUI items are visible
                    _Label_Parameter1.Hidden = false;
                    _Label_Parameter2.Hidden = false;
                    _Input_Parameter1.Hidden = false;
                    _Input_Parameter2.Hidden = false;

                    // Define what values/options the user sees
                    _Label_Parameter1.Text = "Minimum value (0 - 255):";
                    _Label_Parameter2.Text = "Maximum value (0 - 255):";
                    _Input_Parameter1.Text = "10";
                    _Input_Parameter2.Text = "150";

                    break;

                case "Percent Clip":

                    // This section displays/resets the user choice options for PercentClipStretchParameters

                    // Make sure all the GUI items are visible
                    _Label_Parameter1.Hidden = false;
                    _Label_Parameter2.Hidden = false;
                    _Input_Parameter1.Hidden = false;
                    _Input_Parameter2.Hidden = false;

                    // Define what values/options the user sees
                    _Label_Parameter1.Text = "Minimum (0 - 100):";
                    _Label_Parameter2.Text = "Maximum (0 - 100)";
                    _Input_Parameter1.Text = "0";
                    _Input_Parameter2.Text = "50";

                    break;

                case "Standard Deviation":

                    // This section displays/resets the user choice options for StandardDeviationStretchParameters

                    // Make sure that only the necessary GUI items are visible
                    _Label_Parameter1.Hidden = false;
                    _Label_Parameter2.Hidden = true;
                    _Input_Parameter1.Hidden = false;
                    _Input_Parameter2.Hidden = true;

                    // Define what values/options the user sees
                    _Label_Parameter1.Text = "Factor (.25 to 4):";
                    _Input_Parameter1.Text = "0.5";

                    break;
            }

        }

        private void OnUpdateRendererClicked(object sender, EventArgs e)
        {

            // This function acquires the user selection of the stretch renderer from the table view
            // along with the parameters specified, then a stretch renderer is created and applied to 
            // the raster layer

            // Get the user choice for the raster stretch render
            UITableViewSource myUITableViewSource = _myRenderChoiceType.Source;
            TableSource myTableSource = (TableSource)myUITableViewSource;
            string myRendererTypeChoice;

            if (myTableSource.SelectedValue == null)
            {
                // If the user does not click on a choice in the table but just clicks the
                // button, the selected value will be null so use the initial
                // stretch renderer option
                myRendererTypeChoice = "Min Max";
            }
            else
            {
                // The user clicked on an option in the table and thus the selected value
                // will contain a valid choice
                myRendererTypeChoice = myTableSource.SelectedValue; 
            }

            // Create an IEnumerable from an empty list of doubles for the gamma values in the stretch render
            IEnumerable<double> myGammaValues = new List<double> { };

            // Create a color ramp for the stretch renderer
            ColorRamp myColorRamp = ColorRamp.Create(PresetColorRampType.DemLight, 1000);

            // Create the place holder for the stretch renderer
            StretchRenderer myStretchRenderer = null;

            switch (myRendererTypeChoice)
            {
                case "Min Max":

                    // This section creates a stretch renderer based on a MinMaxStretchParameters
                    // TODO: Add you own logic to ensure that accurate min/max stretch values are used

                    // Create an IEnumerable from a list of double min stretch value doubles
                    IEnumerable<double> myMinValues = new List<double> { Convert.ToDouble(_Input_Parameter1.Text) };

                    // Create an IEnumerable from a list of double max stretch value doubles
                    IEnumerable<double> myMaxValues = new List<double> { Convert.ToDouble(_Input_Parameter2.Text) };

                    // Create a new MinMaxStretchParameters based on the user choice for min and max stretch values
                    MinMaxStretchParameters myMinMaxStretchParameters = new MinMaxStretchParameters(myMinValues, myMaxValues);

                    // Create the stretch renderer based on the user defined min/max stretch values, empty gamma values, statistic estimates, and a predefined color ramp 
                    myStretchRenderer = new StretchRenderer(myMinMaxStretchParameters, myGammaValues, true, myColorRamp);

                    break;

                case "Percent Clip":

                    // This section creates a stretch renderer based on a PercentClipStretchParameters
                    // TODO: Add you own logic to ensure that accurate min/max percent clip values are used

                    // Create a new PercentClipStretchParameters based on the user choice for min and max percent clip values
                    PercentClipStretchParameters myPercentClipStretchParameters = new PercentClipStretchParameters(Convert.ToDouble(_Input_Parameter1.Text), Convert.ToDouble(_Input_Parameter2.Text));

                    // Create the percent clip renderer based on the user defined min/max percent clip values, empty gamma values, statistic estimates, and a predefined color ramp 
                    myStretchRenderer = new StretchRenderer(myPercentClipStretchParameters, myGammaValues, true, myColorRamp);

                    break;

                case "Standard Deviation":

                    // This section creates a stretch renderer based on a StandardDeviationStretchParameters
                    // TODO: Add you own logic to ensure that an accurate standard deviation value is used

                    // Create a new StandardDeviationStretchParameters based on the user choice for standard deviation value
                    StandardDeviationStretchParameters myStandardDeviationStretchParameters = new StandardDeviationStretchParameters(Convert.ToDouble(_Input_Parameter1.Text));

                    // Create the standard deviation renderer based on the user defined standard deviation value, empty gamma values, statistic estimates, and a predefined color ramp 
                    myStretchRenderer = new StretchRenderer(myStandardDeviationStretchParameters, myGammaValues, true, myColorRamp);

                    break;
            }

            // Get the existing raster layer in the map
            RasterLayer myRasterLayer = (RasterLayer)_myMapView.Map.OperationalLayers[0];

            // Apply the stretch renderer to the raster layer
            myRasterLayer.Renderer = myStretchRenderer;
        }

        private async Task<string> GetRasterPath()
        {
            // This code assumes that you have already installed the file at the specified path.
	    // For more information and examples of using offline data, see https://github.com/Esri/arcgis-runtime-samples-dotnet/wiki/Offline-Data
	    // The referenced item can be found at http://www.arcgis.com/home/item.html?id=95392f99970d4a71bd25951beb34a508
	    return Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), "ShastaBW.tif" );
        }

    }

    /// <summary>
    /// This is a custom class that defines how the UITableView control renders its 
    /// contents. It implements the UI for the list of strings that display 
    /// 'stretch renderer' options for the user to pick from.
    /// </summary>
    /// <remarks>
    /// Unlike WPF, UWP and Xamarin.Forms; the native Xamarin iOS does not include an out 
    /// of the box a ListView or ComboBox type GUI control. The closest option is an
    /// UITableView control that can display a list of options that users can see and
    /// interact with. In order to present the list of options (typically human readable
    /// strings) to the user, it is required to create a custom class to bind to the 
    /// UITableView.Source property. It is the developers responsibility to write the 
    /// interaction logic of the IUTableView control for things such as obtaining: the 
    /// list of items or the currently selected item in the UITableView. 
    /// </remarks>
    public class TableSource : UITableViewSource
    {
        // Public property to get the items/array (as strings) in the UITableView
        public string[] TableItems;

        // Public property to get the currently selected item in the array of
        // options displayed in the UITableView 
        public string SelectedValue;

        // Public property used when re-using cells to ensure that a cell of the right 
        // type is used
        public string CellIdentifier = "TableCell";

        // Public property to hold a reference to the owning view controller; this will be 
        // the active instance of the ChangeStretchRenderer sample  
        public ChangeStretchRenderer Owner { get; set; }

        // Default constructor to create this custom class that is used as the 
        // UTTableView.Source property. It input parameters take an array of strings
        // and the parent owning view controller.
        public TableSource(string[] items, ChangeStretchRenderer owner)
        {
            // Set the TableItems property
            TableItems = items;

            // Set the Owner property
            Owner = owner;
        }

        // Return an nint count of the total number of rows of data the UITableView 
        // should display, in this case it will return the number of stretch renderer
        // options the user has to choose from
        public override nint RowsInSection(UITableView tableview, nint section)
        {
            // Return the total number of rows in the UITableView 
            return TableItems.Length;
        }

        // This method gets a table view cell for the suggestion at the specified index
        public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
        {
            // Try to get a re-usable cell (this is for performance)
            UITableViewCell cell = tableView.DequeueReusableCell(CellIdentifier);

            // Get the specific item to display
            string item = TableItems[indexPath.Row];

            // If there are no cells to reuse, create a new one
            if (cell == null)
            { cell = new UITableViewCell(UITableViewCellStyle.Default, CellIdentifier); }

            // Set the text on the cell
            cell.TextLabel.Text = item;

            // Return the cell
            return cell;
        }

        // This method handles when the user taps/clicks on an item in the UITableView.
        // It performs two functions: 
        // (1) Invoke a call to the RenderChoiceSelectionChanged method in the ChangeStretchRender
        // class - this modifies the UI parameter controls depending on which stretch 
        // renderer is chosen by the user when clicking the table view 
        // (2) Set the SelectedValue property that gives the developer the ability to 
        // know what was the selected item in the UITableView from the user click
        public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
        {
            // Deselect the row
            tableView.DeselectRow(indexPath, true);

            // Invoke a call to the RenderChoiceSelectionChanged method in the ChangeStretchRender class
            Owner.RenderChoiceSelectionChanged(indexPath.Row);

            // Set the SelectedValue property
            SelectedValue = TableItems[indexPath.Row];
        }
    }

}


In this topic
  1. Instructions
  2. Code