0

I am trying to draw a circle on an OL map and

  • have the center of it a particular color,
  • have the color fade out to transparent as it gets closer to the edges of the circle.

I have been looking at the docs for the createRadialGradient function (https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createRadialGradient), but I'm unsure of the correct numbers to plug into there.

Code looks something like this:

const circle = {center: [myLon, myLat], radius: myRadius };
const context = document.createElement('canvas').getContext('2d');
// How do I get the correct points from OL or circle object?
const grad = context.createRadialGradient(?,?,?,?,?,?);
grad.addColorStop(0, 'transparent');
grad.addColorStop(0.5, 'someColor');
grad.addColorStop(1, 'transparent');

UPDATE:

Code has been updated to this after getting help on this post by TomazicM:

const context = document.createElement('canvas').getContext('2d');
const pixels = map.getPixelFromCoordinate([myLon, myLat])
const grad = context.createRadialGradient(pixels[0],pixels[1],1,pixels[0],pixels[1],0.05);
grad.addColorStop(0, 'transparent');
grad.addColorStop(0.5, 'someColor');
grad.addColorStop(1, 'transparent');

const fill = new Fill();
fill.setColor(grad);

const style = new Style({ 
  fill,
  stroke: new Stroke({
    color: 'someColor',
    width: 2,
  }),
});

const f = new Feature(new Circle(center,radius));
f.setStyle(style);
  • I deleted my answer because I got it completely wrong. – TomazicM Feb 26 at 22:33
  • D'oh :(. Any ideas on the correct way to achieve this? – ekjcfn3902039 Feb 27 at 13:27
  • There are two problems with this gradient. One is to get right coordinates for it's center (see docs at openlayers.org/en/latest/apidoc/…), and the other is that you have to change it for each zoom change. If I find time I'll play with it. – TomazicM Feb 27 at 16:23
  • Thanks, I'd be very appreciative if you do have the time to help! I already know the coordinate for the center of the circle, so I'm part-way there :) – ekjcfn3902039 Mar 4 at 13:27
  • I've found solution, I'll publish answer this evening. – TomazicM Mar 4 at 13:40
1

I could not get it to work correctly with Circle geometry with radial gradient canvas fill.

Another possiblity is to use Image layer and draw gradient with layer's canvasFunction. Below is working example that draws radial gradient circle at circleLonLat coordinates and with circleRadiusM radius in actual (not map) meters.

var circleLonLat = [14.507136, 46.047711];     
var circleRadiusM = 50000;

function canvasFunction(extent, resolution, pixelRatio, size, projection) {       
  var canvas = document.createElement('canvas');
  var context = canvas.getContext('2d');

  canvas.setAttribute('width', size[0]);
  canvas.setAttribute('height', size[1]);

  // Calculate coordinate difference between map origin and canvas origin 
  var mapExtent = map.getView().calculateExtent(map.getSize())
  var canvasOrigin = map.getPixelFromCoordinate([extent[0], extent[3]]);
  var mapOrigin = map.getPixelFromCoordinate([mapExtent[0], mapExtent[3]]);
  var delta = [mapOrigin[0] - canvasOrigin[0], mapOrigin[1] - canvasOrigin[1]];

  // Get circle radius in pixels
  var circleCoords = ol.proj.fromLonLat(circleLonLat);
  var circleCenter = map.getPixelFromCoordinate(circleCoords);
  var circleRadiusOffset = map.getPixelFromCoordinate([circleCoords[0] + circleRadiusM, circleCoords[0]]);
  var radiusPixels = circleRadiusOffset[0] - circleCenter[0];

  // Get circle cente in pixels
  var circleX = circleCenter[0] + delta[0];
  var circleY = circleCenter[1] + delta[1];

  // Modify radius to represent actual meters, not map projected meters
  var view = map.getView();
  var resolutionAtEquator = view.getResolution();
  var center = view.getCenter();
  var pointResolution = ol.proj.getPointResolution(projection, resolutionAtEquator, center);
  var resolutionFactor = resolutionAtEquator/pointResolution;
  var radiusPixels = (radiusPixels / projection.getMetersPerUnit()) * resolutionFactor;

  var grad = context.createRadialGradient(circleX, circleY, 0, circleX, circleY, radiusPixels);
  grad.addColorStop(0, 'blue');
  grad.addColorStop(1, 'transparent');

  context.arc(circleX, circleY, radiusPixels, 0, 2 * Math.PI);

  context.fillStyle = grad;
  context.fill();

  return canvas;
};

var canvasLayer = new ol.layer.Image({
  source: new ol.source.ImageCanvas({
    canvasFunction: canvasFunction,
    projection: map.getView().getProjection()
  })
});

map.addLayer(canvasLayer);
  • Awesome, I tried it out and it's a HUGE headstart towards what I'm trying to do, thanks! btw, I noticed everything from the comment starting with 'modify radius' to the radius variable are not being used in the code. I just wanted to double check that that block of code is unnecessary? – ekjcfn3902039 Mar 4 at 19:24
  • My mistake, it should be radiusPixels = .... Corrected. – TomazicM Mar 4 at 19:27
  • Just curious, but when you said you could not get it working for a Circle object, were you referring to setting its parents (aka feature) fill to the grad variable in your example code? I'm trying to do that now for Polygons and am coming across some trouble. Didnt know if I was wasting my time or if that is in fact the best way to do it. It seems like the OL examples page at openlayers.org/en/latest/examples/canvas-gradient-pattern.html does something similar (but with linear gradients). – ekjcfn3902039 Mar 8 at 20:32
  • Problem with Circle object with gradient color fill is that gradient color fill has life independent from Circle object. Gradient color fill doesn't zoom and moves in a way I couldn't figure out. This has something to do with it's properties. Docs say: The origin for patterns and gradients as fill style is an increment of 512 css pixels from map coordinate [0, 0] (see openlayers.org/en/latest/apidoc/…). – TomazicM Mar 9 at 10:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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