Render sea level

In the previous step, we rendered the Terrain-RGB tiles directly on the map. What we want to do is render sea level on the map instead. And we want users to be able to adjust the height above sea level and see the adjusted height rendered on the map. We'll use a raster source to work with the elevation data directly and get the user input from an input slider on the page.

Let's add the controls to the page first. In your index.html, add the following label and input slider:

<label id="slider">
  Sea level
  <input id="level" type="range" min="0" max="100" value="1"/>
  +<span id="output"></span> m
</label>

Now add some style to those controls (in the <style> of your index.html):

#slider {
  position: absolute;
  bottom: 1rem;
  width: 100%;
  text-align: center;
  text-shadow: 0px 0px 4px rgba(255, 255, 255, 1);
}

Instead of directly rendering the R, G, B, A values from the Terrain-RGB tiles, we want to manipulate the pixel values before rendering. The raster source allows you to do this by accepting any number of input sources and an operation. This operation is a function that gets called for every pixel in the input sources. We only have one input source (elevation), so it will get called with an array of one pixel, where a pixel is a [red, green, blue, alpha] array. The operation also gets called with a data object. We'll use the data object to pass along the value of the input slider.

Add the function below to your main.js. This function decodes the input elevation data — transforming red, green, and blue values into a single elevation measure. For elevation values at or below the user selected value, the function returns a partially transparent blue pixel. For values above the user selected value, the function returns a transparent pixel.

function flood(pixels, data) {
  var pixel = pixels[0];
  if (pixel[3]) {
    // decode R, G, B values as elevation
    var height = -10000 + ((pixel[0] * 256 * 256 + pixel[1] * 256 + pixel[2]) * 0.1);
    if (height <= data.level) {
      // sea blue
      pixel[0] = 145; // red
      pixel[1] = 175; // green
      pixel[2] = 186; // blue
      pixel[3] = 255; // alpha
    } else {
      // transparent
      pixel[3] = 0;
    }
  }
  return pixel;
}

Create a raster source with a single input source (the elevation data), and configure it with the flood operation.

const raster = new RasterSource({
  sources: [elevation],
  operation: flood
});

Listen for changes on the slider input and re-run the raster operations when the user adjusts the value.

const control = document.getElementById('level');
const output = document.getElementById('output');
control.addEventListener('input', function() {
  output.innerText = control.value;
  raster.changed();
});
output.innerText = control.value;

The beforeoperations event is fired before the pixel operations are run on the raster source. This is our opportunity to provide additional data to the operations. In this case, we want to make the range input value (meters above sea level) available.

raster.on('beforeoperations', function(event) {
  event.data.level = control.value;
});

Finally, render the output from the raster operation by adding the source to an image layer. Replace the tile layer with an image layer that uses our raster source (modify the layers array in main.js):

new ImageLayer({
  opacity: 0.8,
  source: raster
})

With all this in place, the map should now have a slider that let's users control changes in sea level.

Sea level rise in Boston
Sea level rise in Boston

results matching ""

    No results matching ""