MultiPolygon类
# 概述
在Openlayers中,MultiPolygon
类顾名思义就是表示由多个多边形组成的几何对象,关于Polygon
类可以参考这篇文章;同Polygon
类一样,MultiPolygon
类继承于SimpleGeometry
类。
本文主要介绍MultiPolygon
类的源码实现和原理。
# 源码分析
# MultiPolygon
类的源码实现
MultiPolygon
类的源码实现如下:
class MultiPolygon extends SimpleGeometry {
constructor(coordinates, layout, endss) {
super();
this.endss_ = [];
this.flatInteriorPointRevision_ = -1;
this.flatInteriorPoints = null;
this.maxDelta_ = -1;
this.maxDeltaRevision_ = -1;
this.orientedRevision_ = -1;
this.orientedFlatCoordinates_ = null;
if (!endss && !Array.isArray(coordinates[0])) {
const polygons = coordinates;
const flatCoordinates = [];
const thisEndss = [];
for (let i = 0, ii = polygons.length; i < ii; ++i) {
const polygon = polygons[i];
const offset = flatCoordinates.length;
const ends = polygon.getEnds();
for (let j = 0, jj = ends.length; j < jj; ++j) {
ends[j] += offset;
}
extend(flatCoordinates, polygon.getFlatCoordinates());
thisEndss.push(ends);
}
layout =
polygons.length === 0 ? this.getLayout() : polygons[0].getLayout();
coordinates = flatCoordinates;
endss = thisEndss;
}
if (layout !== undefined && endss) {
this.setFlatCoordinates(layout, coordinates);
this.endss_ = endss;
} else {
this.setCoordinates(coordinates, layout);
}
}
appendPolygon(polygon) {
let ends;
if (!this.flatCoordinates) {
this.flatCoordinates = polygon.getFlatCoordinates().slice();
ends = polygon.getEnds().slice();
this.endss_.push();
} else {
const offset = this.flatCoordinates.length;
extend(this.flatCoordinates, polygon.getFlatCoordinates());
ends = polygon.getEnds().slice();
for (let i = 0, ii = ends.length; i < ii; ++i) {
ends[i] += offset;
}
}
this.endss_.push(ends);
this.changed();
}
clone() {
const len = this.endss_.length;
const newEndss = new Array(len);
for (let i = 0; i < len; ++i) {
newEndss[i] = this.endss_[i].slice();
}
const multiPolygon = new MultiPolygon(
this.flatCoordinates.slice(),
this.layout,
newEndss
);
multiPolygon.applyProperties(this);
return multiPolygon;
}
closestPointXY(x, y, closestPoint, minSquaredDistance) {
if (minSquaredDistance < closestSquaredDistanceXY(this.getExtent(), x, y)) {
return minSquaredDistance;
}
if (this.maxDeltaRevision_ != this.getRevision()) {
this.maxDelta_ = Math.sqrt(
multiArrayMaxSquaredDelta(
this.flatCoordinates,
0,
this.endss_,
this.stride,
0
)
);
this.maxDeltaRevision_ = this.getRevision();
}
return assignClosestMultiArrayPoint(
this.getOrientedFlatCoordinates(),
0,
this.endss_,
this.stride,
this.maxDelta_,
true,
x,
y,
closestPoint,
minSquaredDistance
);
}
containsXY(x, y) {
return linearRingssContainsXY(
this.getOrientedFlatCoordinates(),
0,
this.endss_,
this.stride,
x,
y
);
}
getArea() {
return linearRingssArea(
this.getOrientedFlatCoordinates(),
0,
this.endss_,
this.stride
);
}
getCoordinates(right) {
let flatCoordinates;
if (right !== undefined) {
flatCoordinates = this.getOrientedFlatCoordinates().slice();
orientLinearRingsArray(
flatCoordinates,
0,
this.endss_,
this.stride,
right
);
} else {
flatCoordinates = this.flatCoordinates;
}
return inflateMultiCoordinatesArray(
flatCoordinates,
0,
this.endss_,
this.stride
);
}
getEnds() {
return this.endss_;
}
getFlatInteriorPoint() {
if (this.flatInteriorPointsRevision_ != this.getRevision()) {
const flatCenters = linearRingssCenter(
this.flatCoordinates,
0,
this.endss_,
this.stride
);
this.flatInteriorPoints_ = getInteriorPointsOfMultiArray(
this.getOrientedFlatCoordinates(),
0,
this.endss_,
this.stride,
flatCenters
);
this.flatInteriorPointsRevision_ = this.getRevision();
}
return this.flatInteriorPoints_;
}
getInteriorPoints() {
return new MultiPoint(this.getFlatInteriorPoints().slice(), "XYM");
}
getOrientedFlatCoordiantes() {
if (this.orientedRevision_ != this.getRevision()) {
const flatCoordinates = this.flatCoordinates;
if (
linearRingssAreOriented(flatCoordinates, 0, this.endss_, this.stride)
) {
this.orientedFlatCoordinates_ = flatCoordinates;
} else {
this.orientedFlatCoordinates_ = flatCoordinates.slice();
this.orientedFlatCoordinates_.length = orientLinearRingsArray(
this.orientedFlatCoordinates_,
0,
this.endss_,
this.stride
);
}
this.orientedRevision_ = this.getRevision();
}
return this.orientedFlatCoordinates_;
}
getSimplifiedGeometryInternal(squaredTolerance) {
const simplifiedFlatCoordinates = [];
const simplifiedEndss = [];
simplifiedFlatCoordinates.length = quantizeMultiArray(
this.flatCoordinates,
0,
this.endss_,
this.stride,
Math.sqrt(squaredTolerance),
simplifiedFlatCoordinates,
0,
simplifiedEndss
);
return new MultiPolygon(simplifiedFlatCoordinates, "XY", simplifiedEndss);
}
getPolygon(index) {
if (index < 0 || this.endss_.length <= index) {
return null;
}
let offset;
if (index === 0) {
offset = 0;
} else {
const prevEnds = this.endss_[index - 1];
offset = prevEnds[prevEnds.length - 1];
}
const ends = this.endss_[index].slice();
const end = ends[ends.length - 1];
if (offset !== 0) {
for (let i = 0, ii = ends.length; i < ii; ++i) {
ends[i] -= offset;
}
}
return new Polygon(
this.flatCoordinates.slice(offset, end),
this.layout,
ends
);
}
getPolygons() {
const layout = this.layout;
const flatCoordinates = this.flatCoordinates;
const endss = this.endss_;
const polygons = [];
let offset = 0;
for (let i = 0, ii = endss.length; i < ii; ++i) {
const ends = endss[i].slice();
const end = ends[ends.length - 1];
if (offset !== 0) {
for (let j = 0, jj = ends.length; j < jj; ++j) {
ends[j] -= offset;
}
}
const polygon = new Polygon(
flatCoordinates.slice(offset, end),
layout,
ends
);
polygons.push(polygon);
offset = end;
}
return polygons;
}
getType() {
return "MultiPolygon";
}
intersectsExtent(extent) {
return intersectsLinearRingMultiArray(
this.getOrientedFlatCoordinates(),
0,
this.endss_,
this.stride,
extent
);
}
setCoordinates(coordinates, layout) {
this.setLayout(layout, coordinates, 3);
if (!this.flatCoordinates) {
this.flatCoordinates = [];
}
const endss = deflateMultiCoordinatesArray(
this.flatCoordinates,
0,
coordinates,
this.stride,
this.endss_
);
if (endss.length === 0) {
this.flatCoordinates.length = 0;
} else {
const lastEnds = endss[endss.length - 1];
this.flatCoordinates.length =
lastEnds.length === 0 ? 0 : lastEnds[lastEnds.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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# MultiPolygon
类的构造函数
MultiPolygon
类构造函数接受三个参数:坐标数据coordinates
、坐标布局layout
和endss
每个多边形结束点数组;在Polygon
类的构造函数中用this.ends_
存储每个线性环的结束坐标的索引,而在MultiPolygon
类中用this.endss_
存储每个多边形的结束点新鲜,每个多边形的结束点是一个坐标数组;其余变量如this.flatInteriorPointRevision_
等等同Polygon
类中一样,都是用于优化几何对象的处理和渲染、比如计算多边形的内部点、顶点排序变化等;MultiPolygon
类的构造函数还会判断,若参数endss
不存在并且coordinates
的第一个值不是数组,即coordinates
是一个包含多个多边形对象的数组,则遍历这些多边形,获取其结束点ends
并将它们根据当前的偏移调整,然后将多个多边形的坐标扁平化最后赋值给coordinates
,将每个多边形的结束点数组存储到this.Endss
最后赋值给endss
;然后根据坐标布局风格layout
和endss
来决定是调用this.setFlatCoordiantes
还是this.setCoordiantes
设置this.endss_
、this.layout
、this.stride
和this.flatCoordinates
。
# MultiPolygon
类的主要方法
MultiPolygon
类的主要方法如下
appendPolygon
方法:该方法是向当前几何对象添加一个多边形,接受一个参数polygon
多边形;首先会判断,若this.flatCoordinates
不存在,则调用polygon.getFlatCoordiantes
方法获取参数多边形的坐标赋值给this.flatCoordiantes
;并且获取多边形的结束点;若存在,则获取多边形的坐标添加到this.faltCoordiantes
中,并且获取多边形坐标的长度,以此来设置该多边形的结束点的偏移值,然后将ends
添加到this.endss_
的末端,最后调用this.changed
方法clone
方法:复制当前几何对象,通过this.endss_
获取每个多边形的结束点信息,然后实例化MultiPolygon
类,调用实例对象的applyProperties
方法应用属性,最后返回实例对象。closestPointXY
方法:计算给定点(x,y)
到当前几何对象的最近距离的平方,以及可能会修改最近点坐标closestPoint
和最近距离的平方minSquaredDistance
;方法内部同Polygon
类中同名函数类似,会基于几何对象发生变化时重新计算this.maxDelta_
containsXY
方法:判断给定点(x,y)
是否在当前几何对象内部或者边界上,内部会逐一判断每个多边形是否包含该点,若包含则返回true
;否则判断下一个多边形,若都不包含,则返回false
.getArea
方法:获取当前几何对象的面积,内部调用的方法是linearRingsArea
方法getCoordinates
方法:获取几何对象的坐标,内部就是调用inflateMultiCoordinatesArray
方法getEnds
方法:获取this.endss_
的值getFlatInteriorPoints
方法:实现原理和Polygon
类中的同名函数类似,不过是需要通过this.endss_
变量获取每个多边形的坐标,再计算对应多边形的内部点,也就说this.flatInteriorPoints_
中保存的是每个多边形的内部点getInteriorPoints
方法:获取当前几何对象每个多边形的内部点getOrientedFlatCoordiantes
方法:实现原理和Polygon
类中的同名函数一样getSimplifiedGeometryInternal
方法:获取简化后的几何对象,接受一个参数squaredTolerance
容差平方,该值越大,表示要去除的点更多;内部是调用quantizeMultiArray
方法进行简化当前几何对象,简化后对象的坐标保存在simplifiedFlatCoordiantes
中,最后调用MultiPolygon
实例化并返回实例对象getPolygon
方法:返回几何对象中索引值对应的多边形,首先会计算参数index
是否合法,然后通过index
和this.endss_
计算该索引值对应的坐标,然后调用Polygon
类实例化一个多边形,最后返回该多边形的实例。getPolygons
方法:获取几何对象的多边形,以数组形式返回;通过this.endss_
变量计算其中某个多边形的坐标(起止位置),然后调用Polygon
进行实例化,将其实例对象保存到数组polygons
中最后返回。getType
方法:返回当前几何对象的类型,MultiPolygon
intersectExtent
方法:判断extent
是否与当前几何对象相交,内部是调用intersectsLinearRingMultiArray
方法setCoordinates
方法:内部是调用delatMultiCoordinatesArray
方法,设置this.flatCoordinates
、this.layout
和this.stride
,最后调用this.changed
方法
# 总结
本文主要介绍了MultiPolygon
类的实现原理,MultiPolygon
类和Polygon
类的实现原理几乎大同小异。