forked from SuperMap/iClient-JavaScript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVideoMap.js
More file actions
307 lines (292 loc) · 8.98 KB
/
VideoMap.js
File metadata and controls
307 lines (292 loc) · 8.98 KB
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
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
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
import mapboxgl from 'mapbox-gl';
import CoordTransfer from './CoordTransfer';
import VideoMapLayer from './layers/VideoMapLayer';
import GeojsonLayer from './layers/GeojsonLayer';
import { transformCoordReverse, fovXToFx, fovYToFy } from './utils/VideoMapUtil';
import GeojsonSource from './GeojsonSource';
const MAP_EVENTS = [
'resize',
'webglcontextlost',
'webglcontextrestored',
'remove',
'movestart',
'load',
'contextmenu',
'dblclick',
'click',
'touchcancel',
'touchmove',
'touchend',
'touchstart',
'dataloading',
'mousemove',
'mouseup',
'mousedown',
'sourcedataloading',
'error',
'data',
'styledata',
'sourcedata',
'mouseout',
'styledataloading',
'moveend',
'move',
'render',
'zoom',
'zoomstart',
'zoomend',
'boxzoomstart',
'boxzoomcancel',
'boxzoomend',
'rotate',
'rotatestart',
'rotateend',
'dragend',
'drag',
'dragstart',
'pitch',
'idle'
];
/**
* @class VideoMap
* @classdesc 视频地图
* @category Visualization Video
* @version 11.2.0
* @modulecategory Mapping
* @param {Object} options - 参数
* @param {string} options.url - 视频 或 流链接。支持 flv, m3u8, map4 格式。
* @param {string} options.videoParameters - 视频地图配准参数
* @param {number} options.videoParameters.pitch - 俯仰角。
* @param {number} options.videoParameters.roll - 侧偏角。
* @param {number} options.videoParameters.yaw - 偏航角。
* @param {number} options.videoParameters.x - 视频 x 坐标。
* @param {number} options.videoParameters.y - 视频 y 坐标。
* @param {number} options.videoParameters.z - 视频 z 坐标。
* @param {number} options.videoParameters.fovX - 水平方向上以像素为单位的焦距。
* @param {number} options.videoParameters.fovY - 垂直方向上以像素为单位的焦距。
* @param {number} options.videoParameters.centerX - 相机中心的水平坐标。
* @param {number} options.videoParameters.centerY - 相机中心的垂直坐标。
* @param {string} [options.container='map'] - 地图容器id
* @param {string} [options.opencv] - opencv 实例
* @param {function} [options.videoWidth] - 视频地图宽度,没设置时默认获取视频宽度
* @param {function} [options.videoHeight] - 视频地图高度,没设置时默认获取视频高度
* @param {Object} [options.styleOptions] - 视频地图风格配置
* @param {string} [options.autoplay=true] - 视频是否自动播放
* @param {string} [options.loop=true] - 视频是否循环播放
* @extends {mapboxgl.Evented}
* @usage
*```
* // 浏览器
* <script type="text/javascript" src="{cdn}"></script>
* <script type="text/javascript" src="https://iclient.supermap.io/web/libs/opencv/3.4/opencv.js"></script>
* <script>
* new {namespace}.VideoMap(options);
*
* </script>
*
* // ES6 Import
* import { VideoMap } from "{npm}";
* // 将上面 opencv 源码拷贝到本地路径引用
* options.opencv = "your opencv path";
* new VideoMap(options);
* ```
*/
export class VideoMap extends mapboxgl.Evented {
constructor(options) {
super();
const { container, url, videoParameters, autoplay, loop, videoWidth, videoHeight, opencv, styleOptions } = options;
this.container = container || 'map';
this.layerCache = {};
this.sourceCache = {};
this.videoWidth = videoWidth;
this.videoHeight = videoHeight;
this.autoplay = autoplay !== undefined ? autoplay : true;
this.loop = loop !== undefined ? loop : true;
this.styleOptions = styleOptions || {};
this.cv = opencv || window.cv;
if (!this.cv) {
throw new Error('opencv.js instance is not existed!');
}
if (!videoParameters) {
throw new Error('videoParameters must be config!');
}
this.videoParameters = videoParameters;
this._createMap().then((map) => {
this.map = map;
this._addVideoLayer(url);
});
}
/**
* @function VideoMap.prototype.addLayer
* @description 添加图层。
* @param {Object} layer - 图层配置。
* @param {string} layer.id - 图层 id
* @param {string} layer.type - 图层类型
* @param {string|Object} layer.source - 数据源配置
* @param {Array} [layer.filter] - 过滤配置
* @param {Object} [layer.layout] - 布局配置
* @param {Object} [layer.paint] - 绘制配置
* @param {number} [layer.maxzoom] - 最大级别
* @param {number} [layer.minzoom] - 最小级别
* @param {string} beforeId - 已经存在的图层 ID。
*/
addLayer(layer, beforeId) {
if (!this._mapExisted()) {
return;
}
if (this.layerCache[layer.id]) {
return;
}
const currentLayer = new GeojsonLayer(this);
currentLayer.add(layer, beforeId);
this.layerCache[layer.id] = currentLayer;
}
/**
* @function VideoMap.prototype.addSource
* @description 添加数据源。
* @param {string} id - 数据源 id。
* @param {Object} source - 图层源配置。
* @param {string} source.type - 只支持 geojson
* @param {Object} source.data - geojson 数据。
*/
addSource(id, source) {
if (!this._mapExisted()) {
return;
}
if (this.sourceCache[id]) {
return;
}
const geojsonSource = new GeojsonSource(this);
geojsonSource.add(id, source);
this.sourceCache[id] = geojsonSource;
}
/**
* @function VideoMap.prototype.removeLayer
* @description 移除图层。
* @param {string} id - 图层 id。
*/
removeLayer(id) {
if (!this._mapExisted()) {
return;
}
if (this.layerCache[id]) {
this.layerCache[id].remove();
this.layerCache[id] = null;
delete this.layerCache[id];
}
}
/**
* @function VideoMap.prototype.removeSource
* @description 移除数据源。
* @param {string} id - 数据源 id。
*/
removeSource(id) {
if (!this._mapExisted()) {
return;
}
if (this.sourceCache[id]) {
this.sourceCache[id].remove();
this.sourceCache[id] = null;
delete this.sourceCache[id];
}
}
/**
* @function VideoMap.prototype.destroy
* @description 销毁视频地图。
*/
destroy() {
this.layerCache = {};
this.sourceCache = {};
if (this.videoMapLayer) {
this.videoMapLayer.remove();
this.videoMapLayer = null;
}
this.map.remove();
this.map = null;
}
_addVideoLayer(src) {
this.videoMapLayer = new VideoMapLayer(this);
this.videoMapLayer.add(src);
this._bindEvents();
}
_initParameters(parameters) {
if (parameters && !Object.keys(parameters).length) {
return;
}
parameters.fx = fovXToFx(parameters.fovX, this.videoWidth);
parameters.fy = fovYToFy(parameters.fovY, this.videoHeight);
return new CoordTransfer(this.cv, parameters).init();
}
_createMap() {
return new Promise((resolve) => {
const container =
typeof this.container === 'string' ? window.document.getElementById(this.container) : this.container;
if (!container) {
throw new Error(`Container '${container}' not found.`);
}
this.width = container.clientWidth || 400;
this.height = container.clientHeight || 300;
let map = new mapboxgl.Map({
container: this.container,
style: {
...this.styleOptions,
version: 8,
sources: {},
layers: []
},
renderWorldCopies: false,
center: [0, 0],
zoom: 8
});
map.on('load', () => {
resolve(map);
});
});
}
_mapExisted() {
return !!this.map;
}
_bindEvents() {
this.videoMapLayer.on('loaded', async ({ originCoordsLeftTop, originCoordsRightBottom, videoWidth, videoHeight }) => {
this.originCoordsLeftTop = originCoordsLeftTop;
this.originCoordsRightBottom = originCoordsRightBottom;
if (this.videoWidth === undefined) {
this.videoWidth = videoWidth;
}
if (this.videoHeight === undefined) {
this.videoHeight = videoHeight;
}
this.coordTransfer = await this._initParameters(this.videoParameters);
this._bindMapEventFn = this._bindMapEvent.bind(this);
MAP_EVENTS.forEach((eventName) => {
this.map.on(eventName, this._bindMapEventFn);
});
this.fire('load', { map: this.map });
});
}
_clearEvents() {
MAP_EVENTS.forEach((eventName) => {
this.map.off(eventName, this._bindMapEventFn);
});
}
_bindMapEvent(e) {
if (e.lngLat) {
if (this.originCoordsRightBottom && this.originCoordsLeftTop && this.videoWidth && this.videoHeight) {
let coord = [e.lngLat.lng, e.lngLat.lat];
if (this.coordTransfer) {
let spatialPoint = this.coordTransfer.toSpatialCoordinate(
transformCoordReverse({
coord,
originCoordsRightBottom: this.originCoordsRightBottom,
originCoordsLeftTop: this.originCoordsLeftTop,
videoHeight: this.videoHeight,
videoWidth: this.videoWidth
})
);
e.spatialPoint = [spatialPoint[0], spatialPoint[1]];
}
}
}
this.fire(e.type, { mapEvent: e });
}
}