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)
  • 核心基类

  • Control控件篇

  • Geom几何图形篇

    • Geom几何图形篇介绍
    • Geometry基类
    • GeometryCollection类
    • SimpleGeometry类
    • Point类
    • MultiPoint类
    • LinearRing类
    • Polygon类
      • 概述
      • 源码解析
        • Polygon类源码实现
        • Polygon类的构造函数
        • Polygon类的主要方法
      • 总结
    • MultiPolygon类
    • LineString类
    • MultiLineString类
    • Circle类
  • Layer图层篇

  • Renderer篇

  • Feature篇

  • style样式篇

  • 《Openlayers源码》笔记
  • Geom几何图形篇
东流
2024-12-25
目录

Polygon类

# 概述

在 Openlayers 中,Polygon类是SimpleGeometry的子类,用于表示多边形几何形状。多边形是由一组点组成的封闭图形,通常由一系列的坐标点和多个边界组成. 关于SimpleGeometry类,可以参考这篇文章

本文主要介绍Polygon类的源码实现和原理。

# 源码解析

# Polygon类源码实现

Polygon类的源码实现如下:

class Polygon extends SimpleGeometry {
  constructor() {
    super();
    this.ends_ = [];
    this.flatInteriorPointRevsion_ = -1;
    this.flatInteriorPoint_ = null;
    this.maxDelta_=-1;
    this.maxDeltaRevision_ = -1;
    this.orientrevision_ = -1;
    this.orientedFlatCoordinates_ = null;
    if (layout !== undefined && ends) {
      this.setFlatCoordinates(layout, coordinates);
      this.ends_ = ends;
    } else {
      this.setCoordinates(coordinates, layout);
    }
  }
  appendLinearRing(linearRing) {
    if (!this.flatCoordinates) {
      this.flatCoordinates = linearRing.getFlatCoordinates().slice();
    } else {
      extend(this.flatCoordinates, linearRing.getFlatCoordinates());
    }
    this.ends_.push(this.flatCoordinates.length);
    this.changed();
  }
  clone() {
    const polygon = new Polygon(
      this.flatCoordinates.slice(),
      this.layout,
      this.ends_.slice()
    );
    polygon.applyProperties(this);
    return polygon;
  }

  closestPointXY(x, y, closestPoint, minSquaredDistance) {
    if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
      return minSquaredDistance;
    }
    if (this.maxDeltaRevision_ != this.getRevision()) {
      this.maxDelta_ = Math.sqrt(
        arrayMaxSquaredDelta(
          this.flatCoordinates,
          0,
          this.ends_,
          this.stride,
          0
        )
      );
      this.maxDeltaRevision_ = this.getRevision();
    }
    return assignClosestArrayPoint(
      this.flatCoordinates,
      0,
      this.ends_,
      this.stride,
      this.maxDelta_,
      true,
      x,
      y,
      closestPoint,
      minSquaredDistance
    );
  }

  containsXY(x, y) {
    return linearRingsContainsXY(
      this.getOrientedFlatCoordinates(),
      0,
      this.ends_,
      this.stride,
      x,
      y
    );
  }

  getArea() {
    return linearRingsArea(
      this.getOrientedFlatCoordinates(),
      0,
      this.ends_,
      this.stride
    );
  }

  getCoordinates(right) {
    let flatCoordinates;
    if (right !== undefined) {
      flatCoordinates = this.getOrientedFlatCoordinates().slice();
      orientLinearRings(flatCoordinates, 0, this.ends_, this.stride, right);
    } else {
      flatCoordinates = this.flatCoordinates;
    }

    return inflateCoordinatesArray(flatCoordinates, 0, this.ends_, this.stride);
  }

  getEnds() {
    return this.ends_;
  }

  getFlatInteriorPoint() {
    if (this.flatInteriorPointRevision_ != this.getRevision()) {
      const flatCenter = getCenter(this.getExtent());
      this.flatInteriorPoint_ = getInteriorPointOfArray(
        this.getOrientedFlatCoordinates(),
        0,
        this.ends_,
        this.stride,
        flatCenter,
        0
      );
      this.flatInteriorPointRevision_ = this.getRevision();
    }
    return this.flatInteriorPoint_;
  }

  getInteriorPoint() {
    return new Point(this.getFlatInteriorPoint(), "XYM");
  }

  getLinearRingCount() {
    return this.ends_.length;
  }

  getLinearRing(index) {
    if (index < 0 || this.ends_.length <= index) {
      return null;
    }
    return new LinearRing(
      this.flatCoordinates.slice(
        index === 0 ? 0 : this.ends_[index - 1],
        this.ends_[index]
      ),
      this.layout
    );
  }

  getLinearRings() {
    const layout = this.layout;
    const flatCoordinates = this.flatCoordinates;
    const ends = this.ends_;
    const linearRings = [];
    let offset = 0;
    for (let i = 0, ii = ends.length; i < ii; ++i) {
      const end = ends[i];
      const linearRing = new LinearRing(
        flatCoordinates.slice(offset, end),
        layout
      );
      linearRings.push(linearRing);
      offset = end;
    }
    return linearRings;
  }

  getOrientedFlatCoordinates() {
    if (this.orientedRevision_ != this.getRevision()) {
      const flatCoordinates = this.flatCoordinates;
      if (linearRingsAreOriented(flatCoordinates, 0, this.ends_, this.stride)) {
        this.orientedFlatCoordinates_ = flatCoordinates;
      } else {
        this.orientedFlatCoordinates_ = flatCoordinates.slice();
        this.orientedFlatCoordinates_.length = orientLinearRings(
          this.orientedFlatCoordinates_,
          0,
          this.ends_,
          this.stride
        );
      }
      this.orientedRevision_ = this.getRevision();
    }
    return this.orientedFlatCoordinates_;
  }

  getSimplifiedGeometryInternal(squaredTolerance) {
    const simplifiedFlatCoordinates = [];
    const simplifiedEnds = [];
    simplifiedFlatCoordinates.length = quantizeArray(
      this.flatCoordinates,
      0,
      this.ends_,
      this.stride,
      Math.sqrt(squaredTolerance),
      simplifiedFlatCoordinates,
      0,
      simplifiedEnds
    );
    return new Polygon(simplifiedFlatCoordinates, "XY", simplifiedEnds);
  }

  getType() {
    return "Polygon";
  }

  intersectsExtent(extent) {
    return intersectsLinearRingArray(
      this.getOrientedFlatCoordinates(),
      0,
      this.ends_,
      this.stride,
      extent
    );
  }

  setCoordinates(coordinates, layout) {
    this.setLayout(layout, coordinates, 2);
    if (!this.flatCoordinates) {
      this.flatCoordinates = [];
    }
    const ends = deflateCoordinatesArray(
      this.flatCoordinates,
      0,
      coordinates,
      this.stride,
      this.ends_
    );
    this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1];
    this.changed();
  }
}
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

# Polygon类的构造函数

Polygon类的构造函数接受三个参数:coordinates坐标点、layout坐标风格和ends边界点;另外构造函数中还有如下成员变量:

  • this.ends_:初始化为空数组[],用于存储多边形的边界信息。对于多边形,ends存储的是每一个环(环形边界)的结束坐标索引。

  • this.flatInteriorPointRevision:初始化为-1,用于记录多边形内部点的版本信息,主要是在多边形优化和缓存内部点计算时用到

  • this.flatInteriorPoint_:默认为null,用于缓存多边形的内部点或质心

  • this.maxDeltaREvision_:多边形形状更新的最大变化版本,和LinearRing类中的同名变量类似,可以参考

  • this.orientedRevision_:初始为-1,用于表示多边形的方向版本;如果多边形的坐标顺序发生变化,它将被更新

  • this.orientedFlatCoordiantes_:初始为null,用于存储有方向性的平面坐标

Polygon类的构造函数会判断,若参数layout坐标布局风格和环信息ends存在,则调用父类SimpleGeometry类中的this.setFlatCoordinates方法设置坐标;否则,调用this.setCoordiantes方法。

# Polygon类的主要方法

Polygone类的主要方法如下:

  • setCoordiantes方法:用于设置坐标this.flatCoordiantes、坐标布局风格this.layout和步幅this.stride,最后会调用this.changed方法,该方法会修改this.revision_的值,后面几个方法内部调用this.getRevision()方法就是获取this.revision_的值,它们是在Observable类中定义的,可以参考这篇文章

  • intersectsExtent方法:该方法用于判断几何对象是否与extent交叉

  • getType方法:返回几何对象多边形的类型,Polygon

  • getSimplifiedGeometryInternal方法:获取简化后的几何对象,接受一个参数squaredTolerance容差值平方,内部就是调用quantizeArray方法简化几何对象,然后获取简化几何对象的坐标,再调用Polygon进行实例化,并返回

  • getOrientedFlatCoordiantes方法:返回多边形顶点的有向平面坐标;首先会判断多边形是否发生变化,若未变化,则直接返回this.orientedFlatCoordinates_;否则调用linearRingsAreOriented方法判断线性环坐标是否是有向的,若是,则直接返回原始坐标;否则复制一份原始坐标数组,然后调用orientLinearRings方法重新调整坐标的方向,确保环的方向正确,说白了就是会调整坐标的顺序,然后修改this.orientedFlatCoordintes_和this.orientedRevision_

  • getLinearRing方法:根据索引值index获取某一个具体的线性环,首先会判断索引值index的合法性,然后实例化LinearRing类并返回实例对象,关于LinearRing类,可以参考这篇文章

  • getLinearRings方法:获取多边形的环,内部会遍历this.ends_,前面提过这个变量存储的是环的结束点坐标索引,即每个环的最后一个坐标在this.flatCoordinates中的位置,遍历this.ends_然后截取this.flatCoordiantes就可以得到每个环的坐标数组,然后调用LinearRing类实例化线性环,将其值保存到数组linearRings中,最后返回linearRings

  • getLinearRingCount方法:获取多边形环的个数

  • getInteriorPoint方法:方法内部会调用this.getFlatInteriorPoint()方法获取多边形内部的代表点,然后实例化Point类并返回实例对象。

  • getFlatInteriorPoint方法:获取几何对象的内部点,该方法主要用于计算并返回多边形的一个内部点(通常是内部几何中心点),并缓存该结果。它会在多边形发生变更时重新计算,并在未发生变化时直接返回缓存的结果。首先会判断几何对象是否更新变化过,若变化了,则调用getCenter方法计算几何对象包围盒的中心点,然后调用getInteriorPointOfArray计算出一个内部点-多边形内部的代表点并返回,将其赋值给this.flatInteriorPoint_变量,最后修改this.flatInteriorPointRevision_的值;若未变化,则返回this.flatInteriorPoint_

  • getEnds方法:获取变量this.ends_的值,即多边形中环的结束点索引位置数组

  • getCoordinates方法:获取几何对象的坐标数组

  • getArea方法:获取多边形面积,内部调用的是linearRingArea方法计算多边形面积,运用的是高斯面积公式

  • containsXY方法:用于判断给定点(x,y)是否在多边形内部或边界上,内部调用的是linearRingsContainsXY方法

  • closestPointXY方法:几何对象中很常见的方法了,给定一个目标点(x,y)、最近点坐标closestPoint和最小平方距离minSquaredDistance,然后计算多边形中距离目标点最近的点,并返回多边形距离目标点最近距离的平方;closestPointXY方法内部会先调用closestSquaredDistanceXY计算几何对象包围盒距离目标点最近的距离,若参数minSquaredDistance小于该距离则直接返回它;然后判断this.maxDeltaRevision_是否与this.getResvision()方法返回的变量相等,也就是判断几何对象多边形是否发生了改变,若发生了改变,就调用arrayMaxSquaredDelta方法来计算多边形顶点的最大坐标变化,并将其平方根保存在this.maxDelta变量中,最后更新this.maxDeltaRevision_为当前修订版本号;最后调用assignCloestArrayPoint方法来计算目标点(x,y)与多边形最近的点,并更新最近点坐标closestPoint和最小距离平方``minSquaredDistance`并返回最小距离平方.

  • clone方法:该方法内部就是调用Polygon类实例化一个多边形,然后调用实例的applyProperties方法应用属性,最后返回实例。

  • appendLinearRing方法:参数linearRing是一个LinearRing类的实例,该方法用于将线性环添加到多边形;首先会判断,若this.flatCoordiantes不存在,即多边形没有坐标点,则将调用线性环的getFlatCoordiantes方法获取它的坐标,赋值给this.flatCoordinates,否则调用extent方法,将线性环的坐标添加到this.flatCoordiantes中;更新变量this.ends,最后调用this.changed()方法

# 总结

本文主要介绍Polygon类的实现原理,LinearRing类是Polygon类的基础。

编辑 (opens new window)
上次更新: 2024/12/28, 15:32:49
LinearRing类
MultiPolygon类

← LinearRing类 MultiPolygon类→

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