import * as THREE from "../../../../libs/three.js/build/three.module.js"; import math from '../../utils/math.js' import browser from '../../utils/browser.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 //略大于半个周长(mapSizeM/2) const MAX_VERTICAL_DIST = 2 const MAX_VERTICAL_DIST_TO_BEST = 1 function defineLocalProj(locationLonLat){ proj4.defs("LOCAL_MAP", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15)); } //高德坐标拾取工具 : https://lbs.amap.com/tools/picker export class MapLayer extends THREE.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 = new THREE.Color(16777215) this.viewport = viewport this.changeViewer(viewer_) //添加地图 var map = new TiledMapOpenStreetMap(this, this.tileColor ) this.addMap(map) //map.setEnable(false) } addMapEntity(data, datasetId){ if(!data || !data[0]){ Potree.Log('平面图无数据',{font:'red'}) return } var floorplan = new TiledMapFromEntity(this, this.tileColor, data[0] )//[0]? if(floorplan){ floorplan.name += "_"+ datasetId this.addMap(floorplan) floorplan.updateProjection() floorplan.updateObjectGroup() let visible = false if(datasetId in Potree.settings.floorplanEnables){ visible = Potree.settings.floorplanEnables[datasetId] }else{ visible = Potree.settings.floorplanEnable } if(visible){ this.needUpdate = true }else{ floorplan.setEnable(false) } this.dispatchEvent({type:'floorplanLoaded', floorplan}) } return floorplan } getFloorplan(datasetId){ return this.maps.find(e=>e.name == 'floorplan'+"_"+ datasetId ) } addMap(t){ this.maps.push(t) //this.view.invalidateScene() this.needUpdate = true this.viewer.mapChanged = 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.mapChanged = true } 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) } } update(){ this.needUpdate = false if(this.disabled || !this.maps.find(e=>!e.disabled) || !this.maps.find(e=>e.objectGroup.visible) )return //add this.viewer.mapChanged = true var e, n, i, r, o; this.updateTimer = void 0, e = this.viewport.camera, n = e.projectionMatrix.clone() let expandRatio = 1.3 n.elements[0] /= expandRatio n.elements[5] /= expandRatio // 为了缓存吗,使边界处也提前加载,扩大显示区域 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] } updateProjection(){ for (var t = 0, e = this.maps; t < e.length; t++){ var n = e[t]; n.clearProjection(), n.updateObjectGroup() } } } export class TiledMapBase extends THREE.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.objectGroup, this.tileColor, null, '0'), this.isTileVisibleBox = new THREE.Box3, this.isTileVisibleVec = new THREE.Vector3 this.projection = projection this._zoomLevel = 0;//1-20 this.objectGroup.addEventListener('isVisible',()=>{ this.mapLayer.viewer.mapChanged = true }) this.computeCount = 0 } get zoomLevel(){ return this._zoomLevel } set zoomLevel(zoomLevel){ if(this._zoomLevel != zoomLevel){ this._zoomLevel = zoomLevel //this.dispatchEvent('zoomLevelChange',zoomLevel) //if(this.name == 'map')console.log(zoomLevel,viewer.mapViewer.camera.zoom) } } updateObjectGroup(){ this.position && this.objectGroup.position.copy(this.position), this.quaternion && this.objectGroup.quaternion.copy(this.quaternion), this.objectGroup.updateMatrixWorld(!0) } updateProjection(){ if(!this.transformMapToLocal){ if(proj4.defs("LOCAL_MAP")){ /* 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, "LOCAL_MAP") //} } } } clearProjection(){ this.transformMapToLocal = void 0 this.projection !== 'LOCAL_MAP' && this.baseTile.remove() } setEnable(enable){//add if(!this.disabled == enable)return if(enable){ console.log('setEnable',true) } this.disabled = !enable Potree.Utils.updateVisible(this.objectGroup, 'setEnable', enable) if(!enable){ this.baseTile.remove() }else{ this.mapLayer.needUpdate = true } } update(e, n){ this.computeCount = 0 var unavailable = (this.disabled || !this.objectGroup.visible)//地图即使不显示也要获得zoomlevel if(this.name != 'map' && unavailable)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; //add 高纬度的因倾斜而造成tile较小,所以放大些,否则会造成显示的tile过多而卡 let lonlat = viewer.transform.lonlatToLocal.inverse(viewport.camera.position.clone()) let cos = Math.cos(THREE.Math.degToRad(lonlat.y)); //越小就在纬度上越高,tile表现越小 //为什么lonlat.y会超出90? if(lonlat.y>90){ console.log('lonlat.y>90',lonlat.y) } cos = THREE.Math.clamp(cos, 0,1) let lonShift = Math.abs(viewer.mapViewer.camera.position.x / this.mapSizeM * 16 ) //越大就在经度离中心越远,tile表现越大 。 lonShift = THREE.Math.clamp(lonShift, 0, Math.PI) lonShift = (1 - Math.sin( 1/2 * lonShift + Math.PI/2 )) * Math.PI // 0-Math.PI sin增速向上 let scale = 0.5 * cos * (1+lonShift) + 0.5 * Math.pow(cos, lonShift) var c = this.tileSizePx / i.length() / scale //多除以一个scale缩放因子,scale越大level越小 , 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 /* if(isNaN(this.zoomLevel )){ console.log(level, cos , scale , lonlat ) } */ if(!unavailable){ 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) //e转化为local 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) //仿造createMesh写的准确版,但会因为大的tile非矩形,而视口是矩形,若视口刚好在tile的曲线边缘外却识别为可见,就会创建冗余tile。 但上面那个简单版在zoomlevel低的时候地球边缘容易有识别不到的tile,造成黑色三角形。 //容易出现奇怪的mesh /* this.isTileVisibleBox.makeEmpty() this.isTileVisibleVec.set(e.x - r, e.y - r, e.z) this.transformMapToLocal.forward(this.isTileVisibleVec) this.isTileVisibleVec.applyMatrix4(this.objectGroup.matrixWorld) this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec) this.isTileVisibleVec.set(e.x - r, e.y + r, e.z) this.transformMapToLocal.forward(this.isTileVisibleVec) this.isTileVisibleVec.applyMatrix4(this.objectGroup.matrixWorld) this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec) this.isTileVisibleVec.set(e.x + r, e.y - r, e.z) this.transformMapToLocal.forward(this.isTileVisibleVec) this.isTileVisibleVec.applyMatrix4(this.objectGroup.matrixWorld) this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec) this.isTileVisibleVec.set(e.x + r, e.y + r, e.z) this.transformMapToLocal.forward(this.isTileVisibleVec) this.isTileVisibleVec.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, e, n, parent, name){ this.map = map; this.name = name; this.parent = parent; 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); this.map.computeCount ++ //console.log(this.map.computeCount, this.name, 'level:',level) if (entity.isTileVisible(tempVector, .5 * o, n)){ this.children[p] || (this.children[p] = new MapTile(this.map, this.objectGroup,this.tileColor, this, this.name+p )) 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)); Potree.Utils.setObjectLayers(this.mesh, 'map' ) this.mesh.renderOrder = -(1e6 - l - 100 * (t.zIndex || 0)); this.mesh.name = this.name //add var h = this.mesh.material; /* let area = math.getArea(this.mesh.geometry.vertices.slice(0,3)); if(area >0){ this.mesh.visible = false console.log('area>0',this.mesh.name) } */ var loadDone = ()=>{ this.map.mapLayer.loadingInProgress-- if(this.map.mapLayer.loadingInProgress == 0){ this.map.mapLayer.dispatchEvent('loadDone') } } h.map = texLoader.load(p, (tex)=>{ //如果一直加载不了会影响其他的加载,如google地图没有vpn会使全景图一直加载不了 if(this.mesh){//如果还要显示的话 this.textureLoaded = true this.mesh.material.opacity = 1 //this.mapLayer.view.invalidateScene() this.map.mapLayer.viewer.mapChanged = true 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.mapChanged = true } 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, }); if(Potree.settings.isTest) { var colorHue = Math.random() t.color = new THREE.Color().setHSL(colorHue, 0.6, 0.92) }else{ t.color = this.tileColor ? this.tileColor : new THREE.Color(16777215) } return t } traverse(f){//add return THREE.Mesh.prototype.traverse.call(this,f) } 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){ let baseUrl, attribution, projection, maxDepth; if(Potree.settings.mapCompany == 'google'){ projection = "EPSG:900913"//"EPSG:4326"//4550 baseUrl = "https://mt2.google.com/vt/lyrs=m@159000000&hl=zh-CN&gl=cn&x=${x}&y=${y}&z=${z}&s=mt1" /* "http://mt2.google.cn/vt/lyrs=m@177000000&hl=zh-CN&gl=cn&src=app&x=${x}&y=${y}&z=${z}" */ //最高只到19 attribution = "© PopSmart, © 谷歌地图" maxDepth = 22 }else{ projection = "EPSG:3857" baseUrl = "https://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&style=7&x=${x}&y=${y}&z=${z}" // https://blog.csdn.net/fredricen/article/details/77189453 attribution = "© PopSmart, © 高德地图" maxDepth = 19 } super('map', mapLayer, tileColor, projection ) //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.maxDepth = maxDepth this.baseUrl = baseUrl; this.attribution = attribution this.tileSizePx = 256 this.mapSizeM = 40075017 //总占据多少米(地球赤道周长) 和三维空间的不一样 - -, 空间上的是直径,地图上的是半个圆周 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)) } } //yrs=y为混合地图,s为卫星地图,m为普通地图。我们使用谷歌地图的瓦片图层的时候默认采用的是lyrs=s,也就是普通的卫星图层,现在我们希望包含路网信息,只需要设置lyrs=y就OK了。 export class TiledMapFromEntity extends TiledMapBase{ constructor(mapLayer, tileColor, data){ super('floorplan', mapLayer, tileColor, "LOCAL" /* "EPSG:3857" *//* "WGS84" */) //直接就是本地坐标,没有projec let entity = this.tiledMapEntity = this.fillFromData(data) let time = entity.updateTime || entity.createTime this.tileSizePx = entity.tileSizePx, this.mapSizeM = entity.mapSizeM, this.maxDepth = entity.maxDepth; this.postStamp = time ? time.replace(/[^0-9]/ig,'') : (new Date).getTime() //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 = `${Potree.settings.urls.prefix1}${e.file_path}` //}else{ // data.filePath = `${Potree.settings.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("LOCAL_MAP")){ let lonlat = this.tiledMapEntity.globalLocation /* if(window.AMapWith84){//需要转换 lonlat = AMapWith84.wgs84ToAMap(lonlat) } */ lonlat = viewer.transform.lonlatToLocal.forward(lonlat) this.tiledMapEntity.location = new THREE.Vector3().copy(lonlat) } } 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=" + this.postStamp //this.RestService.addAuthorizationQueryParameter(i) //???? } 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))) } } /* note: 目前缩小了能看出形态是一个地球。相机在高空朝下观测,地球平放着。 所以越靠近赤道和地球朝上的那面所在的中央经度(也就是local 0,0,0所对应的初始经度),tile越接近正方形。 所以在两极地区要怎么显示? 注册地理坐标时需要滚动地球吗?(修改初始经度、重定义NAVVIS:TMERC, 就需要更新所有三维世界中的物体位置) 切换中心点: var locationLonLat = viewer.transform.lonlatToLocal.inverse(viewer.mapViewer.camera.position.clone()) proj4.defs("LOCAL_MAP", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat.x.toPrecision(15) + " +lat_0=" + locationLonLat.y.toPrecision(15)); viewer.mapViewer.mapLayer.maps[0].transformMapToLocal = null 地理注册部分地图上的1和2标记有两层意思。当地图全屏展示时,标记的是当前右侧经纬度的位置;当地图为小窗时,标记的是对应场景里三维位置。(所以感觉最好换一个ui?)且在2023.2.1之前才改好,之前都是后者。 为什么边缘总是有奇怪的mesh,是因为有顶点到背面了吗 https://lbs.amap.com/tools/picker 高德坐标拾取器,但和这里展示的不一样, 要用AMapWith84.aMapToWgs84({x: ,y: })转成84。 (qq or 手机登录) https://www.google.com/maps/@77.7730021,-34.4952712,4z google取点 打印所有mapTile的名字,字符串最长的代表有显示的mesh。 viewer.mapViewer.mapLayer.maps[0].baseTile.traverse(function(e){console.log(e.name)}) 能查看有几个显示的mesh viewer.mapViewer.mapLayer.maps[0].objectGroup.children */