Skip to content

矢量图形

本页记录 Leaflet 中的具体矢量形状类:PolylinePolygonCircleCircleMarkerRectangle。这些类扩展 Path 基类,代表在地图上以矢量形式绘制的几何形状。每个形状类处理坐标转换、边界计算、投影和几何特定操作。

关于矢量渲染系统(Canvas 和 SVG 渲染器)的信息,请参阅 矢量渲染系统。关于基类 Path 和共享功能,请参阅 矢量图层


类层次结构

所有矢量形状都从 Path 基类扩展,该基类提供通用样式和渲染集成。层次结构按几何类型组织:

SVG
100%

形状比较

形状扩展坐标类型半径类型支持多几何支持孔洞
PolylinePathLatLng 数组N/A
PolygonPolyline数组的数组N/A
RectanglePolygonLatLngBoundsN/A
CircleMarkerPath单个 LatLng像素
CircleCircleMarker单个 LatLng

坐标结构模式

矢量形状支持不同级别的坐标嵌套,以表示简单形状、多部分形状和带孔洞的形状:

SVG
100%

Polyline

Polyline 类绘制连接地理点的线。它支持简单折线(单线)和 MultiPolyline(多条独立线)。

关键方法

方法描述返回类型
getLatLngs()返回点数组或多折线的嵌套数组LatLng[]LatLng[][]
setLatLngs(latlngs)替换所有点并重新绘制this
addLatLng(latlng, latlngs?)添加一个点,可选地添加到特定环this
isEmpty()如果没有点则返回 trueBoolean
getBounds()返回边界框LatLngBounds
getCenter()返回质心LatLng
closestLayerPoint(p)找到线上距离给定点最近的点Point

选项

{
  smoothFactor: 1.0,  // 简化容差(越高 = 越简化)
  noClip: false       // 禁用折线裁剪以提高性能
}

内部坐标处理

Polyline 类对坐标执行多项转换:

SVG
100%

实现细节

坐标转换 (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:每个元素是一个多边形(本身可以有孔洞)
SVG
100%

实现细节

坐标规范化 (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 投影比较

SVG
100%

Circle 地球投影详情

对于 Earth CRS(Web Mercator),Circle 执行复杂的计算 (src/layer/vector/Circle.js76-92):

  1. 计算纬度半径(以度为单位)从米半径:

    latR = (meters / Earth.R) / (π/180)
  2. 投影顶部和底部点[lat ± latR, lng]

  3. 计算中心和调整后的纬度 从投影点

  4. 计算经度半径 使用球面三角学:

    cos(latR) = cos(lat) * cos(lat2) * cos(lngR) + sin(lat) * sin(lat2)
  5. 处理边界情况:在极点附近回退到简化计算

  6. 存储椭圆参数_radius(x)、_radiusY(y)用于非圆形投影

这考虑了地球表面上的圆在 Web Mercator 投影中投影为椭圆的事实,在高纬度地区变形增加。

方法比较

方法CircleMarkerCircle
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 转换为逆时针顺序的四个角点:

  1. 西南角
  2. 西北角
  3. 东北角
  4. 东南角

这为渲染器创建了一个正确方向的多边形。

SVG
100%

通用操作

所有矢量形状从 Path 继承某些操作,并以形状特定的方式实现。

边界计算

每个形状以不同方式计算其边界框:

形状边界计算
Polyline转换期间为每个点扩展边界
Polygon继承自 Polyline
CircleMarker中心 ± 像素半径
Circle将中心 ± 半径投影到 LatLng 边界
Rectangle由构造函数 LatLngBounds 定义

中心计算

SVG
100%

注意:

  • Polyline 和 Polygon 的 getCenter() 要求形状首先添加到地图(否则抛出错误)
  • 这是因为中心计算需要地图 CRS 的投影坐标
  • Circle 简单地返回其中心点

点击检测

点击检测由 Canvas 渲染器用于确定鼠标/触摸事件是否与形状相交:

形状算法实现
Polyline点到线段距离LineUtil.pointToSegmentDistance() ≤ tolerance
Polygon光线投射 + 描边检查计算边交叉数,然后检查 Polyline
CircleMarker距离检查point.distanceTo(center) ≤ radius
Circle距离检查继承自 CircleMarker

性能优化

矢量形状采用多项优化来维护大数据集的性能。

裁剪

目的:通过排除可见区域外的几何图形来减少渲染工作量。

实现:

流程:

SVG
100%

简化

目的:使用 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});

更新管道

基于折线的形状的完整更新管道:

SVG
100%

文件参考汇总

核心实现文件

测试文件