This example uses OpenLayers v10.9.0. The latest is v.
Edit

#### Draw and Modify Geodesic Circles

draw 17 edit 13 modify 11 vector 86 circle 4 sphere 1 geodesic 1

#####

Geometry type

Example of using Draw and Modify interactions for geodesic circles.

Example of using the `ol/interaction/Draw` interaction with a custom geometry function together with the `ol/interaction/Modify` interaction to draw and modify geodesic circles (a `ol/geom/Polygon#circular` polygon representing a circle on the surface of the Earth's sphere). The polygon is placed in a `ol/geom/GeometryCollection` together with a `ol/geom/Point` which allows the Modify interaction to adjust the circle center as well as the radius. Custom style functions ensure the correct final geometry is displayed throughout. `ol/geom/Circle` projected (planar) geometries can also be drawn and modified. The difference between geodesic and projected circles can be seen when their centers are moved between northern and southern latitudes in the Web Mercator projection. The `ol/interaction/Snap` interaction can be used to create concentric circles.

##### main.js

    import Map from 'ol/Map.js';
    import View from 'ol/View.js';
    import GeometryCollection from 'ol/geom/GeometryCollection.js';
    import Point from 'ol/geom/Point.js';
    import Polygon, {circular} from 'ol/geom/Polygon.js';
    import Draw from 'ol/interaction/Draw.js';
    import Modify from 'ol/interaction/Modify.js';
    import Snap from 'ol/interaction/Snap.js';
    import TileLayer from 'ol/layer/Tile.js';
    import VectorLayer from 'ol/layer/Vector.js';
    import {transform} from 'ol/proj.js';
    import OSM from 'ol/source/OSM.js';
    import VectorSource from 'ol/source/Vector.js';
    import {getDistance} from 'ol/sphere.js';
    import CircleStyle from 'ol/style/Circle.js';
    import Fill from 'ol/style/Fill.js';
    import Stroke from 'ol/style/Stroke.js';
    import Style from 'ol/style/Style.js';

    const raster = new TileLayer({
      source: new OSM(),
    });

    const source = new VectorSource();

    const style = new Style({
      fill: new Fill({
        color: 'rgba(255, 255, 255, 0.2)',
      }),
      stroke: new Stroke({
        color: '#33cc33',
        width: 2,
      }),
      image: new CircleStyle({
        radius: 7,
        fill: new Fill({
          color: '#ffcc33',
        }),
      }),
    });

    const geodesicStyle = new Style({
      geometry: function (feature) {
        return feature.get('modifyGeometry') || feature.getGeometry();
      },
      fill: new Fill({
        color: 'rgba(255, 255, 255, 0.2)',
      }),
      stroke: new Stroke({
        color: '#ff3333',
        width: 2,
      }),
      image: new CircleStyle({
        radius: 7,
        fill: new Fill({
          color: 'rgba(0, 0, 0, 0)',
        }),
      }),
    });

    const vector = new VectorLayer({
      source: source,
      style: function (feature) {
        const geometry = feature.getGeometry();
        return geometry.getType() === 'GeometryCollection' ? geodesicStyle : style;
      },
    });

    const map = new Map({
      layers: [raster, vector],
      target: 'map',
      view: new View({
        center: [-11000000, 6600000],
        zoom: 3,
      }),
    });

    const defaultStyle = new Modify({source: source})
      .getOverlay()
      .getStyleFunction();

    const modify = new Modify({
      source: source,
      style: function (feature, resolution) {
        feature.get('features').forEach(function (modifyFeature) {
          const modifyGeometry = modifyFeature.get('modifyGeometry');
          if (modifyGeometry) {
            const modifyPoint = feature.getGeometry().getCoordinates();
            const geometries = modifyFeature.getGeometry().getGeometries();
            const polygon = geometries[0].getCoordinates()[0];
            const center = geometries[1].getCoordinates();
            const projection = map.getView().getProjection();
            let first, last, radius;
            if (modifyPoint[0] === center[0] && modifyPoint[1] === center[1]) {
              // center is being modified
              // get unchanged radius from diameter between polygon vertices
              first = transform(polygon[0], projection, 'EPSG:4326');
              last = transform(
                polygon[(polygon.length - 1) / 2],
                projection,
                'EPSG:4326',
              );
              radius = getDistance(first, last) / 2;
            } else {
              // radius is being modified
              first = transform(center, projection, 'EPSG:4326');
              last = transform(modifyPoint, projection, 'EPSG:4326');
              radius = getDistance(first, last);
            }
            // update the polygon using new center or radius
            const circle = circular(
              transform(center, projection, 'EPSG:4326'),
              radius,
              128,
            );
            circle.transform('EPSG:4326', projection);
            geometries[0].setCoordinates(circle.getCoordinates());
            // save changes to be applied at the end of the interaction
            modifyGeometry.setGeometries(geometries);
          }
        });
        return defaultStyle(feature, resolution);
      },
    });

    modify.on('modifystart', function (event) {
      event.features.forEach(function (feature) {
        const geometry = feature.getGeometry();
        if (geometry.getType() === 'GeometryCollection') {
          feature.set('modifyGeometry', geometry.clone(), true);
        }
      });
    });

    modify.on('modifyend', function (event) {
      event.features.forEach(function (feature) {
        const modifyGeometry = feature.get('modifyGeometry');
        if (modifyGeometry) {
          feature.setGeometry(modifyGeometry);
          feature.unset('modifyGeometry', true);
        }
      });
    });

    map.addInteraction(modify);

    let draw, snap; // global so we can remove them later
    const typeSelect = document.getElementById('type');

    function addInteractions() {
      let value = typeSelect.value;
      let geometryFunction;
      if (value === 'Geodesic') {
        value = 'Circle';
        geometryFunction = function (coordinates, geometry, projection) {
          if (!geometry) {
            geometry = new GeometryCollection([
              new Polygon([]),
              new Point(coordinates[0]),
            ]);
          }
          const geometries = geometry.getGeometries();
          const center = transform(coordinates[0], projection, 'EPSG:4326');
          const last = transform(coordinates[1], projection, 'EPSG:4326');
          const radius = getDistance(center, last);
          const circle = circular(center, radius, 128);
          circle.transform('EPSG:4326', projection);
          geometries[0].setCoordinates(circle.getCoordinates());
          geometry.setGeometries(geometries);
          return geometry;
        };
      }
      draw = new Draw({
        source: source,
        type: value,
        geometryFunction: geometryFunction,
      });
      map.addInteraction(draw);
      snap = new Snap({source: source});
      map.addInteraction(snap);
    }

    /**
     * Handle change event.
     */
    typeSelect.onchange = function () {
      map.removeInteraction(draw);
      map.removeInteraction(snap);
      addInteractions();
    };

    addInteractions();

##### index.html

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>Draw and Modify Geodesic Circles</title>
        <link rel="stylesheet" href="node_modules/ol/ol.css">
        <style>
          .map {
            width: 100%;
            height: 400px;
          }
        </style>
      </head>
      <body>
        <div id="map" class="map"></div>
        <form>
          <label for="type">Geometry type &nbsp;</label>
          <select id="type">
            <option value="Point">Point</option>
            <option value="LineString">LineString</option>
            <option value="Polygon">Polygon</option>
            <option value="Circle">Circle Geometry</option>
            <option value="Geodesic" selected>Geodesic Circle</option>
          </select>
        </form>

        <script type="module" src="main.js"></script>
      </body>
    </html>

##### package.json

    {
      "name": "draw-and-modify-geodesic",
      "dependencies": {
        "ol": "10.9.0"
      },
      "devDependencies": {
        "vite": "^3.2.3"
      },
      "scripts": {
        "start": "vite",
        "build": "vite build"
      }
    }