let PigeonGLConstants = require("./constants.js"); import CameraControl from "./Camera/CameraControl.js"; import Layer from "./Layers/Layer.js"; /** * 信鸽地图地图核心类,用于生成3d空间地图底层 */ class Map extends Layer { _layerid = 0; zIndex = 1000; status = "running"; type = "gps"; layers = []; /** * 地图构造函数 * @constructor * @param {Object} map - 地图绘制对象,目前支持高德,mapbox或者纯空间 * @param {Number} map.width - 地图宽度, default:1000 * @param {Number} map.height - 地图高度, default:600 * @param {Number} map.rotation - 地图旋转角度, default:60 * @param {Number} map.pitch - 地图俯视角度, default:45 * @param {Array} map.center - 地图默认中心位置经纬度, default:[0,0] * @param {Number} map.zoom - 地图缩放级别,default:21 * @param {dom} map.container - 容器dom */ constructor(map) { super(); this.clock = new THREE.Clock(); this.clock.start(); this.initMap(map); this.initWorld(); if (this.map.hasGround) { this.addGround(); } this.update(); } initMap(map) { this.map = Object.assign( { rotation: 0, width: 1000, height: 600, pitch: 45, zoom: 21, center: [0, 0] }, map ); } initWorld() { // Set up a THREE.js scene this.renderer = new THREE.WebGLRenderer( Object.assign({ alpha: true, antialias: true }, this.map.rendererOptions) ); this.renderer.setSize(this.map.width, this.map.height); this.map.container.appendChild(this.renderer.domElement); this.renderer.domElement.style["position"] = "relative"; this.renderer.domElement.style["pointer-events"] = "none"; this.renderer.domElement.style["z-index"] = ++this.zIndex; /** * @property {Object} scene - this.scene为three.js场景 */ this.scene = new THREE.Scene(Object.assign({}, this.map.sceneOptions || {})); /** * @property {Object} camera - this.camera.js场景 */ this.camera = new THREE.PerspectiveCamera( 45, this.map.width / this.map.height, 0.000001, 5000000000 ); /** * @property {Object} layers - 图层集合 */ this.layers = []; /** * @property {Object} world - this.world为世界模型组,所有模型被添加到该组内 */ this.world = new THREE.Group(); this.scene.add(this.world); this.camera.position.z = 10; this.camera.position.x = this.map.center[0]; this.camera.position.y = this.map.center[1]; /** * @property {CameraControl} cameraControl - 摄像机控制类 */ this.cameraControl = new CameraControl(this); } moveTo(obj, coord, options = {}) { let scale = options.preScale; let geoGroup; if (obj.userData.isGeoGroup) geoGroup = obj; else if (obj.parent && obj.parent.userData.isGeoGroup) geoGroup = obj.parent; geoGroup.scale.copy(scale || 1); geoGroup.position.copy(this.projectToWorld(coord)); obj.coordinates = coord; return obj; } /** * 自动刷新渲染,可以改变this.status=='top',停止自动刷新 * @parma {Number} delta - 每帧时间差,用来保持不同机器动画速度相同,用不到不必搭理 */ update() { let delta = this.clock.getDelta(); //子layer更新 for (let x in this.layers) { if (this.layers[x].update) { this.layers[x].update(delta); } } this.renderer.render(this.scene, this.camera); let device; if ((device = this.renderer.vr.getDevice())) { this.animationframe = device.requestAnimationFrame((timestamp) => { if (this.status != "stop") this.update(delta, timestamp); }); return; } this.animationframe = requestAnimationFrame((timestamp) => { if (this.status != "stop") this.update(delta, timestamp); }); } projectToWorld(coord) { return { x: coord[0], y: coord[1], z: coord[2] || 0 }; } unprojectFromWorld(world) { return [world.x, world.y, world.z]; } /** * 经纬度转换屏幕坐标 * @param {Array} coords - 经纬度 [lng,lat] * @return {Object} position - 返回距离容器左上角的距离 {x,y} */ projectToScreen(param) { let coords = Object.assign({}, param); coords[1] = -coords[1]; let world = this.projectToWorld(coords); let worldPosition = this.world.getWorldPosition().clone(); let standarVector = worldPosition.add(world).project(this.camera); //世界坐标转设备坐标 let a = this.map.width / 2; let b = this.map.height / 2; let x = Math.round(standarVector.x * a + a); //标准设备坐标转屏幕坐标 let y = this.map.height - Math.round(standarVector.y * b + b); //标准设备坐标转屏幕坐标 return { x: x, y: y }; } /** * 屏幕坐标转转经纬度 * @param {Object} pixel - 屏幕坐标 {x,y} * @return {Array} coord - [x,y] */ unprojectFromScreen(pixel) { let a = this.map.width / 2; let b = this.map.height / 2; let standX = -(pixel.x - a) / a; let standY = (this.map.height - pixel.y - b) / b; let standarVector = new THREE.Vector3(standX, standY, 0); let world = standarVector.unproject(this.camera); let worldPosition = this.world.getWorldPosition().clone(); let coord = this.unprojectFromWorld(world.sub(worldPosition)); coord[1] = -coord[1]; return coord; } /** * 添加物体到3d空间 * @param {Object} obj - Three.js的mesh * @param {coord} Array - 物体的经纬度 {lng,lat} * @param {Object} options - 配置 * @param {Boolean} options.scaleToLatitude - 是否按照1px = 1m的比例进行缩放 默认true * @param {Number} options.preScale - 改变像素到米的换算比例 默认1 * @return {Object} obj -物体mesh */ add(obj, coord, options) { let geoGroup = new THREE.Group(); geoGroup.userData.isGeoGroup = true; geoGroup.add(obj); this.world.add(geoGroup); this.moveTo(obj, coord, options); return obj; } /** * 添加默认地面 */ addGround() { let geometry = new THREE.PlaneGeometry( PigeonGLConstants.WORLD_SIZE, PigeonGLConstants.WORLD_SIZE ); let material = new THREE.MeshPhongMaterial({ color: this.map.groundColor || 0x666666, shininess: 30 }); let plane = new THREE.Mesh(geometry, material); // plane.position.x = plane.position.y = - PigeonGLConstants.WORLD_SIZE/2; plane.position.z = -0.01; this.addAtCoordinate(plane, this.map.center, { scaleToLatitude: true, preScale: 100 }); } /** * 移除物体mesh * @param {Object} obj - mesh */ remove(obj) { this.world.remove(obj); } /** * 设置默认光源 */ setupDefaultLights() { this.scene.add(new THREE.AmbientLight(0xcccccc)); let sunlight = new THREE.DirectionalLight(0xffffff, 0.5); sunlight.position.set(0, 800, 1000); sunlight.matrixWorldNeedsUpdate = true; this.world.add(sunlight); } } export default Map;