import * as THREE from "../../libs/three.js/build/three.module.js"; import math from './utils/math.js' import browser from './utils/browser.js' import './three.shim.js' import "./potree.shim.js" window.THREE = THREE let baseZ = 0 //所以数据集高度都要减去这个值。在laser场景里该值为初始数据集的高程 Potree.datasetData = [] var transformPointcloud = (pointcloud, dataset)=>{ var locationLonLat = dataset.location.slice(0,3) // [lon,lat,高程海拔] //当只有一个dataset时,无论如何transform 点云和漫游点都能对应上。 var location = viewer.transform.lonlatToLocal.forward(locationLonLat) //transform.inverse() //初始化位置 viewer.sidebar && viewer.sidebar.addAlignmentButton(pointcloud) //dataset.orientation = 0 let Alignment = viewer.modules.Alignment Alignment.rotate(pointcloud, null, dataset.quaternion?.length == 4 ? dataset.quaternion : dataset.orientation) Alignment.translate(pointcloud, new THREE.Vector3(location[0], location[1], dataset.location[2]-baseZ)) //要使初始数据集的z为0,所以要减去初始数据集的z pointcloud.updateMatrixWorld() Potree.Log(`点云${pointcloud.dataset_id}(${pointcloud.name})旋转值: qua ${dataset.quaternion||''}、orient ${ math.toPrecision(dataset.orientation,3) }, 位置${math.toPrecision(pointcloud.translateUser.toArray(),3)}, 经纬度 ${dataset.location}, spacing ${pointcloud.material.spacing}`,{font:{color:"#f49",fontSize:13}} ) } export function start(dom, mapDom, number ){ //t-Zvd3w0m /* { let obj = JSON.parse(localStorage.getItem('setting')) for(let i in obj){ console.log(i + ': ' + obj[i]) } } */ console.log('2024.9') Potree.settings.number = number || 't-o5YMR13'// 't-iksBApb'// 写在viewer前 if(!Potree.settings.isOfficial){ if(/* Potree.settings.isTest && */ browser.isMobile()){ changeLog() } } if(browser.urlHasValue('google'))Potree.settings.mapCompany = 'google' if(browser.urlHasValue('timing'))Potree.measureTimings = 1 let viewer = new Potree.Viewer(dom , mapDom); let Alignment = viewer.modules.Alignment viewer.setEDLEnabled(false); viewer.setFOV(Potree.config.view.fov); //viewer.loadSettingsFromURL(); Potree.settings.cameraFar = Potree.config.view.far if(!Potree.settings.isOfficial){ viewer.loadGUI(() => { viewer.setLanguage('en'); //$("#menu_appearance").next().show(); $("#menu_tools").next().show(); $("#menu_scene").next().show(); $("#siteModel").show(); //$("#alignment").show(); viewer.toggleSidebar(); }); Potree.settings.sizeFitToLevel = true//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2 Potree.settings.rotAroundPoint = false } Potree.loadDatasetsCallback = function(data, ifReload){ if(!data || data.length == 0)return console.error('getDataSet加载的数据为空') Potree.datasetData = data viewer.transform = null var datasetLength = data.length var pointcloudLoaded = 0 var panosLoaded = 0 var pointcloudLoadDone = function(){//点云cloud.js加载完毕后 } var panosLoadDone = function(){ viewer.images360.loadDone() viewer.scene.add360Images(viewer.images360); viewer.mapViewer.addListener(viewer.images360) viewer.updateModelBound() //需等pano加载完 let {boundSize, center} = viewer.bound if(!Potree.settings.isOfficial){ Potree.loadMapEntity('all') //加载floorplan } Potree.loadNeighborFile() Potree.setNeighborGui(); if(!ifReload){ viewer.dispatchEvent({type:'loadPointCloudDone'}) if(!Potree.settings.UserPointDensity){ Potree.settings.UserPointDensity = 'high'//'middle' } Potree.Log('loadPointCloudDone 点云加载完毕' ) } {//初始位置 var panoId = browser.urlHasValue('pano',true); if(panoId !== ''){ var pos var pano = viewer.images360.panos.find(e=>e.id==panoId); if(pano){ viewer.images360.focusPano({ pano, duration:0, callback:()=>{/* Potree.settings.displayMode = 'showPanos' */} }) } }else{//考虑到多数据集距离很远,或者像隧道那种场景,要使视野范围内一定能看到点云,最好初始点设置在漫游点上 let {boundSize, center} = viewer.bound let pano = viewer.images360.findNearestPano(center) if(pano){ viewer.images360.flyToPano({ pano, duration:0, target : viewer.images360.bound.center.setZ(pano.position.z) //平视中心区域(但也不能保证这个方向一定能看到点云密集区,如果在边缘的话) }) }else{// 无漫游点 避免加载时看不到点云 SG-t-DXmdymgZ2sX SG-t-rVB03a5GXr8 //SG-t-XPf1k9pv3Zg 总有极端的场景,如这是倾斜的桥 - -, 只能调整为看到全局了 viewer.mainViewport.view.pitch = -0.7 //相对俯视 let bound = viewer.bound.boundingBox.clone() viewer.focusOnObject({boundingBox:bound},'boundingBox',0,{dontChangeCamDir:true}) if(viewer.bound.boundSize.length() > 20){ //否则有可能超出far范围 viewer.mainViewport.camera.far = 10000; viewer.mainViewport.camera.updateProjectionMatrix() viewer.fixCamFar = true //不允许修改camera.far //等有点云加载出来后,再去focus其中一个,使camera.far不超过最大值 let count_ = 0; let done = ()=>{ viewer.fixCamFar = false viewer.mainViewport.camera.far = Potree.settings.cameraFar; viewer.mainViewport.camera.updateProjectionMatrix() viewer.removeEventListener('pageVisible', focusPoint ) clearTimeout(timer) } let timer let focusPoint = (e)=>{//拉近到某个点 if(e && e.v === false)return viewer.removeEventListener('pageVisible', focusPoint ) let pointcloud = viewer.scene.pointclouds.find(e=>e.root.geometryNode) console.log('初始加载focus点云', e , pointcloud) if(!pointcloud){ if(count_ < 10 ){//可能没加载到,可能被隐藏 if(document.hidden){//等回到页面再focus console.log('focus hidden') return viewer.addEventListener('pageVisible', focusPoint ) } count_ ++ //如果在别的 timer = setTimeout(focusPoint, 200) }else{ //放弃 console.log('初始加载focus点云 放弃') done() } return console.warn('no!!!!!!!!!!!!!!') } viewer.flyToDataset({focusOnPoint:true, pointcloud, duration:0, }) console.warn('ok!!!!!!!!!!!!!!!!') done() } let focus = ()=>{ timer = setTimeout(focusPoint, 300) } viewer.addEventListener('setPose',()=>{//设置了初始画面 viewer.removeEventListener('pointcloud_changed',focus) done() }) viewer.addEventListener('pointcloud_changed',focus,{once:true})//加载了点之后 } } } } viewer.addVideo()//addFire() viewer.scene.pointclouds.some(e=>!e.hasDepthTex) && (Potree.config.shelterMargin*=3) //没有深度图的话在全景模式测量有偏差 console.log('allLoaded') viewer.addTube({ 't-8KbK1JjubE':[ {//地上管子 黄色 path:[{"x":-109.83,"y":-68.33,"z":-7.52},{"x":-95.17,"y":-59.3,"z":-7.38}, {"x":-38.75,"y":-24.01,"z":-6.01},{"x":0.5,"y":0.19,"z":-3.89},{"x":39.29,"y":24.41,"z":-1.31} ,{"x":43.58,"y":27.7,"z":-0.97},{"x":40.22,"y":35.37,"z":-0.67}// 拐弯向右 ,{"x":39.18,"y":36.71,"z":0.35},{"x":38.69,"y":36.04,"z":18.04} // 拐弯向上 } ], color:'#b86', radius:0.08, height:0.13, tension:0.3, lessSpace:60 }, {//地下管子 藍色 path:[{"x":-108.24,"y":-70.61,"z":-7.52}, {"x":-57.8,"y":-39.31,"z":-6.72},{"x":-18.8,"y":-15.35,"z":-5.01},{"x":55.87,"y":31.67,"z":-0.04},{"x":110.53,"y":66.48,"z":5.14} ], color:'#48a', radius:0.08, height:-0.5, lessSpace:60 } ] , /* 'SG-t-h0I7LnfGFwj':[ //港华M层 { path:[[20.295,-10.269,-1.152],[19.996,-10.252,-1.153],[19.98,-10.253,-1.156],[19.967,-10.252,-1.165],[19.957,-10.251,-1.179],[19.953,-10.251,-1.225],[19.961,-10.242,-1.267],[19.968,-10.223,-1.296],[19.967,-10.193,-1.304],[20.007,-10.092,-1.321],[19.997,-10.063,-1.309],[19.979,-10.039,-1.284],[19.989,-10.028,-1.256],[19.993,-10.024,-1.23],[19.972,-10.026,-1.181],[20.009,-10.045,-1.026],[20.025,-10.048,-1.013],[20.045,-10.05,-1.006],[20.056,-10.052,-1.001],[20.413,-10.073,-1.016],[20.461,-10.075,-1.02],[20.498,-10.053,-1.018],[20.525,-10.022,-1.025],[20.538,-9.983,-1.029],[20.554,-9.536,-1.111],[20.554,-9.487,-1.118],[20.545,-9.445,-1.129],[20.521,-9.418,-1.14],[20.478,-9.396,-1.156],[20.427,-9.379,-1.182],[20.367,-9.369,-1.198],[19.971,-9.304,-1.221],[19.749,-9.197,-1.345]] ,color:'#8c99aa', radius:0.025, height:-0, fromDataset:true, tension:0.01, spaceDis:0.02,visiEntity:'厨房' }, { path:[[19.708,-2.921,-1.145],[19.721,-2.924,-1.119],[20.704,-2.881,-1.134],[20.736,-2.87,-1.138],[20.76,-2.852,-1.147],[20.773,-2.828,-1.148],[20.777,-2.809,-1.148],[20.794,-2.493,-1.167],[20.793,-2.461,-1.169],[20.776,-2.436,-1.174],[20.742,-2.425,-1.198],[20.14,-2.459,-1.309],[20.123,-2.457,-1.309],[20.108,-2.451,-1.31],[20.101,-2.439,-1.311],[20.095,-2.42,-1.313],[20.094,-2.399,-1.311],[20.092,-1.966,-1.295],[20.093,-1.945,-1.286],[20.094,-1.931,-1.269],[20.093,-1.929,-1.251],[20.09,-1.921,-1.197],[20.096,-1.914,-1.181],[20.107,-1.909,-1.174],[20.176,-1.893,-1.141],[20.699,-1.848,-1.076],[20.759,-1.824,-1.068],[20.81,-1.754,-1.076],[20.837,-1.71,-1.086],[20.85,-1.634,-1.1],[20.815,-1.01,-1.104],[20.824,-0.979,-1.115],[20.83,-0.957,-1.124],[20.843,-0.936,-1.132],[20.871,-0.925,-1.139],[21.065,-0.936,-1.166],[21.096,-0.935,-1.167],[21.115,-0.919,-1.167],[21.129,-0.887,-1.165],[21.136,-0.783,-1.167],[21.136,0.555,-1.201],[21.128,0.599,-1.201],[21.1,0.627,-1.199],[21.058,0.639,-1.198],[20.832,0.616,-1.179],[20.782,0.62,-1.186],[20.748,0.637,-1.19],[20.728,0.698,-1.21],[20.731,1.311,-1.233],[20.68,2.081,-1.292],[20.654,2.157,-1.307],[20.576,2.215,-1.314],[20.484,2.274,-1.321],[20.035,2.354,-1.353],[20.013,2.367,-1.363],[20.008,2.39,-1.371],[19.989,3.124,-1.384],[19.993,3.175,-1.39],[19.995,3.208,-1.393],[19.995,3.233,-1.374],[20.002,3.246,-1.324],[20.006,3.248,-1.266],[20.027,3.243,-1.245],[20.063,3.241,-1.239],[20.62,3.229,-1.226],[20.645,3.235,-1.204],[20.658,3.251,-1.191],[20.667,3.278,-1.184],[20.675,3.312,-1.193],[20.716,3.71,-1.24],[20.722,3.767,-1.251],[20.704,3.823,-1.268],[20.652,3.864,-1.279],[20.026,3.901,-1.368],[19.999,3.919,-1.37],[19.982,3.95,-1.361],[19.933,4.69,-1.285],[19.972,4.812,-1.332],[19.978,4.817,-1.233],[19.992,4.814,-1.203],[20.047,4.81,-1.196],[20.738,4.794,-1.196]] ,color:'#8c99aa', radius:0.025, height:-0, fromDataset:true, tension:0.01, spaceDis:0.02,visiEntity:'厨房' }, { path:[[19.685,-2.896,-1.115],[19.699,-2.93,-1.274],[19.701,-2.936,-1.295],[19.707,-2.94,-1.316],[19.712,-2.951,-1.34],[19.714,-2.965,-1.353],[19.716,-2.986,-1.359],[19.7,-3.595,-1.309],[19.703,-3.619,-1.303],[19.704,-3.628,-1.274],[19.719,-3.632,-1.256],[20.063,-3.633,-1.275],[20.089,-3.637,-1.254],[20.103,-3.64,-1.227],[20.11,-3.642,-1.191],[20.083,-3.669,-1.027],[20.075,-3.68,-0.999],[20.053,-3.694,-0.978],[20.053,-3.805,-0.957],[20.063,-3.827,-0.963],[20.045,-3.836,-0.962],[20.01,-3.841,-0.957],[19.737,-3.818,-0.964],[19.712,-3.814,-0.985],[19.699,-3.819,-1.059],[19.724,-3.817,-1.247],[19.725,-3.827,-1.28],[19.719,-3.842,-1.309],[19.719,-3.858,-1.33],[19.714,-3.881,-1.341],[19.699,-4.56,-1.339],[19.7,-4.62,-1.336],[19.7,-4.643,-1.334],[19.699,-4.66,-1.326],[19.698,-4.675,-1.31],[19.697,-4.678,-1.279],[19.697,-4.678,-1.21],[19.704,-4.682,-1.187],[19.716,-4.682,-1.173],[19.736,-4.683,-1.164],[20.481,-4.817,-1.026],[20.529,-4.847,-0.984],[20.562,-4.89,-0.935],[20.585,-4.932,-0.909],[20.59,-4.992,-0.887],[20.565,-5.98,-0.847],[20.547,-6.08,-0.846],[20.541,-6.17,-0.86],[20.476,-6.21,-0.858],[19.847,-6.207,-0.808],[19.803,-6.22,-0.827],[19.775,-6.217,-0.885],[19.75,-6.188,-1.289],[19.743,-6.193,-1.341],[19.742,-6.207,-1.375],[19.75,-6.232,-1.401],[19.722,-6.905,-1.359],[19.723,-6.958,-1.352],[19.722,-6.987,-1.342],[19.721,-7.001,-1.31],[19.721,-7.004,-1.231],[19.734,-7.03,-0.976],[19.742,-7.041,-0.947],[19.747,-7.059,-0.922],[19.755,-7.284,-0.764],[19.761,-7.312,-0.731],[19.776,-7.337,-0.711],[19.795,-7.355,-0.702],[20.502,-7.453,-0.715],[20.578,-7.496,-0.715],[20.631,-7.557,-0.719],[20.66,-7.653,-0.718],[20.658,-8.639,-0.766],[20.654,-8.789,-0.763],[20.639,-8.856,-0.763],[20.605,-8.89,-0.76],[20.57,-8.916,-0.761],[20.539,-8.945,-0.773],[19.879,-9.028,-0.814],[19.853,-9.032,-0.828],[19.834,-9.033,-0.842],[19.83,-9.033,-0.876],[19.857,-9.033,-1.292],[19.837,-9.037,-1.333],[19.819,-9.051,-1.354],[19.812,-9.082,-1.381],[19.79,-9.184,-1.386]] ,color:'#8c99aa', radius:0.02, height:-0, fromDataset:true, tension:0.01, spaceDis:0.02,visiEntity:'厨房' } ], 'SG-t-RGUFiJAoxvL':[ //济南工地覆土后 {//--旧的 整条 path: [[23.522,-19.432,-1.744],[25.344,20.312,-1.855],[25.344,21.302,-1.861],[25.253,21.904,-1.861],[25.088,22.381,-1.859],[24.748,22.772,-1.861],[24.222,22.893,-1.873],[-24.547,16.774,-1.319]] ,color:'#445', radius:0.08, height:-1.16, fromDataset:true, tension:0.01,// spaceDis:0.1 }, { path:[[22.726,22.678,-1.938],[-24.541,16.782,-1.319]] ,color:'#445', radius:0.08, height:-1.16, fromDataset:true, tension:0,// spaceDis:0.1 }, { path:[[23.519,-19.473,-1.744],[25.117,15.447,-1.783]] ,color:'#445', radius:0.08, height:-1.16, fromDataset:true, tension:0,// spaceDis:0.1 } ], 'SG-t-TK0S5EqWBxd':[ //济南工地 { path:[[-25.768,-12.695,-1.305],[-26.334,-21.834,-1.511],[-26.355,-22.153,-1.514],[-26.366,-22.418,-1.513],[-26.31,-22.66,-1.515],[-26.126,-22.841,-1.519],[-25.871,-22.968,-1.525],[-25.443,-22.969,-1.532],[-24.953,-22.956,-1.585],[-23.697,-22.854,-1.637]] ,color:'#445', radius:0.08, height:-1.16, fromDataset:true, spaceDis:0.2// tension:0.01, }, ],*/ }) viewer.dispatchEvent('allLoaded') } if(!Potree.settings.originDatasetId)Potree.settings.originDatasetId = data[0].id var originDataset = data.find(e=>e.id == Potree.settings.originDatasetId) baseZ = originDataset.location[2] /* originDataset.location[0] = 113.60608878174709 originDataset.location[1] = 22.381189423935155 let ano = data.find(e=>e.name == 'SG-t-Jw0xyhL6oSY') ano.location[0] = 113.6060509967498 ano.location[1] = 22.381061273279244 */ {//拿初始数据集作为基准。它的位置要放到000 var locationLonLat = originDataset.location.slice(0,2) Potree.setLonlat(locationLonLat[0], locationLonLat[1]) viewer.mapViewer && viewer.mapViewer.mapLayer.maps[0].updateProjection() } data.forEach((dataset,index)=>{ if(!ifReload){ var datasetCode = dataset.sceneCode || dataset.name //对应4dkk的场景码 if(Potree.settings.isLocal && dataset.mapping){ var cloudPath = `${Potree.settings.urls.prefix1}/${dataset.mapping}/${dataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢 }else{ var cloudPath = `${Potree.settings.urls.prefix1}/${dataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢 } //var cloudPath = `${Potree.scriptPath}/data/test/${dataset.name}/cloud.js` var timeStamp = dataset.updateTime ? dataset.updateTime.replace(/[^0-9]/ig,'') : ''; //每重算一次后缀随updateTime更新一次 //console.warn(dataset.name, 'timeStamp', timeStamp) Potree.loadPointCloud(cloudPath, dataset.name ,datasetCode, timeStamp, e => { let scene = viewer.scene; let pointcloud = e.pointcloud; let config = Potree.config.material let material = pointcloud.material; pointcloud.datasetData = dataset pointcloud.dataset_id = dataset.id;//供漫游点找到属于的dataset点云 pointcloud.hasDepthTex = Potree.settings.useDepthTex && (!!dataset.has_depth || Potree.settings.isLocalhost && Potree.settings.number == 'SS-t-7DUfWAUZ3V') //test pointcloud.hasTempTex = !!dataset.has_ir material.minSize = config.minSize material.maxSize = config.maxSize material.pointSizeType =/* Potree.settings.isOfficial ? */ config.pointSizeType /* : 'ADAPTIVE' */ //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED pointcloud.changePointSize(config.realPointSize) //material.size = config.pointSize; pointcloud.changePointOpacity(1) material.shape = Potree.PointShape.SQUARE; pointcloud.color = pointcloud.material.color = dataset.color pointcloud.timeStamp = timeStamp transformPointcloud(pointcloud,dataset) scene.addPointCloud(pointcloud); if(!Potree.settings.isOfficial){ Potree.settings.floorplanEnables[dataset.id] = true Potree.settings.floorplanType[dataset.id] = 'default' } pointcloudLoaded ++; if(pointcloudLoaded == datasetLength)pointcloudLoadDone() Potree.loadPanos(dataset.id, (data) => { //console.log('loadPanos',dataset.sceneCode, dataset.id, data) viewer.images360.addPanoData(data, pointcloud) panosLoaded ++; let f = ()=>{ if(panosLoaded == datasetLength){ Potree.loadImgVersion((e={})=>{ Potree.settings.panoVersion = e.imgVersion//全景图被替换后 panosLoadDone() }) } } if(dataset.has_ir){//红外热成像漫游点数据 /* if(Potree.settings.isLocal && dataset.mapping){ var cloudPath = `${Potree.settings.urls.prefix1}/${dataset.mapping}/${dataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢 } */ let mapping = Potree.settings.isLocal && dataset.mapping ? ('/'+dataset.mapping): '' pointcloud.typesUrl = `${Potree.settings.urls.prefix1}${mapping}/${Potree.settings.webSite2 || Potree.settings.webSite}/${pointcloud.sceneCode}/data/${pointcloud.sceneCode}/imagemap/` let typesUrl = pointcloud.typesUrl + 'types.json' Potree.loadFile(typesUrl,null,(e)=>{ e.imagemap_types.forEach(o=>{ o.uuids.forEach(uuid=>{ //if(o.property == 'temp')return //只要ir let pano = viewer.images360.getPano(`${pointcloud.dataset_id}|${uuid}`,'sid') pano['has_'+o.property] = true }) }) f() },()=>{ console.error('红外热成像数据加载失败 ') f() }) }else{ f() } }) }) }else{ let pointcloud = viewer.scene.pointclouds.find(p => p.dataset_id == dataset.id) if(!pointcloud){ Potree.Log('数据集id变了,自动使用第一个', {font: {color:'#500' }} ) pointcloud = viewer.scene.pointclouds[0] } //先归零 Alignment.translate(pointcloud, pointcloud.translateUser.clone().negate()) Alignment.rotate(pointcloud, null, - pointcloud.orientationUser) transformPointcloud(pointcloud, dataset) } }) if(ifReload){ //loadDone() } } if(number){ if(window._datasetList){ Potree.loadDatasetsCallback(window._datasetList) }else{ Potree.loadDatasets(Potree.loadDatasetsCallback) } } /* //调试用,加载多个本地 Potree.loadDatasetsCallback([ {name:'webcloud_0',id:0, orientation:0, location:[0,0,0]}, {name:'webcloud_1',id:1, orientation:0.047234761795199476, location:[-0.07925513345058573,-0.0010590072536559839,-2.403613132687564]}, {name:'webcloud_2',id:2, orientation:-1.5299545647758208, location:[1.5603736310030292, -0.0009340812579088904, -2.4464530770974139]}, ]) */ window.testTransform = function(locationLonLat, location1, location2){ proj4.defs("NAVVIS:test", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15)); let transform = proj4("WGS84", "NAVVIS:test"); //这个ok navvis里也是这两种转换 见proj4Factory if(location1){//经纬度 return transform.forward(location1) }else{ return transform.inverse(location2) } } window.buttonFunction = function(){ viewer.scene.pointclouds.forEach(e=>e.predictNodeMaxLevel()) /* viewer.startScreenshot({type:'measure', measurement:viewer.scene.measurements[0]}) viewer.modules.RouteGuider.routeStart = new THREE.Vector3(0,0,-1.3) viewer.modules.RouteGuider.routeEnd = new THREE.Vector3(-10,0,-1.3) */ } if(Potree.settings.isLocalhost){ let before = {} viewer.inputHandler.addEventListener('keydown',e=>{ //测试的代码 if(e.event.key == 't'){ viewer.images360.cube.visible = true viewer.images360.cube.material.wireframe = true }else if(e.event.key == 'y'){ viewer.images360.cube.material.wireframe = false viewer.images360.cube.visible = Potree.settings.displayMode == 'showPanos' } }) //-------------------------------- /* if(!number){ Potree.settings.boundAddObjs = true Potree.settings.intersectOnObjs = true // Load untextured bunny from ply viewer.loadModel({ fileType:'ply', url:Potree.resourcePath + "/models/indoor.ply", name:'test', }, (object)=>{ object.isModel = true viewer.updateModelBound() } ) } */ } } //======================================================================= /* 漫游点编辑 */ //======================================================================= export function panoEditStart(dom, number, EditCloudsArgs){ Potree.settings.editType = 'pano' Potree.settings.number = number Potree.settings.sizeFitToLevel = true//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2 let datasetData let viewer = new Potree.Viewer(dom); let Alignment = viewer.modules.Alignment viewer.setEDLEnabled(false); viewer.setFOV(Potree.config.view.fov); //viewer.loadSettingsFromURL(); let datasetLoaded = 0; if(!Potree.settings.isOfficial){ viewer.loadGUI(() => { viewer.setLanguage('en'); $("#menu_tools").next().show(); $("#panos").show(); $("#alignment").show(); viewer.toggleSidebar(); }); Potree.settings.sizeFitToLevel = true } var pointcloudLoadDone = function( ){//所有点云cloud.js加载完毕后 viewer.scene.pointclouds.forEach(c=>{ transformPointcloud(c) }) viewer.images360.loadDone() viewer.scene.add360Images(viewer.images360); viewer.updateModelBound() let {boundSize, center} = viewer.bound Potree.Log(`中心点: ${math.toPrecision(center.toArray(),2)}, boundSize: ${math.toPrecision(boundSize.toArray(),2)} ` ) viewer.scene.view.setView({ position: center.clone().add(new THREE.Vector3(10,5,10)), target: center }) viewer.dispatchEvent({type:'loadPointCloudDone'}) if(!Potree.settings.UserPointDensity){ Potree.settings.UserPointDensity = 'panoEdit'//'middle' } Potree.Log('loadPointCloudDone 点云加载完毕',{font:[null, 10]}) viewer.dispatchEvent('allLoaded'); } var transformPointcloud = (pointcloud )=>{ //初始化位置 viewer.sidebar && viewer.sidebar.addAlignmentButton(pointcloud) /* let orientation = pointcloud.panos[0].dataRotation.z + Math.PI let location = pointcloud.panos[0].dataPosition.clone()//.negate() Alignment.rotate(pointcloud, null, orientation ) Alignment.translate(pointcloud, location ) */ viewer.modules.PanoEditor.initCloud(pointcloud) pointcloud.updateMatrixWorld() } let loadPanosDone = Potree.loadPanosDone = (datasetId, panoData )=>{ //一个数据集获取到它的panos后 Potree.settings.datasetsPanos[datasetId] = {panoData, panos:[]} console.log('panoData', datasetId, panoData) let panoCount = panoData.length let pointcloudLoaded = 0 let datasetsCount = Object.keys(Potree.settings.datasetsPanos).length let datasetData = Potree.datasetData.find(e=>e.datasetId = datasetId) panoData.forEach((pano, index)=>{ //let cloudPath = `${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${Potree.settings.number}/data/bundle_${Potree.settings.number}/building/uuidcloud/${pano.uuid}/cloud.js` //2024.12.9: 加mapping let mapping = (Potree.settings.isLocal && (datasetData ? datasetData.mapping : browser.urlHasValue('mapping',true))) || '' mapping && (mapping += '/') let cloudPath = `${Potree.settings.urls.prefix1}/${mapping}${Potree.settings.webSite}/${Potree.settings.number}/data/bundle_${Potree.settings.number}/building/uuidcloud/${pano.uuid}/cloud.js` /* if(Potree.settings.isLocal && dataset.mapping){ var cloudPath = `${Potree.settings.urls.prefix1}/${dataset.mapping}/${dataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢 }else{ var cloudPath = `${Potree.settings.urls.prefix1}/${dataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢 } */ let name = datasetId + '-'+pano.uuid let timeStamp = 0 pano.index = index //注意:index不等于uuid,因为有的uuid缺失。但是visibles中存的是下标! Potree.loadPointCloud(cloudPath, name , number, timeStamp, e => { //开始加载点云 let scene = viewer.scene; let pointcloud = e.pointcloud; let config = Potree.config.material let material = pointcloud.material; material.minSize = config.minSize material.maxSize = config.maxSize material.pointSizeType = /* 'ADAPTIVE'// */config.pointSizeType //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED pointcloud.changePointSize( 0.1 /* config.realPointSize */ ) //material.size = config.pointSize; pointcloud.changePointOpacity(1) material.shape = Potree.PointShape.SQUARE; pointcloud.color = config.pointColor pointcloud.dataset_id = datasetId; //多个点云指向一个datasetId pointcloud.panoUuid = pano.uuid pointcloud.timeStamp = timeStamp if(datasetData){//非4dkk pointcloud.datasetData = datasetData pointcloud.hasDepthTex = Potree.settings.useDepthTex && !!datasetData.has_depth } //transformPointcloud(pointcloud, pano) scene.addPointCloud(pointcloud); pointcloudLoaded ++; if(pointcloudLoaded == panoCount ){ datasetLoaded ++ viewer.images360.addPanoData(panoData , pointcloud ) if(datasetLoaded == datasetsCount){ pointcloudLoadDone() } } }) }) } if(!Potree.settings.isOfficial){ Potree.settings.datasetsPano = {'testDataset':null} Potree.loadPanosInfo( data=>{loadPanosDone('testDataset', data.sweepLocations)} ) } Potree.loadPanosCloudStart = (EditCloudsArgs)=>{ Potree.loadDatasets((datasets)=>{ Potree.datasetData = datasets //4dkk的加载为空[] Potree.loadImgVersion((e={})=>{ Potree.settings.panoVersion = e.imgVersion//全景图被替换后 if(!(EditCloudsArgs instanceof Array)) EditCloudsArgs = [EditCloudsArgs] EditCloudsArgs.forEach((e,i)=>{ //加载点云和漫游点 if(e.datasetId == void 0){ console.error('没有datasetId ')// 看看和看见没有或为0 e.datasetId = i; //经常没有datasetId所以自己加 } Potree.settings.datasetsPanos[e.datasetId] = null Potree.loadPanosDone(e.datasetId, e.clouds) }) }) },number) } EditCloudsArgs && Potree.loadPanosCloudStart(EditCloudsArgs) } ///////////////////////////////////////////////////////------------------------- ///////////////////////////////////////////////////////------------------------- ///////////////////////////////////////////////////////------------------------- export function realTimePanosStart(dom, number){//边拍边传,和点位编辑一样每个点位一个电源 Potree.settings.editType = 'singlePanoToCloud' Potree.settings.isLocal = Potree.settings.tileOriginUrl = true; Potree.settings.intersectOnObjs = true Potree.settings.boundAddObjs = true Potree.settings.UserPointDensity = 'high'//'middle' let viewer = new Potree.Viewer(dom, null, {noMap:true}); let Alignment = viewer.modules.Alignment viewer.setEDLEnabled(false); viewer.setFOV(Potree.config.view.fov); Potree.settings.urls.templates.panoTile = 'laser-data/testdata/{sceneCode}/data/preview/{id}/' const rotQua = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0), -Math.PI / 2 ) //new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), -Math.PI ) const rotQua2 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), -Math.PI ) viewer.scene.add360Images(viewer.images360); const datasetId = 1 Potree.loadPanos = function(panoData, callback){ panoData = panoData.filter(e=> !viewer.images360.panos.some(p=>p.uuid == e.uuid)) let panoCount = panoData.length let pointcloudLoaded = 0 if(!panoCount){ return callback() } console.log('loadPanos',panoData) panoData.forEach((d,index)=>{ let timeStamp = 0 let cloudPath = `laser-data/testdata/${number}/data/preview/${d.uuid}/webcloud/cloud.js` //pano.index = index //注意:index不等于uuid,因为有的uuid缺失。但是visibles中存的是下标! Potree.loadPointCloud(cloudPath, d.uuid , number, timeStamp, e => { //开始加载点云 let pointcloud = e.pointcloud; let config = Potree.config.material let material = pointcloud.material; material.minSize = config.minSize material.maxSize = config.maxSize material.pointSizeType = /* 'ADAPTIVE'// */config.pointSizeType //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED pointcloud.changePointSize( 0.1 /* config.realPointSize */ ) //material.size = config.pointSize; pointcloud.changePointOpacity(1) material.shape = Potree.PointShape.SQUARE; pointcloud.color = config.pointColor pointcloud.dataset_id = datasetId; //多个点云指向一个datasetId pointcloud.panoUuid = d.uuid ///!! pointcloud.timeStamp = timeStamp /* if(datasetData){//非4dkk pointcloud.datasetData = datasetData pointcloud.hasDepthTex = Potree.settings.useDepthTex && !!datasetData.has_depth } */ viewer.scene.addPointCloud(pointcloud); pointcloudLoaded ++; if(pointcloudLoaded == panoCount ){ let datas = panoData.map(p=>{ let newData = Object.assign(p,{ single:true }) return newData }) viewer.images360.addPanoData(datas ) panoData.forEach(ee => { let pano = viewer.images360.panos.find(e=>e.uuid == ee.uuid) let data = pano.panoData let dataPosition = new THREE.Vector3().fromArray(data.pose3d.position) let qua = data.pose3d.rotation qua = [qua[1], qua[2], qua[3], qua[0]] let dataQuaternion = new THREE.Quaternion().fromArray(qua) pano.pointcloud.orientationUser = new THREE.Quaternion().multiplyQuaternions(dataQuaternion,rotQua2)//新版 pano.pointcloud.translateUser.copy(dataPosition) Alignment.setMatrix(pano.pointcloud); }) viewer.images360.loadDone() callback() /* let {boundSize, center} = viewer.bound Potree.Log(`中心点: ${math.toPrecision(center.toArray(),2)}, boundSize: ${math.toPrecision(boundSize.toArray(),2)} ` ) viewer.scene.view.setView({ position: center.clone().add(new THREE.Vector3(10,5,10)), target: center }) */ } }) }) } } ///////////////////////////////////////////////////////////////----------------------- export function mergeEditStart(dom, mapDom){ Potree.settings.editType = 'merge' Potree.settings.intersectOnObjs = true Potree.settings.boundAddObjs = true Potree.settings.unableNavigate = true if(Potree.settings.queryCloudLonLatUrl){ //点云使用其经纬度作为默认位置 Potree.loadControlPoint = /* async */function(callback,sceneCode,onError,prefix){//点云绑定地图 return Potree.loadFile(Potree.settings.queryCloudLonLatUrl.replace('{sceneCode}', sceneCode), { fetchMethod: 'post' }, callback,onError) } } let viewer = new Potree.Viewer(dom, mapDom ); let Alignment = viewer.modules.Alignment viewer.setEDLEnabled(false); viewer.setFOV(Potree.config.view.fov); //viewer.loadSettingsFromURL(); { viewer.mainViewport.view.position.set(30,30,30) viewer.mainViewport.view.lookAt(0,0,0) viewer.updateModelBound()//init //this.bound = new THREE.Box3(new THREE.Vector3(-1,-1,-1),new THREE.Vector3(1,1,1)) viewer.transformationTool.setModeEnable(['translation','rotation'] ) //viewer.ssaaRenderPass.sampleLevel = 1 // sampleLevel为1 的话,ground就不会变黑 viewer.inputHandler.fixSelection = true //不通过点击屏幕而切换transfrom选中状态 } Potree.settings.sizeFitToLevel = true//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2 Potree.loadPointCloudScene = function(url, type, id, title, done, onError, prefix1, prefix2 ){//对应4dkk的场景码 let dataset , useLonLat let loadCloud = ({cloudPath, sceneName, sceneCode, timeStamp, color } )=>{ Potree.loadPointCloud(cloudPath, sceneName , sceneCode, timeStamp, e => { let scene = viewer.scene; let pointcloud = e.pointcloud; let config = Potree.config.material let material = pointcloud.material; pointcloud.datasetData = dataset pointcloud.hasDepthTex = dataset && Potree.settings.useDepthTex && !!dataset.has_depth pointcloud.initialPosition = pointcloud.position.clone() pointcloud.pos1MatrixInvert = new THREE.Matrix4().setPosition(pointcloud.initialPosition).invert() material.minSize = config.minSize material.maxSize = config.maxSize material.pointSizeType = config.pointSizeType //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED pointcloud.changePointSize(config.realPointSize) //material.size = config.pointSize; pointcloud.changePointOpacity(1) dataset && useLonLat && (transformPointcloud(pointcloud, dataset), pointcloud.hasLonLat = true) material.shape = Potree.PointShape.SQUARE; color && (pointcloud.color = pointcloud.material.color = color) pointcloud.timeStamp = timeStamp scene.addPointCloud(pointcloud); { viewer.updateModelBound() let {boundSize, center} = viewer.bound viewer.dispatchEvent({type:'loadPointCloudDone'}) if(!Potree.settings.UserPointDensity){ Potree.settings.UserPointDensity = 'high'//'middle' } Potree.Log(' 点云加载完毕', sceneName, sceneCode ) } viewer.dispatchEvent('allLoaded') done(pointcloud) },onError) } if(type == 'laser'){ let sceneCode = url let load = ()=>{ Potree.loadDatasets((data)=>{ let originDataset = data.find(e=>e.sceneCode == sceneCode);//只加载初始数据集 /* if(!originDataset){ //应该是data为空,原因未知 } */ Potree.settings.cloudAddMapping && (prefix2 += '/'+originDataset.mapping) let timeStamp = originDataset.updateTime ? originDataset.updateTime.replace(/[^0-9]/ig,'') : ''; //每重算一次后缀随updateTime更新一次 //let cloudPath = `${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${sceneCode}/data/${sceneCode}/webcloud/cloud.js` let cloudPath = `${prefix2}/${originDataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢 dataset = originDataset loadCloud({ cloudPath, sceneName:originDataset.sceneName, sceneCode, timeStamp, color:originDataset.color}) }, sceneCode, onError, prefix1 ) } if(Potree.settings.queryCloudLonLatUrl){ Potree.loadControlPoint((ctlData)=>{ useLonLat = ctlData.controlPoint.status==1 //设置了地理位置 load() }, sceneCode, (e)=>{ console.error('loadControlPoint error',e) load() }) }else{ load() } }else{//las or ply 直接用url let name = type + '|' + id + '|' + title //有漫游点吗 if(url instanceof Array){ if(url.length == 1){ url = url[0] }else{ console.error('有多个点云?暂时还不支持', url, name)//多个点云要一起移动没想好怎么写 } } let cloudPath = url + '/cloud.js' loadCloud({cloudPath, sceneName:name, sceneCode:name, timeStamp:'' }) } } let setMatrix = (pointcloud)=>{//为了漫游点变换,要算一下 类似setMatrix /* pointcloud.transformMatrix = new THREE.Matrix4().multiplyMatrices(pointcloud.matrix, pointcloud.pos1MatrixInvert)//还原一点位移 pointcloud.transformInvMatrix.copy(pointcloud.transformMatrix).invert() pointcloud.rotateMatrix = new THREE.Matrix4().makeRotationFromEuler(pointcloud.rotation); pointcloud.rotateInvMatrix.copy(pointcloud.rotateMatrix).invert() pointcloud.panos.forEach(e=>e.transformByPointcloud()) */ //pointcloud.updateBound() //pointcloud.getPanosBound() viewer.updateModelBound() } const moveModelWhenLoad = false let moveModel = (e)=>{//根据鼠标移动的位置改变位置 let {x,y} = Potree.Utils.getPointerPosAtHeight(0,e.pointer) //过后改为根据intersect的点来设置底部高度;这样的话,需要发送高度 /*let pos = new THREE.Vector3(x,y, planeZ ) modelEditing.updateMatrixWorld() let boundCenter = modelEditing.boundingBox.getCenter(new THREE.Vector3).applyMatrix4(modelEditing.matrixWorld); */ MergeEditor.moveBoundCenterTo(modelEditing,new THREE.Vector3(x,y, modelEditing.boundCenter.z)) //使模型中心的xy在鼠标所在位置 modelEditing.dispatchEvent("position_changed") } let cancelMove = ()=>{ modelEditing = null viewer.removeEventListener('global_mousemove', moveModel); viewer.removeEventListener('global_click', confirmPos); } let confirmPos = ()=>{ MergeEditor.focusOn(modelEditing) cancelMove() return {stopContinue:true} } let modelType, modelEditing, MergeEditor = viewer.modules.MergeEditor Potree.addModel = function(prop, done, onProgress, onError){ //加载模型 let loadDone = (model)=>{ model.dataset_id = prop.id //唯一标识 {//设置下默认经纬度位置,当点击恢复默认时要恢复到此位置 if(!model.isPointcloud){ //有经纬度 3dtiles model.rotation.copy(prop.baseRotation) //有的需要翻转90度 let lonlat = prop.raw.wgs84 || prop.raw.rtkLocation //前者为素材库的osgb的 if(lonlat){ var locationLonLat = lonlat.split(',').map(e=>parseFloat(e)) var location = new THREE.Vector3().fromArray(viewer.transform.lonlatToLocal.forward(locationLonLat)) model.hasLonLat = true if( model.fileType == '3dTiles' && prop.is4dkkModel ){ //深时深光mesh model.position.fromArray(model.runtime.getTileset().tileset.root.boundingVolume.box.slice(0,3))//必须要平移一段才能重合 model.position.copy(Potree.math.convertVector.ZupToYup(model.position)) //饶原点旋转 类似Alignment.setMatrix 和点云一样处理 if(prop.raw.orientation){ model.updateMatrixWorld()//此时和点云没有旋转平移时一样。 let rotMatrix = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(0,0,1), parseFloat(prop.raw.orientation) ) let pos2Matrix = new THREE.Matrix4().setPosition(location);//最后是平移 var matrix = new THREE.Matrix4().multiplyMatrices(pos2Matrix, rotMatrix); model.matrix.premultiply(matrix) model.matrix.decompose(model.position,model.quaternion,model.scale) }else{ model.position.add(location) } }else{ MergeEditor.moveBoundCenterTo(model, location ) if(prop.raw.orientation){ model.rotation.y = parseFloat(prop.raw.orientation) } if(prop.is4dkkModel){ console.warn('遇到is4dkkModel的且有经纬度的mesh,但不是3dtiles! 位置估计不准', model) //看见的场景说是市场不会带rtk的。所以我们这边标品也没存rtk的坐标信息。和产品聊了,不处理。意思就是,只有激光场景开启了rtk的rtkLocation才有值 /* if(model.panos?.length){//只能通过漫游点经纬度来校准 //但这时候panos还没加载。。。。 let sceneCode = ...从url中解析 Potree.loadDatasets((data)=>{ //获取datasetId let originDataset = data.find(e=>e.sceneCode == sceneCode);//只加载初始数据集 Potree.loadPanos(originDataset.datasetId,()=>{ shouldPos = 获取坐标 data[0].location经纬度 model.position.add(new THREE.Vector3().subVectors(shouldPos, model.panos[0].position)) }) }, sceneCode, (e)=>{ console.log(e) } , prop.prefix) } */ } } } } if(model.fileType == 'shp'){ if(model.prjNotSure){ MergeEditor.moveBoundCenterTo(model,new THREE.Vector3() )//因为不确定坐标类型,而点坐标可能几万米,所以放原点好一些 } model.hasLonLat = true } MergeEditor.setModelBtmHeight(model, 0)// 离地高度为0 (因为不想在地图下方所以高程不管了,都在地面上即可) if(model.hasLonLat){ model.lonLatPos = model.position.clone() model.lonLatRot = model.rotation.clone() } } if(prop.position ){ if( prop.position.x != 0 || prop.position.y != 0 || prop.position.z != 0 ){//移动过后使用移动后的坐标 model.position.copy(prop.position) }else{//用户没设置位置(首次添加后没按保存) } } if(prop.rotation && (prop.rotation.x != 0 || prop.rotation.y != 0 || prop.rotation.z != 0 )){ //model.rotation.setFromVector3(prop.rotation) model.rotation.copy(prop.rotation) } if(prop.scale != void 0){ model.scale.set(prop.scale,prop.scale,prop.scale) } if(model.isPointcloud){ model.renderOrder = Potree.config.renderOrders.model; //same as glb } if(Potree.settings.maintainBtmZ) {//transform --------维持离地高度和中心点的版本(local ver) let updateBound = ()=>{ model.updateMatrixWorld() viewer.updateModelBound() } let maintainBtmZAndCenter = ()=>{ MergeEditor.maintainBoundXY(model) MergeEditor.setModelBtmHeight(model) updateBound() model.dispatchEvent('transformChanged') } model.addEventListener('position_changed', ()=>{ updateBound() MergeEditor.getBoundCenter(model);//更新boundcenter MergeEditor.computeBtmHeight(model) if(prop.bottomRange && (model.btmHeight > prop.bottomRange.max || model.btmHeight < prop.bottomRange.min)){ model.btmHeight = THREE.Math.clamp(model.btmHeight, prop.bottomRange.min, prop.bottomRange.max) MergeEditor.setModelBtmHeight(model) updateBound() } model.dispatchEvent('transformChanged') }) model.addEventListener("rotation_changed", maintainBtmZAndCenter ) model.addEventListener("scale_changed", maintainBtmZAndCenter ) model.addEventListener('transformChanged', ()=>{ MergeEditor.modelTransformCallback(model) }) //离地高度只是boundingbox在transform后的最低点的高度,而非模型transform后的最低点的高度,所以旋转过后看起来不太准确 } else {//transform --------维持中心点的版本 let updateBound = ()=>{ model.updateMatrixWorld() viewer.updateModelBound() } let maintainCenter = (e)=>{ //MergeEditor.maintainBoundXY(model) e.by2d || MergeEditor.maintainBoundCenter(model) updateBound() model.dispatchEvent({type:'transformChanged', byControl:e.byControl}) } model.addEventListener('position_changed', (e)=>{//要先发送position_changed再其他 updateBound() MergeEditor.getBoundCenter(model);//更新boundcenter model.dispatchEvent({type:'transformChanged', byControl:e.byControl}) }) model.addEventListener("rotation_changed", maintainCenter ) model.addEventListener("scale_changed", maintainCenter ) model.addEventListener('transformChanged', ()=>{ MergeEditor.modelTransformCallback(model) }) } model.updateMatrixWorld() viewer.updateModelBound() MergeEditor.getBoundCenter(model) //初始化 //model.lastMatrixWorld = model.matrixWorld.clone() model.lastMatrixWorld = new THREE.Matrix4 MergeEditor.modelTransformCallback(model, true) prop.scale != void 0 && model.isPointcloud && model.changePointSize() //有的被缩放的很小导致testMaxNodeLevel时距离较远时被return 但点云过大急需changesize done(model) // 先发送成功,因为2d界面会随机执行changePosition等初始化,然后这边再将模型移到中心地面上 if(prop.isFirstLoad){ if(model.hasLonLat || !moveModelWhenLoad){ setTimeout(()=>{MergeEditor.focusOn(model)} , 1) }else{ MergeEditor.moveBoundCenterTo(model, new THREE.Vector3(0,0,0)) } MergeEditor.setModelBtmHeight(model, 0) //初始加载设置离地高度为0 if(prop.mode != 'single'){//如果不是模型展示页,模型会随着鼠标位置移动 modelEditing = model; if(!model.hasLonLat && moveModelWhenLoad){ viewer.addEventListener('global_mousemove', moveModel); viewer.addEventListener('global_click', confirmPos, {importance:3}); } } model.dispatchEvent("position_changed") }else{ //MergeEditor.setModelBtmHeight(model, prop.bottom || 0) //默认离地高度为0 modelEditing = null } MergeEditor.modelAdded(model) } if(prop.type == 'obj' || prop.type == 'glb'){ let callback = (object)=>{ object.isModel = true object.traverse(e=>e.material && (e.material.transparent = true)) loadDone(object) } let info = { fileType: prop.type, id: prop.id, unlit: prop.unlit, url : prop.url, name : prop.title, } viewer.loadModel(info , callback, onProgress, onError) }else if(prop.type == 'osgb' || prop.type == 'b3dm'){ //3d tiles let callback = (object)=>{ object.isModel = true loadDone(object) } viewer.loadModel({ fileType: '3dTiles', id: prop.id, name : prop.title, maximumScreenSpaceError: prop.maximumScreenSpaceError, url:prop.url, },callback,onprogress) }else if(prop.type == 'shp'){ let callback = (object)=>{ object.isModel = true loadDone(object) } viewer.loadModel({ fileType: 'shp', id: prop.id, name : prop.title, url:prop.url, },callback,onprogress) }else if(prop.type == '3dgs'){ let callback = (object)=>{ object.isModel = true loadDone(object) } viewer.loadModel({ fileType: '3dgs', id: prop.id, name : prop.title, url:prop.url, },callback,onprogress) }else{ prop.url instanceof Array && (prop.url = prop.url[0]) //deal bug let cloudPrefix = Potree.settings.urls.getPrefix(1, prop) Potree.loadPointCloudScene(prop.url, prop.type, prop.modelId, prop.title, (pointcloud)=>{ { pointcloud.matrixAutoUpdate = true if(pointcloud.hasLonLat){ pointcloud.matrix.decompose(pointcloud.position, pointcloud.quaternion, pointcloud.scale) //将数据集的经纬度和旋转应用到rotation和position (注意position和translateUser并不一样) }else if(!prop.isFirstLoad){//点云一般加载后position都不是0, 但后台初始化为0所以先归零要不然撤销后容易错 pointcloud.position.set(0,0,0) } } if(Potree.settings.mergeType2 && pointcloud.datasetData){ Potree.loadPanos(pointcloud.datasetData.id, (data) => { viewer.images360.addPanoData(data, pointcloud ) viewer.images360.loadDone() viewer.scene.add360Images(viewer.images360); loadDone(pointcloud) },prop.url) }else{ loadDone(pointcloud) } }, onError, prop.prefix, cloudPrefix) } } return {THREE} } var changeLog = ()=>{ var textarea = document.createElement('textarea'); textarea.id = "consoleLog"; textarea.style.width = '160px'; textarea.style.height = '200px' textarea.style.position = 'fixed' textarea.style.left = 0 textarea.style.bottom = '50px' textarea.style['z-index'] = 9999; textarea.style.color = 'black'; textarea.style.opacity = 0.9; textarea.style['font-size'] = '12px'; textarea.style['backgroundColor'] = '#ffffff' document.getElementsByTagName("body")[0].appendChild(textarea); var list = ["log", "error", "warn", "debug", "info", "time", "timeEnd"] var exchange = function (o) { console["old" + o] = console[o]; console[o] = function () { var args = Array.from(arguments) console["old" + o].apply(this, arguments) var t = document.getElementById("consoleLog").innerHTML; var str = '' args.forEach(a=>{ str += a + ' ' }) document.getElementById("consoleLog").innerHTML = str + "\n\n" + t; } } for (var i = 0; i < list.length; i++) { exchange(list[i]) } } /* 坐标转换问题: 由于控制点可以随便输入,所以本地和地理位置的转换也是可拉伸的。而navvis的转换是等比由中心展开, 所以对比两种转化方式时误差较大。 另外地理注册控制点是有参考数据集的,若参考数据集和我放置在0,0,0的数据集一致,就可直接使用,否则要转换。 --------- lonlat和空间坐标其实并非线性关系,因为lonlat其实是角度。当两个数据集在地球两端时,它们之间的夹角都相差180度了。 所以若要准确展示的话,需要将点云内所有物体,如漫游点,都先获取lonlat再去算local。或者直接将点云整体的transformMatrix考虑上在地球上相对于初始数据集的偏转。 支持ctrl+z、ctrl+Y 撤销回退的页面有: 测量、土方量、空间模型 这三个页面的点线拖拽;点云裁剪、点云下载中的裁剪 的框; 点云编辑的变换; 数据集校准; (所有数据一旦删除则无效 ) 其他快捷键: 按alt鼠标滚轮或WS键放慢。 测量or土方量: 按Alt键可以平行拖拽点。&dragPolyBeyondPoint 后缀则可平行拖拽到无点云区域 。 按M键拖拽点可以复制出当前点 点云按空格键+左键拖拽场景,可以不改相机位置的旋转视角 */