Jinuss's blog Jinuss's blog
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 《Vue》
    • 《React》
    • 《Git》
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

前端可视化
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 《Vue》
    • 《React》
    • 《Git》
    • JS设计模式总结
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 框架

  • core模块

  • dom模块

  • control

  • geometry

  • geo

  • layer

    • marker

    • tile

    • vector

      • Renderer
      • Canvas
      • Path
      • Polyline
      • Polygon
        • 概述
        • 源码分析
          • 源码实现
          • 源码详解
          • 数据模型调整
          • 核心方法扩展
          • 渲染与性能优化
          • 多环与嵌套结构
        • 总结
      • Rectangle
      • Render.getRenderer
      • SVG
      • SVG.Util
      • SVG.VML
      • CircleMarker
      • Circle
    • Layer
    • LayerGroup
    • FeatureGroup
    • DivOverlay
    • Popup
    • Tooltip
    • ImageOverlay
    • SVGOverlay
    • VideoOverlay
    • GeoJSON
  • Map

  • 《Leaflet源码》笔记
  • layer
  • vector
东流
2025-04-15
目录

Polygon

# 概述

Leaflet中的Polygon类继承自Polyline类,在折线基础上扩展了闭合区域、填充、点包含检测等特性。

# 源码分析

# 源码实现

Polygon的源码实现如下:

export var Polygon = Polyline.extend({
  options: {
    fill: true, //默认启用填充
  },
  isEmpty: function () {
    return !this._latlngs.length || !this._latlngs[0].length;
  },
  getCenter: function () {
    if (!this._map) {
      throw new Error("Must add layer to map before using getCenter()");
    }
    return PolyUtil.polygonCenter(this._defaultShape(), this._map.options.crs);
  },
  _convertLatLngs: function (latlngs) {
    var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
      len = result.length;
    // 移除首尾重复点(自动闭合)
    if (
      len >= 2 &&
      result[0] instanceof LatLng &&
      result[0].equals(result[len - 1])
    ) {
      result.pop();
    }
    return result;
  },
  _setLatLngs: function (latlngs) {
    // 顶点数据扁平化为二维数组,单个环转为二维数组
    Polyline.prototype._setLatLngs.call(this, latlngs);
    if (LineUtil.isFlat(this._latlngs)) {
      this._latlngs = [this._latlngs];
    }
  },
  _defaultShape: function () {
    return LineUtil.isFlat(this._latlngs[0])
      ? this._latlngs[0]
      : this._latlngs[0][0];
  },
  _clipPoints: function () {
    var bounds = this._renderer._bounds,
      w = this.options.weight,
      p = new Point(w, w);

    bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));

    this._parts = [];
    if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
      return;
    }

    if (this.options.noClip) {
      this._parts = this._rings;
      return;
    }

    for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
      clipped = PolyUtil.clipPolygon(this._rings[i], bounds, true); // 裁剪多边形
      if (clipped.length) {
        this._parts.push(clipped);
      }
    }
  },
  _updatePath: function () {
    this._renderer._updatePoly(this, true); //渲染器进行绘制
  },
  _containsPoint: function (p) {
    var inside = false,
      part,
      p1,
      p2,
      i,
      j,
      k,
      len,
      len2;

    if (!this._pxBounds || !this._pxBounds.contains(p)) {
      return false;
    }

    for (i = 0, len = this._parts.length; i < len; i++) {
      part = this._parts[i];

      for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
        p1 = part[j];
        p2 = part[k];
        // 射线法:水平向右射线与边交点数的奇偶性 
        if (
          p1.y > p.y !== p2.y > p.y &&
          p.x < ((p2.x - p1.x) * (p.y - p1.y)) / (p2.y - p1.y) + p1.x
        ) {
          inside = !inside;
        }
      }
    }
    // 同时检测边界(容差内视为在线上)
    return inside || Polyline.prototype._containsPoint.call(this, p, true);
  },
});

// 工厂函数,创建多边形实例
export function polygon(latlngs, options) {
  return new Polygon(latlngs, options);
}
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104

# 源码详解

# 数据模型调整

闭合环处理

  • 自动闭合:若首尾点重复,移除末尾点(存储时冗余,渲染时自动闭合)
  • 强制二维数组数据结构:即使输入是单环平面数组,也包裹成环结构,确保统一处理多环(如带洞多边形)

# 核心方法扩展

中心点计算

  • 面积加权法:计算多边形质心(与折线的线段中心不同),需要考虑每个环的面积占比

边界裁剪

  • 多边形裁剪:使用PolyUtil.clipPolygon方法替代折线的线段裁剪,保持闭合区域完整性

点包含检测

  • 射线法:计算点是否在多边形内部,处理多环(如外环和内洞)
  • 边界容差:若点靠近边界,复用折线的线段距离检测(true参数表示闭合路径)

# 渲染与性能优化

路径更新

  • 填充标记:传递true告知渲染器填充多边形

空数据检查

  • 严格校验:确保至少存在一个非空环,避免无效渲染

# 多环与嵌套结构

  • 支持多环:支持多个环,如带洞多边形
  • 嵌套结构:支持嵌套的多边形结构,如多环多边形

# 总结

Polygon 类在 Polyline 基础上扩展的关键点包括:

​​1.闭合处理​​:自动闭合路径,数据存储优化。 ​​2.填充支持​​:默认启用填充,与渲染器协作实现。 ​​3.点包含算法​​:射线法检测内部点,结合边界容差。 ​​4.多环结构​​:支持带洞多边形,数据模型强制二维化。 ​​5.裁剪与中心计算​​:专用多边形算法提升准确性。

其设计充分考虑了地理多边形的复杂场景(如岛屿、湖泊带岛等),通过继承复用折线逻辑,针对性优化关键算法,平衡了功能与性能。

编辑 (opens new window)
上次更新: 2025/05/09, 05:23:58
Polyline
Rectangle

← Polyline Rectangle→

最近更新
01
GeoJSON
05-08
02
Circle
04-15
03
CircleMarker
04-15
更多文章>
Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式