Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 35e5063

Browse files
mapboxgl 视频图层支持加载非定点数据
1 parent 592a544 commit 35e5063

File tree

9 files changed

+687
-124
lines changed

9 files changed

+687
-124
lines changed

‎examples/locales/en-US/resources.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,7 @@ window.examplesResources = {
738738
"title_dataAttributes": "Attributes",
739739
"title_videoLayer":"Video Layer",
740740
"title_videoMap":"Video Map",
741+
"title_uavVideo":"Uav Video",
741742
"title_knowledgeGraphMap": "KnowledgeGraph",
742743
"title_l7_grid_map": "Grid Map",
743744
"title_l7_circular_sweeping_city": "Circular Sweeping City",

‎examples/locales/zh-CN/resources.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,7 @@ window.examplesResources = {
713713
"text_selectEndNode": "捕捉结束节点",
714714
"title_videoLayer":"视频图层",
715715
"title_videoMap":"视频地图",
716+
"title_uavVideo":"无人机视频",
716717
"title_knowledgeGraphMap": "知识图谱",
717718
"title_l7_grid_map": "网格地图",
718719
"title_l7_circular_sweeping_city": "圆形扫光城市",

‎examples/mapboxgl/config.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,6 +1875,13 @@ var exampleConfig = {
18751875
version: '11.2.0',
18761876
thumbnail: 'videoMap.png',
18771877
fileName: 'videoMap'
1878+
},
1879+
{
1880+
name: '无人机视频',
1881+
name_en: 'UAV video',
1882+
version: '11.2.0',
1883+
thumbnail: 'videoLayerWithTime.png',
1884+
fileName: 'videoLayerWithTime'
18781885
}
18791886
]
18801887
}
121 KB
Loading[フレーム]
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<!--********************************************************************
2+
* Copyright© 2000 - 2025 SuperMap Software Co.Ltd. All rights reserved.
3+
*********************************************************************-->
4+
<!--********************************************************************
5+
* 该示例需要引入
6+
* mapbox-gl-enhance (https://iclient.supermap.io/web/libs/mapbox-gl-js-enhance/1.12.1-10/mapbox-gl-enhance.js)
7+
* Turf (https://github.com/Turfjs/turf/)
8+
* proj4js (https://github.com/proj4js/proj4js)
9+
* video.js (https://github.com/videojs/video.js)
10+
* opencv (https://github.com/opencv/opencv)
11+
*********************************************************************-->
12+
<!DOCTYPE html>
13+
<html>
14+
15+
<head>
16+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
17+
<title data-i18n="resources.title_uavVideo"></title>
18+
<style type="text/css">
19+
body {
20+
margin: 0;
21+
padding: 0;
22+
}
23+
24+
#map {
25+
position: absolute;
26+
top: 0;
27+
bottom: 0;
28+
width: 100%;
29+
}
30+
</style>
31+
</head>
32+
33+
<body>
34+
<div id="map"></div>
35+
<script type="text/javascript" include="bootstrap" src="../js/include-web.js"></script>
36+
<script type="text/javascript" include="mapbox-gl-enhance,proj4,turf,videojs,opencv"
37+
src="../../dist/mapboxgl/include-mapboxgl.js"></script>
38+
<script type="text/javascript">
39+
var map, videoLayer;
40+
var attribution =
41+
"<a href='https://www.mapbox.com/about/maps/' target='_blank'>© Mapbox </a>" +
42+
" with <span>© <a href='https://iclient.supermap.io' target='_blank'>SuperMap iClient</a> | </span>" +
43+
" Map Data <span>© <a href='http://support.supermap.com.cn/product/iServer.aspx' target='_blank'>SuperMap iServer</a></span> ";
44+
var dataUrl = 'https://iserver.supermap.io/iserver/services/data-video/rest/data';
45+
var datasourceName = 'VideoData';
46+
var datasetName = 'DJI_09304';
47+
var map = new mapboxgl.Map({
48+
container: 'map',
49+
renderWorldCopies: false,
50+
style: {
51+
version: 8,
52+
sources: {
53+
'raster-tiles': {
54+
type: 'raster',
55+
tileSize: 256,
56+
tiles: [
57+
'https://t4.tianditu.gov.cn/img_w/wmts?service=WMTS&request=GetTile&version=1.0.0&style=default&tilematrixSet=w&format=tiles&width=256&height=256&layer=img&tilematrix={z}&tilerow={y}&tilecol={x}&tk=1d109683f4d84198e37a38c442d68311'
58+
]
59+
}
60+
},
61+
layers: [
62+
{
63+
id: 'simple-tiles',
64+
type: 'raster',
65+
source: 'raster-tiles',
66+
minzoom: 0,
67+
maxzoom: 22
68+
}
69+
]
70+
},
71+
center: [104.0685393316582, 30.491810619411483],
72+
zoom: 17
73+
});
74+
map.addControl(new mapboxgl.NavigationControl(), 'top-left');
75+
map.addControl(new mapboxgl.supermap.LogoControl({ link: 'https://iclient.supermap.io' }), 'bottom-right');
76+
function query() {
77+
var sqlParam = new mapboxgl.supermap.GetFeaturesBySQLParameters({
78+
queryParameter: {
79+
name: datasetName + '@' + datasourceName,
80+
attributeFilter: 'SMID > 0'
81+
},
82+
datasetNames: [datasourceName + ':' + datasetName]
83+
});
84+
85+
new mapboxgl.supermap.FeatureService(dataUrl).getFeaturesBySQL(sqlParam).then(function (serviceResult) {
86+
const timeData = [];
87+
serviceResult.result.features.features[0].properties.videoParameters.videoParameterList.forEach((param) => {
88+
let coordinates1 = [];
89+
param.extent.points.forEach((coord) => {
90+
const res = proj4('EPSG:3857', 'EPSG:4326', coord);
91+
coordinates1.push(res);
92+
});
93+
let cameraLocation = param.calibrationModel.cameraLocation;
94+
let calibrationModel = param.calibrationModel;
95+
timeData.push({
96+
time: param.time,
97+
extent: [coordinates1[3], coordinates1[2], coordinates1[1], coordinates1[0]],
98+
fovX: calibrationModel.fovX,
99+
fovY: calibrationModel.fovY,
100+
centerX: calibrationModel.centerX,
101+
centerY: calibrationModel.centerY,
102+
pitch: cameraLocation.cameraPitch,
103+
roll: cameraLocation.cameraRoll,
104+
yaw: cameraLocation.cameraYaw,
105+
x: cameraLocation.cameraX,
106+
y: cameraLocation.cameraY,
107+
z: cameraLocation.cameraZ
108+
});
109+
});
110+
let coordinates = [];
111+
turf.featureEach(serviceResult.result.features, function (currentFeature, featureIndex) {
112+
currentFeature.geometry.coordinates[0][0].forEach((coord) => {
113+
const res = proj4('EPSG:3857', 'EPSG:4326', coord);
114+
coordinates.push(res);
115+
});
116+
});
117+
var url = serviceResult.result.features.features[0].properties.address;
118+
var cameraLocation = serviceResult.result.features.features[0].properties.cameraLocation;
119+
var calibrationModel =
120+
serviceResult.result.features.features[0].properties.videoParameters.videoParameterList[0].calibrationModel;
121+
var clipRegionPoints =
122+
serviceResult.result.features.features[0].properties.videoParameters.videoParameterList[0].clipRegion
123+
.points;
124+
videoLayer = new mapboxgl.supermap.VideoLayer({
125+
url,
126+
videoParameters: timeData,
127+
clipRegion: [
128+
[clipRegionPoints[0].x, clipRegionPoints[0].y],
129+
[clipRegionPoints[1].x, clipRegionPoints[1].y],
130+
[clipRegionPoints[2].x, clipRegionPoints[2].y],
131+
[clipRegionPoints[3].x, clipRegionPoints[3].y]
132+
]
133+
});
134+
map.addLayer(videoLayer);
135+
});
136+
}
137+
map.on('load', function () {
138+
query();
139+
});
140+
</script>
141+
</body>
142+
143+
</html>

‎src/common/overlay/video/VideoLayerRenderer.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,14 @@ export class VideoLayerRenderer {
5757
muted: true,
5858
loop: this.loop
5959
};
60-
if (this.url.includes('mp4')) {
60+
if (this.url.includes('mp4')||this.url.includes('MP4')) {
6161
options['sources'] = [
6262
{
6363
src: this.url,
6464
type: 'video/mp4'
6565
}
6666
];
67-
} else if (this.url.includes('flv')) {
67+
} else if (this.url.includes('flv')||this.url.includes('FLV')) {
6868
options = {
6969
loop: this.loop,
7070
autoplay: this.autoplay,
@@ -84,7 +84,7 @@ export class VideoLayerRenderer {
8484
}
8585
]
8686
};
87-
} else if (this.url.includes('m3u8')) {
87+
} else if (this.url.includes('m3u8')||this.url.includes('M3U8')) {
8888
options['sources'] = [
8989
{
9090
src: this.url

‎src/mapboxgl/mapping/utils/VideoMapUtil.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,133 @@ export const fovXToFx = (fovX, videoWidth) => {
2828

2929
export const fovYToFy = (fovY, videoHeight) => {
3030
return videoHeight / (2 * Math.tan(fovY / 2 * Math.PI / 180));
31+
}
32+
33+
/**
34+
* @private
35+
*/
36+
37+
export class FastRangeSearcher {
38+
constructor(sortedArray) {
39+
this.data = sortedArray.map(n => Math.round(n * 1000));
40+
}
41+
42+
findNearest(target) {
43+
const targetInt = Math.round(target * 1000);
44+
const minVal = targetInt;
45+
const maxVal = targetInt + 3000;
46+
47+
let closestIndex = -1;
48+
let minDiff = Infinity;
49+
50+
for (let i = 0; i < this.data.length; i++) {
51+
const current = this.data[i];
52+
53+
if (current > maxVal) {
54+
break
55+
}
56+
57+
if (current >= minVal) {
58+
const diff = current - targetInt;
59+
if (diff < minDiff) {
60+
minDiff = diff;
61+
closestIndex = i;
62+
}
63+
}
64+
}
65+
66+
if (closestIndex !== -1) {
67+
return {
68+
value: +(this.data[closestIndex] / 1000).toFixed(3)
69+
};
70+
}
71+
72+
return null;
73+
}
74+
}
75+
76+
/**
77+
* @private
78+
* @param {Object} params 配置参数
79+
* @param {number} params.interval 目标时间间隔(秒,≥0.001)
80+
* @param {Array} params.data 时间序列数据
81+
* @returns {Array} 处理结果
82+
*/
83+
export function smartTimeProcessor(interval, data, properties = []) {
84+
if (interval < 0.001) {
85+
throw new Error("time interval can't' less than 0.001");
86+
}
87+
if (!data || data.length === 0) {
88+
return [];
89+
}
90+
91+
const processed = data.map(p => ({
92+
...p,
93+
time: Math.round(p.time * 1000)
94+
}));
95+
96+
// 计算最小原始间隔和有效时间范围
97+
let minInterval = Infinity;
98+
for (let i = 1; i < processed.length; i++) {
99+
minInterval = Math.min(minInterval, processed[i].time - processed[i-1].time);
100+
}
101+
const targetInterval = Math.round(interval * 1000);
102+
const [startMs, endMs] = [
103+
processed[0].time,
104+
processed[processed.length - 1].time
105+
];
106+
107+
if (targetInterval === minInterval) {
108+
return data;
109+
}
110+
111+
const useInterpolation = targetInterval < minInterval;
112+
113+
let result = [];
114+
let dataPtr = 0;
115+
116+
for (let t = startMs; t <= endMs; t += targetInterval) {
117+
while (dataPtr < processed.length - 1 && processed[dataPtr + 1].time < t) {
118+
dataPtr++;
119+
}
120+
121+
const current = processed[dataPtr];
122+
const next = processed[dataPtr + 1] || current;
123+
124+
if (useInterpolation) {
125+
const ratio = next.time === current.time ? 0 : (t - current.time) / (next.time - current.time);
126+
let res = {};
127+
properties.forEach(prop => {
128+
if (prop === 'extent') {
129+
res[prop] = current[prop].map((item, index) => {
130+
return [current[prop][index].x + (next[prop][index].x - current[prop][index].x) * ratio, current[prop][index].y + (next[prop][index].y - current[prop][index].y) * ratio];
131+
});
132+
} else {
133+
const value = current[prop] + (next[prop] - current[prop]) * ratio;
134+
res[prop] = +value;
135+
}
136+
});
137+
result.push({
138+
...current,
139+
time: +((t / 1000).toFixed(3)),
140+
...res
141+
});
142+
} else {
143+
if (current.time === t) {
144+
result.push(formatPoint(current));
145+
dataPtr++;
146+
} else if (next.time === t) {
147+
result.push(formatPoint(next));
148+
dataPtr++;
149+
}
150+
}
151+
}
152+
return result;
153+
}
154+
155+
function formatPoint(point) {
156+
return {
157+
...point,
158+
time: +(point.time / 1000).toFixed(3)
159+
};
31160
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /