1

I have a DEM of the state of florida as an ImageService, here. I want to render this DEM using nicer colors than black and white using the ArcGIS JS API. Following the example ImageryTileLayer - shaded relief renderer , I apply a RasterShadedReliefRender as the imagelayer's renderer:

import RasterShadedReliefRenderer from "@arcgis/core/renderers/RasterShadedReliefRenderer";
import AlgorithmicColorRamp from "@arcgis/core/rest/support/AlgorithmicColorRamp.js";
import Color from "@arcgis/core/Color.js";
import MultipartColorRamp from "@arcgis/core/rest/support/MultipartColorRamp.js";

const renderer = new RasterShadedReliefRenderer({
  hillshadeType: "traditional",
  zFactor: 15,
  scalingType: "adjusted",
  colorRamp: new MultipartColorRamp({
    colorRamps: [
      new AlgorithmicColorRamp({
        fromColor: new Color("#2F86A6"),
        toColor: new Color("#12372A"),
      }),
    ],
  }),
});


const layer = new ImageryLayer({
  url: the_url_from_above,
  renderer
})

This is working, except the range of colors from the color ramp doesn't seem to span the full range of elevations in the layer. The resulting image:

enter image description here

You can see there is a nice gradient right around the sea level, but above that, the hillshade seems to be colored on more of a greyscale.

I tried adding more ramps within the colorRamp, but the same problem occurs. For example

export const renderer = new RasterShadedReliefRenderer({
  hillshadeType: "traditional",
  zFactor: 15,
  scalingType: "adjusted",
  colorRamp: new MultipartColorRamp({
    colorRamps: [
      new AlgorithmicColorRamp({
        fromColor: new Color("#2F86A6"),
        toColor: new Color("#12372A"),
      }),
      new AlgorithmicColorRamp({
        fromColor: new Color("#12372A"),
        toColor: new Color("#436850"),
      }),
      new AlgorithmicColorRamp({
        fromColor: new Color("#436850"),
        toColor: new Color("#ADBC9F"),
      }),
    ],
  }),
});

This results in:

enter image description here

To make the point stupidly obviously, with more ridiculous colors added in there, I get results like these:

enter image description here enter image description here

You can see that adding more color ramps just compresses the colors into the same range. While the colors are darker in the last images, you can see there is still more terrain being represented by a darkening of the last color.

The goal here is that the start of the color ramp should take the lowest value in the DEM, and the end of it should take the highest value in the DEM. That doesn't seem to be happening here.

What I doing wrong? How can I create a continuous color ramp that continuously belnds from the fromColor to the toColor across the range of values in the DEM ImageryLayer?

Edit: A working layer

I notice that when I try an alternative but older DEM, I get the desired result:

enter image description here

The difference that I see is that this older DEM is just of florida, and has a min and max value listed of 0-336 - the elevations in m within the state of Florida. And the serviceDataType is esriImageServiceDataTypeGeneric. Whereas the one that is not working, the min and max values are listed as -0.7808904001722112 and 13.772270436049439, and the Service Data Type is esriImageServiceDataTypeElevation. I don't quite understand this second one - why is the range of min and max values so small? What units is it in? How does esriImageServiceDataTypeGeneric differ from esriImageServiceDataTypeElevation, and how does that affect my shaded relief renderer?

1 Answer 1

1

This is not really a solution, just some findings that show there might be something wrong with the way that RasterShadedReliefRenderer interprets ImageryLayer.

If in ImageryLayer creation instead of specifying renderer option pixelFilter is used and in the filer function min and max values are checked, these are the value:

min: -1.695206642150879
max: 182.93194580078125

This shows that there are values over 13.772270436049439, but are all presented with the same color.

This code:

function maskPixels(pixelData) {
  const minVal = pixelData.pixelBlock.statistics[0].minValue;
  const maxVal = pixelData.pixelBlock.statistics[0].maxValue;
  console.log(minVal, maxVal);
}

const layer = new ImageryLayer({
  url,
  pixelFilter: maskPixels
});

gives the following image:

enter image description here

This shows that obviously there is full range of heights, it's just that RasterShadedReliefRenderer does not interpret them correctly. I tried all the color ramps like in this example https://developers.arcgis.com/javascript/latest/sample-code/layers-imagery-renderer/, but all had the same problem.

7
  • Interestingly, applying a rasterFunction of rasterFunctionUtils.grayscale seems to achieve exactly the same result as what you have above. If I then apply the RasterShadedReliefRenderer, it gives the desired result. Maybe I'll post an answer with these findings as well. Sort of unrelated question - would it be possible to use the pixelFilter to make all pixel values less than 0 transparent, to create a sort of faux-mask of the state according to sea level? Commented Feb 29 at 18:10
  • I'm trying to use rasterFunction: rasterFunctionUtils.grayscale option, but I'm getting this error in browser console: Uncaught TypeError: g.includes is not a function
    – TomazicM
    Commented Feb 29 at 19:38
  • Greyscale was added in 4.29, which was released really recently - not sure if that has an affect on your setup Commented Feb 29 at 20:02
  • As a last resort I had a look at the docs :-) and realized it has to be: rasterFunctionUtils.grayscale({weights: [1]}).
    – TomazicM
    Commented Feb 29 at 20:05
  • Masking is possible in pixelFilter function, but when this option is used, renderer is ignored.
    – TomazicM
    Commented Feb 29 at 20:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.