矢量图形
本页记录 Leaflet 中的具体矢量形状类:Polyline、Polygon、Circle、CircleMarker 和 Rectangle。这些类扩展 Path 基类,代表在地图上以矢量形式绘制的几何形状。每个形状类处理坐标转换、边界计算、投影和几何特定操作。
关于矢量渲染系统(Canvas 和 SVG 渲染器)的信息,请参阅 矢量渲染系统。关于基类 Path 和共享功能,请参阅 矢量图层。
类层次结构
所有矢量形状都从 Path 基类扩展,该基类提供通用样式和渲染集成。层次结构按几何类型组织:
形状比较
| 形状 | 扩展 | 坐标类型 | 半径类型 | 支持多几何 | 支持孔洞 |
|---|---|---|---|---|---|
| Polyline | Path | LatLng 数组 | N/A | 是 | 否 |
| Polygon | Polyline | 数组的数组 | N/A | 是 | 是 |
| Rectangle | Polygon | LatLngBounds | N/A | 否 | 否 |
| CircleMarker | Path | 单个 LatLng | 像素 | 否 | 否 |
| Circle | CircleMarker | 单个 LatLng | 米 | 否 | 否 |
坐标结构模式
矢量形状支持不同级别的坐标嵌套,以表示简单形状、多部分形状和带孔洞的形状:
Polyline
Polyline 类绘制连接地理点的线。它支持简单折线(单线)和 MultiPolyline(多条独立线)。
关键方法
| 方法 | 描述 | 返回类型 |
|---|---|---|
getLatLngs() | 返回点数组或多折线的嵌套数组 | LatLng[] 或 LatLng[][] |
setLatLngs(latlngs) | 替换所有点并重新绘制 | this |
addLatLng(latlng, latlngs?) | 添加一个点,可选地添加到特定环 | this |
isEmpty() | 如果没有点则返回 true | Boolean |
getBounds() | 返回边界框 | LatLngBounds |
getCenter() | 返回质心 | LatLng |
closestLayerPoint(p) | 找到线上距离给定点最近的点 | Point |
选项
{
smoothFactor: 1.0, // 简化容差(越高 = 越简化)
noClip: false // 禁用折线裁剪以提高性能
}内部坐标处理
Polyline 类对坐标执行多项转换:
实现细节
坐标转换 (src/layer/vector/Polyline.js158-172):
- 递归地将输入数组转换为
LatLng实例 - 使用
LineUtil.isFlat()确定数组是扁平(单线)还是嵌套(多线) - 转换期间计算并扩展
_bounds
投影 (src/layer/vector/Polyline.js174-210):
- 通过
map.latLngToLayerPoint()将地理坐标转换为像素坐标 - 创建投影坐标数组的
_rings数组 - 维护未投影边界的
_rawPxBounds
裁剪 (src/layer/vector/Polyline.js213-247):
- 使用
LineUtil.clipSegment()将线段裁剪到渲染器边界 - 创建包含可见段的
_parts数组 - 如果设置了
noClip: true选项则跳过
简化 (src/layer/vector/Polyline.js250-257):
- 通过
LineUtil.simplify()应用 Douglas-Peucker 算法 - 由
smoothFactor选项控制 - 减少点数以提高性能,同时保持视觉精度
点击检测 (src/layer/vector/Polyline.js272-291):
- Canvas 渲染器用于交互性
- 检查点是否在
_clickTolerance()范围内的任何线段上 - 使用
LineUtil.pointToSegmentDistance()进行距离计算
Polygon
Polygon 类扩展 Polyline 以绘制闭合形状。它支持孔洞(内环)和 MultiPolygon 几何。
关键方法
| 方法 | 描述 | 备注 |
|---|---|---|
isEmpty() | 如果没有环或第一个环为空则返回 true | 覆盖 Polyline 版本 |
getCenter() | 返回多边形质心 | 使用 PolyUtil.polygonCenter() |
坐标结构
多边形使用比折线更复杂的嵌套结构:
- 简单多边形:
[[[lat,lng], [lat,lng], ...]]- 单外环包裹在数组中 - 带孔洞的多边形:第一个数组是外环,后续数组是孔洞
- MultiPolygon:每个元素是一个多边形(本身可以有孔洞)
实现细节
坐标规范化 (src/layer/vector/Polygon.js76-92):
- 确保
_latlngs从不扁平(总是至少包裹在一层数组中) - 如果最后一个点等于第一个点则移除重复的最后一点
- 第一个环始终是外边界,其他是孔洞
裁剪算法 (src/layer/vector/Polygon.js98-124):
- 使用
PolyUtil.clipPolygon()代替线段裁剪 - 扩展裁剪边界以包含描边宽度以避免边缘伪影
- 独立裁剪每个环
点击检测 (src/layer/vector/Polygon.js131-153):
- 使用光线投射算法检查点是否在多边形内部
- 回退到 Polyline 的
_containsPoint()检查描边 - 通过交替内外状态处理孔洞
Circle 和 CircleMarker
这两个类提供两种根本不同坐标系统的圆形。
CircleMarker:固定像素半径
CircleMarker 绘制具有固定像素半径的圆,该半径不随缩放级别变化。适用于标记和点要素。
关键特性:
- 半径以 像素 指定
- 跨缩放级别半径保持不变
- 简单投影:仅将中心点转换为图层坐标
选项:
{
fill: true,
radius: 10 // 像素
}Circle:地理半径
Circle 绘制具有 米 为单位的半径的圆,考虑地图投影。像素半径随缩放和纬度变化。
关键特性:
- 半径以 米 指定(地理距离)
- 像素大小随缩放级别和纬度变化
- 针对 Earth CRS 的复杂投影计算
选项:
{
radius: 200 // 米(必需)
}Circle 投影比较
Circle 地球投影详情
对于 Earth CRS(Web Mercator),Circle 执行复杂的计算 (src/layer/vector/Circle.js76-92):
计算纬度半径(以度为单位)从米半径:
latR = (meters / Earth.R) / (π/180)投影顶部和底部点 在
[lat ± latR, lng]计算中心和调整后的纬度 从投影点
计算经度半径 使用球面三角学:
cos(latR) = cos(lat) * cos(lat2) * cos(lngR) + sin(lat) * sin(lat2)处理边界情况:在极点附近回退到简化计算
存储椭圆参数:
_radius(x)、_radiusY(y)用于非圆形投影
这考虑了地球表面上的圆在 Web Mercator 投影中投影为椭圆的事实,在高纬度地区变形增加。
方法比较
| 方法 | CircleMarker | Circle |
|---|---|---|
setRadius(radius) | 像素 | 米 |
getRadius() | 返回像素半径 | 返回米半径 |
setLatLng(latlng) | ✓ | ✓(继承) |
getLatLng() | ✓ | ✓(继承) |
getBounds() | 基于像素的边界 | 地理边界 |
Rectangle
Rectangle 类是一个专门的 Polygon,从 LatLngBounds 对象创建四角形状。
构造函数和方法
// 从 LatLngBounds 创建
const bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
const rectangle = new Rectangle(bounds, {color: "#ff7800", weight: 1});
// 更新边界
rectangle.setBounds(newBounds);内部实现
边界到坐标转换 (src/layer/vector/Rectangle.js41-49):
_boundsToLatLngs() 方法将 LatLngBounds 转换为逆时针顺序的四个角点:
- 西南角
- 西北角
- 东北角
- 东南角
这为渲染器创建了一个正确方向的多边形。
通用操作
所有矢量形状从 Path 继承某些操作,并以形状特定的方式实现。
边界计算
每个形状以不同方式计算其边界框:
| 形状 | 边界计算 |
|---|---|
| Polyline | 转换期间为每个点扩展边界 |
| Polygon | 继承自 Polyline |
| CircleMarker | 中心 ± 像素半径 |
| Circle | 将中心 ± 半径投影到 LatLng 边界 |
| Rectangle | 由构造函数 LatLngBounds 定义 |
中心计算
注意:
- Polyline 和 Polygon 的
getCenter()要求形状首先添加到地图(否则抛出错误) - 这是因为中心计算需要地图 CRS 的投影坐标
- Circle 简单地返回其中心点
点击检测
点击检测由 Canvas 渲染器用于确定鼠标/触摸事件是否与形状相交:
| 形状 | 算法 | 实现 |
|---|---|---|
| Polyline | 点到线段距离 | LineUtil.pointToSegmentDistance() ≤ tolerance |
| Polygon | 光线投射 + 描边检查 | 计算边交叉数,然后检查 Polyline |
| CircleMarker | 距离检查 | point.distanceTo(center) ≤ radius |
| Circle | 距离检查 | 继承自 CircleMarker |
性能优化
矢量形状采用多项优化来维护大数据集的性能。
裁剪
目的:通过排除可见区域外的几何图形来减少渲染工作量。
实现:
- Polyline:使用
LineUtil.clipSegment()将每个线段裁剪到渲染器边界 (src/layer/vector/Polyline.js213-247) - Polygon:使用
PolyUtil.clipPolygon()并扩展边界以包含描边宽度 (src/layer/vector/Polygon.js98-124) - 可以用
noClip: true选项禁用
流程:
简化
目的:使用 Douglas-Peucker 算法在保持视觉精度的同时减少点数。
配置:
- 由
smoothFactor选项控制(默认:1.0) - 较高的值 = 更激进的简化
- 通过
LineUtil.simplify()在裁剪后应用
权衡:
- 较高的 smoothFactor:更好的性能,较少的细节
- 较低的 smoothFactor:更多的细节,较慢的渲染
- 值为 0 禁用简化
// 重度简化(更快,精度较低)
const polyline1 = new Polyline(coords, {smoothFactor: 3.0});
// 最小简化(较慢,更精确)
const polyline2 = new Polyline(coords, {smoothFactor: 0.5});
// 无简化(最慢,精确)
const polyline3 = new Polyline(coords, {smoothFactor: 0});更新管道
基于折线的形状的完整更新管道:
文件参考汇总
核心实现文件
- src/layer/vector/Polyline.js - Polyline 类、坐标处理、裁剪、简化
- src/layer/vector/Polygon.js - Polygon 类、孔洞支持、光线投射点击检测
- src/layer/vector/Circle.js - 带有地理半径的 Circle 类
- src/layer/vector/CircleMarker.js - 带有像素半径的 CircleMarker 类
- src/layer/vector/Rectangle.js - Rectangle 类、LatLngBounds 转换
测试文件
- spec/suites/layer/vector/PolylineSpec.js - 包括多线支持的 Polyline 测试
- spec/suites/layer/vector/PolygonSpec.js - 包括孔洞和 MultiPolygon 的 Polygon 测试