start.js 71 KB

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