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();
}
}
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
类的基础。