Search code examples
vectorgeometryopenlayers

Create a circle with a radius & projection - Open Layers


Currently whenever I zoom in / out my circle looses the area or the radius will be incorrect, I guess my radius need to have a projected to the map. Here is my code

 ...
 var circle = new ol.layer.Vector({
    source: new ol.source.Vector(),
    style: new ol.style.Style({
        image: new ol.style.Circle({
            radius: radius, //I guess this is wrong
            fill: null,
            stroke: new ol.style.Stroke({
                color: 'rgba(255,0,0,0.9)',
                width: 2
            })
        })
    })
});

map.addLayer(circle);

var marker = new ol.Feature(new ol.geom.Point([x, y]));
circle.getSource().addFeature(marker);
....

Solution

  • The radius of a Circle style in in pixels, it does not change with the view resolution. For a radius in projection units you need a Circle geometry

    style: new ol.style.Style({
                geometry: function (feature) {
                    return new ol.geom.Circle(feature.getGeometry().getCoordinates(), radius);
                },
                fill: null,
                stroke: new ol.style.Stroke({
                    color: 'rgba(255,0,0,0.9)',
                    width: 2
                })
        })
    

    Note that a projection unit circle is not geodesic, so it may not be a true radius or a true circle on the ground - see https://openlayers.org/en/latest/examples/draw-and-modify-geodesic.html

    <!DOCTYPE html>
    <html>
      <head>
      <title>WFS</title>
      <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@v7.3.0/ol.css">
      <script src="https://cdn.jsdelivr.net/npm/ol@v7.3.0/dist/ol.js"></script>
      <style>
        html, body {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
        }
        .map {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
        }
      </style>
      </head>
      <body>
      <div id="map" class="map"></div>
      <script>
    
    const radius = 500000;
    
    const feature1 = new ol.Feature(
      new ol.geom.Point(ol.proj.fromLonLat([-45, 0]))
    );
    feature1.setStyle(
      new ol.style.Style({
        geometry: function (feature) {
          return new ol.geom.Circle(feature.getGeometry().getCoordinates(), radius);
        },
        fill: null,
        stroke: new ol.style.Stroke({
          color: 'rgba(255,0,0,0.9)',
          width: 2
        })
      })
    );
    
    const feature2 = new ol.Feature(
      new ol.geom.Point(ol.proj.fromLonLat([45, 0]))
    );
    feature2.setStyle(
      new ol.style.Style({
        geometry: function (feature) {
          return ol.geom.Polygon.circular(
            ol.proj.toLonLat(feature.getGeometry().getCoordinates()),
            radius
          ).transform('EPSG:4326', map.getView().getProjection());
        },
        fill: null,
        stroke: new ol.style.Stroke({
          color: 'rgba(255,0,0,0.9)',
          width: 2
        })
      })
    );
    
    const map = new ol.Map({
      layers: [
        new ol.layer.Tile({
          source: new ol.source.OSM(),
        }),
        new ol.layer.Vector({
          source: new ol.source.Vector({
            features: [feature1, feature2]
          })
        })
      ],
      target: 'map',
      view: new ol.View({
        center: [0, 0],
        zoom: 2,
      }),
    });
    
    let latitude = 0;
    setInterval(function() {
      latitude += 0.1;
      latitude %= 60;
      feature1.getGeometry().setCoordinates(ol.proj.fromLonLat([-45, latitude]))
      feature2.getGeometry().setCoordinates(ol.proj.fromLonLat([45, latitude]))
    }, 10);
    
      </script>
      </body>
    </html>