源码分析之Leaflet中的Circle

概述

Circle是用于在地图上绘制圆形覆盖物的类,继承自CircleMarker类,二者有所差异:CircleMarker类是固定像素半径,而Circle则是以地理单位()为半径,在投影转换上是不同的。

源码分析

源码实现

Circle的源码实现如下:

export var Circle = CircleMarker.extend({
  initialize: function (latlng, options, legacyOptions) {
    if (typeof options === "number") {
      options = Util.extend({}, legacyOptions, { radius: options });
    }
    Util.setOptions(this, options);
    this._latlng = toLatLng(latlng);
    if (isNaN(this.options.radius)) {
      throw new Error("Circle radius cannot be NaN");
    }
    this._mRadius = this.options.radius;
  },
  // 设置地理半径(米)
  setRadius: function (radius) {
    this._mRadius = radius;
    return this.redraw(); //触发重投影和重绘
  },
  // 获取地理半径(米)
  getRadius: function () {
    return this.mRadius;
  },
  // 边界计算
  getBounds: function () {
    // 基于像素半径计算地理边界
    var half = [this._radius, this._radiusY || this._radius];

    return new LatLngBounds(
      this._map.layerPointToLatLng(this._point.subtract(half)),
      this._map.layerPointToLatLng(this._point.add(half))
    );
  },
  setStyle: Path.prototype.setStyle,
  // 投影转换
  _project: function () {
    var lng = this._latlng.lng,
      lat = this._latlng.lat,
      map = this._map,
      crs = map.options.crs; // 获取当前地图的参考坐标系
     
     // 判断当前是否是EPSG:4326坐标系
    if (crs.distance === Earth.distance) {
      // 复杂数学计算,考虑地球曲率;计算南北极点投影;横向半径修正(高纬度变形补偿等)
      var d = Math.PI / 180,
        latR = this._mRadius / Earth.R / d,
        top = map.project([lat + latR, lng]),
        bottom = map.project([lat - latR, lng]),
        p = top.add(bottom).divideBy(2),
        lat2 = map.unproject(p).lat,
        lngR =
          Math.acos(
            (Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
              (Math.cos(lat * d) * Math.cos(lat2 * d))
          ) / d;

      if (isNaN(lngR) || lngR === 0) {
        lngR = latR / Math.cos((Math.PI / 180) * lat); // Fallback for edge case, #2425
      }

      this._point = p.subtract(map.getPixelOrigin()); // 圆心像素坐标
      this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
      this._radiusY = p.y - top.y;
    } else {
      // 其他坐标系计算圆心坐标和半径
      var latlng2 = crs.unproject(
        crs.project(this._latlng).subtract([this._mRadius, 0])
      );

      this._point = map.latLngToLayerPoint(this._latlng);
      this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
    }

    this._updateBounds(); //更新包围盒
  },
});

export function circle(latlng, options, legacyOptions) {
  return new Circle(latlng, options, legacyOptions);
}

源码解析

投影转换_project
  • 地球坐标系(WGS84/EPSG:4326 )

    • 纵向半径:直接通过纬度差计算
    • 横向半径:使用三角函数补偿高纬度地区投影拉伸
    • 修正逻辑:当纬度接近极地时(cos(lat) → 0),使用备用公式避免除零错误
  • 投影坐标系(Web Mercator)

    • 直接通过投影坐标差计算像素半径
与其他模块的关系
模块关系
CircleMarker继承自该模块,复用像素级渲染逻辑,重写半径计算和投影方法
Path继承链的一环,提供样式管理、事件系统和基础渲染生命周期
CRS.Earth依赖其地球半径常数(Earth.R)进行WGS84坐标系的半径计算
注意事项

​1.投影失真​​:在高纬度地区,地理圆形可能显示为椭圆(因墨卡托投影拉伸)。
​2.性能影响​​:频繁调用 setRadius 会触发重投影,建议批量更新。
​​3.坐标系限制​​:非地球坐标系下(如 EPSG:3857)的计算精度可能不同。

​总结​​

Leaflet 的 Circle 类通过以下设计实现地理半径圆形:

1.​​动态投影转换​​:实时计算像素半径,适配不同坐标系。
​2.数学修正​​:处理地球坐标系的纬度变形问题。
​​3.兼容性层​​:支持新旧 API 参数格式。
​​4.性能优化​​:缓存计算结果,减少重复投影。

该模块是 Leaflet 处理地理空间圆形覆盖物的核心实现,兼顾数学精确性与渲染性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jinuss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值