Search code examples
openlayersheatmap

How can I apply alpha color components in an OpenLayers heatmap gradient?


The code below produces:

final image

However, I have specified that both colors have an alpha value of 0.3, but it is clear that has been ignored.

It is possible to specify an alpha value for the heatmap gradient? If so, how?

I would not only like to be able to see the map elements underneath the heatmap, but where the blue and yellow overlap, see the color green.

When I apply a blur value, there is some alpha component being applied, but expected color mixing is not happening.

const osm = new ol.source.OSM();

osm.setTileGridForProjection(
  "EPSG:4326",
  ol.tilegrid.createXYZ({ extent: [-180, -90, 180, 90] })
);

//
// Heatmap A
//

const heatmapLayerA = new ol.layer.Heatmap({
  source: new ol.source.Vector(),
  blur: 0,
  radius: 20,
  zIndex: 10,
  weight: function (feature) {
    return feature.get("weight");
  }
});

const color = "hsla(60, 100%, 50%, 0.3)";
const gradient = [color, color];
heatmapLayerA.setGradient(gradient);

const coordinateA = [-77.0, 38.9];
const feature = new ol.Feature({
  geometry: new ol.geom.Point(coordinateA),
  weight: 1
});

heatmapLayerA.getSource().addFeature(feature);

//
// Heatmap B
//

const heatmapLayerB = new ol.layer.Heatmap({
  source: new ol.source.Vector(),
  blur: 0,
  radius: 20,
  zIndex: 10,
  weight: function (feature) {
    return feature.get("weight");
  }
});

const colorB = "hsla(240, 100%, 50%, 0.3)";
const gradientB = [colorB, colorB];
heatmapLayerB.setGradient(gradientB);

const coordinateB = [-77.0, 38.9001];
const featureB = new ol.Feature({
  geometry: new ol.geom.Point(coordinateB),
  weight: 1
});

heatmapLayerB.getSource().addFeature(featureB);

//
//
//

const tileLayer = new ol.layer.Tile({ source: osm });

const map = new ol.Map({
  target: "map",
  layers: [tileLayer, heatmapLayerA, heatmapLayerB],
  view: new ol.View({
    projection: "EPSG:4326",
    center: coordinateA,
    zoom: 18
  })
});

ol.proj.get("EPSG:4326").setExtent([-180, -85, 180, 85]);

let visible = true;
#map {
  height: 512px;
  width: 512px;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/ol.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/dist/ol.min.js"></script>

<div id="map"></div>


Solution

  • You are correct, only the RGB components of the colors are used, and alpha is for blur. You could combine the output of the layers using ol.source.Rasterwhere a simple mix of the RGB components of blue and yellow would produce white (if you prefer a CMYK blend producing green you could apply the algorithm in https://stackoverflow.com/a/62369367/10118270)

    const osm = new ol.source.OSM();
    
    osm.setTileGridForProjection(
      "EPSG:4326",
      ol.tilegrid.createXYZ({ extent: [-180, -90, 180, 90] })
    );
    
    //
    // Heatmap A
    //
    
    const heatmapLayerA = new ol.layer.Heatmap({
      source: new ol.source.Vector(),
      blur: 0,
      radius: 20,
      zIndex: 10,
      weight: function (feature) {
        return feature.get("weight");
      }
    });
    
    const color = "hsla(60, 100%, 50%, 0.3)";
    const gradient = [color, color];
    heatmapLayerA.setGradient(gradient);
    
    const coordinateA = [-77.0, 38.9];
    const feature = new ol.Feature({
      geometry: new ol.geom.Point(coordinateA),
      weight: 1
    });
    
    heatmapLayerA.getSource().addFeature(feature);
    
    //
    // Heatmap B
    //
    
    const heatmapLayerB = new ol.layer.Heatmap({
      source: new ol.source.Vector(),
      blur: 0,
      radius: 20,
      zIndex: 10,
      weight: function (feature) {
        return feature.get("weight");
      }
    });
    
    const colorB = "hsla(240, 100%, 50%, 0.3)";
    const gradientB = [colorB, colorB];
    heatmapLayerB.setGradient(gradientB);
    
    const coordinateB = [-77.0, 38.9001];
    const featureB = new ol.Feature({
      geometry: new ol.geom.Point(coordinateB),
      weight: 1
    });
    
    heatmapLayerB.getSource().addFeature(featureB);
    
    //
    //
    //
    
    const tileLayer = new ol.layer.Tile({ source: osm });
    const raster = new ol.layer.Image({
      source: new ol.source.Raster({
        sources: [heatmapLayerA, heatmapLayerB],
        operation: (pixels) => [
          Math.max(pixels[0][0], pixels[1][0]),
          Math.max(pixels[0][1], pixels[1][1]),
          Math.max(pixels[0][2], pixels[1][2]),
          Math.max(pixels[0][3], pixels[1][3]),
        ],
      })
    });
    const map = new ol.Map({
      target: "map",
      layers: [tileLayer, raster],
      view: new ol.View({
        projection: "EPSG:4326",
        center: coordinateA,
        zoom: 18
      })
    });
    
    ol.proj.get("EPSG:4326").setExtent([-180, -85, 180, 85]);
    
    let visible = true;
    #map {
      height: 512px;
      width: 512px;
    }
    <link href="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/ol.min.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/openlayers/7.3.0/dist/ol.min.js"></script>
    
    <div id="map"></div>

    You can also use the opacity property of any layer to provide additional alpha.