欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 培训 > panolens加载全景图片,二维坐标转换球型坐标显示绘制范围

panolens加载全景图片,二维坐标转换球型坐标显示绘制范围

2024/10/24 2:17:19 来源:https://blog.csdn.net/hzqzzz/article/details/143190891  浏览:    关键词:panolens加载全景图片,二维坐标转换球型坐标显示绘制范围

使用panolens结合Threejs,实现一个全景图中加载标注功能,其中标注数据是包含xy坐标的,经过转换后得到球型xyz坐标,与二维标注范围展示的一致,并且添加中心标注点,点击对应的点可以显示弹窗详情。

效果

在这里插入图片描述
在这里插入图片描述

1.显示全景图

//container 是默认的元素
let viewer_side = new PANOLENS.Viewer({container: document.getElementById("container"),output: 'console',autoHideInfospot: false});// img1是全景图地址,我这里直接使用的是全景图,这个也支持上传六个包含不同面的图片,详情去看官网const panorama = new PANOLENS.ImagePanorama(img1);viewer_side.add(panorama);

1.二维标注转球坐标

 	const panoramaWidth = 8192; // 以像素为单位const panoramaHeight = 4096; // 以像素为单位const radius = panoramaWidth / (2 * Math.PI);; // 球体的半径const infospotList = []  //标识列表// 将平面坐标转换为球面坐标function convertFlatCoordsToSphere(flatCoords, panoWidth, panoHeight, radius) {const sphereCoords = [];for (let i = 0; i < flatCoords.length; i += 2) {const x = flatCoords[i];const y = flatCoords[i + 1];const theta = (x / panoWidth) * Math.PI * 2;const phi = ((panoHeight - y) / panoHeight) * Math.PI;const px = radius * Math.sin(phi) * Math.cos(theta);const py = radius * Math.cos(phi);const pz = radius * Math.sin(phi) * Math.sin(theta);sphereCoords.push(new THREE.Vector3(px, py, pz));}return sphereCoords;}// 设置标识点自适应大小function calculateInfospotSize(points) {let maxDistance = 0;for (let i = 0; i < points.length; i++) {for (let j = i + 1; j < points.length; j++) {const distance = points[i].distanceTo(points[j]);if (distance > maxDistance) {maxDistance = distance;}}}return maxDistance * 0.2; // 调整系数来缩放大小// return 60}labelList.forEach(item => {const flatCoordinates = item.flatCoordinates// 将平面坐标转换为球面上的三维坐标const spherePoints = convertFlatCoordsToSphere(flatCoordinates, panoramaWidth, panoramaHeight, radius);// 创建一个形状const shape = new THREE.Shape();shape.moveTo(spherePoints[0].x, spherePoints[0].y);for (let i = 1; i < spherePoints.length; i++) {shape.lineTo(spherePoints[i].x, spherePoints[i].y);}// 使用THREE.js 创建矩形const geometry = new THREE.Geometry();geometry.vertices.push(...spherePoints);// 创建一个线框矩形const material = new THREE.LineBasicMaterial({ color: 0xff0000 });const line = new THREE.Line(geometry, material);// 将矩形添加到全景图中panorama.add(line);//----------------------------// 创建几何体和材质const shapeGeometry = new THREE.ShapeGeometry(shape);const shapeMaterial = new THREE.MeshBasicMaterial({color: 0xff0000,side: THREE.DoubleSide,transparent: true,opacity: 0.5});const shapeMesh = new THREE.Mesh(shapeGeometry, shapeMaterial);// 将多边形添加到全景图中panorama.add(shapeMesh);// 计算多边形在平面上的中心点let centerX = 0, centerY = 0;for (let i = 0; i < flatCoordinates.length; i += 2) {centerX += flatCoordinates[i];centerY += flatCoordinates[i + 1];}centerX /= flatCoordinates.length / 2;centerY /= flatCoordinates.length / 2;// 将平面中心点转换为球面坐标const phi = (1 - centerY / panoramaHeight) * Math.PI;const theta = (centerX / panoramaWidth) * Math.PI * 2;const x = -radius * Math.sin(phi) * Math.cos(theta);const y = radius * Math.cos(phi);const z = radius * Math.sin(phi) * Math.sin(theta);// 创建标识点(Infospot)const infospotSize = calculateInfospotSize(spherePoints); // 计算适合的大小const infospot = new PANOLENS.Infospot(infospotSize, PANOLENS.DataImage.Info, false);infospot.labelId = item.idinfospot.position.set(x, y, z);infospotList.push(infospot)// 创建悬停元素const hoverElement = document.createElement('div');hoverElement.className = 'labelBox'hoverElement.style.backgroundColor = '#fff'; // 设置背景色为红色hoverElement.style.padding = '10px'; // 设置内边距hoverElement.style.borderRadius = '5px'; // 设置圆角// hoverElement.style.position = 'absolute'; // 设置元素位置模式为绝对定位// hoverElement.style.bottom = '0'; // 将元素从下方推离一定距离// hoverElement.style.right = '0'; // 从右侧推离一定距离// 将悬停元素添加到Infospot上infospot.addHoverElement(hoverElement, 150); // 第一个参数是悬停元素,第二个参数是悬停持续时间infospot.lookAt(new THREE.Vector3(0, 0, 0));  // 确保法线方向正确infospot.addHoverText('');// 将标识点添加到全景图中panorama.add(infospot);// 添加点击事件infospot.addEventListener('click', function (e) {const labelBoxList = document.getElementsByClassName("labelBox")// 当标注被点击时,隐藏所有标注for (let i = 0; i < labelBoxList.length; i++) {labelBoxList[i].style.display = 'none';}e.target.element.style.display = 'block'// e.target.element.innerText('112131321')// 获取对应标注详情const resultLabel = labelList.find(item => item.id === e.target.labelId)console.log(resultLabel.svgID)});})

其中labelList标注数据为:

labelList = [{"isClickDelete": false,"color": "#EC27E6","isClickPos": false,"flags": {},"points": [[3642,2561],[3633,2572],[3631,2580],[3631,2635],[3634,2642],[3641,2649],[3647,2653],[3662,2660],[3664,2660],[3690,2673],[3699,2681],[3703,2688],[3706,2697],[3706,2703],[3707,2704],[3707,2733],[3710,2742],[3716,2748],[3723,2752],[3752,2761],[3763,2766],[3780,2769],[3785,2771],[3796,2770],[3821,2755],[3826,2750],[3831,2741],[3833,2734],[3835,2715],[3837,2709],[3837,2693],[3836,2692],[3835,2670],[3834,2669],[3833,2631],[3832,2630],[3832,2623],[3831,2622],[3830,2613],[3826,2605],[3817,2596],[3802,2588],[3787,2584],[3771,2583],[3770,2582],[3765,2582],[3764,2581],[3759,2581],[3758,2580],[3753,2580],[3752,2579],[3747,2579],[3746,2578],[3732,2576],[3702,2561],[3691,2557],[3675,2555],[3674,2554],[3658,2554],[3649,2557],[3642,2561]],"isClickLock": false,"labelId": "3d256ffc-3772-4b95-b9a8-620bc628dba2","isHoverDelete": false,"id": "3d256ffc-3772-4b95-b9a8-620bc628dba2","isHoverShow": false,"flatCoordinates": [3642,1535,3633,1524,3631,1516,3631,1461,3634,1454,3641,1447,3647,1443,3662,1436,3664,1436,3690,1423,3699,1415,3703,1408,3706,1399,3706,1393,3707,1392,3707,1363,3710,1354,3716,1348,3723,1344,3752,1335,3763,1330,3780,1327,3785,1325,3796,1326,3821,1341,3826.0000000000005,1346,3831,1355,3832.9999999999995,1362,3834.9999999999995,1381,3836.9999999999995,1387,3836.9999999999995,1403,3836,1404,3834.9999999999995,1426,3834,1427,3832.9999999999995,1465,3832,1466,3832,1473,3831,1474,3830.0000000000005,1483,3826.0000000000005,1491,3817,1500,3802,1508,3787,1512,3771,1513,3770,1514,3765,1514,3764,1515,3759,1515,3758,1516,3753,1516,3752,1517,3747,1517,3746,1518,3732,1520,3702,1535,3691,1539,3675,1541,3674,1542,3658,1542,3649,1539,3642,1535],"imageWidth": 8192,"level": 2,"isHoverPos": false,"label": "2","isHoverLock": false,"stroke": "#0c1af4","isClickDefault": false,"imageHeight": 4096,"labelColor": "#0c1af4","shapeType": "polygon","createBy": "3fa85f64-5717-4562-b3fc-2c963f66afa6","svgID": "93f9cf5b-a8ab-4b50-8572-f692f7a246b7","createTime": "2024-08-09 11:31:04","strokeBeforeColor": "#0c1af4","name": "2","shape_type": "polygon","taskId": "94df30c5-e196-4bcf-b7df-1ea94d627baf"},
]

在上面代码中,核心代码是convertFlatCoordsToSphere方法,根据球型的宽高,结合一定运算得出z轴坐标,使用threejsVector3去保存坐标,因为panolens本就是集成的threejs,所以可以通用。

基本流程如下:

1.使用convertFlatCoordsToSphere计算坐标

// 创建一个形状const shape = new THREE.Shape();shape.moveTo(spherePoints[0].x, spherePoints[0].y);for (let i = 1; i < spherePoints.length; i++) {shape.lineTo(spherePoints[i].x, spherePoints[i].y);}// 使用THREE.js 创建矩形const geometry = new THREE.Geometry();geometry.vertices.push(...spherePoints);// 创建一个线框矩形const material = new THREE.LineBasicMaterial({ color: 0xff0000 });const line = new THREE.Line(geometry, material);// 将矩形添加到全景图中panorama.add(line);

2.创建Shape材质绘制形状,使用线条连接起来,使用Geometry创建矩形
3.使用panorama的add方法追加到球体中显示标注

不足:无法设置中间内容的透明填充色,没找到合适的方法

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com