Potree.js 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030
  1. //export * from "./Potree_update_visibility.js"; //因加载顺序问题,该文件直接在shim中重写
  2. export * from "./custom/start.js";
  3. export {settings, config} from './custom/settings.js'
  4. export * from './custom/viewer/map/MapViewer.js'
  5. export * from "./Actions.js";
  6. export * from "./Annotation.js";
  7. export * from "./defines.js";
  8. export * from "./Enum.js";
  9. export * from "./EventDispatcher.js";
  10. export * from "./Features.js";
  11. export * from "./KeyCodes.js";
  12. export * from "./LRU.js";
  13. export * from "./PointCloudEptGeometry.js";
  14. export * from "./ExtendPointCloudOctree.js";
  15. export * from "./PointCloudOctreeGeometry.js";
  16. export * from "./PointCloudTree.js";
  17. export * from "./Points.js";
  18. export * from "./PotreeRenderer.js";
  19. export * from "./ProfileRequest.js";
  20. export * from "./custom/objects/TextSprite.js";
  21. export * from "./custom/objects/3dgs/Splat.js";
  22. export * from "./custom/objects/3dgs/SplatNPY.js";
  23. export * from "./utils.js";
  24. export * from "./Version.js";
  25. export * from "./WorkerPool.js";
  26. export * from "./XHRFactory.js";
  27. export * from "./viewer/SaveProject.js";
  28. export * from "./viewer/LoadProject.js";
  29. export * from "./materials/ClassificationScheme.js";
  30. export * from "./materials/EyeDomeLightingMaterial.js";
  31. export * from "./materials/Gradients.js";
  32. export * from "./materials/NormalizationEDLMaterial.js";
  33. export * from "./materials/NormalizationMaterial.js";
  34. export * from "./materials/ExtendPointCloudMaterial.js";
  35. export * from "./loader/POCLoaderNew.js";
  36. export * from "./modules/loader/2.0/OctreeLoader.js";
  37. export * from "./loader/EptLoader.js";
  38. export * from "./loader/ept/BinaryLoader.js";
  39. export * from "./loader/ept/LaszipLoader.js";
  40. export * from "./loader/ept/ZstandardLoader.js";
  41. export * from "./loader/PointAttributes.js";
  42. export * from "./loader/ShapefileLoader.js";
  43. export * from "./loader/GeoPackageLoader.js";
  44. export * from "./utils/Box3Helper.js";
  45. export * from "./utils/ClippingTool.js";
  46. export * from "./utils/ClipVolume.js";
  47. export * from "./utils/GeoTIFF.js";
  48. export * from "./utils/Message.js";
  49. export * from "./utils/PointCloudSM.js";
  50. // export * from "./objects/tool/PolygonClipVolume.js";
  51. // export * from "./objects/tool/Profile.js";
  52. // export * from "./objects/tool/ProfileTool.js";
  53. // export * from "./objects/tool/ScreenBoxSelectTool.js";
  54. // export * from "./objects/tool/SpotLightHelper.js";
  55. // export * from "./objects/tool/TransformationTool.js";
  56. // export * from "./objects/tool/Volume.js";
  57. // export * from "./objects/tool/VolumeTool.js";
  58. // export * from "./objects/tool/Compass.js";
  59. export * from "./custom/utils/DrawUtil.js";
  60. export * from "./custom/objects/tool/Measure.js";
  61. export * from "./custom/objects/tool/Path.js";
  62. export * from "./custom/objects/tool/MeasuringTool.js";
  63. export * from "./utils/PolygonClipVolume.js";
  64. export * from "./utils/Profile.js";
  65. export * from "./utils/ProfileTool.js";
  66. export * from "./utils/ScreenBoxSelectTool.js";
  67. export * from "./utils/SpotLightHelper.js";
  68. export * from "./utils/TransformationToolNew.js";
  69. export * from "./utils/VolumeNew.js";
  70. export * from "./utils/VolumeTool.js";
  71. //export * from "./custom/viewer/ExtendViewer.js";
  72. export * from "./custom/viewer/ViewerNew.js";
  73. export * from "./viewer/ExtendScene.js";
  74. export * from "./viewer/HierarchicalSlider.js";
  75. export * from "./modules/OrientedImages/OrientedImages.js";
  76. export * from "./custom/modules/panos/Images360.js";
  77. export * from "./custom/modules/CameraAnimation/CameraAnimation.js";
  78. export * from "./modules/loader/2.0/OctreeLoader.js";
  79. export {OrbitControls} from "./navigation/OrbitControls.js";
  80. export {FirstPersonControls} from "./navigation/FirstPersonControls.js";
  81. import "./extensions/OrthographicCamera.js";
  82. import "./extensions/PerspectiveCamera.js";
  83. import "./extensions/Ray.js";
  84. import {LRU} from "./LRU.js";
  85. import {OctreeLoader} from "./modules/loader/2.0/OctreeLoader.js";
  86. import {POCLoader} from "./loader/POCLoaderNew.js";
  87. import {EptLoader} from "./loader/EptLoader.js";
  88. import {ExtendPointCloudOctree} from "./ExtendPointCloudOctree.js";
  89. import {WorkerPool} from "./WorkerPool.js";
  90. export const workerPool = new WorkerPool();
  91. export const version = {
  92. major: 1,
  93. minor: 8,
  94. suffix: '.0'
  95. };
  96. export let lru = new LRU();
  97. console.log('Potree ' + version.major + '.' + version.minor + version.suffix);
  98. export let pointBudget = 1 * 1000 * 1000;
  99. export let framenumber = 0;
  100. export let numNodesLoading = 0;
  101. export let maxNodesLoading = 6//4;
  102. export const debug = {};
  103. let scriptPath = "";
  104. if (document.currentScript && document.currentScript.src) {
  105. scriptPath = new URL(document.currentScript.src + '/..').href;
  106. if (scriptPath.slice(-1) === '/') {
  107. scriptPath = scriptPath.slice(0, -1);
  108. }
  109. } else if(import.meta){
  110. scriptPath = new URL(import.meta.url + "/..").href;
  111. if (scriptPath.slice(-1) === '/') {
  112. scriptPath = scriptPath.slice(0, -1);
  113. }
  114. }else {
  115. console.error('Potree was unable to find its script path using document.currentScript. Is Potree included with a script tag? Does your browser support this function?');
  116. }
  117. let resourcePath = scriptPath + '/resources';
  118. // scriptPath: build/potree
  119. // resourcePath:build/potree/resources
  120. export {scriptPath, resourcePath};
  121. //add:
  122. export async function loadFile(path, params , callback, onError){
  123. params = params || {}
  124. let fetchMethod = params.fetchMethod || 'get'
  125. delete params.fetchMethod
  126. if(Potree.fileServer){
  127. Potree.fileServer[fetchMethod](path, { params }).then(data=>{
  128. if(data.data)data = data.data
  129. if(data.data)data = data.data //融合页面getdataset需要查找两次data
  130. callback && callback(data)
  131. }).catch(onError)
  132. }else{
  133. try{
  134. if(Object.keys(params).length > 0){
  135. path+='?'
  136. let index = 0
  137. for(let i in params){
  138. if(index>0) path += '&'
  139. path+=i; path+='='; path+=params[i]
  140. index ++
  141. }
  142. }
  143. let info={}
  144. if(fetchMethod == 'post')info.method = 'POST'
  145. if(Potree.fileStorage){//本地直接获取
  146. Potree.fileStorage.get(path).then(data=>{
  147. if(data.data)data = data.data
  148. if(data.data)data = data.data //融合页面getdataset需要查找两次data
  149. callback && callback(data)
  150. }).catch(onError)
  151. }else{
  152. let response = await fetch(path, info);
  153. if(!response.ok){
  154. console.log('loadFile失败' , path )
  155. return onError && onError()
  156. }
  157. let text = await response.text();
  158. var data = params.returnText ? text : JSON.parse(text)
  159. if(data.data) data = data.data
  160. callback && callback(data)
  161. return data
  162. }
  163. }catch(e){
  164. console.log('loadFile出错', e)
  165. onError && onError(e)
  166. }
  167. }
  168. //查询: http://192.168.0.26:8080/doc.html#/default/filter-%E6%BC%AB%E6%B8%B8%E7%82%B9/filterUsingGET
  169. }
  170. export async function loadDatasets(callback,sceneCode,onError,prefix){//之后直接把path写进来
  171. let path
  172. sceneCode = sceneCode || Potree.settings.number
  173. if(Potree.fileServer){
  174. path = `/laser/dataset/${sceneCode}/getDataSet`
  175. }else{
  176. //path = `${Potree.settings.urls.prefix2}/indoor/${Potree.settings.number}/api/datasets`
  177. //现在只能加载得了本地的了
  178. path = `${prefix || Potree.settings.urls.prefix}/laser/dataset/${sceneCode}/getDataSet`
  179. //path = `${Potree.scriptPath}/data/${sceneCode}/getDataSet.json`
  180. }
  181. return loadFile(path, null, callback,onError)
  182. }
  183. export function loadNeighborFile(){
  184. if(Potree.config.neighbourPath){
  185. let path = `${Potree.scriptPath}/${Potree.config.neighbourPath}`
  186. loadFile(path, null, (data)=>{
  187. Potree.extraNeighbours = data
  188. for(let datasetid in data){
  189. let pointcloud = viewer.scene.pointclouds.find(e=>e.dataset_id == datasetid)
  190. let types = ['enable','unable']
  191. types.forEach(type=>{
  192. if(!data[datasetid][type])return
  193. for(let id1 in data[datasetid][type]){
  194. data[datasetid][type][id1].forEach(id2=>{
  195. let pano2, sid = id2
  196. if(!id2.includes('|')){
  197. sid = datasetid+'|'+id2
  198. }
  199. Potree.setExtraNeighbour(datasetid+'|'+id1, sid, type, {dontWrite:true})
  200. })
  201. }
  202. })
  203. }
  204. })
  205. }
  206. }
  207. /*
  208. Potree.setExtraNeighbour('1765955963253690368|90','1768872851646451713|7', 'enable' )
  209. JSON.stringify(Potree.extraNeighbours)
  210. */
  211. export function setExtraNeighbour(sid1,sid2,type, {dontWrite}={}){
  212. if(!Potree.extraNeighbours)Potree.extraNeighbours = {};
  213. (()=>{
  214. if(!dontWrite){
  215. if(sid1 > sid2){//顺序固定一下要不然两边都要写
  216. let temp = sid1 //对调
  217. sid1 = sid2
  218. sid2 = temp
  219. }
  220. let id1 = sid1.split('|')[1], id2, object
  221. object = Potree.extraNeighbours[sid1.split('|')[0]]
  222. if(!object) {
  223. if(type == 'clear')return
  224. object = Potree.extraNeighbours[sid1.split('|')[0]] = {}
  225. }
  226. if(type!='clear'){
  227. if(!object[type]){
  228. object[type] = {}
  229. }
  230. if(!object[type][id1]){
  231. object[type][id1] = []
  232. }
  233. }
  234. if(sid1.split('|')[0] == sid2.split('|')[0]){
  235. id2 = sid2.split('|')[1]
  236. }else{
  237. id2 = sid2
  238. }
  239. let remove = (type_)=>{
  240. if(object[type_] && object[type_][id1] ){
  241. let index = object[type_][id1].indexOf(id2)
  242. index>-1 && object[type_][id1].splice(index,1)
  243. }
  244. }
  245. if(type != 'clear'){
  246. if(!object[type][id1].includes(id2)){
  247. object[type][id1].push(id2)
  248. }
  249. }else{
  250. remove('unable')
  251. }
  252. let anotherType = type == 'enable' ? 'unable' : 'enable'
  253. remove(anotherType)
  254. }
  255. })()
  256. let pano1 = viewer.images360.getPano(sid1,'sid'),
  257. pano2 = viewer.images360.getPano(sid2,'sid')
  258. if(type == 'enable'){
  259. viewer.images360.neighbourMap[pano1.id][pano2.id] = 1 //用0和1 表示是在此被强制修改的
  260. viewer.images360.neighbourMap[pano2.id][pano1.id] = 1
  261. pano1.neighbours.push(pano2)
  262. pano2.neighbours.push(pano1)
  263. }else{
  264. if(type == 'clear') {
  265. viewer.images360.neighbourMap[pano1.id][pano2.id] = undefined
  266. viewer.images360.neighbourMap[pano2.id][pano1.id] = undefined
  267. }else{
  268. viewer.images360.neighbourMap[pano1.id][pano2.id] = 0
  269. viewer.images360.neighbourMap[pano2.id][pano1.id] = 0
  270. }
  271. let index = pano1.neighbours.indexOf(pano2)
  272. index>-1 && pano1.neighbours.splice(index,1)
  273. index = pano2.neighbours.indexOf(pano1)
  274. index>-1 && pano2.neighbours.splice(index,1)
  275. }
  276. }
  277. /* 开启漫游点通行的强制设置: 在浏览器中加入后缀 &neighGui,如 http://127.0.0.1:5002/offline.html?m=1829339452406239232&neighGui#/
  278. 开启后面板右侧会出现一列按钮。 在需要更改连通性的两个漫游点点击第一个按钮,就能选中点位。然后点击第二个按钮可以强行连接漫游点,使原本无法通行的两个漫游点可以互相通行;
  279. 类似的,第三个按钮则是强行断开漫游点,使原本可以通行的两个漫游点无法通行。 第四个按钮则用于取消设置。点击第五个按钮可以导出设置好的数据在剪贴板里,直接粘贴到extraNeighbours.js里保存, 该文件在离线包的 www\static\lib\potree里。下次刷新场景设置就生效了。
  280. 注意:settings中neighbourPath需要设置才会读取extraNeighbours.js。 如果新的离线包也需要设置的话就先创建一个空的extraNeighbours.js
  281. Enable Roaming Point Passage Forced Settings:
  282. Add the suffix &neighGui to the URL in the browser, for example:http://127.0.0.1:5002/offline.html?m=1829339452406239232&neighGui#/
  283. 󠁪After enabling, a column of buttons will appear on the right side of the backend panel.
  284. To change the connectivity between two roaming points, click the first button on both points to select them. Then, click the second button to forcibly connect the roaming points, allowing two originally unconnected roaming points to become interconnected.Similarly, the third button forcibly disconnects the roaming points, preventing two originally connected roaming points from being accessible to each other.The fourth button is used to cancel the settings.
  285. Clicking the fifth button exports the configured data to the clipboard. You can directly paste this data into extraNeighbours.js located in the offline package's www\static\lib\potree directory. The settings will take effect the next time you refresh the scene.
  286. */
  287. export function setNeighborGui(){//&neighGui
  288. if(!Potree.settings.showNeighSetGui)return
  289. let button1 = $('<button style="bottom:70%; background:#fff;">Select current roaming point</button>')
  290. let button2 = $('<button style="bottom:60%; background:#0F4;">Make the two points absolutely connected</button>')
  291. let button3 = $('<button style="bottom:50%; background:#766;">Make the two points absolutely disconnected</button>')
  292. let button4 = $('<button style="bottom:40%; background:#ffffff4d;">Clear the connection state of these two points</button>');
  293. let button5 = $('<button style="bottom:30%; background:#cacfff;">Copy Connections data </button>');
  294. ([button1,button2,button3,button4,button5]).forEach(button=>{
  295. button.css({
  296. right:'4px','z-index':100, position:'absolute', width:'130px',padding:'4px','border-radius':'6px',color:'#222'
  297. })
  298. viewer.renderer.domElement.parentElement.appendChild(button[0])
  299. })
  300. const bgColor = {
  301. normal: {r: 0, g: 0, b: 0, a: 0.4 },
  302. connected : {r: 0, g: 240, b: 100, a: 0.6 },
  303. disconnected : {r: 130, g: 120, b: 120, a: 0.6 },
  304. }
  305. let createLabel = ()=>{
  306. let panoLabel = new Potree.TextSprite({
  307. sizeInfo: {minSize : 40 , maxSize : 150, nearBound : 1, farBound : 50},
  308. backgroundColor:bgColor.normal,
  309. textColor:{r: 255, g: 255, b: 255, a: 1 },
  310. margin:{x:20,y:14},
  311. fontsize:20,
  312. borderRadius: 20,
  313. renderOrder:10,
  314. depthTest:false,
  315. text:'default'
  316. });
  317. viewer.images360.node.add(panoLabel);
  318. panoLabel.visible = false
  319. Potree.Utils.setObjectLayers(panoLabel,'bothMapAndScene')
  320. return panoLabel
  321. }
  322. let panoLabels = [createLabel(), createLabel()]
  323. let panoSelected = []
  324. let updateButton = ()=>{
  325. if(panoSelected.length == 2){
  326. button2.show()
  327. button3.show()
  328. button4.show()
  329. }else{
  330. button2.hide()
  331. button3.hide()
  332. button4.hide()
  333. }
  334. }
  335. let updateLabel = ()=>{
  336. let i=0
  337. let connectState
  338. if(panoSelected.length == 2){
  339. connectState = viewer.images360.neighbourMap[panoSelected[0].id][panoSelected[1].id]
  340. }
  341. while(i<2){
  342. if(panoSelected[i]) {
  343. panoLabels[i].visible = true
  344. panoLabels[i].position.copy(panoSelected[i].position)
  345. panoLabels[i].position.z -= 0.5
  346. let text = ['SID of current point:',panoSelected[i].sid]
  347. if(connectState === 1) {
  348. text.push('Already absolutely connected to:', panoSelected[(i+1)%2].sid)
  349. panoLabels[i].setBackgroundColor(bgColor.connected)
  350. }else if(connectState === 0){
  351. text.push('Already absolutely disconnected to:', panoSelected[(i+1)%2].sid)
  352. panoLabels[i].setBackgroundColor(bgColor.disconnected)
  353. }else{
  354. panoLabels[i].setBackgroundColor(bgColor.normal)
  355. }
  356. panoLabels[i].setText(text)
  357. }else{
  358. panoLabels[i].visible = false
  359. }
  360. i++
  361. }
  362. viewer.dispatchEvent('content_changed')
  363. viewer.mapViewer.dispatchEvent('content_changed')
  364. }
  365. button1.on('click',(e)=>{
  366. if(!viewer.images360.isAtPano())return alert('Please walk to the roaming point before clicking')
  367. if(panoSelected.includes(viewer.images360.currentPano))return
  368. if(panoSelected.length==2){
  369. panoSelected.splice(0,1)
  370. }
  371. panoSelected.push(viewer.images360.currentPano)
  372. updateLabel()
  373. updateButton()
  374. })
  375. button2.on('click',(e)=>{
  376. Potree.setExtraNeighbour(panoSelected[0].sid,panoSelected[1].sid,'enable')
  377. updateLabel()
  378. })
  379. button3.on('click',(e)=>{
  380. Potree.setExtraNeighbour(panoSelected[0].sid,panoSelected[1].sid,'unable')
  381. updateLabel()
  382. })
  383. button4.on('click',(e)=>{
  384. Potree.setExtraNeighbour(panoSelected[0].sid,panoSelected[1].sid,'clear')
  385. updateLabel()
  386. })
  387. button5.on('click',(e)=>{
  388. navigator.clipboard.writeText(JSON.stringify(Potree.extraNeighbours||{}))
  389. })
  390. updateButton()
  391. updateLabel()
  392. }
  393. //目前上传平面图后如果不点击保存按钮,数据还是旧的不生效
  394. export async function loadMapEntity(datasetId, force){
  395. if(!Potree.settings.floorplanEnable && !force && (Potree.fileServer||Potree.fileStorage) )return /* 等待平面图类型定义好会加载 */
  396. let loaded = 0
  397. let needLoads = datasetId == 'all' ? viewer.scene.pointclouds.map(e=>e.dataset_id) : [datasetId]
  398. let callback = (dataset_id, floorplanType, data )=>{
  399. if(!data || data.length == 0)return console.log('平面图没有数据', dataset_id, floorplanType )
  400. //要防止旧的比新的先获取到导致覆盖新的,因为两种type随时可能切换
  401. if(floorplanType != Potree.settings.floorplanType[dataset_id]) return //如果请求的floorplanType不是当前最新的floorplanType就返回
  402. var map = viewer.mapViewer.mapLayer.maps.find(e => e.name == 'floorplan_'+ dataset_id)
  403. if(map){
  404. viewer.mapViewer.mapLayer.removeMap(map)
  405. }
  406. var mapNew = viewer.mapViewer.mapLayer.addMapEntity(data.data || data, dataset_id)
  407. if(map){
  408. mapNew.visibleReasons = map.visibleReasons
  409. mapNew.unvisibleReasons = map.unvisibleReasons
  410. }
  411. loaded ++;
  412. }
  413. needLoads.forEach(dataset_id=>{
  414. let floorplanType = Potree.settings.floorplanType[dataset_id], prefix = ''
  415. if(!Potree.fileServer){
  416. prefix = Potree.settings.urls.prefix
  417. }
  418. if(!floorplanType)return
  419. var path
  420. /* if(Potree.fileServer){
  421. path = `/laser/tiledMap/${Potree.settings.number}/tiledMap/${floorplanType}/${dataset_id}`
  422. }else{
  423. path = `${Potree.settings.urls.prefix2}/indoor/${Potree.settings.number}/api/tiled_maps`
  424. } */
  425. path = `${prefix}/laser/tiledMap/${Potree.settings.number}/tiledMap/${floorplanType}/${dataset_id}`
  426. Potree.settings.floorplanRequests[dataset_id] = true //开始加载了
  427. return loadFile(path, null, callback.bind(this, dataset_id, floorplanType) )
  428. })
  429. }
  430. export async function loadPanos(datasetId, callback, number){
  431. let path
  432. number = number || Potree.settings.number
  433. //let query = `?datasetId=${datasetId}` //`?lat=${center.lat}&lon=${center.lon}&radius=200000`
  434. if(Potree.fileServer && !Potree.settings.mergeType){
  435. path = `/laser/filter/${number}/query`
  436. }/* else if(Potree.settings.mergeType2){ //每个场景只加载初始数据集
  437. path = `${Potree.settings.urls.prefix}/laser/filter/${number}/query`
  438. } */else{
  439. path = `${Potree.settings.urls.prefix}/laser/filter/${number}/query`
  440. }
  441. return loadFile(path, {datasetId:datasetId}, callback)
  442. }
  443. export async function loadPanosInfo(callback){
  444. var path
  445. if(Potree.fileServer){
  446. }else{
  447. path = `${Potree.scriptPath}/data/panoEdit/vision_edit.txt`
  448. }
  449. return loadFile(path, null, callback)
  450. }
  451. export function load4dkkPanos(sceneCode, model, defaultRotation, done, tileRes){ //加载四维看看的漫游点并转换
  452. model.is4dkkModel = true
  453. model.panos = []
  454. //模拟点云,需要rotX(90)+平移一段才能和四维看看的bound一样
  455. let rot1M = new THREE.Matrix4().makeRotationFromEuler(defaultRotation)
  456. let pos1 = new THREE.Vector3
  457. if(model.fileType == '3dTiles'){
  458. pos1.fromArray(model.runtime.getTileset().tileset.root.boundingVolume.box.slice(0,3))//必须要平移一段才能重合
  459. pos1.copy(Potree.math.convertVector.ZupToYup(pos1))
  460. //rot1M.makeRotationX(Math.PI/2);
  461. }
  462. let pos1M = new THREE.Matrix4().setPosition(pos1)
  463. model.posRot1MatrixInvert = new THREE.Matrix4().multiplyMatrices(pos1M, rot1M ).invert();
  464. model.rot1MatrixInvert = rot1M.clone().invert();
  465. model.transformMatrix = new THREE.Matrix4
  466. model.rotateMatrix = new THREE.Matrix4
  467. model.transformInvMatrix = new THREE.Matrix4
  468. model.rotateInvMatrix = new THREE.Matrix4
  469. model.datasetData = {
  470. sceneVersion : 'V4' ,
  471. mapping: model.props.raw.mapping
  472. }
  473. model.tileRes = tileRes
  474. model.bound = new THREE.Box3
  475. let path
  476. if(sceneCode.includes('/') ){
  477. path = sceneCode
  478. }else{
  479. let prefix = Potree.settings.urls.getPrefix(8,model) || 'https://4dkk.4dage.com'
  480. //path = `${prefix}/swkk/${sceneCode}/wwwroot/scene_view_data/${sceneCode}/images/vision.txt`
  481. if(Potree.settings.urls.templates.vision){
  482. path = prefix + Potree.Common.replaceAll(Potree.settings.urls.templates.vision, '{sceneCode}', sceneCode)
  483. }else{
  484. path = `${prefix}/scene_view_data/${sceneCode}/images/vision.txt`
  485. }
  486. }
  487. model.sceneCode = sceneCode
  488. loadFile(path,{},(data)=>{
  489. let panoData = data.sweepLocations.map(e=>{
  490. let qua = e.pose.rotation
  491. return {
  492. file_id : e.uuid,
  493. dataset_location : new THREE.Vector3().copy(e.pose.translation).toArray(),
  494. dataset_floor_location : new THREE.Vector3().copy(e.puck).toArray(),
  495. dataset_orientation: [qua.w, qua.x, qua.y, qua.z],
  496. }
  497. })
  498. viewer.images360.addPanoData(panoData, model)
  499. viewer.images360.loadDone()
  500. viewer.scene.add360Images(viewer.images360);
  501. {//neighbourMap全部填满,否则之后会被修改
  502. data.sweepLocations.forEach(pano1Data=>{
  503. let pano1 = model.panos.find(e=>e.originID == pano1Data.uuid)
  504. model.panos.forEach((pano2, i)=>{
  505. if(pano1 == pano2)return
  506. let v = pano1Data.visibles.includes(i)
  507. viewer.images360.neighbourMap[pano1.id][pano2.id] = v
  508. viewer.images360.neighbourMap[pano2.id][pano1.id] = v
  509. if(v){
  510. pano1.neighbours.includes(pano2) || pano1.neighbours.push(pano2)
  511. pano2.neighbours.includes(pano1) || pano2.neighbours.push(pano1)
  512. }
  513. })
  514. })
  515. }
  516. viewer.modules.MergeEditor.modelTransformCallback(model, true) //初始化pano的pose
  517. done && done()
  518. })
  519. /* neighbours: e.visibles3 || e.visibles,
  520. noBlocks: e.visibles2,
  521. seeMarkers: e.visibles, */
  522. }
  523. export async function loadImgVersion( callback){
  524. if(Potree.settings.isLocal || !Potree.settings.isOfficial){//potree本地请求不到
  525. return callback({})
  526. }
  527. var path
  528. path = `/laser/init/getSceneNumVersion/${Potree.settings.number}`
  529. return loadFile(path, {fetchMethod:'post'}, callback, callback)
  530. }
  531. export function setLonlat(lon,lat){
  532. let locationLonLat = [lon,lat]
  533. if(window.AMapWith84 && Potree.settings.mapCompany != 'google'){//需要转换为高德的,但该函数不准确,转入后再转出,和原来的有偏差. navvis的我看data中存的globalLocation直接输入到高德地图后的定位和其要展示的定位一致,而我们要转为高德后才一致,猜测是navvis后台转为了高德可用的经纬度。 若不转的话,其他看起来没问题,仅高德地图定位不准确,因其为被加密后的火星坐标系。
  534. locationLonLat = AMapWith84.wgs84ToAMap({x:lon, y:lat})
  535. locationLonLat = [locationLonLat.x,locationLonLat.y]
  536. }
  537. proj4.defs("LOCAL", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15)); //高德坐标系
  538. proj4.defs("LOCAL_MAP", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15)); //地图和本地一样
  539. proj4.defs("WGS84", "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs");
  540. let transform1 = proj4("WGS84", "LOCAL"); //这个ok 是展开的平面投影 LOCAL即NAVVIS:TMERC
  541. let transform2 = proj4("+proj=tmerc +lat_0=0 +lon_0=123 +k=1 +x_0=500000 +y_0=0 +ellps=GRS80 +units=m +no_defs;");
  542. //注:转入后再转出,和原来的有偏差。如果输入是local坐标,数字越大偏差越大,当百万时就明显了。如果是lonlat,很奇怪经度小于50时就乱了。
  543. viewer.transform = {
  544. lonlatToLocal : transform1,
  545. lonlatTo4550 : transform2 // 转大地坐标EPSG:4550
  546. }
  547. if(window.transform){
  548. viewer.transform.lonlatToLocal = window.transform.lonlatToLocal
  549. }
  550. Potree.Log(`setLonlat ${lon}, ${lat}`,{font:{color:"#f49",fontSize:13}} )
  551. if(window.AMapWith84 && Potree.settings.mapCompany != 'google'){//需要转换, 因本地高德用的lonlat和数据里的84不一样. (google地图在国内也用的高德,国外84)
  552. let change = (transform)=>{
  553. let forward = transform.forward
  554. let inverse = transform.inverse;
  555. transform.forward = function(e, not84){
  556. let needTran = e.x == void 0
  557. if(needTran)var a1 = {x:e[0],y:e[1]}
  558. else var a1 = e
  559. var a = not84 ? a1 : AMapWith84.wgs84ToAMap(a1)
  560. if(needTran){
  561. a = [a.x, a.y]; e[2] != void 0 && (a[2] = e[2])
  562. }else{
  563. e.z != void 0 && (a.z = e.z)
  564. }
  565. return forward(a)
  566. }
  567. transform.inverse = function(e, not84){
  568. let needTran = e.x == void 0
  569. var a = inverse(e)
  570. needTran && (a = {x:a[0],y:a[1]})
  571. a = not84 ? a : AMapWith84.aMapToWgs84(a)
  572. if(needTran){
  573. a = [a.x,a.y]; e[2] != void 0 && (a[2] = e[2])
  574. }else{
  575. e.z != void 0 && (a.z = e.z)
  576. }
  577. return a
  578. }
  579. }
  580. for(let f in viewer.transform){
  581. change(viewer.transform[f])
  582. }
  583. //注意:把几万米转成经纬度再转回来差值会很大
  584. }
  585. }
  586. //site_model
  587. /* {
  588. "area": 2503.30551910935,
  589. "attributes": {},
  590. "center": [
  591. 113.59568277455075,
  592. 22.366566635195288,
  593. 12.78751625
  594. ],
  595. "children": [],
  596. "geometry_hash": 1891071345,
  597. "id": 10,
  598. "name": "港湾一号",
  599. "parentId": null,
  600. "polygon": {
  601. "coordinates": [
  602. [
  603. [
  604. 113.59590810534583,
  605. 22.36679132753878
  606. ],
  607. [
  608. 113.59590810534583,
  609. 22.366807172528629
  610. ],
  611. [
  612. 113.59545610274934,
  613. 22.366807172528629
  614. ],
  615. [
  616. 113.59545610274934,
  617. 22.36679132753878
  618. ]
  619. ]
  620. ],
  621. "type": "Polygon"
  622. },
  623. "type": "BUILDING",
  624. "volume": null,
  625. "z_max": null,
  626. "z_min": null
  627. }
  628. */
  629. export function Log(){
  630. let args = Array.from(arguments)
  631. let params = args[args.length-1]
  632. if(params && params.font) {params = params.font, args.pop()}
  633. else params = {}
  634. let str = '', color = params.color || '#13f', fontSize = params.fontSize || 12
  635. args.forEach((e,i)=>{
  636. i > 0 && (str += ' , ' )
  637. /* if(params.toFixed && typeof e == 'number'){
  638. e = e.toFixed(params.toFixed)
  639. } */
  640. if(params.toFixed ){
  641. e = Potree.math.toPrecision(e, params.toFixed)
  642. }
  643. str += e //object可以JSON.stringify,但不是所有都行
  644. })
  645. console.warn(`%c${str}`, `color:${color};font-size:${fontSize}px`)
  646. }
  647. export function loadPointCloud(path, name, sceneCode, timeStamp, callback, onError){
  648. let loaded = function(e){
  649. e.pointcloud.name = name;
  650. e.pointcloud.sceneCode = sceneCode //对应4dkk的场景码
  651. callback(e);
  652. };
  653. let promise = new Promise( resolve => {
  654. // load pointcloud
  655. if (!path){
  656. // TODO: callback? comment? Hello? Bueller? Anyone?
  657. } else if (path.indexOf('ept.json') > 0) {
  658. EptLoader.load(path, function(geometry) {
  659. if (!geometry) {
  660. console.error(new Error(`failed to load point cloud from URL: ${path}`));
  661. }
  662. else {
  663. let pointcloud = new ExtendPointCloudOctree(geometry);
  664. //loaded(pointcloud);
  665. resolve({type: 'pointcloud_loaded', pointcloud: pointcloud});
  666. }
  667. });
  668. } else if (path.indexOf('cloud.js') > 0) {
  669. POCLoader.load(path, timeStamp, function (geometry) {
  670. if (!geometry) {
  671. //callback({type: 'loading_failed'});
  672. console.error(new Error(`failed to load point cloud from URL: ${path}`));
  673. onError && onError()
  674. } else {
  675. let pointcloud = new ExtendPointCloudOctree(geometry);
  676. // loaded(pointcloud);
  677. resolve({type: 'pointcloud_loaded', pointcloud: pointcloud});
  678. }
  679. });
  680. }/* else if (path.indexOf('metadata.json') > 0) { //部分浏览器(如uc)不支持NodeLoader中的1n的大数据写法
  681. Potree.OctreeLoader.load(path).then(e => {
  682. let geometry = e.geometry;
  683. if(!geometry){
  684. console.error(new Error(`failed to load point cloud from URL: ${path}`));
  685. }else{
  686. let pointcloud = new ExtendPointCloudOctree(geometry);
  687. let aPosition = pointcloud.getAttribute("position");
  688. let material = pointcloud.material;
  689. material.elevationRange = [
  690. aPosition.range[0][2],
  691. aPosition.range[1][2],
  692. ];
  693. // loaded(pointcloud);
  694. resolve({type: 'pointcloud_loaded', pointcloud: pointcloud});
  695. }
  696. });
  697. OctreeLoader.load(path, function (geometry) {
  698. if (!geometry) {
  699. //callback({type: 'loading_failed'});
  700. console.error(new Error(`failed to load point cloud from URL: ${path}`));
  701. } else {
  702. let pointcloud = new ExtendPointCloudOctree(geometry);
  703. // loaded(pointcloud);
  704. resolve({type: 'pointcloud_loaded', pointcloud: pointcloud});
  705. }
  706. });
  707. } */else if (path.indexOf('.vpc') > 0) {
  708. PointCloudArena4DGeometry.load(path, function (geometry) {
  709. if (!geometry) {
  710. //callback({type: 'loading_failed'});
  711. console.error(new Error(`failed to load point cloud from URL: ${path}`));
  712. } else {
  713. let pointcloud = new PointCloudArena4D(geometry);
  714. // loaded(pointcloud);
  715. resolve({type: 'pointcloud_loaded', pointcloud: pointcloud});
  716. }
  717. });
  718. } else {
  719. //callback({'type': 'loading_failed'});
  720. console.error(new Error(`failed to load point cloud from URL: ${path}`));
  721. }
  722. });
  723. if(callback){
  724. promise.then(pointcloud => {
  725. loaded(pointcloud);
  726. });
  727. }else{
  728. return promise;
  729. }
  730. };
  731. // add selectgroup
  732. (function($){
  733. $.fn.extend({
  734. selectgroup: function(args = {}){
  735. let elGroup = $(this);
  736. let rootID = elGroup.prop("id");
  737. let groupID = `${rootID}`;
  738. let groupTitle = (args.title !== undefined) ? args.title : "";
  739. let elButtons = [];
  740. elGroup.find("option").each((index, value) => {
  741. let buttonID = $(value).prop("id");
  742. let label = $(value).html();
  743. let optionValue = $(value).prop("value");
  744. let elButton = $(`
  745. <span style="flex-grow: 1; display: inherit">
  746. <label for="${buttonID}" class="ui-button" style="width: 100%; padding: .4em .1em">${label}</label>
  747. <input type="radio" name="${groupID}" id="${buttonID}" value="${optionValue}" style="display: none"/>
  748. </span>
  749. `);
  750. let elLabel = elButton.find("label");
  751. let elInput = elButton.find("input");
  752. elInput.change( () => {
  753. elGroup.find("label").removeClass("ui-state-active");
  754. elGroup.find("label").addClass("ui-state-default");
  755. if(elInput.is(":checked")){
  756. elLabel.addClass("ui-state-active");
  757. }else{
  758. //elLabel.addClass("ui-state-default");
  759. }
  760. });
  761. elButtons.push(elButton);
  762. });
  763. let elFieldset = $(`
  764. <fieldset style="border: none; margin: 0px; padding: 0px">
  765. <legend>${groupTitle}</legend>
  766. <span style="display: flex">
  767. </span>
  768. </fieldset>
  769. `);
  770. let elButtonContainer = elFieldset.find("span");
  771. for(let elButton of elButtons){
  772. elButtonContainer.append(elButton);
  773. }
  774. elButtonContainer.find("label").each( (index, value) => {
  775. $(value).css("margin", "0px");
  776. $(value).css("border-radius", "0px");
  777. $(value).css("border", "1px solid black");
  778. $(value).css("border-left", "none");
  779. });
  780. elButtonContainer.find("label:first").each( (index, value) => {
  781. $(value).css("border-radius", "4px 0px 0px 4px");
  782. });
  783. elButtonContainer.find("label:last").each( (index, value) => {
  784. $(value).css("border-radius", "0px 4px 4px 0px");
  785. $(value).css("border-left", "none");
  786. });
  787. elGroup.empty();
  788. elGroup.append(elFieldset);
  789. }
  790. });
  791. })(jQuery);
  792. //在这之后export的内容才赋值到Potree中