start.js 66 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455
  1. import * as THREE from "../../libs/three.js/build/three.module.js";
  2. import math from './utils/math.js'
  3. import browser from './utils/browser.js'
  4. import './three.shim.js'
  5. import "./potree.shim.js"
  6. import ReportInfoStream from './utils/reportInfoStream.js'
  7. import {lerp} from './utils/transitions.js'
  8. window.THREE = THREE
  9. let baseZ = 0 //所以数据集高度都要减去这个值。在laser场景里该值为初始数据集的高程
  10. Potree.datasetData = []
  11. var transformPointcloud = (pointcloud, dataset)=>{
  12. var locationLonLat = dataset.location.slice(0,3) // [lon,lat,高程海拔]
  13. //当只有一个dataset时,无论如何transform 点云和漫游点都能对应上。
  14. var location = viewer.transform.lonlatToLocal.forward(locationLonLat) //transform.inverse()
  15. //初始化位置
  16. viewer.sidebar && viewer.sidebar.addAlignmentButton(pointcloud)
  17. //dataset.orientation = 1
  18. let Alignment = viewer.modules.Alignment
  19. Alignment.rotate(pointcloud, null, dataset.quaternion?.length == 4 ? dataset.quaternion : dataset.orientation)
  20. Alignment.translate(pointcloud, new THREE.Vector3(location[0], location[1], dataset.location[2]-baseZ)) //要使初始数据集的z为0,所以要减去初始数据集的z
  21. pointcloud.updateMatrixWorld()
  22. Potree.Log(`点云${pointcloud.dataset_id}(${pointcloud.name})旋转值: qua ${dataset.quaternion||''}、orient ${ math.toPrecision(dataset.orientation,3) },
  23. 位置${math.toPrecision(pointcloud.translateUser.toArray(),3)}, 经纬度 ${dataset.location}, spacing ${pointcloud.material.spacing}`,{font:{color:"#f49",fontSize:13}} )
  24. }
  25. export function start(dom, mapDom, number, info={} ){ //t-Zvd3w0m
  26. /* {
  27. let obj = JSON.parse(localStorage.getItem('setting'))
  28. for(let i in obj){
  29. console.log(i + ': ' + obj[i])
  30. }
  31. }
  32. */
  33. console.log('2024.9')
  34. Potree.settings.number = number || 't-o5YMR13'// 't-iksBApb'// 写在viewer前
  35. if(!Potree.settings.isOfficial){
  36. if(/* Potree.settings.isTest && */ browser.isMobile()){
  37. changeLog()
  38. }
  39. }
  40. if(browser.urlHasValue('google'))Potree.settings.mapCompany = 'google'
  41. if(browser.urlHasValue('timing'))Potree.measureTimings = 1
  42. let viewer = new Potree.Viewer(dom , mapDom, info);
  43. let Alignment = viewer.modules.Alignment
  44. viewer.setEDLEnabled(false);
  45. viewer.setFOV(Potree.config.view.fov);
  46. //viewer.loadSettingsFromURL();
  47. Potree.settings.cameraFar = Potree.config.view.far
  48. if(!Potree.settings.isOfficial){
  49. viewer.loadGUI(() => {
  50. viewer.setLanguage('en');
  51. //$("#menu_appearance").next().show();
  52. $("#menu_tools").next().show();
  53. $("#menu_scene").next().show();
  54. $("#siteModel").show();
  55. //$("#alignment").show();
  56. viewer.toggleSidebar();
  57. });
  58. Potree.settings.sizeFitToLevel = true//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2
  59. Potree.settings.rotAroundPoint = false
  60. }
  61. Potree.loadDatasetsCallback = function(data, ifReload){
  62. if(!data || data.length == 0)return console.error('getDataSet加载的数据为空')
  63. Potree.datasetData = data
  64. viewer.transform = null
  65. var datasetLength = data.length
  66. var pointcloudLoaded = 0
  67. var panosLoaded = 0
  68. var pointcloudLoadDone = function(){//点云cloud.js加载完毕后
  69. }
  70. var panosLoadDone = function(){
  71. viewer.images360.loadDone()
  72. viewer.scene.add360Images(viewer.images360);
  73. viewer.mapViewer.addListener(viewer.images360)
  74. viewer.updateModelBound() //需等pano加载完
  75. let {boundSize, center} = viewer.bound
  76. if(!Potree.settings.isOfficial){
  77. Potree.loadMapEntity('all') //加载floorplan
  78. }
  79. Potree.loadNeighborFile()
  80. Potree.setNeighborGui();
  81. if(!ifReload){
  82. viewer.dispatchEvent({type:'loadPointCloudDone'})
  83. if(!Potree.settings.UserPointDensity){
  84. Potree.settings.UserPointDensity = 'high'//'middle'
  85. }
  86. Potree.Log('loadPointCloudDone 点云加载完毕' )
  87. }
  88. {//初始位置
  89. var panoId = browser.urlHasValue('pano',true);
  90. if(panoId !== ''){
  91. var pos
  92. var pano = viewer.images360.panos.find(e=>e.id==panoId);
  93. if(pano){
  94. viewer.images360.focusPano({
  95. pano,
  96. duration:0,
  97. callback:()=>{/* Potree.settings.displayMode = 'showPanos' */}
  98. })
  99. }
  100. }else{//考虑到多数据集距离很远,或者像隧道那种场景,要使视野范围内一定能看到点云,最好初始点设置在漫游点上
  101. let {boundSize, center} = viewer.bound
  102. let pano = viewer.images360.findNearestPano(center)
  103. if(pano){
  104. viewer.images360.flyToPano({
  105. pano, duration:0,
  106. target : viewer.images360.bound.center.setZ(pano.position.z) //平视中心区域(但也不能保证这个方向一定能看到点云密集区,如果在边缘的话)
  107. })
  108. }else{// 无漫游点 避免加载时看不到点云 SG-t-DXmdymgZ2sX SG-t-rVB03a5GXr8
  109. //SG-t-XPf1k9pv3Zg 总有极端的场景,如这是倾斜的桥 - -, 只能调整为看到全局了
  110. viewer.mainViewport.view.pitch = -0.7 //相对俯视
  111. let bound = viewer.bound.boundingBox.clone()
  112. viewer.focusOnObject({boundingBox:bound},'boundingBox',0,{dontChangeCamDir:true})
  113. if(viewer.bound.boundSize.length() > 20){ //否则有可能超出far范围
  114. viewer.mainViewport.camera.far = 10000;
  115. viewer.mainViewport.camera.updateProjectionMatrix()
  116. viewer.fixCamFar = true //不允许修改camera.far
  117. //等有点云加载出来后,再去focus其中一个,使camera.far不超过最大值
  118. let count_ = 0;
  119. let done = ()=>{
  120. viewer.fixCamFar = false
  121. viewer.mainViewport.camera.far = Potree.settings.cameraFar;
  122. viewer.mainViewport.camera.updateProjectionMatrix()
  123. viewer.removeEventListener('pageVisible', focusPoint )
  124. clearTimeout(timer)
  125. }
  126. let timer
  127. let focusPoint = (e)=>{//拉近到某个点
  128. if(e && e.v === false)return
  129. viewer.removeEventListener('pageVisible', focusPoint )
  130. if(Potree.settings.dontShowPoints){//手机 高斯
  131. viewer.focusOnObject({boundingBox:viewer.bound.boundingBox},'boundingBox',0,{})
  132. }else{
  133. let pointcloud = viewer.scene.pointclouds.find(e=>e.root.geometryNode)
  134. console.log('初始加载focus点云', e , pointcloud)
  135. if(!pointcloud){
  136. if(count_ < 10 ){//可能没加载到,可能被隐藏
  137. if(document.hidden){//等回到页面再focus
  138. console.log('focus hidden')
  139. return viewer.addEventListener('pageVisible', focusPoint )
  140. }
  141. count_ ++ //如果在别的
  142. timer = setTimeout(focusPoint, 200)
  143. }else{ //放弃
  144. console.log('初始加载focus点云 放弃')
  145. done()
  146. }
  147. return console.warn('no!!!!!!!!!!!!!!')
  148. }
  149. viewer.flyToDataset({focusOnPoint:true, pointcloud, duration:0, })
  150. //console.warn('ok!!!!!!!!!!!!!!!!')
  151. }
  152. done()
  153. }
  154. let focus = ()=>{
  155. timer = setTimeout(focusPoint, 300)
  156. }
  157. viewer.addEventListener('setPose',()=>{//设置了初始画面
  158. viewer.removeEventListener('pointcloud_changed',focus)
  159. done()
  160. })
  161. viewer.addEventListener('pointcloud_changed',focus,{once:true})//加载了点之后
  162. }
  163. }
  164. }
  165. }
  166. viewer.addVideo()//addFire()
  167. viewer.scene.pointclouds.some(e=>!e.hasDepthTex) && (Potree.config.shelterMargin*=3) //没有深度图的话在全景模式测量有偏差
  168. console.log('allLoaded')
  169. viewer.addTube({
  170. 't-8KbK1JjubE':[
  171. {//地上管子 黄色
  172. 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}
  173. ,{"x":43.58,"y":27.7,"z":-0.97},{"x":40.22,"y":35.37,"z":-0.67}// 拐弯向右
  174. ,{"x":39.18,"y":36.71,"z":0.35},{"x":38.69,"y":36.04,"z":18.04} // 拐弯向上 }
  175. ],
  176. color:'#b86', radius:0.08, height:0.13, tension:0.3, lessSpace:60
  177. },
  178. {//地下管子 藍色
  179. 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}
  180. ],
  181. color:'#48a', radius:0.08, height:-0.5, lessSpace:60
  182. }
  183. ] ,
  184. /* 'SG-t-h0I7LnfGFwj':[ //港华M层
  185. {
  186. 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]]
  187. ,color:'#8c99aa', radius:0.025, height:-0, fromDataset:true, tension:0.01, spaceDis:0.02,visiEntity:'厨房'
  188. },
  189. {
  190. 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]]
  191. ,color:'#8c99aa', radius:0.025, height:-0, fromDataset:true, tension:0.01, spaceDis:0.02,visiEntity:'厨房'
  192. },
  193. {
  194. 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]]
  195. ,color:'#8c99aa', radius:0.02, height:-0, fromDataset:true, tension:0.01, spaceDis:0.02,visiEntity:'厨房'
  196. }
  197. ],
  198. 'SG-t-RGUFiJAoxvL':[ //济南工地覆土后
  199. {//--旧的 整条
  200. 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]]
  201. ,color:'#445', radius:0.08, height:-1.16, fromDataset:true, tension:0.01,// spaceDis:0.1
  202. },
  203. {
  204. path:[[22.726,22.678,-1.938],[-24.541,16.782,-1.319]]
  205. ,color:'#445', radius:0.08, height:-1.16, fromDataset:true, tension:0,// spaceDis:0.1
  206. },
  207. {
  208. path:[[23.519,-19.473,-1.744],[25.117,15.447,-1.783]]
  209. ,color:'#445', radius:0.08, height:-1.16, fromDataset:true, tension:0,// spaceDis:0.1
  210. }
  211. ],
  212. 'SG-t-TK0S5EqWBxd':[ //济南工地
  213. {
  214. 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]]
  215. ,color:'#445', radius:0.08, height:-1.16, fromDataset:true, spaceDis:0.2// tension:0.01,
  216. },
  217. ],*/
  218. })
  219. viewer.dispatchEvent('allLoaded')
  220. }
  221. if(!Potree.settings.originDatasetId)Potree.settings.originDatasetId = data[0].id
  222. var originDataset = data.find(e=>e.id == Potree.settings.originDatasetId)
  223. baseZ = originDataset.location[2]
  224. /* originDataset.location[0] = 113.60608878174709
  225. originDataset.location[1] = 22.381189423935155
  226. let ano = data.find(e=>e.name == 'SG-t-Jw0xyhL6oSY')
  227. ano.location[0] = 113.6060509967498
  228. ano.location[1] = 22.381061273279244 */
  229. {//拿初始数据集作为基准。它的位置要放到000
  230. var locationLonLat = originDataset.location.slice(0,2)
  231. Potree.setLonlat(locationLonLat[0], locationLonLat[1])
  232. viewer.mapViewer && viewer.mapViewer.mapLayer.maps[0].updateProjection()
  233. }
  234. data.forEach((dataset,index)=>{
  235. if(!ifReload){
  236. var datasetCode = dataset.sceneCode || dataset.name //对应4dkk的场景码
  237. if(Potree.settings.isLocal && dataset.mapping){
  238. var cloudPath = `${Potree.settings.urls.prefix1}/${dataset.mapping}/${dataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢
  239. }else{
  240. var cloudPath = `${Potree.settings.urls.prefix1}/${dataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢
  241. }
  242. //var cloudPath = `${Potree.scriptPath}/data/test/${dataset.name}/cloud.js`
  243. var timeStamp = dataset.updateTime ? dataset.updateTime.replace(/[^0-9]/ig,'') : ''; //每重算一次后缀随updateTime更新一次
  244. //console.warn(dataset.name, 'timeStamp', timeStamp)
  245. Potree.loadPointCloud(cloudPath, dataset.name ,datasetCode, timeStamp, e => {
  246. let scene = viewer.scene;
  247. let pointcloud = e.pointcloud;
  248. let config = Potree.config.material
  249. let material = pointcloud.material;
  250. pointcloud.datasetData = dataset
  251. pointcloud.dataset_id = dataset.id;//供漫游点找到属于的dataset点云
  252. pointcloud.hasDepthTex = Potree.settings.useDepthTex && (!!dataset.has_depth || Potree.settings.isLocalhost && Potree.settings.number == 'SS-t-7DUfWAUZ3V') //test
  253. pointcloud.hasTempTex = !!dataset.has_ir
  254. material.minSize = config.minSize
  255. material.maxSize = config.maxSize
  256. material.pointSizeType =/* Potree.settings.isOfficial ? */ config.pointSizeType /* : 'ADAPTIVE' */ //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED
  257. pointcloud.changePointSize(config.realPointSize) //material.size = config.pointSize;
  258. pointcloud.changePointOpacity(1)
  259. material.shape = Potree.PointShape.SQUARE;
  260. pointcloud.color = pointcloud.material.color = dataset.color
  261. pointcloud.timeStamp = timeStamp
  262. transformPointcloud(pointcloud,dataset)
  263. scene.addPointCloud(pointcloud);
  264. if(!Potree.settings.isOfficial){
  265. Potree.settings.floorplanEnables[dataset.id] = true
  266. Potree.settings.floorplanType[dataset.id] = 'default'
  267. }
  268. pointcloudLoaded ++;
  269. if(pointcloudLoaded == datasetLength)pointcloudLoadDone()
  270. {
  271. let resolution = dataset.sceneResolution || '4k' //融合页面还有一个tileRes
  272. if(resolution.indexOf('/') != -1) {
  273. resolution = resolution.split('/')[1]
  274. }
  275. pointcloud.tileClass = parseInt(resolution) * 1024
  276. }
  277. Potree.loadPanos(dataset.id, (data) => {
  278. //console.log('loadPanos',dataset.sceneCode, dataset.id, data)
  279. viewer.images360.addPanoData(data, pointcloud)
  280. panosLoaded ++;
  281. let f = ()=>{
  282. if(panosLoaded == datasetLength){
  283. Potree.loadImgVersion((e={})=>{
  284. Potree.settings.panoVersion = e.imgVersion//全景图被替换后
  285. panosLoadDone()
  286. })
  287. }
  288. }
  289. if(dataset.has_ir){//红外热成像漫游点数据
  290. /* if(Potree.settings.isLocal && dataset.mapping){
  291. var cloudPath = `${Potree.settings.urls.prefix1}/${dataset.mapping}/${dataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢
  292. } */
  293. let mapping = Potree.settings.isLocal && dataset.mapping ? ('/'+dataset.mapping): ''
  294. pointcloud.typesUrl = `${Potree.settings.urls.prefix1}${mapping}/${Potree.settings.webSite2 || Potree.settings.webSite}/${pointcloud.sceneCode}/data/${pointcloud.sceneCode}/imagemap/`
  295. let typesUrl = pointcloud.typesUrl + 'types.json'
  296. Potree.loadFile(typesUrl,null,(e)=>{
  297. e.imagemap_types.forEach(o=>{
  298. o.uuids.forEach(uuid=>{
  299. //if(o.property == 'temp')return //只要ir
  300. let pano = viewer.images360.getPano(`${pointcloud.dataset_id}|${uuid}`,'sid')
  301. pano['has_'+o.property] = true
  302. })
  303. })
  304. f()
  305. },()=>{
  306. console.error('红外热成像数据加载失败 ')
  307. f()
  308. })
  309. }else{
  310. f()
  311. }
  312. })
  313. })
  314. }else{
  315. let pointcloud = viewer.scene.pointclouds.find(p => p.dataset_id == dataset.id)
  316. if(!pointcloud){
  317. Potree.Log('数据集id变了,自动使用第一个', {font: {color:'#500' }} )
  318. pointcloud = viewer.scene.pointclouds[0]
  319. }
  320. //先归零
  321. Alignment.translate(pointcloud, pointcloud.translateUser.clone().negate())
  322. Alignment.rotate(pointcloud, null, - pointcloud.orientationUser)
  323. transformPointcloud(pointcloud, dataset)
  324. }
  325. })
  326. if(ifReload){
  327. //loadDone()
  328. }
  329. }
  330. if(number){
  331. if(window._datasetList){
  332. Potree.loadDatasetsCallback(window._datasetList)
  333. }else{
  334. Potree.loadDatasets(Potree.loadDatasetsCallback)
  335. }
  336. }
  337. /*
  338. //调试用,加载多个本地
  339. Potree.loadDatasetsCallback([
  340. {name:'webcloud_0',id:0, orientation:0, location:[0,0,0]},
  341. {name:'webcloud_1',id:1, orientation:0.047234761795199476, location:[-0.07925513345058573,-0.0010590072536559839,-2.403613132687564]},
  342. {name:'webcloud_2',id:2, orientation:-1.5299545647758208, location:[1.5603736310030292, -0.0009340812579088904, -2.4464530770974139]},
  343. ]) */
  344. window.testTransform = function(locationLonLat, location1, location2){
  345. proj4.defs("NAVVIS:test", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15));
  346. let transform = proj4("WGS84", "NAVVIS:test"); //这个ok navvis里也是这两种转换 见proj4Factory
  347. if(location1){//经纬度
  348. return transform.forward(location1)
  349. }else{
  350. return transform.inverse(location2)
  351. }
  352. }
  353. window.buttonFunction = function(){
  354. viewer.scene.pointclouds.forEach(e=>e.predictNodeMaxLevel())
  355. /*
  356. viewer.startScreenshot({type:'measure', measurement:viewer.scene.measurements[0]})
  357. viewer.modules.RouteGuider.routeStart = new THREE.Vector3(0,0,-1.3)
  358. viewer.modules.RouteGuider.routeEnd = new THREE.Vector3(-10,0,-1.3)
  359. */
  360. }
  361. if(Potree.settings.isLocalhost){
  362. let before = {}
  363. viewer.inputHandler.addEventListener('keydown',e=>{ //测试的代码
  364. if(e.event.key == 't'){
  365. viewer.images360.cube.visible = true
  366. viewer.images360.cube.material.wireframe = true
  367. }else if(e.event.key == 'y'){
  368. viewer.images360.cube.material.wireframe = false
  369. viewer.images360.cube.visible = Potree.settings.displayMode == 'showPanos'
  370. }
  371. })
  372. //--------------------------------
  373. /* if(!number){
  374. Potree.settings.boundAddObjs = true
  375. Potree.settings.intersectOnObjs = true
  376. // Load untextured bunny from ply
  377. viewer.loadModel({
  378. fileType:'ply',
  379. url:Potree.resourcePath + "/models/indoor.ply",
  380. name:'test',
  381. },
  382. (object)=>{
  383. object.isModel = true
  384. viewer.updateModelBound()
  385. }
  386. )
  387. } */
  388. }
  389. }
  390. //=======================================================================
  391. /*
  392. 漫游点编辑
  393. */
  394. //=======================================================================
  395. export function panoEditStart(dom, number, EditCloudsArgs){
  396. Potree.settings.editType = 'pano'
  397. Potree.settings.number = number
  398. Potree.settings.sizeFitToLevel = true//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2
  399. let datasetData
  400. let viewer = new Potree.Viewer(dom);
  401. let Alignment = viewer.modules.Alignment
  402. viewer.setEDLEnabled(false);
  403. viewer.setFOV(Potree.config.view.fov);
  404. //viewer.loadSettingsFromURL();
  405. let datasetLoaded = 0;
  406. if(!Potree.settings.isOfficial){
  407. viewer.loadGUI(() => {
  408. viewer.setLanguage('en');
  409. $("#menu_tools").next().show();
  410. $("#panos").show();
  411. $("#alignment").show();
  412. viewer.toggleSidebar();
  413. });
  414. Potree.settings.sizeFitToLevel = true
  415. }
  416. var pointcloudLoadDone = function( ){//所有点云cloud.js加载完毕后
  417. viewer.scene.pointclouds.forEach(c=>{
  418. transformPointcloud(c)
  419. })
  420. viewer.images360.loadDone()
  421. viewer.scene.add360Images(viewer.images360);
  422. viewer.updateModelBound()
  423. let {boundSize, center} = viewer.bound
  424. Potree.Log(`中心点: ${math.toPrecision(center.toArray(),2)}, boundSize: ${math.toPrecision(boundSize.toArray(),2)} ` )
  425. viewer.scene.view.setView({
  426. position: center.clone().add(new THREE.Vector3(10,5,10)),
  427. target: center
  428. })
  429. viewer.dispatchEvent({type:'loadPointCloudDone'})
  430. if(!Potree.settings.UserPointDensity){
  431. Potree.settings.UserPointDensity = 'panoEdit'//'middle'
  432. }
  433. Potree.Log('loadPointCloudDone 点云加载完毕',{font:[null, 10]})
  434. viewer.dispatchEvent('allLoaded');
  435. }
  436. var transformPointcloud = (pointcloud )=>{ //初始化位置
  437. viewer.sidebar && viewer.sidebar.addAlignmentButton(pointcloud)
  438. /* let orientation = pointcloud.panos[0].dataRotation.z + Math.PI
  439. let location = pointcloud.panos[0].dataPosition.clone()//.negate()
  440. Alignment.rotate(pointcloud, null, orientation )
  441. Alignment.translate(pointcloud, location ) */
  442. viewer.modules.PanoEditor.initCloud(pointcloud)
  443. pointcloud.updateMatrixWorld()
  444. }
  445. let loadPanosDone = Potree.loadPanosDone = (datasetId, panoData )=>{ //一个数据集获取到它的panos后
  446. Potree.settings.datasetsPanos[datasetId] = {panoData, panos:[]}
  447. console.log('panoData', datasetId, panoData)
  448. let panoCount = panoData.length
  449. let pointcloudLoaded = 0
  450. let datasetsCount = Object.keys(Potree.settings.datasetsPanos).length
  451. let datasetData = Potree.datasetData.find(e=>e.datasetId = datasetId)
  452. panoData.forEach((pano, index)=>{
  453. //let cloudPath = `${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${Potree.settings.number}/data/bundle_${Potree.settings.number}/building/uuidcloud/${pano.uuid}/cloud.js`
  454. //2024.12.9: 加mapping
  455. let mapping = (Potree.settings.isLocal && (datasetData ? datasetData.mapping : browser.urlHasValue('mapping',true))) || ''
  456. mapping && (mapping += '/')
  457. let cloudPath = `${Potree.settings.urls.prefix1}/${mapping}${Potree.settings.webSite}/${Potree.settings.number}/data/bundle_${Potree.settings.number}/building/uuidcloud/${pano.uuid}/cloud.js`
  458. /* if(Potree.settings.isLocal && dataset.mapping){
  459. var cloudPath = `${Potree.settings.urls.prefix1}/${dataset.mapping}/${dataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢
  460. }else{
  461. var cloudPath = `${Potree.settings.urls.prefix1}/${dataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢
  462. } */
  463. let name = datasetId + '-'+pano.uuid
  464. let timeStamp = 0
  465. pano.index = index //注意:index不等于uuid,因为有的uuid缺失。但是visibles中存的是下标!
  466. Potree.loadPointCloud(cloudPath, name , number, timeStamp, e => { //开始加载点云
  467. let scene = viewer.scene;
  468. let pointcloud = e.pointcloud;
  469. let config = Potree.config.material
  470. let material = pointcloud.material;
  471. material.minSize = config.minSize
  472. material.maxSize = config.maxSize
  473. material.pointSizeType = /* 'ADAPTIVE'// */config.pointSizeType //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED
  474. pointcloud.changePointSize( 0.1 /* config.realPointSize */ ) //material.size = config.pointSize;
  475. pointcloud.changePointOpacity(1)
  476. material.shape = Potree.PointShape.SQUARE;
  477. pointcloud.color = config.pointColor
  478. pointcloud.dataset_id = datasetId; //多个点云指向一个datasetId
  479. pointcloud.panoUuid = pano.uuid
  480. pointcloud.timeStamp = timeStamp
  481. if(datasetData){//非4dkk
  482. pointcloud.datasetData = datasetData
  483. pointcloud.hasDepthTex = Potree.settings.useDepthTex && !!datasetData.has_depth
  484. }
  485. //transformPointcloud(pointcloud, pano)
  486. scene.addPointCloud(pointcloud);
  487. pointcloudLoaded ++;
  488. if(pointcloudLoaded == panoCount ){
  489. datasetLoaded ++
  490. viewer.images360.addPanoData(panoData , pointcloud )
  491. if(datasetLoaded == datasetsCount){
  492. pointcloudLoadDone()
  493. }
  494. }
  495. })
  496. })
  497. }
  498. if(!Potree.settings.isOfficial){
  499. Potree.settings.datasetsPano = {'testDataset':null}
  500. Potree.loadPanosInfo( data=>{loadPanosDone('testDataset', data.sweepLocations)} )
  501. }
  502. Potree.loadPanosCloudStart = (EditCloudsArgs)=>{
  503. Potree.loadDatasets((datasets)=>{
  504. Potree.datasetData = datasets //4dkk的加载为空[]
  505. Potree.loadImgVersion((e={})=>{
  506. Potree.settings.panoVersion = e.imgVersion//全景图被替换后
  507. if(!(EditCloudsArgs instanceof Array)) EditCloudsArgs = [EditCloudsArgs]
  508. EditCloudsArgs.forEach((e,i)=>{ //加载点云和漫游点
  509. if(e.datasetId == void 0){
  510. console.error('没有datasetId ')// 看看和看见没有或为0
  511. e.datasetId = i; //经常没有datasetId所以自己加
  512. }
  513. Potree.settings.datasetsPanos[e.datasetId] = null
  514. Potree.loadPanosDone(e.datasetId, e.clouds)
  515. })
  516. })
  517. },number)
  518. }
  519. EditCloudsArgs && Potree.loadPanosCloudStart(EditCloudsArgs)
  520. }
  521. ///////////////////////////////////////////////////////-------------------------
  522. ///////////////////////////////////////////////////////-------------------------
  523. ///////////////////////////////////////////////////////-------------------------
  524. export function realTimePanosStart(dom, number){//边拍边传,和点位编辑一样每个点位一个电源
  525. Potree.settings.editType = 'singlePanoToCloud'
  526. Potree.settings.isLocal = Potree.settings.tileOriginUrl = true;
  527. Potree.settings.intersectOnObjs = true
  528. Potree.settings.boundAddObjs = true
  529. Potree.settings.UserPointDensity = 'high'//'middle'
  530. let viewer = new Potree.Viewer(dom, null, {noMap:true});
  531. let Alignment = viewer.modules.Alignment
  532. viewer.setEDLEnabled(false);
  533. viewer.setFOV(Potree.config.view.fov);
  534. Potree.settings.urls.templates.panoTile = location.origin + '/laser-data/testdata/{sceneCode}/data/preview/{id}/'
  535. const rotQua = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0), -Math.PI / 2 )
  536. //new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), -Math.PI )
  537. const rotQua2 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), -Math.PI )
  538. viewer.scene.add360Images(viewer.images360);
  539. const datasetId = 1
  540. Potree.loadPanos = function(panoData, callback){
  541. panoData = panoData.filter(e=> !viewer.images360.panos.some(p=>p.uuid == e.uuid))
  542. let panoCount = panoData.length
  543. let pointcloudLoaded = 0
  544. if(!panoCount){
  545. return callback()
  546. }
  547. console.log('loadPanos',panoData)
  548. panoData.forEach((d,index)=>{
  549. let timeStamp = 0
  550. let cloudPath = `laser-data/testdata/${number}/data/preview/${d.uuid}/webcloud/cloud.js`
  551. //pano.index = index //注意:index不等于uuid,因为有的uuid缺失。但是visibles中存的是下标!
  552. Potree.loadPointCloud(cloudPath, d.uuid , number, timeStamp, e => { //开始加载点云
  553. let pointcloud = e.pointcloud;
  554. let config = Potree.config.material
  555. let material = pointcloud.material;
  556. material.minSize = config.minSize
  557. material.maxSize = config.maxSize
  558. material.pointSizeType = /* 'ADAPTIVE'// */config.pointSizeType //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED
  559. pointcloud.changePointSize( 0.1 /* config.realPointSize */ ) //material.size = config.pointSize;
  560. pointcloud.changePointOpacity(1)
  561. material.shape = Potree.PointShape.SQUARE;
  562. pointcloud.color = config.pointColor
  563. pointcloud.dataset_id = datasetId; //多个点云指向一个datasetId
  564. pointcloud.panoUuid = d.uuid ///!!
  565. pointcloud.timeStamp = timeStamp
  566. /* if(datasetData){//非4dkk
  567. pointcloud.datasetData = datasetData
  568. pointcloud.hasDepthTex = Potree.settings.useDepthTex && !!datasetData.has_depth
  569. } */
  570. viewer.scene.addPointCloud(pointcloud);
  571. pointcloudLoaded ++;
  572. if(pointcloudLoaded == panoCount ){
  573. let datas = panoData.map(p=>{
  574. let newData = Object.assign(p,{
  575. single:true
  576. })
  577. return newData
  578. })
  579. viewer.images360.addPanoData(datas )
  580. panoData.forEach(ee => {
  581. let pano = viewer.images360.panos.find(e=>e.uuid == ee.uuid)
  582. let data = pano.panoData
  583. let dataPosition = new THREE.Vector3().fromArray(data.pose3d.position)
  584. let qua = data.pose3d.rotation
  585. qua = [qua[1], qua[2], qua[3], qua[0]]
  586. let dataQuaternion = new THREE.Quaternion().fromArray(qua)
  587. pano.pointcloud.orientationUser = new THREE.Quaternion().multiplyQuaternions(dataQuaternion,rotQua2)//新版
  588. pano.pointcloud.translateUser.copy(dataPosition)
  589. Alignment.setMatrix(pano.pointcloud);
  590. })
  591. viewer.images360.loadDone()
  592. callback()
  593. /*
  594. let {boundSize, center} = viewer.bound
  595. Potree.Log(`中心点: ${math.toPrecision(center.toArray(),2)}, boundSize: ${math.toPrecision(boundSize.toArray(),2)} ` )
  596. viewer.scene.view.setView({
  597. position: center.clone().add(new THREE.Vector3(10,5,10)),
  598. target: center
  599. }) */
  600. }
  601. })
  602. })
  603. }
  604. }
  605. ///////////////////////////////////////////////////////////////-----------------------
  606. export function mergeEditStart(dom, mapDom){
  607. Potree.settings.editType = 'merge'
  608. Potree.settings.intersectOnObjs = true
  609. Potree.settings.boundAddObjs = true
  610. Potree.settings.unableNavigate = true
  611. if(Potree.settings.queryCloudLonLatUrl){ //点云使用其经纬度作为默认位置
  612. Potree.loadControlPoint = /* async */function(callback,sceneCode,onError,prefix){//点云绑定地图
  613. return Potree.loadFile(Potree.settings.queryCloudLonLatUrl.replace('{sceneCode}', sceneCode), {
  614. fetchMethod: 'post'
  615. }, callback,onError)
  616. }
  617. }
  618. let viewer = new Potree.Viewer(dom, mapDom );
  619. let Alignment = viewer.modules.Alignment
  620. viewer.setEDLEnabled(false);
  621. viewer.setFOV(Potree.config.view.fov);
  622. //viewer.loadSettingsFromURL();
  623. {
  624. viewer.mainViewport.view.position.set(30,30,30)
  625. viewer.mainViewport.view.lookAt(0,0,0)
  626. viewer.updateModelBound()//init
  627. //this.bound = new THREE.Box3(new THREE.Vector3(-1,-1,-1),new THREE.Vector3(1,1,1))
  628. viewer.transformationTool.setModeEnable(['translation','rotation'] )
  629. //viewer.ssaaRenderPass.sampleLevel = 1 // sampleLevel为1 的话,ground就不会变黑
  630. viewer.inputHandler.fixSelection = true //不通过点击屏幕而切换transfrom选中状态
  631. }
  632. Potree.settings.sizeFitToLevel = true//当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2
  633. Potree.loadPointCloudScene = function(url, type, id, title, done, onError, prefix1, prefix2 ){//对应4dkk的场景码
  634. let dataset , useLonLat
  635. let loadCloud = ({cloudPath, sceneName, sceneCode, timeStamp, color } )=>{
  636. Potree.loadPointCloud(cloudPath, sceneName , sceneCode, timeStamp, e => {
  637. let scene = viewer.scene;
  638. let pointcloud = e.pointcloud;
  639. let config = Potree.config.material
  640. let material = pointcloud.material;
  641. pointcloud.datasetData = dataset
  642. pointcloud.hasDepthTex = dataset && Potree.settings.useDepthTex && !!dataset.has_depth
  643. pointcloud.initialPosition = pointcloud.position.clone()
  644. pointcloud.pos1MatrixInvert = new THREE.Matrix4().setPosition(pointcloud.initialPosition).invert()
  645. material.minSize = config.minSize
  646. material.maxSize = config.maxSize
  647. material.pointSizeType = config.pointSizeType //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED
  648. pointcloud.changePointSize(config.realPointSize) //material.size = config.pointSize;
  649. pointcloud.changePointOpacity(1)
  650. dataset && useLonLat && (transformPointcloud(pointcloud, dataset), pointcloud.hasLonLat = true)
  651. material.shape = Potree.PointShape.SQUARE;
  652. color && (pointcloud.color = pointcloud.material.color = color)
  653. pointcloud.timeStamp = timeStamp
  654. scene.addPointCloud(pointcloud);
  655. {
  656. viewer.updateModelBound()
  657. let {boundSize, center} = viewer.bound
  658. viewer.dispatchEvent({type:'loadPointCloudDone'})
  659. if(!Potree.settings.UserPointDensity){
  660. Potree.settings.UserPointDensity = 'high'//'middle'
  661. }
  662. Potree.Log(' 点云加载完毕', sceneName, sceneCode )
  663. }
  664. viewer.dispatchEvent('allLoaded')
  665. done(pointcloud)
  666. },onError)
  667. }
  668. if(type == 'laser'){
  669. let sceneCode = url
  670. let load = ()=>{
  671. Potree.loadDatasets((data)=>{
  672. let originDataset = data.find(e=>e.sceneCode == sceneCode);//只加载初始数据集
  673. /* if(!originDataset){
  674. //应该是data为空,原因未知
  675. } */
  676. Potree.settings.cloudAddMapping && (prefix2 += '/'+originDataset.mapping)
  677. let timeStamp = originDataset.updateTime ? originDataset.updateTime.replace(/[^0-9]/ig,'') : ''; //每重算一次后缀随updateTime更新一次
  678. //let cloudPath = `${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${sceneCode}/data/${sceneCode}/webcloud/cloud.js`
  679. let cloudPath = `${prefix2}/${originDataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢
  680. dataset = originDataset
  681. loadCloud({ cloudPath, sceneName:originDataset.sceneName, sceneCode, timeStamp, color:originDataset.color})
  682. }, sceneCode, onError, prefix1 )
  683. }
  684. if(Potree.settings.queryCloudLonLatUrl){
  685. Potree.loadControlPoint((ctlData)=>{
  686. useLonLat = ctlData.controlPoint.status==1 //设置了地理位置
  687. load()
  688. }, sceneCode, (e)=>{
  689. console.error('loadControlPoint error',e)
  690. load()
  691. })
  692. }else{
  693. load()
  694. }
  695. }else{//las or ply 直接用url
  696. let name = type + '|' + id + '|' + title
  697. //有漫游点吗
  698. if(url instanceof Array){
  699. if(url.length == 1){
  700. url = url[0]
  701. }else{
  702. console.error('有多个点云?暂时还不支持', url, name)//多个点云要一起移动没想好怎么写
  703. }
  704. }
  705. let cloudPath = url + '/cloud.js'
  706. loadCloud({cloudPath, sceneName:name, sceneCode:name, timeStamp:'' })
  707. }
  708. }
  709. let setMatrix = (pointcloud)=>{//为了漫游点变换,要算一下 类似setMatrix
  710. /* pointcloud.transformMatrix = new THREE.Matrix4().multiplyMatrices(pointcloud.matrix, pointcloud.pos1MatrixInvert)//还原一点位移
  711. pointcloud.transformInvMatrix.copy(pointcloud.transformMatrix).invert()
  712. pointcloud.rotateMatrix = new THREE.Matrix4().makeRotationFromEuler(pointcloud.rotation);
  713. pointcloud.rotateInvMatrix.copy(pointcloud.rotateMatrix).invert()
  714. pointcloud.panos.forEach(e=>e.transformByPointcloud()) */
  715. //pointcloud.updateBound()
  716. //pointcloud.getPanosBound()
  717. viewer.updateModelBound()
  718. }
  719. const moveModelWhenLoad = false
  720. let moveModel = (e)=>{//根据鼠标移动的位置改变位置
  721. let {x,y} = Potree.Utils.getPointerPosAtHeight(0,e.pointer)
  722. //过后改为根据intersect的点来设置底部高度;这样的话,需要发送高度
  723. /*let pos = new THREE.Vector3(x,y, planeZ )
  724. modelEditing.updateMatrixWorld()
  725. let boundCenter = modelEditing.boundingBox.getCenter(new THREE.Vector3).applyMatrix4(modelEditing.matrixWorld);
  726. */
  727. MergeEditor.moveBoundCenterTo(modelEditing,new THREE.Vector3(x,y, modelEditing.boundCenter.z)) //使模型中心的xy在鼠标所在位置
  728. modelEditing.dispatchEvent("position_changed")
  729. }
  730. let cancelMove = ()=>{
  731. modelEditing = null
  732. viewer.removeEventListener('global_mousemove', moveModel);
  733. viewer.removeEventListener('global_click', confirmPos);
  734. }
  735. let confirmPos = ()=>{
  736. MergeEditor.focusOn(modelEditing)
  737. cancelMove()
  738. return {stopContinue:true}
  739. }
  740. let modelType, modelEditing, MergeEditor = viewer.modules.MergeEditor
  741. Potree.addModel = function(prop, done, onProgress, onError){ //加载模型
  742. let loadDone = (model)=>{
  743. model.dataset_id = prop.id //唯一标识
  744. {//设置下默认经纬度位置,当点击恢复默认时要恢复到此位置
  745. if(!model.isPointcloud){ //有经纬度 3dtiles
  746. model.rotation.copy(prop.baseRotation) //有的需要翻转90度
  747. let lonlat = prop.raw.wgs84 || prop.raw.rtkLocation //前者为素材库的osgb的
  748. if(lonlat){
  749. var locationLonLat = lonlat.split(',').map(e=>parseFloat(e))
  750. var location = new THREE.Vector3().fromArray(viewer.transform.lonlatToLocal.forward(locationLonLat))
  751. model.hasLonLat = true
  752. if( model.fileType == '3dTiles' && prop.is4dkkModel ){ //深时深光mesh
  753. model.position.fromArray(model.runtime.getTileset().tileset.root.boundingVolume.box.slice(0,3))//必须要平移一段才能重合
  754. model.position.copy(Potree.math.convertVector.ZupToYup(model.position))
  755. //饶原点旋转 类似Alignment.setMatrix 和点云一样处理
  756. if(prop.raw.orientation){
  757. model.updateMatrixWorld()//此时和点云没有旋转平移时一样。
  758. let rotMatrix = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(0,0,1), parseFloat(prop.raw.orientation) )
  759. let pos2Matrix = new THREE.Matrix4().setPosition(location);//最后是平移
  760. var matrix = new THREE.Matrix4().multiplyMatrices(pos2Matrix, rotMatrix);
  761. model.matrix.premultiply(matrix)
  762. model.matrix.decompose(model.position,model.quaternion,model.scale)
  763. }else{
  764. model.position.add(location)
  765. }
  766. }else{
  767. MergeEditor.moveBoundCenterTo(model, location )
  768. if(prop.raw.orientation){
  769. model.rotation.y = parseFloat(prop.raw.orientation)
  770. }
  771. if(prop.is4dkkModel){
  772. console.warn('遇到is4dkkModel的且有经纬度的mesh,但不是3dtiles! 位置估计不准', model)
  773. //看见的场景说是市场不会带rtk的。所以我们这边标品也没存rtk的坐标信息。和产品聊了,不处理。意思就是,只有激光场景开启了rtk的rtkLocation才有值
  774. /* if(model.panos?.length){//只能通过漫游点经纬度来校准
  775. //但这时候panos还没加载。。。。
  776. let sceneCode = ...从url中解析
  777. Potree.loadDatasets((data)=>{ //获取datasetId
  778. let originDataset = data.find(e=>e.sceneCode == sceneCode);//只加载初始数据集
  779. Potree.loadPanos(originDataset.datasetId,()=>{
  780. shouldPos = 获取坐标 data[0].location经纬度
  781. model.position.add(new THREE.Vector3().subVectors(shouldPos, model.panos[0].position))
  782. })
  783. }, sceneCode, (e)=>{
  784. console.log(e)
  785. } , prop.prefix)
  786. } */
  787. }
  788. }
  789. }
  790. }
  791. if(model.fileType == 'shp'){
  792. if(model.prjNotSure){
  793. MergeEditor.moveBoundCenterTo(model,new THREE.Vector3() )//因为不确定坐标类型,而点坐标可能几万米,所以放原点好一些
  794. }
  795. model.hasLonLat = true
  796. }
  797. MergeEditor.setModelBtmHeight(model, 0)// 离地高度为0 (因为不想在地图下方所以高程不管了,都在地面上即可)
  798. if(model.hasLonLat){
  799. model.lonLatPos = model.position.clone()
  800. model.lonLatRot = model.rotation.clone()
  801. }
  802. }
  803. if(prop.position ){
  804. if( prop.position.x != 0 || prop.position.y != 0 || prop.position.z != 0 ){//移动过后使用移动后的坐标
  805. model.position.copy(prop.position)
  806. }else{//用户没设置位置(首次添加后没按保存)
  807. }
  808. }
  809. if(prop.rotation && (prop.rotation.x != 0 || prop.rotation.y != 0 || prop.rotation.z != 0 )){
  810. //model.rotation.setFromVector3(prop.rotation)
  811. model.rotation.copy(prop.rotation)
  812. }
  813. if(prop.scale != void 0){
  814. model.scale.set(prop.scale,prop.scale,prop.scale)
  815. }
  816. if(model.isPointcloud){
  817. model.renderOrder = Potree.config.renderOrders.model; //same as glb
  818. }
  819. if(Potree.settings.maintainBtmZ)
  820. {//transform --------维持离地高度和中心点的版本(local ver)
  821. let updateBound = ()=>{
  822. model.updateMatrixWorld()
  823. viewer.updateModelBound()
  824. }
  825. let maintainBtmZAndCenter = ()=>{
  826. MergeEditor.maintainBoundXY(model)
  827. MergeEditor.setModelBtmHeight(model)
  828. updateBound()
  829. model.dispatchEvent('transformChanged')
  830. }
  831. model.addEventListener('position_changed', ()=>{
  832. updateBound()
  833. MergeEditor.getBoundCenter(model);//更新boundcenter
  834. MergeEditor.computeBtmHeight(model)
  835. if(prop.bottomRange && (model.btmHeight > prop.bottomRange.max || model.btmHeight < prop.bottomRange.min)){
  836. model.btmHeight = THREE.Math.clamp(model.btmHeight, prop.bottomRange.min, prop.bottomRange.max)
  837. MergeEditor.setModelBtmHeight(model)
  838. updateBound()
  839. }
  840. model.dispatchEvent('transformChanged')
  841. })
  842. model.addEventListener("rotation_changed", maintainBtmZAndCenter )
  843. model.addEventListener("scale_changed", maintainBtmZAndCenter )
  844. model.addEventListener('transformChanged', ()=>{
  845. MergeEditor.modelTransformCallback(model)
  846. })
  847. //离地高度只是boundingbox在transform后的最低点的高度,而非模型transform后的最低点的高度,所以旋转过后看起来不太准确
  848. } else
  849. {//transform --------维持中心点的版本
  850. let updateBound = ()=>{
  851. model.updateMatrixWorld()
  852. viewer.updateModelBound()
  853. }
  854. let maintainCenter = (e)=>{
  855. //MergeEditor.maintainBoundXY(model)
  856. /* e.by2d || */ e.byControl && MergeEditor.maintainBoundCenter(model)
  857. updateBound()
  858. model.dispatchEvent({type:'transformChanged', byControl:e.byControl})
  859. }
  860. model.addEventListener('position_changed', (e)=>{//要先发送position_changed再其他
  861. updateBound()
  862. MergeEditor.getBoundCenter(model);//更新boundcenter
  863. model.dispatchEvent({type:'transformChanged', byControl:e.byControl})
  864. })
  865. model.addEventListener("rotation_changed", maintainCenter )
  866. model.addEventListener("scale_changed", maintainCenter )
  867. model.addEventListener('transformChanged', ()=>{
  868. MergeEditor.modelTransformCallback(model)
  869. })
  870. }
  871. model.updateMatrixWorld()
  872. viewer.updateModelBound()
  873. MergeEditor.getBoundCenter(model) //初始化
  874. //model.lastMatrixWorld = model.matrixWorld.clone()
  875. model.lastMatrixWorld = new THREE.Matrix4
  876. MergeEditor.modelTransformCallback(model, true)
  877. prop.scale != void 0 && model.isPointcloud && model.changePointSize() //有的被缩放的很小导致testMaxNodeLevel时距离较远时被return 但点云过大急需changesize
  878. done(model) // 先发送成功,因为2d界面会随机执行changePosition等初始化,然后这边再将模型移到中心地面上
  879. if(prop.isFirstLoad){
  880. if(model.hasLonLat || !moveModelWhenLoad){
  881. setTimeout(()=>{MergeEditor.focusOn(model)} , 1)
  882. }else{
  883. MergeEditor.moveBoundCenterTo(model, new THREE.Vector3(0,0,0))
  884. }
  885. MergeEditor.setModelBtmHeight(model, 0) //初始加载设置离地高度为0
  886. if(prop.mode != 'single'){//如果不是模型展示页,模型会随着鼠标位置移动
  887. modelEditing = model;
  888. if(!model.hasLonLat && moveModelWhenLoad){
  889. viewer.addEventListener('global_mousemove', moveModel);
  890. viewer.addEventListener('global_click', confirmPos, {importance:3});
  891. }
  892. }
  893. model.dispatchEvent("position_changed")
  894. }else{
  895. //MergeEditor.setModelBtmHeight(model, prop.bottom || 0) //默认离地高度为0
  896. modelEditing = null
  897. }
  898. MergeEditor.modelAdded(model)
  899. }
  900. if(prop.type == 'obj' || prop.type == 'glb'){
  901. let callback = (object)=>{
  902. object.isModel = true
  903. object.traverse(e=>e.material && (e.material.transparent = true))
  904. loadDone(object)
  905. }
  906. let info = {
  907. fileType: prop.type,
  908. id: prop.id,
  909. unlit: prop.unlit,
  910. url : prop.url,
  911. name : prop.title,
  912. }
  913. viewer.loadModel(info , callback, onProgress, onError)
  914. }else if(prop.type == 'osgb' || prop.type == 'b3dm'){ //3d tiles
  915. let callback = (object)=>{
  916. object.isModel = true
  917. loadDone(object)
  918. }
  919. viewer.loadModel({
  920. fileType: '3dTiles',
  921. id: prop.id,
  922. name : prop.title,
  923. maximumScreenSpaceError: prop.maximumScreenSpaceError,
  924. url:prop.url,
  925. },callback,onprogress)
  926. }else if(prop.type == 'shp'){
  927. let callback = (object)=>{
  928. object.isModel = true
  929. loadDone(object)
  930. }
  931. viewer.loadModel({
  932. fileType: 'shp',
  933. id: prop.id,
  934. name : prop.title,
  935. url:prop.url,
  936. },callback,onprogress)
  937. }else if(prop.type == '3dgs'){
  938. let callback = (object)=>{
  939. object.isModel = true
  940. loadDone(object)
  941. }
  942. viewer.loadModel({
  943. fileType: '3dgs',
  944. id: prop.id,
  945. name : prop.title,
  946. url:prop.url,
  947. },callback,onprogress)
  948. }else{
  949. prop.url instanceof Array && (prop.url = prop.url[0]) //deal bug
  950. let cloudPrefix = Potree.settings.urls.getPrefix(1, prop)
  951. Potree.loadPointCloudScene(prop.url, prop.type, prop.modelId, prop.title, (pointcloud)=>{
  952. {
  953. pointcloud.matrixAutoUpdate = true
  954. if(pointcloud.hasLonLat){
  955. pointcloud.matrix.decompose(pointcloud.position, pointcloud.quaternion, pointcloud.scale) //将数据集的经纬度和旋转应用到rotation和position (注意position和translateUser并不一样)
  956. }else if(!prop.isFirstLoad){//点云一般加载后position都不是0, 但后台初始化为0所以先归零要不然撤销后容易错
  957. pointcloud.position.set(0,0,0)
  958. }
  959. }
  960. if(Potree.settings.mergeType2 && pointcloud.datasetData){
  961. Potree.loadPanos(pointcloud.datasetData.id, (data) => {
  962. viewer.images360.addPanoData(data, pointcloud )
  963. viewer.images360.loadDone()
  964. viewer.scene.add360Images(viewer.images360);
  965. loadDone(pointcloud)
  966. },prop.url)
  967. }else{
  968. loadDone(pointcloud)
  969. }
  970. }, onError, prop.prefix, cloudPrefix)
  971. }
  972. }
  973. return {THREE}
  974. }
  975. var changeLog = ()=>{
  976. var textarea = document.createElement('textarea');
  977. textarea.id = "consoleLog";
  978. textarea.style.width = '160px';
  979. textarea.style.height = '200px'
  980. textarea.style.position = 'fixed'
  981. textarea.style.left = 0
  982. textarea.style.bottom = '50px'
  983. textarea.style['z-index'] = 9999;
  984. textarea.style.color = 'black';
  985. textarea.style.opacity = 0.9;
  986. textarea.style['font-size'] = '12px';
  987. textarea.style['backgroundColor'] = '#ffffff'
  988. document.getElementsByTagName("body")[0].appendChild(textarea);
  989. var list = ["log", "error", "warn", "debug", "info", "time", "timeEnd"]
  990. var exchange = function (o) {
  991. console["old" + o] = console[o];
  992. console[o] = function () {
  993. var args = Array.from(arguments)
  994. console["old" + o].apply(this, arguments)
  995. var t = document.getElementById("consoleLog").innerHTML;
  996. var str = ''
  997. args.forEach(a=>{
  998. str += a + ' '
  999. })
  1000. document.getElementById("consoleLog").innerHTML = str + "\n\n" + t;
  1001. }
  1002. }
  1003. for (var i = 0; i < list.length; i++) {
  1004. exchange(list[i])
  1005. }
  1006. }
  1007. /*
  1008. 坐标转换问题:
  1009. 由于控制点可以随便输入,所以本地和地理位置的转换也是可拉伸的。而navvis的转换是等比由中心展开,
  1010. 所以对比两种转化方式时误差较大。
  1011. 另外地理注册控制点是有参考数据集的,若参考数据集和我放置在0,0,0的数据集一致,就可直接使用,否则要转换。
  1012. ---------
  1013. lonlat和空间坐标其实并非线性关系,因为lonlat其实是角度。当两个数据集在地球两端时,它们之间的夹角都相差180度了。
  1014. 所以若要准确展示的话,需要将点云内所有物体,如漫游点,都先获取lonlat再去算local。或者直接将点云整体的transformMatrix考虑上在地球上相对于初始数据集的偏转。
  1015. 支持ctrl+z、ctrl+Y 撤销回退的页面有:
  1016. 测量、土方量、空间模型 这三个页面的点线拖拽;点云裁剪、点云下载中的裁剪 的框; 点云编辑的变换; 数据集校准;
  1017. (所有数据一旦删除则无效 )
  1018. 其他快捷键:
  1019. 按alt鼠标滚轮或WS键放慢。
  1020. 测量or土方量: 按Alt键可以平行拖拽点。&dragPolyBeyondPoint 后缀则可平行拖拽到无点云区域 。
  1021. 按M键拖拽点可以复制出当前点
  1022. 点云按空格键+左键拖拽场景,可以不改相机位置的旋转视角
  1023. */