three.js 地球与卫星绕地飞行【真实三维数据】

(真实经纬度运行轨迹)

完整代码

<template>
	<div class="home3dMap" id="home3dMap" v-loading="loading"></div>
</template>

<script>
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
export default {
	name:"home3dMap",
	data(){
		return {
			loading:true,
			scene:null,		//场景
			camera:null,   //相机
			meshMaterial:null,  //网络模型
			renderer:null,  //渲染器
			controls:null,  //控制器
			areaSize:{
				width:960,
				height:685,
			},
			//canvas内的数据
			map:{
				earthRadius:75,  //地球半径
				satList:[],  //卫星列表
				originalData:{},  //原始数据
				satellitePoint:[],  //卫星画线 (包括卫星名称,三维xyz)
				circleLonLat:[],  //单个卫星画圈集合(卫星轨迹)  用于圆环跟随地球旋转
				satelliteArr:[],   //卫星集合 (针对卫星)
				circleTime:"",  //卫星间隔时间
				satTime:[],   //画圈定时器  (多个圆环的定时)
				setInteI:[],   //卫星画圈定时器索引   (多个卫星圆环的定时器初始值)
				flag:true,  //true:实时模式、false:历史模式
				speedValue:1, //最终选择的倍速值
			},
		}
	},
	beforeDestroy(){
		//清除定时器,如果定时器存在
		if(this.map.satTime.length > 0){
			for(let i=0,list=this.map.satTime; i<list.length; i++){
				window.clearInterval(list[i]);
			}
		}
		this.map.satTime = [];
		//重置定时器初始值
		this.map.setInteI = [];
	},
	mounted(){
		//初始化
		this.init();
	},
	methods:{
		init(){
			this.createScene();   //创建场景
			this.createLight();   //创建光源
			this.createCamera();  //创建相机
			//稍后启动
			this.laterInit()
		},
		
		//稍后启动
		async laterInit(){
			let sadData = await this.orbitCalcFunAcquisition();  //世界地图轨道数据
			this.loading = false;  //关闭loading
			if(sadData.length < 1){
				return;
			}
			this.createMesh();    //创建几何体
			this.createRender();  //创建渲染器
			this.createControls();   //创建轨道控制器
			this.animate();
		},
		
		//创建场景
		createScene(){
			let scene = new THREE.Scene();
			this.scene = scene;
		},
		
		//创建光源
		createLight(){
			// 环境光
			const ambientLight = new THREE.AmbientLight(0xcccccc, 2)
			this.scene.add(ambientLight)
			// 平行光
			let directionalLight = new THREE.DirectionalLight(0xffffff, 0.2)
			directionalLight.position.set(1, 0.2, 0).normalize()
			// 平行光2
			let directionalLight2 = new THREE.DirectionalLight(0xff2ffff, 0.2)
			directionalLight2.position.set(1, 0.2, 0.1).normalize()
			this.scene.add(directionalLight)
			this.scene.add(directionalLight2)
			// 半球光
			let hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.2)
			hemiLight.position.set(5, 50, 0)
			// this.scene.add(hemiLight)
			// 平行光3
			let directionalLight3 = new THREE.DirectionalLight(0xffffff, 0)
			// directionalLight3.position.set(1, 50, -2)
			// 开启阴影
			directionalLight3.castShadow = true
			// 设置光边界
			// directionalLight3.shadow.camera.top = 18
			// directionalLight3.shadow.camera.bottom = -10
			// directionalLight3.shadow.camera.left = -52
			// directionalLight3.shadow.camera.right = 12
			this.scene.add(directionalLight3)
		},
		
		//创建相机
		createCamera(){
			let areaSize = this.areaSize;
			//渲染区域
			let camera = new THREE.PerspectiveCamera(60,  areaSize.width / areaSize.height, 0.1, 10000)
			//设置相机位置
			camera.position.set(50, -10, 200)
			//设置相机方向
			camera.lookAt(0, 0, 0)
			this.camera = camera;
			this.scene.add(this.camera);
		},
	
		//创建几何体
		createMesh(){
			//地球
			let geometry = new THREE.SphereGeometry( this.map.earthRadius, 64, 32);
			let earthImgSrc = require('@/assets/img/home/home3dMapBackground.png');
			//材质
			let earthMater = new THREE.MeshPhongMaterial({
				map: new THREE.TextureLoader().load(earthImgSrc),
				transparent:true,
				depthWrite:false,
			});
			//网络模型对象 -- 地球
			let meshMaterial = new THREE.Mesh(geometry,earthMater);
			//地球模型
			this.meshMaterial = meshMaterial;
			//添加到场景中
			this.scene.add(meshMaterial);
			//添加圆环
			this.initSatellite(meshMaterial);
		},
		
		//添加圆环
		initSatellite(meshMaterial){
			//返回一个卫星和轨道的组合体
			// satelliteSize/卫星大小   satelliteRadius/卫星旋转半径   rotation	/组合体的旋转方向     speed/卫星运动速度
			
			// 圆环图片
			let sadImgSrc = require('@/assets/img/control/satellite.png');
			
			//循环卫星轨迹   默认一个轨迹上只有一个卫星  【卫星轨迹的第一个位置数据就是卫星初次的位置数据】
			for(let i=0,list=this.map.satellitePoint; i<list.length; i++){
				// 卫星轨迹使用曲线办法,曲线轨迹不是闭合的,第一圈和下一圈似乎轨迹位置不是重合的】
				// 卫星轨迹三维点数据
				let circleArr = list[i].mapArray;
				// 数据格式格式化
				let circleArrForamt = [];
				//当前卫星轨迹曲线  
				for(let j=0; j<circleArr.length; j++){
					let item = circleArr[j];
					circleArrForamt.push(new THREE.Vector3(item.x3d,item.y3d,item.z3d))
				}
				const curve = new THREE.CatmullRomCurve3( circleArrForamt );
				const points = curve.getPoints( 70 );
				const geometry = new THREE.BufferGeometry().setFromPoints( points );
				const material = new THREE.LineBasicMaterial( { color: 0xffffff } );
				const circleLonLat = new THREE.Line( geometry, material );
				//将圆环存储
				this.map.circleLonLat.push(circleLonLat);   //用于圆环跟随地球旋转
				
				//卫星
				let satellite = new THREE.Sprite(new THREE.SpriteMaterial({
					map: new THREE.TextureLoader().load(sadImgSrc),
					blending: THREE.AdditiveBlending
				}));
				
				//卫星大小
				satellite.scale.x = satellite.scale.y = satellite.scale.z = 12;
				//卫星旋转半径
				satellite.position.set(circleArr[0].x3d, circleArr[0].y3d, circleArr[0].z3d);
				
				//Object3D
				let pivotPoint = new THREE.Object3D();
				pivotPoint.add(satellite);
				//将卫星对象增加到圆环轨迹中
				circleLonLat.add(pivotPoint);
				
				//将卫星存储
				this.map.satelliteArr.push(satellite);
				//将圆环添加到场景中
				this.scene.add(circleLonLat);
				
				//物体延线移动方法
				if(curve && satellite){   //轨道和卫星都存在
					this.map.satTime = [];
					this.map.setInteI[i] = 0;
					this.map.satTime[i] = setInterval(()=>{
						if(this.map.setInteI[i] - circleArr.length <= 0){
							//每一个
							// let point = curve.getPointAt(this.map.setInteI);   //获取曲线指定点坐标
							// if(point){
							// 	satellite.position.set(point.x, point.y, point.z);
							// }
							let point = circleArr[this.map.setInteI[i]];
							if(point){
								satellite.position.set(point.x3d, point.y3d, point.z3d);
							}
						}else{
							//实时模式重新触发接口事件
							if(this.map.flag){
								this.laterInit();
							}else{
								//历史械重新走数据
							}
							this.map.setInteI[i] = 0;
						}
						//定时器开启
						this.map.setInteI[i]++;
					},Math.ceil(this.map.circleTime/this.map.speedValue))
				}
			}
		},
		
		//发光的星星
		generateSprite(color){
			let canvas = document.createElement('canvas');
			canvas.width = 16;
			canvas.height = 16;
			let context = canvas.getContext('2d');
			let gradient = context.createRadialGradient(canvas.width / 2, canvas.height / 2, 0, canvas.width / 2, canvas.height / 2, canvas.width / 2);
			gradient.addColorStop(0, 'rgba(' + color + ',1)');
			gradient.addColorStop(0.2, 'rgba(' + color + ',1)');
			gradient.addColorStop(0.4, 'rgba(' + color + ',.6)');
			gradient.addColorStop(1, 'rgba(0,0,0,0)');
			context.fillStyle = gradient;
			context.fillRect(0, 0, canvas.width, canvas.height);
			return canvas;
    	},
		
		//创建渲染器
		createRender(){
			let element = document.getElementById("home3dMap");
			//创建渲染器
			let renderer = new THREE.WebGLRenderer({
				antialias:true,  //开启抗锯齿
				alpha:true,
			})
			let areaSize = this.areaSize;
			renderer.setSize(areaSize.width,areaSize.height)   //设置渲染区域尺寸
			renderer.shadowMap.enabled = true;   //显示阴影
			renderer.shadowMap.type = THREE.PCFSoftShadowMap;
			renderer.setClearColor(0x3f3f3f, 0);   //设置背景颜色
			
			this.renderer = renderer;
						
			element.appendChild(this.renderer.domElement)
		},
		
		//创建轨道控制器
		createControls(){
			let controls = new OrbitControls(this.camera, this.renderer.domElement);
			controls.enableDamping = true;  //开启衰弱
			controls.maxZoom = Infinity;
			controls.minDistance = 75;   //设置最小可缩放
			this.controls = controls;
		},
		
		//循环
		animate(){
			this.controls.update();  //控制阻尼器
			//地球自传
			this.meshMaterial.rotation.y += 0.0015;
			this.renderer.render(this.scene, this.camera);

			//卫星轨迹转动
			for(let i=0; i<this.map.circleLonLat.length; i++){
				this.map.circleLonLat[i].rotation.y += 0.0015;
			}
			
			requestAnimationFrame(this.animate.bind(this));
		},
		
		//卫星列表
		async sadListAcquisition(){
			return new Promise(resolve => {
				this.$apilist.OBSERVE_stationControl_satellite_list("").then(res => {
					if(res.code == "200"){
						this.map.satList = res.data;
						resolve(res.data);
					}
				})
			})
		},
		
		//世界地图轨道数据
		async orbitCalcFunAcquisition(){
			//关闭定时器
			if(this.map.satTime.length > 0){
				for(let i=0,list=this.map.satTime; i<list.length; i++){
					window.clearInterval(list[i]);
				}
			}
			let satList = await this.sadListAcquisition();
			if(satList.length < 1){
				this.$message.warning("卫星列表为空");
				return;
			}
			//公共方法
			return new Promise(resolve=>{
				this.orbitCalcFunAcquisitionPublic(resolve);
			})
		},

		//公共方法
		async orbitCalcFunAcquisitionPublic(resolve){
			let jsonData = {
				startTime:this.$dateFormat.format(new Date()),
				endTime:this.$dateFormat.format(new Date(new Date().getTime() + 2*50*60*1000))
			};
			
			this.$apilist.OBSERVE_stationControl_globalMapTrack_list(jsonData).then(res => {
				if(res.code == "200"){
					this.map.originalData = res.data;   //保存原始数据
					this.map.satellitePoint = [];   //清空卫星画线
					let mapArray = res.data.positionMap;   //卫星数组(卫星经纬度-轨迹经纬度)
					let mapTime = res.data.dateTimeList;   //卫星时间
					this.map.circleTime = (res.data.timeGap + '000')*1; //卫星间隔时间
					//卫星轨迹高度    卫星轨迹高度870km/地球半径6378km*当前地球球体半径(this.map.earthRadius)
					let satelliteRadius = (this.map.earthRadius-0)+(870-0)/6378*(this.map.earthRadius-0); 
					//轨迹三维
					for(let key in mapArray){
						mapArray[key].forEach((item,index)=>{
							let obj = this.$lnglatFormat.lnlt2xyz(item.lon,item.lat,satelliteRadius);
							item.x3d = obj.x;
							item.y3d = obj.y;
							item.z3d = obj.z;
						});
						this.map.satellitePoint.push({
							sat:key,
							mapArray:mapArray[key],
						})
					};
					
					//push卫星名
					this.map.satellitePoint.forEach((item,index)=>{
						this.map.satList.forEach((value,i)=>{
							if(item.sat == value.satelliteCode){
								item.satName = value.satelliteName;
							}
						})
					});
					resolve(this.map.satellitePoint);
				}
			})
		}
	},
}
</script>

<style>
	.home3dMap{
		width:100%;
		height:100%;
		/* background:rgba(103,22,173,0.3); */
	}
</style>

lnlt2xyz方法:

//二维经纬度数据转为三维空间位置数据
lnlt2xyz(lon, lat, h){
	let radius = h;
	const phi = (360 + lon) * (Math.PI / 180)
	const theta = (180 - lat) * (Math.PI / 180)
	return {
		x: -radius * Math.sin(theta) * Math.cos(phi),
		y: radius * Math.cos(theta),
		z: radius * Math.sin(theta) * Math.sin(phi),
	}
},

最终效果

请添加图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值