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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# 源码解析
# 投影转换_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 处理地理空间圆形覆盖物的核心实现,兼顾数学精确性与渲染性能。
编辑 (opens new window)
上次更新: 2025/05/08, 10:32:25