PolygonBuilder类
# 概述
在 Openlayers 中,CanvasPolygonBuilder
类是用于构造几何图形Circle
、多边形Polygon
和多个多边形MultiPolygon
绘制指令集的构造器,CanvasPolygonBuilder
类继承于CanvasBuilder
类,关于CanvasBuilder
类可以参考这篇文章
# 源码分析
# CanvasPolygonBuilder
类的源码实现
CanvasPolygonBuilder
类的源码实现如下:
class CanvasPolygonBuilder extends CanvasBuilder {
constructor(tolerance, maxExtent, resolution, pixelRatio) {
super(tolerance, maxExtent, resolution, pixelRatio);
}
setFillStrokeStyles_() {
const state = this.state;
const fillStyle = state.fillStyle;
if (fillStyle !== undefined) {
this.updateFillStyle(state, this.createFill);
}
if (state.strokeStyle !== undefined) {
this.updateStrokeStyle(state, this.applyStroke);
}
}
drawFlatCoordinatess_(flatCoordinates, offset, ends, stride) {
const state = this.state;
const fill = state.fillStyle !== undefined;
const stroke = state.strokeStyle !== undefined;
const numEnds = ends.length;
this.instructions.push(beginPathInstruction);
this.hitDetectionInstructions.push(beginPathInstruction);
for (let i = 0; i < numEnds; ++i) {
const end = ends[i];
const myBegin = this.coordinates.length;
const myEnd = this.appendFlatLineCoordinates(
flatCoordinates,
offset,
end,
stride,
true,
!stroke
);
const moveToLineToInstruction = [
CanvasInstruction.MOVE_TO_LINE_TO,
myBegin,
myEnd,
];
this.instructions.push(moveToLineToInstruction);
this.hitDetectionInstructions.push(moveToLineToInstruction);
if (stroke) {
this.instructions.push(closePathInstruction);
this.hitDetectionInstructions.push(closePathInstruction);
}
offset = end;
}
if (fill) {
this.instructions.push(fillInstruction);
this.hitDetectionInstructions.push(fillInstruction);
}
if (stroke) {
this.instructions.push(strokeInstruction);
this.hitDetectionInstructions.push(strokeInstruction);
}
return offset;
}
drawCircle(circleGeometry, feature, index) {
const state = this.state;
const fillStyle = state.fillStyle;
const strokeStyle = state.strokeStyle;
if (fillStyle === undefined && strokeStyle === undefined) {
return;
}
this.setFillStrokeStyles_();
this.beginGeometry(circleGeometry, feature, index);
if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE,
defaultFillStyle,
]);
}
if (state.strokeStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle,
state.lineWidth,
state.lineCap,
state.lineJoin,
state.miterLimit,
defaultLineDash,
defaultLineDashOffset,
]);
}
const flatCoordinates = circleGeometry.getFlatCoordinates();
const stride = circleGeometry.getStride();
const myBegin = this.coordinates.length;
this.appendFlatLineCoordinates(
flatCoordinates,
0,
flatCoordinates.length,
stride,
false,
false
);
const circleInstruction = [CanvasInstruction.CIRCLE, myBegin];
this.instructions.push(beginPathInstruction, circleInstruction);
this.hitDetectionInstructions.push(beginPathInstruction, circleInstruction);
if (state.fillStyle !== undefined) {
this.instructions.push(fillInstruction);
this.hitDetectionInstructions.push(fillInstruction);
}
if (state.strokeStyle !== undefined) {
this.instructions.push(strokeInstruction);
this.hitDetectionInstructions.push(strokeInstruction);
}
this.endGeometry(feature);
}
drawPolygon(polygonGeometry, feature, index) {
const state = this.state;
const fillStyle = state.fillStyle;
const strokeStyle = state.strokeStyle;
if (fillStyle === undefined && strokeStyle === undefined) {
return;
}
this.setFillStrokeStyles_();
this.beginGeometry(polygonGeometry, feature, index);
if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE,
defaultFillStyle,
]);
}
if (state.strokeStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle,
state.lineWidth,
state.lineCap,
state.lineJoin,
state.miterLimit,
defaultLineDash,
defaultLineDashOffset,
]);
}
const ends = polygonGeometry.getEnds();
const flatCoordinates = polygonGeometry.getOrientedFlatCoordinates();
const stride = polygonGeometry.getStride();
this.drawFlatCoordinatess_(flatCoordinates, 0, ends, stride);
this.endGeometry(feature);
}
drawMultiPolygon(multiPolygonGeometry, feature, index) {
const state = this.state;
const fillStyle = state.fillStyle;
const strokeStyle = state.strokeStyle;
if (fillStyle === undefined && strokeStyle === undefined) {
return;
}
this.setFillStrokeStyles_();
this.beginGeometry(multiPolygonGeometry, feature, index);
if (state.fillStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_FILL_STYLE,
defaultFillStyle,
]);
}
if (state.strokeStyle !== undefined) {
this.hitDetectionInstructions.push([
CanvasInstruction.SET_STROKE_STYLE,
state.strokeStyle,
state.lineWidth,
state.lineCap,
state.lineJoin,
state.miterLimit,
defaultLineDash,
defaultLineDashOffset,
]);
}
const endss = multiPolygonGeometry.getEndss();
const flatCoordinates = multiPolygonGeometry.getOrientedFlatCoordinates();
const stride = multiPolygonGeometry.getStride();
let offset = 0;
for (let i = 0, ii = endss.length; i < ii; ++i) {
offset = this.drawFlatCoordinatess_(
flatCoordinates,
offset,
endss[i],
stride
);
}
this.endGeometry(feature);
}
finish() {
this.reverseHitDetectionInstructions();
this.state = null;
const tolerance = this.tolerance;
if (tolerance !== 0) {
const coordinates = this.coordinates;
for (let i = 0, ii = coordinates.length; i < ii; ++i) {
coordinates[i] = snap(coordinates[i], tolerance);
}
}
return super.finish();
}
}
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
# CanvasPolygonBuilder
类的构造函数
CanvasPolygonBuilder
类的构造函数比较通用,接受以下参数:
tolerance
:容差,通常用于指定绘制精度或容许的误差范围。maxExtent
:最大范围,用来限制图形的显示范围。resolution
:地图的分辨率。pixelRatio
:像素比率,用来处理不同屏幕或设备的显示效果。
# CanvasPolygonBuilder
类的主要方法
drawFlatCoordinatess_(flatCoordinates,offset,ends,stride)
方法
drawFlatCoordinatess_
方法是一个内部方法,画多边形会被调用,接受四个参数flatCoordinates
坐标数组、offset
起始位置、ends
多边形每条线段结束点的位置索引和stride
步幅;该方法主要的目的就是根据参数生成不同的指令,这些指令主要包括绘制指令:beginPathInstruction
路径绘制开始指令、moveToLineToInstruction
绘制路径指令、closePathInstruction
路径绘制结束指令以及样式指令:fillInstruction
填充样式指令和strokeInstruction
。简单来讲就是计算出多边形线段的起始点位置的索引,与指令相对应,在执行绘制过程中根据指令中的索引位置获取对应的坐标进行操作,然后根据样式指令进行装饰。
drawCircle(circleGeometry,feature,index)
方法
drawCircle
方法用于绘制圆形,接受三个参数:circleGeometry
圆形几何对象、feature
要素和index
索引;drawCircle
方法首先从this.state
中取样式state.fillStyle
和state.strokeStyle
,若它们未定义,则直接返回,不做任何操作;否则会调用this.setFillStrokeStyles_
内部方法,用于设置或新增和样式有关的指令;然后调用父类的beginGeometry
方法,生成绘制几何图形开始的指令,接着判断,若样式state.fillStyle
和state.strokeStyle
存在,则新增CanvasInstruction.SET_STROKE_STYLE
和CanvasInstruction.SET_FILL_STYLE
类型的指令;然后从circleGeometry
几何对象上获取坐标数据,调用this.appendFlatLineCoordinates
方法设置this.coordinates
,然后新增CanvasInstruction.CIRCLE
类型的指令,然后还是根据样式属性设置fillInstruction
和strokeInstruction
的指令,最后调用this.endGeometry
方法,生成绘制几何图形结束的指令。
drawPolygon(polygonGeometry,feature,index)
方法
drawPolygon
方法用于绘制多边形,大体步骤和drawCircle
方法类似,不同的是,绘制线段时drawPolygon
方法是调用this.drawFlatCoordinatess_
,与之相关的指令类型是beginPathInstruction
、CanvasInstruction.MOVE_TO_LINE_TO
和closePathInstruction
,而drawCircle
方法则是CanvasInstruction.CIRCLE
。
drawMultiPolygon(multiPolygonGeometry,feature,index)
方法
drawMultiPolygon
方法用于绘制多个多边形,无非就是获取多个多边形的坐标数据,循环调用this.drawFlatCoordinatess_
方法。
finish()
方法
finish
方法是在构建绘制指令集、碰撞检测指令集和样式指令集后调用,方法内部会先调用this.reverseHitDetectionInstructions
方法反转碰撞检测指令,然后判断,若容差精度this.tolerance
不为0
,则遍历this.coordinates
,对每一个坐标调用snap
方法,目的是保留拓扑结构的前提下,对多边形进行量化和点消除,以此简化多边形。最后调用父类的finish
方法。
setFillStrokeStyles_()
方法
setFillStrokeStyles_
方法用于设置填充样式和边框样式的指令,主要是调用父类的this.updateFillStyle
和this.updateStrokeStyle
方法。
# 总结
本文主要介绍了CanvasPolygonBuilder
类的实现,它是构建绘制圆和多边形指令集的核心类,在实际应用中会被实例化,然后调用实例对象的方法进行构建和获取指令集。