import * as THREE from "../../../libs/three.js/build/three.module.js"; import { EventDispatcher } from "../../EventDispatcher.js"; let texLoader = new THREE.TextureLoader() texLoader.crossOrigin = "anonymous" let createErrorMaterial = function() { var t = new THREE.MeshBasicMaterial({ transparent: !0, depthWrite: !1, depthTest: !0, opacity: 1, side: THREE.DoubleSide }); return t.color = new THREE.Color(3355443), t } let tempVector = new THREE.Vector3, //sharedata face1 = new THREE.Face3(0,1,2), face2 = new THREE.Face3(2,3,0), errorMaterial = createErrorMaterial(), uv00 = new THREE.Vector2(0,0), uv01 = new THREE.Vector2(0,1), uv10 = new THREE.Vector2(1,0), uv11 = new THREE.Vector2(1,1), face1UV = [uv00, uv10, uv11], face2UV = [uv11, uv01, uv00] const HALF_WORLD_SIZE = 21e6 const MAX_VERTICAL_DIST = 2 const MAX_VERTICAL_DIST_TO_BEST = 1 export class MapLayer extends EventDispatcher{ // 包括了 MapLayerBase SceneLayer constructor(viewer_, viewport){ super() this.sceneGroup = new THREE.Object3D; this.sceneGroup.name = "MapLayer" this.loadingInProgress = 0 this.maps = [] this.frustum = new THREE.Frustum this.frustumMatrix = new THREE.Matrix4 this.tileColor = /* i && i.tileColor ? i.tileColor : */new THREE.Color(16777215) //添加地图 var map = new TiledMapOpenStreetMap(this, this.tileColor ) this.addMap(map) this.viewport = viewport this.changeViewer(viewer_) /* this.on('needUpdate',()=>{ this.mapLayer.update() }) */ } addMapEntity(data){ if(!data || !data[0]){ Potree.Log('平面图无数据','red') return } var floorplan = new TiledMapFromEntity(this, this.tileColor, data[0] )//[0]? this.addMap(floorplan) floorplan.updateProjection() floorplan.updateObjectGroup() this.needUpdate = true this.dispatchEvent({type:'floorplanLoaded', floorplan}) } addMap(t){ this.maps.push(t) //this.view.invalidateScene() this.needUpdate = true } removeMap(t){ var e = this.maps.indexOf(t); if(e >= 0){ t.removeFromSceneGroup(this.sceneGroup) this.maps.splice(e, 1) } /* this.view.invalidateScene() */ this.needUpdate = true this.viewer.dispatchEvent({ type:'content_changed' }) } changeViewer(viewer_){//add this.viewer = viewer_ } initProjection(){ this.maps.forEach(map=>{ map.updateProjection() map.updateObjectGroup() }) } visibilityChanged(){ if (!this.visible) for (var t = 0, e = this.maps; t < e.length; t++){ e[t].removeFromSceneGroup(this.sceneGroup) } } onAfterRenderViewport(e){ var n = this; /* this.isVisibleInViewport(e) && (this.updateTimer || this.loadingInProgress || (this.updateTimer = window.setTimeout((function(){ n.update(e).then((function(t){ t && n.loadComplete.emit(!0) } )).catch(u.handleWarning) } ), 100))) */ } update(){ this.needUpdate = false if(this.disabled || !this.maps.find(e=>!e.disabled) || !this.maps.find(e=>e.objectGroup.visible) )return //add var e, n, i, r, o; this.updateTimer = void 0, e = this.viewport.camera, n = e.projectionMatrix.clone(), n.elements[0] /= 1.5, n.elements[5] /= 1.5, this.frustumMatrix.multiplyMatrices(n, e.matrixWorldInverse), this.frustum.setFromProjectionMatrix(this.frustumMatrix), this.frustum.planes[4].setComponents(0, 0, 0, 0), this.frustum.planes[5].setComponents(0, 0, 0, 0), i = !0 for (r = 0; r < this.maps.length; r++){ var map = this.maps[r] i = map.update(this.frustum, this.sceneGroup) && i; } return [2, i] } getAttributions(){ for (var t = {}, e = 0, n = this.maps; e < n.length; e++){ n[e].fillAttributions(t) } return t } updateProjection(){ for (var t = 0, e = this.maps; t < e.length; t++){ var n = e[t]; n.clearProjection(), n.updateObjectGroup() } } } export class TiledMapBase extends EventDispatcher{ constructor(/* t, */name, mapLayer, tileColor, projection){ super(); this.name = name //this.TransformService = t, this.mapLayer = mapLayer, this.tileColor = tileColor, this.bias = 0 this.zIndex = -1 this.objectGroup = new THREE.Object3D; this.objectGroup.name = name this.objectGroupAdded = !1, this.baseTile = new MapTile(this,/* this.mapLayer, */this.objectGroup,this.tileColor), this.isTileVisibleBox = new THREE.Box3, this.isTileVisibleVec = new THREE.Vector3 this.projection = projection this._zoomLevel = 0;//1-20 } get zoomLevel(){ return this._zoomLevel } set zoomLevel(zoomLevel){ if(this._zoomLevel != zoomLevel){ this._zoomLevel = zoomLevel this.emit('zoomLevelChange',zoomLevel) } } updateObjectGroup(){ this.position && this.objectGroup.position.copy(this.position), this.quaternion && this.objectGroup.quaternion.copy(this.quaternion), this.objectGroup.updateMatrixWorld(!0) } updateProjection(){ //this.transformMapToLocal || (this.transformMapToLocal = this.TransformService.getTransform(this.projection, this.TransformService.crsLocal)) if(!this.transformMapToLocal){ if(proj4.defs("NAVVIS:TMERC")){ if(this.projection == "EPSG:4550"){ this.transformMapToLocal = { forward:(e)=>{ var a = viewer.transform.lonlatTo4550.inverse(e) return viewer.transform.lonlatToLocal.forward(a) }, } }else{ this.transformMapToLocal = proj4(this.projection, "NAVVIS:TMERC") } //this.transformMapToLocal = proj4(this.projection, "NAVVIS:TMERC") } } } setEnable(enable){//add if(!this.disabled == enable)return this.disabled = !enable viewer.updateVisible(this.objectGroup, 'setEnable', enable) if(!enable){ this.baseTile.remove() }else{ this.mapLayer.needUpdate = true } this.mapLayer.viewer.dispatchEvent({ type:'content_changed' }) } /* clearProjection(){ this.transformMapToLocal = void 0, this.projection.name !== this.TransformService.NAVVIS_LOCAL && this.baseTile.remove() } */ update(e, n){ if(this.disabled || !this.objectGroup.visible)return this.updateProjection() if(!this.transformMapToLocal)return if ( !this.isTileVisible(new THREE.Vector3(0,0,0), this.mapSizeM, e)) return this.removeFromSceneGroup(n), !0; let viewport = this.mapLayer.viewport var i = new THREE.Vector3(-.5 * this.mapSizeM,0,0); i.applyMatrix4(this.objectGroup.matrixWorld), i.project(viewport.camera); var o = new THREE.Vector3(.5 * this.mapSizeM,0,0); o.applyMatrix4(this.objectGroup.matrixWorld), o.project(viewport.camera); var a = viewport.resolution.x , s = viewport.resolution.y if (a <= 0 || s <= 0 || isNaN(i.x) || isNaN(o.x)) return !1; i.sub(o), i.x *= a / 2, i.y *= s / 2; var c = this.tileSizePx / i.length() , level = Math.ceil(-Math.log(c) / Math.log(2) - this.bias); level = Math.max(level, 0) level = Math.min(level, void 0 === this.maxDepth ? 1 / 0 : this.maxDepth) this.zoomLevel = level//add //console.log(level) this.addToSceneGroup(n) return this.baseTile.update(this, e, level, this.mapSizeM, 0, 0, "") } isTileVisible(e, n, i){ if (n > HALF_WORLD_SIZE) return !0; var r = .5 * n; this.transformMapToLocal.forward(e) this.isTileVisibleBox.makeEmpty() this.isTileVisibleVec.set(e.x - r, e.y - r, e.z).applyMatrix4(this.objectGroup.matrixWorld) this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec) this.isTileVisibleVec.set(e.x - r, e.y + r, e.z).applyMatrix4(this.objectGroup.matrixWorld) this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec) this.isTileVisibleVec.set(e.x + r, e.y - r, e.z).applyMatrix4(this.objectGroup.matrixWorld) this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec) this.isTileVisibleVec.set(e.x + r, e.y + r, e.z).applyMatrix4(this.objectGroup.matrixWorld) this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec) return i.intersectsBox(this.isTileVisibleBox) } addToSceneGroup(t){ this.objectGroupAdded || (t.add(this.objectGroup), this.objectGroupAdded = !0) } removeFromSceneGroup(t){ this.baseTile.remove(), this.objectGroupAdded && (t.remove(this.objectGroup), this.objectGroupAdded = !1) } } export class MapTile{ constructor(map,/* t, */ e, n){ this.map = map; //this.mapLayer = t, this.objectGroup = e, this.tileColor = n, this.meshAdded = !1, this.textureLoaded = !1, this.children = [] } update(e, n, i, r, o, a, s){ return !!this.doesNotContainTilesToBeDisplayed(e) || (0 === i ? this.updateTile(e, r, o, a) : this.updateSubTiles(e, n, i, r, o, a, s)) } doesNotContainTilesToBeDisplayed(t){ return t.tilePresenceMap && t.tilePresenceMap.empty } updateTile(t, e, n, i){ if(!this.mesh){ this.createTileObject(t, e, n, i) } if(!this.meshAdded){ this.objectGroup.add(this.mesh) this.meshAdded = !0 } if(this.textureLoaded){ this.removeChildren() } return this.textureLoaded } updateSubTiles(entity, n, level, o, a, s, c){ for (var l = !0, u = [-.25 * o, .25 * o, -.25 * o, .25 * o], d = [.25 * o, .25 * o, -.25 * o, -.25 * o], p = 0; p < 4; ++p){ var h = c + p.toString(10); //一级(512):0 1 2 3分别为左上、右上、左下、右下。二级(1024)就是把一级的每一块分裂,如00 01 02 03分别是0的左上、右上、左下、右下…… /* if(entity.name == 'floorplan'){ console.log(1) } */ if (!entity.tilePresenceMap || entity.tilePresenceMap[h]){ //去掉判断,直接显示 var f = a + u[p] , m = s + d[p]; tempVector.set(f, m, 0); if (entity.isTileVisible(tempVector, .5 * o, n)){ this.children[p] || (this.children[p] = new MapTile(this.map, this.objectGroup,this.tileColor)) l = this.children[p].update(entity, n, level - 1, .5 * o, f, m, h) && l } else { if (this.children[p]){ this.children[p].remove() delete this.children[p] } } } } return l && this.removeObject3D(), l } createTileObject(t, e, n, a){ var s = this; this.mesh = this.createMesh(t.transformMapToLocal, e, n, a), this.textureLoaded = !1; var c = t.mapSizeM / e , l = Math.log(c) / Math.log(2) , u = n / e + .5 * (c - 1) , d = -a / e + .5 * (c - 1) , p = t.getTileUrl(Math.round(l), Math.round(u), Math.round(d)); viewer.setObjectLayers(this.mesh, 'map' ) this.mesh.renderOrder = -(1e6 - l - 100 * (t.zIndex || 0)); var h = this.mesh.material; var loadDone = ()=>{ this.map.mapLayer.loadingInProgress-- if(this.map.mapLayer.loadingInProgress == 0){ this.map.mapLayer.emit('loadDone') } } h.map = texLoader.load(p, (tex)=>{ if(this.mesh){//如果还要显示的话 this.textureLoaded = true this.mesh.material.opacity = 1 //this.mapLayer.view.invalidateScene() this.map.mapLayer.viewer.dispatchEvent({ type:'content_changed' }) this.map.mapLayer.needUpdate = true //表示还要继续update(以removeChildren) }else{ tex.dispose() } loadDone() } , void 0, (()=>{//error this.textureLoaded = !0 if(this.mesh){ this.mesh.material.dispose() //o.disposeMeshMaterial(this.mesh) this.mesh.material = errorMaterial //this.map.mapLayer.view.invalidateScene()) this.map.mapLayer.viewer.dispatchEvent({ type:'content_changed' }) } loadDone() })) h.map.anisotropy = 0, h.map.generateMipmaps = !1, h.map.minFilter = THREE.LinearFilter, h.map.magFilter = THREE.LinearFilter, this.map.mapLayer.loadingInProgress++ } createMesh(t, e, n, o){ var a = new THREE.Geometry; return tempVector.set(n - e / 2, o - e / 2, 0), a.vertices.push(new THREE.Vector3().copy(t.forward(tempVector))), tempVector.set(n + e / 2, o - e / 2, 0), a.vertices.push(new THREE.Vector3().copy(t.forward(tempVector))), tempVector.set(n + e / 2, o + e / 2, 0), a.vertices.push(new THREE.Vector3().copy(t.forward(tempVector))), tempVector.set(n - e / 2, o + e / 2, 0), a.vertices.push(new THREE.Vector3().copy(t.forward(tempVector))), a.faces.push(face1), a.faces.push(face2), a.faceVertexUvs[0].push(face1UV), a.faceVertexUvs[0].push(face2UV), new THREE.Mesh(a,this.createMaterial()) } createMaterial(){ var t = new THREE.MeshBasicMaterial({ transparent: !0, depthWrite: !1, depthTest: !0, opacity: 0, side: THREE.DoubleSide }); return t.color = this.tileColor ? this.tileColor : new THREE.Color(16777215), t } remove(){ this.removeObject3D(), this.removeChildren() } removeObject3D(){ if (this.mesh){ if (this.objectGroup.remove(this.mesh), this.textureLoaded){ var t = this.mesh.material.map; t && t.dispose() } this.mesh.material.dispose() //o.disposeMeshMaterial(this.mesh), this.mesh.geometry.dispose() this.mesh = void 0 } this.meshAdded = !1, this.textureLoaded = !1 } removeChildren(){ for (var t = 0, e = this.children; t < e.length; t++){ var n = e[t]; n && (n.removeObject3D(), n.removeChildren()) } this.children.length = 0 } } proj4.defs("EPSG:3857", "+title=WGS 84 / Pseudo-Mercator +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs") //这里地图世界的中心是不是lon:0,lat:0 export class TiledMapOpenStreetMap extends TiledMapBase{ constructor(mapLayer, tileColor){ super('map', mapLayer, tileColor, /* "EPSG:4550" */ "EPSG:3857" ) //EPSG projection this.baseUrl = "https://wprd03.is.autonavi.com/appmaptile?style=7&x=${x}&y=${y}&z=${z}", //this.baseUrl = "https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x=${x}&y=${y}&z=${z}" //最高只到18 level this.attribution = "© PopSmart, © 高德地图", this.tileSizePx = 256 this.mapSizeM = 40075017 this.maxDepth = 20 this.bias = 0.5 } getTileUrl(t, e, n){ return this.baseUrl.replace(/\${z}/, t.toString(10)).replace(/\${x}/, e.toString(10)).replace(/\${y}/, n.toString(10)) } fillAttributions(t){ t[this.attribution] = { score: 50 } } } export class TiledMapFromEntity extends TiledMapBase{ constructor(mapLayer, tileColor, data){ super('floorplan', mapLayer, tileColor, "NAVVIS:TMERC" /* "EPSG:3857" *//* "WGS84" */) //直接就是本地坐标,没有projec var entity = this.fillFromData(data) this.tiledMapEntity = entity //c.RestService = s, this.tileSizePx = entity.tileSizePx, this.mapSizeM = entity.mapSizeM, this.maxDepth = entity.maxDepth, //this.projection = n.crsLocal, this.zIndex = 0, this.tilePresenceMap = this.decodeBitStream(this.tiledMapEntity.quadtree) //包含tile分裂信息,如果写错了会造成tile显示不全 } fillFromData(e){ let data = {} data.id = e.id data.globalLocation = Potree.Utils.VectorFactory.fromArray3(e.location), data.orientation = Potree.Utils.QuaternionFactory.fromArray(e.orientation) if(Potree.fileServer){ data.filePath = `https://${Potree.config.urls.prefix}${e.file_path}` }else{ data.filePath = `https://${Potree.config.urls.prefix}/data/${Potree.settings.number}/${e.file_path}` } //if(!data.filePath.includes('building_1'))data.filePath = data.filePath.replace('building','building_1')//暂时 data.fileName = '$DEPTH/$X/$Y.png',//e.file_name, data.type = e.type, data.mapSizeM = e.map_size_m, data.tileSizePx = e.tile_size_px, data.maxDepth = e.max_depth, data.quadtree = e.quadtree, data.floorId = e.floor_id, data.bundleId = e.bundle_id //this.computeLocalCoordinates() return data } computeLocalCoordinates(){ if(proj4.defs("NAVVIS:TMERC")){ this.tiledMapEntity.location = new THREE.Vector3().copy(viewer.transform.lonlatToLocal.forward(this.tiledMapEntity.globalLocation)) } } updateProjection() { super.updateProjection() if(!this.position){ this.computeLocalCoordinates() } /* this.projection = this.TransformService.crsLocal, t.prototype.updateProjection.call(this) */ } get position(){ return this.tiledMapEntity.location /* enumerable: !0, configurable: !0 */ } get quaternion(){ return this.tiledMapEntity.orientation /* enumerable: !0, configurable: !0 */ } getTileUrl(t, e, n) { var i = (this.tiledMapEntity.filePath + "/" + this.tiledMapEntity.fileName).replace(/\$DEPTH/g, t.toString(10)).replace(/\$X/g, e.toString(10)).replace(/\$Y/g, n.toString(10)); return i += "?t=" + (new Date).getTime() //this.RestService.addAuthorizationQueryParameter(i) //???? } fillAttributions(t) { t.NavVis = { score: 100 } } decodeBitStream(t) { if (!t) return { empty: !0 }; for (var e = {}, n = [e], i = 0; i < t.length; i++) { var r = n.shift() , o = parseInt(t.substr(i, 1), 16); if (1 & o) { var a = {}; r[0] = a, n.push(a) } 2 & o && (a = {}, r[1] = a, n.push(a)), 4 & o && (a = {}, r[2] = a, n.push(a)), 8 & o && (a = {}, r[3] = a, n.push(a)) } var s = { empty: !0 }; return this.computeHashes(s, e, ""), s } computeHashes(t, e, n) { for (var i = 0; i < 4; i++) e[i] && (t[n + i.toString(10)] = !0, t.empty = !1, this.computeHashes(t, e[i], n + i.toString(10))) } } /* { "bundle_id": 1, //t-CwfhfqJ "file_name": "$DEPTH/$X/$Y.png", "file_path": "data/bundle_t-CwfhfqJ/building_1/map_tiles/11", "floor_id": 11, "id": 1, "location": [ 113.5957510575092, 22.366605927999239, 0.0 ], "map_size_m": 61.44, "max_depth": 3, "orientation": [ 0.7071067811865476, 0.0, 0.0, 0.7071067811865475 ], "quadtree": "fe5f7c7fcffff7f53", "sceneCode": "t-CwfhfqJ", "tile_size_px": 256, "type": "TILED_PYRAMID" } */