Werner Raath
Werner Raath

Reputation: 1512

OL3 ImageCanvas renderFunction bad performance

So I'm experimenting using an Esri opensource example (windy.js) and rendering it on OpenLayers 3 using something similar to the OL3 D3 example.

windy.js renders a wind-animating map on a canvas. Canvas size seems to not change rendering performance.

First, I render the windy.js to a canvas and update it every 0.5 seconds:

g_windy = new Windy({
    canvas: $("#canvas")[0],
    data: //Some data
});
//map is an OL3 Map object
function redraw() {
  windy.stop();

  setTimeout(function() {
    var mapSize = map.getSize();
    var extent = map.getView().calculateExtent(mapSize);

    extent = ol.proj.transformExtent(extent, 'EPSG:3857', 'EPSG:4326');

    var canvas_bounds = [
        [0,0],
        [mapSize[0]*2, mapSize[1]*2]
      ]; 

      var map_projected_bounds = [
        [
          extent[0],
          extent[1]
        ],
        [
          extent[2],
          extent[3]
        ]
      ];

    windy.start(
      canvas_bounds,
      mapSize[0]*2, mapSize[1]*2, 
      map_projected_bounds
    );
  }, 500);
}

redraw();

My canvasFunction implementation references the canvas that windy.js renders to and returns it:

var canvasFunction = function(extent, resolution, pixelRatio, size, projection) {
  canvas = $("canvas#canvas")[0];
  canvas.setAttribute('width', map.getSize()[0]*4);
  canvas.setAttribute('height', map.getSize()[1]*4);
  return canvas;
};

I now add the layer to my map:

var canvasLayer = new ol.layer.Image({
  source: new ol.source.ImageCanvas({
    canvasFunction: canvasFunction,
    projection: 'EPSG:3857',
    ratio: 1
  })
});

map.addLayer(canvasLayer);
map.getView().on('change:center', redraw);
map.getView().on('change:resolution', redraw);

//Next I update my map to rerender at a fast interval. Note that having 1 millisecond vs 60 frames/second does not change performace.
setInterval(function() { map.updateSize();}, 1);

My result gives me the windy.js layer on my map, but I am stuck with one issues:

Copying the canvas is not a perfect implementation of what we are trying to achieve, but it is more efficient that doing a pull-request from OL3 and adding functionality that can support this feature.

Upvotes: 2

Views: 1134

Answers (1)

tsauerwein
tsauerwein

Reputation: 6051

Because I am a big fan of these wind maps, I couldn't resist to give it a try.

The main difference to the D3 example is, that the D3 canvas is static and only has to be updated when the map extent changes. Windy.js runs its own animation loop and constantly would have to be updated. First windy.js draws into its own canvas, then ol3 would have to draw this canvas onto the map. Maybe this technique could be optimized, but I would suggest a different approach:

Create a canvas element in which windy.js can draw. Next to it create a div element for OpenLayers:

<div id="map" class="map">
  <canvas id="windyMap" class="fill"></canvas>
  <div id="olMap" class="fill"></div>
</div>

With some lines of CSS position the canvas above the map div and make sure that events are still passed-through (pointer-events: none). Every time the map is panned/zoomed, restart the windy.js animation. That's all.

Demo - Code

Upvotes: 1

Related Questions