2

How can I create a circle (or "buffer") around a point anywhere on the globe in modern Open Layers with a radius specified in actual meters?

"Actual" meters meaning that a geodetic measurement from the center of the circle to one of its points would return about the same distance as was used to create the buffer. "Fake" meters like in EPSG:3857 are what I am trying to escape.

OL has a Circle constructor that takes a radius parameter which is used in units of the projection. As there is no global projection that allows us to create nice circular buffers with actual meters, this cannot be used.

Using Geodesic Measurements for Circle Radii? is for an hugely outdated version of Openlayers.

bugmenot123
  • 11,011
  • 3
  • 34
  • 69
  • 4
    Divide the actual meters by the cosine of the latitude of the center and draw the circle with those fake meters. – Gabriel De Luca Jan 17 '21 at 20:51
  • That looks interesting thanks! A first try in QGIS looks good but shows errors of up to ~0.5% near the equator or towards the poles (995m instead of 1000m). Is there something more accurate? – bugmenot123 Jan 17 '21 at 21:15
  • Yes but hard, compute the coordinates of a geodesic problem for each vertex of the circle. – Gabriel De Luca Jan 17 '21 at 21:22
  • 3
    Instead of Circle geometry using circular https://openlayers.org/en/main/apidoc/module-ol_geom_Polygon.html#.circular – Mike Jan 17 '21 at 22:11
  • The 0.5% error may be due to the difference between medium and real radius. Other choice can be draw it in ellipsoidal Mercator projection and transform. – Gabriel De Luca Jan 17 '21 at 22:38
  • Thank you @Mike, that looks like exactly the perfect answer I was looking for. Do you want to add it as answer? Otherwise I will do so tomorrow. – bugmenot123 Jan 18 '21 at 21:10

2 Answers2

4

The following is only valid for differential elements of a direct Mercator projection on a unit sphere:

  • In the direction of parallels, deformation modulus is constant. And a parallel circumference measures 2 π cos(φ) on the sphere surface and 2 π on the projected image.

  • We know that it is conform, so we will save ourselves from calculating the deformation in the direction of the meridians, which respect the orthogonality with the parallels on both surfaces. (The projection is analytically conform, the transformation in the direction of the meridians is the one that fulfills that the deformation modulus in that direction is the same as in the parallels one.)

  • So being conform, let's say the longitudinal Mercator modulus of deformation at a point is 1 / cos(φ) in any direction.

Although this is not true for a Web Mercator projection of a non-spherical objective surface, it is nevertheless quite close.

So if the tolerance of your work allows it, one way to reach your goal is to divide the actual meters by the cosine of the latitude of the center of the circle, and use that measurement as the radius.

Gabriel De Luca
  • 14,289
  • 3
  • 20
  • 51
0

One option would be to create a circular polygon with the correct radius. The function below makes a 32 vertex circular polygon centered on a point:

function drawCircle(point, radius, dir) { 
  var d2r = Math.PI / 180;   // degrees to radians 
  var r2d = 180 / Math.PI;   // radians to degrees 
  var earthsradius = 6378137.0; // 6378137.0 is the radius of the earth in meters

var points = 32;

// find the raidus in lat/lon var rlat = (radius / earthsradius) * r2d; var rlng = rlat / Math.cos(point[1] * d2r);

var extp = new Array(); if (dir==1) {var start=0;var end=points+1} // one extra here makes sure we connect the else {var start=points+1;var end=0} for (var i=start; (dir==1 ? i < end : i > end); i=i+dir)
{ var theta = Math.PI * (i / (points/2)); ey = point[0] + (rlng * Math.cos(theta)); // center a + radius x * cos(theta) ex = point[1] + (rlat * Math.sin(theta)); // center b + radius y * sin(theta) extp.push([ey, ex]); } return extp; }

live example (red circle from the function, blue circle is the OpenLayers Circle)

geocodezip
  • 316
  • 1
  • 4
  • 9
  • OpenLayers could do the red circle directly with var polygon = ol.geom.Polygon.circular([parseFloat(response[0].lon), parseFloat(response[0].lat)], 4000); instead of var polygon = new ol.geom.Polygon([ring]); – Mike Jan 18 '21 at 20:25