Source: bemap-ol/bemap-ol-v10/bemap-map-ol-circle.js

/**
 * Add a bemap.Circle to the layer
 * @public
 * @param {bemap.Circle} bemap.Circle.
 * @param {object} options
 * @return {bemap.OlMap}
 */
bemap.OlMap.prototype.addCircle = function(circle, options) {
  if (circle && bemap.inheritsof(circle, bemap.Circle)) {
    circle.native = new ol.Feature({
      geometry: ol.geom.Polygon.circular(circle.getCoordinate().getLonLatArray(), circle.getRadius(), 128),
    });

    if (circle.map === null) {
      circle.map = this;
    }

    if (circle.id) {
      circle.native.setId(circle.id);
    }

    bemap.OlMap.prototype._addOwnToProperties(circle);

    circle.native.getGeometry().transform(bemap.Map.PROJ.EPSG_WGS84, this.native.getView().getProjection());

    if (circle.style) {
      if (circle.style.native === null) {
        this.buildCircleStyle(circle.style);
      }
      circle.native.setStyle(circle.style.native);
    }

    var opts = options || {};
    var l = null;

    if (opts.layer && bemap.inheritsof(opts.layer, bemap.VectorLayer)) {
      l = opts.layer;
    } else {
      l = this.getLayerByName(bemap.Map.DEFAULT_LAYER.CIRCLE);
      if (l === null) {
        l = new bemap.VectorLayer({
          name: bemap.Map.DEFAULT_LAYER.CIRCLE
        });
        this.addLayer(l);
      }
    }

    if (circle.layer === null) {
      circle.layer = l;
    }

    l.native.getSource().addFeature(circle.native);
  }
  return this;
};

/**
 * Rebuild a circle's native geometry from its CURRENT centre + radius (BEMAP-1898).
 *
 * `addCircle` builds the native as `ol.geom.Polygon.circular(...)` — a **Polygon**,
 * whose coordinates are nested two deep (`[[[x,y],…]]`). A Polygon has no radius and
 * no centre to poke, so both setters have to rebuild it, exactly the way `addCircle`
 * does. The previous code instead pushed a FLAT `[x,y]` pair into
 * `Polygon#setCoordinates`, which OL 4/5/6 tolerated (it produced a degenerate
 * `[[],[]]`) but OL 10 rejects: `setLayout` descends two levels, lands on
 * `undefined` and reads `.length` → `TypeError: Cannot read properties of undefined
 * (reading 'length')`. That is the crash reported on the BeMap docs portal.
 *
 * `Number()` on the radius is deliberate: callers pass `<input>` values, which are
 * strings. OL 10.8.0 happens to coerce them, but relying on that is luck.
 * @private
 * @param {bemap.Circle} circle
 * @param {bemap.Coordinate} center
 * @return {bemap.OlMap} this
 */
bemap.OlMap.prototype._rebuildCircleGeometry = function(circle, center) {
  var geometry = ol.geom.Polygon.circular(
    center.getLonLatArray(), Number(circle.getRadius()), 128);
  geometry.transform(bemap.Map.PROJ.EPSG_WGS84, this.native.getView().getProjection());
  circle.native.setGeometry(geometry);
  return this;
};

/**
 * Set the coordinates of the circle.
 * @protected
 * @param {bemap.Circle} circle the circle object to move.
 * @return {bemap.OlMap} this
 */
bemap.OlMap.prototype.setCoordinateCircle = function(circle) {
  // `getCoordinate()`, NOT `getCenter()`: getCenter() calls updateCircleCenter(),
  // which overwrites circle.coordinate from the geometry that is still at the OLD
  // position — so reading it here would throw away the centre we were just asked
  // to move to and re-apply the previous one. `setCenter()` was a no-op for that
  // reason even before the crash above (BEMAP-1898).
  return this._rebuildCircleGeometry(circle, circle.getCoordinate());
};

/**
 * Set the radius of the circle.
 * @protected
 * @param {bemap.Circle} circle the circle object to resize.
 * @return {bemap.OlMap} this
 */
bemap.OlMap.prototype.setRadiusCircle = function(circle) {
  // getCenter() is right here: the centre is NOT changing, and syncing it from the
  // live geometry first keeps a circle that has been dragged in place.
  return this._rebuildCircleGeometry(circle, circle.getCenter());
};

/**
 * Remove a circle from his layer.
 * @public
 * @param {bemap.Circle} circle the circle object to remove.
 */
bemap.OlMap.prototype.removeCircle = function(circle) {
  if (circle && circle.layer && circle.layer.native && bemap.inheritsof(circle, bemap.Circle)) {
    circle.layer.native.getSource().removeFeature(circle.native);
    circle.layer = null;
    circle.map = null;
  }
  return this;
};

/**
 * Set the listner when an specified eventType occur on bemap.Circle.
 * @public
 * @param {bemap.Circle} circle
 * @param {bemap.Map.EventType} eventType Event type.
 * @param {function} callback Function will be called when the specified eventType is occur.
 * @param {object} options options.
 * @return {bemap.Listener} this.
 */
bemap.OlMap.prototype.onCircle = function(circle, eventType, callback, options) {
  return this._onFeature(circle, eventType, callback, options, {
    singleFeature: true
  });
};

/**
 * Define the draggable capability for bemap.Circle.
 * @protected
 * @param {bemap.Circle} circle bemap object.
 * @param {function} callback Function will be called when the specified eventType is occur.
 * @param {object} options Options.
 * @param {bemap.Layer} options.layerFilter set the bemap layer used as filter.
 * @return {bemap.Listener} bemap.listener.
 */
bemap.OlMap.prototype.draggableCircle = function(circle, callback, options) {
  return this._draggableFeature(circle, callback, options, {
    singleFeature: true
  });
};

/**
 * Define the draggable capability for all bemap.Circle.
 * @protected
 * @param {function} callback Function will be called when the specified eventType is occur.
 * @param {object} options Options.
 * @param {bemap.Layer} options.layerFilter set the bemap layer used as filter.
 * @return {bemap.Listener} bemap.listener.
 */
bemap.OlMap.prototype.draggableCircles = function(callback, options) {
  return this._draggableFeature(null, callback, options, {
    circles: true
  });
};

/**
 * Update the coordinate from the native map browser.
 * @public
 */
bemap.OlMap.prototype.updateCircleCenter = function(circle) {
  var cir = circle.native.getGeometry().getExtent();
  var oo = ol.extent.getCenter(cir);
  var circ = ol.proj.transform(oo, this.native.getView().getProjection(), bemap.Map.PROJ.EPSG_WGS84);

  var coord = circle.coordinate;

  if (coord && coord !== null && bemap.inheritsof(coord, bemap.Coordinate)) {
    coord.setLon(circ[0]);
    coord.setLat(circ[1]);
  } else {
    circle.coordinate = new bemap.Coordinate(circ[0], circ[1]);
  }

  // (Removed here, BEMAP-1898: a block that compared `circle.coordinate.length`
  // against the extent's length and called `circle.coordinate.pop()`. A
  // bemap.Coordinate is a plain `{lon, lat}` object — no `length`, no `pop` — so
  // the guard was always `undefined > 4` === false and the body was unreachable.
  // Had it ever run it would have thrown "pop is not a function".)
};