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;