Search code examples
openlayers

How to contrast stretch multiple bands Geotiff in Openlayers?


In Openlayers,I want to realize render multiple bands Geotiff by Contrast Stretch Min and Max. Like by QGIS or Geoserver using SLD:


<sld:RasterSymbolizer>
<sld:ChannelSelection>
  <sld:RedChannel>
    <sld:SourceChannelName>1</sld:SourceChannelName>
    <sld:ContrastEnhancement>
      <sld:Normalize>
        <sld:VendorOption name="minValue">-3.509304523468</sld:VendorOption>
        <sld:VendorOption name="maxValue">701.02770996094</sld:VendorOption>
        <sld:VendorOption name="algorithm">StretchToMinimumMaximum</sld:VendorOption>
      </sld:Normalize>
    </sld:ContrastEnhancement>
  </sld:RedChannel>
  <sld:GreenChannel>
    <sld:SourceChannelName>2</sld:SourceChannelName>
    <sld:ContrastEnhancement>
      <sld:Normalize>
        <sld:VendorOption name="minValue">12.172845840454</sld:VendorOption>
        <sld:VendorOption name="maxValue">20.350427627563</sld:VendorOption>
        <sld:VendorOption name="algorithm">StretchToMinimumMaximum</sld:VendorOption>
      </sld:Normalize>
    </sld:ContrastEnhancement>
  </sld:GreenChannel>
  <sld:BlueChannel>
    <sld:SourceChannelName>1</sld:SourceChannelName>
    <sld:ContrastEnhancement>
      <sld:Normalize>
        <sld:VendorOption name="minValue">-3.509304523468</sld:VendorOption>
        <sld:VendorOption name="maxValue">701.02770996094</sld:VendorOption>
        <sld:VendorOption name="algorithm">StretchToMinimumMaximum</sld:VendorOption>
      </sld:Normalize>
    </sld:ContrastEnhancement>
  </sld:BlueChannel>
</sld:ChannelSelection>
<sld:ContrastEnhancement/>
</sld:RasterSymbolizer>

I have seen the examples like https://openlayers.org/en/latest/examples/cog-stretch.html and https://openlayers.org/en/latest/examples/cog-style.html.

But I dit not find the method which I need. Can you help me ? Appreciate any clues or suggestions.Thank you.


Solution

  • There are two ways to replicate that SLD

    Either let OpenLayers normalize it and use the default RGBA style. As each band has different min and max values the url will need to be loaded multiple tiles

    const rMin = -3.509304523468;
    const rMax = 701.02770996094;
    
    const gMin = 12.172845840454;
    const gMax = 20.350427627563;
    
    const bMin = -3.509304523468;
    const bMax = 701.02770996094;
    
    const source = new GeoTIFF({
      sources: [
        {
          bands: [1],
          min: rMin,
          max: rMax,
          url: url,
        },
        {
          bands: [2],
          min: gMin,
          max: gMax,
          url: url,
        },
        {
          bands: [3],
          min: bMin,
          max: bMax,
          url: url,
        },
      ],
    });
    
    const layer = new TileLayer({
      source: source,
    });
    

    Or normalize it between the min and max values for each band yourself, which should be more efficient

    function normalize(band, min, max) {
      return [
        '/',
        ['-', ['clamp', ['band', band], min, max], min],
        ['-', max, min],
      ];
    }
    
    const source = new GeoTIFF({
      normalize: false,
      sources: [
        {
          url: url,
        },
      ],
    });
    
    const layer = new TileLayer({
      source: source,
      style: {
        color: [
          'array',
          normalize(1, rMin, rMax),
          normalize(2, gMin, gMax),
          normalize(3, bMin, bMax),
          1,
        ],
      },
    });