Openlayers 3 Circle radius in meters
Asked Answered
G

2

6

How to get Circle radius in meters Maybe this is the existing question, but I am not getting the proper result. I am trying to create a Polygon in postgis with the same radius & center getting from openlayers circle.

To get a radius in meters I followed this. Running example link.

var radiusInMeters = circleRadius * ol.proj.METERS_PER_UNIT['m'];

After getting center, radius (in meters) I am trying to generate Polygon(WKT) with postgis (server job) & drawing that feature in a map like this.

select st_astext(st_buffer('POINT(79.25887485937808 17.036647682474722 0)'::geography, 365.70644956827164));

But both are not covering the same area. Can anybody please let me know what I am doing wrong?

Basically, my input/output to/from Circle will be in meters only.

Gabo answered 25/8, 2015 at 11:35 Comment(0)
M
21

ol.geom.Circle might not represent a circle

OpenLayers Circle geometries are defined on the projected plane. This means that they are always circular on the map, but the area covered might not represent an actual circle on earth. The actual shape and size of the area covered by the circle will depend on the projection used.

This could be visualized by Tissot's indicatrix, which shows how circular areas on the globe are transformed when projected onto a plane. Using the projection EPSG:3857, this would look like:

Tissot's indicatrix using EPSG:3857

The image is from OpenLayer 3's Tissot example and displays areas that all have a radius of 800 000 meters. If these circles were drawn as ol.geom.Circle with a radius of 800000 (using EPSG:3857), they would all be the same size on the map but the ones closer to the poles would represent a much smaller area of the globe.

This is true for most things with OpenLayers geometries. The radius, length or area of a geometry are all reported in the projected plane.

So if you have an ol.geom.Circle, getting the actual surface radius would depend on the projection and features location. For some projections (such as EPSG:4326), there would not be an accurate answer since the geometry might not even represent a circular area.

However, assuming you are using EPSG:3857 and not drawing extremely big circles or very close to the poles, the Circle will be a good representation of a circular area.

ol.proj.METERS_PER_UNIT

ol.proj.METERS_PER_UNIT is just a conversion table between meters and some other units. ol.proj.METERS_PER_UNIT['m'] will always return 1, since the unit 'm' is meters. EPSG:3857 uses meters as units, but as noted they are distorted towards the poles.

Solution (use after reading and understanding the above)

To get the actual on-the-ground radius of an ol.geom.Circle, you must find the distance between the center of the circle and a point on it's edge. This could be done using ol.Sphere:

var center = geometry.getCenter()
var radius = geometry.getRadius()
var edgeCoordinate = [center[0] + radius, center[1]];
var wgs84Sphere = new ol.Sphere(6378137);
var groundRadius = wgs84Sphere.haversineDistance(
    ol.proj.transform(center, 'EPSG:3857', 'EPSG:4326'), 
    ol.proj.transform(edgeCoordinate, 'EPSG:3857', 'EPSG:4326')
);

More options

If you wish to add a geometry representing a circular area on the globe, you should consider using the method used in the Tissot example above. That is, defining a regular polygon with enough points to appear smooth. That would make it transferable between projections, and appears to be what you are doing server side. OpenLayers 3 enables this by ol.geom.Polygon.circular:

var circularPolygon = ol.geom.Polygon.circular(wgs84Sphere, center, radius, 64);

There is also ol.geom.Polygon.fromCircle, which takes an ol.geom.Circle and transforms it into a Polygon representing the same area.

Madras answered 27/8, 2015 at 20:46 Comment(0)
G
4

My answer is a complement of the great answer by Alvin.

Imagine you want to draw a circle of a given radius (in meters) around a point feature. In my particular case, a 200m circle around a moving vehicle.

If this circle has a small diameter (< some kilometers), you can ignore earth roudness. Then, you can use the marker "Circle" in the style function of your point feature.

Here is my style function :

private pointStyle(feature: Feature, resolution: number): Array<Style> {
  const viewProjection = map.getView().getProjection();      
  const coordsInViewProjection = (<Point>(feature.getGeometry())).getCoordinates();
  const longLat = toLonLat(coordsInViewProjection, viewProjection);
  const latitude_rad = longLat[1] * Math.PI / 180.;
  const circle = new Style({
     image: new CircleStyle({
          stroke: new Stroke({color: '#7c8692'});,
          radius: this._circleRadius_m / (resolution / viewProjection.getMetersPerUnit() * Math.cos(latitude_rad)),
        }),
      });
   
  return [circle];
}

The trick is to scale the radius by the latitude cosine. This will "locally" disable the distortion effect we can observe in the Tissot Example.

Glendon answered 18/5, 2021 at 8:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.