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

news2024/11/17 11:42:25

(真实经纬度运行轨迹)

完整代码

<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),
	}
},

最终效果

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/852944.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【K8S 的二进制搭建】

目录 一、二进制搭建 Kubernetes v1.201、准备环境 二、操作系统初始化配置三、部署 etcd 集群1、准备签发证书环境2、在 master01 节点上操作1、生成Etcd证书 3、在 node01 节点上操作4、在 node02 节点上操作 四、部署 Master 组件五、部署 docker引擎六、部署 Worker Node 组…

【算法|数组】手撕经典二分法

算法|数组——二分查找 文章目录 算法|数组——二分查找引言二分查找左闭右闭写法左闭右开写法 总结 引言 首先学习这个算法之前需要了解数组知识&#xff1a;数组。 大概介绍以下&#xff1a; 数组是存储在连续内存空间上的相同类型数据的集合。数组下标都是从0开始。数组在…

【SpringCloud】Gateway服务网关

Spring Cloud Gateway 是 Spring Cloud 的一个全新项目&#xff0c;该项目是基于 Spring 5.0&#xff0c;Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关&#xff0c;它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。 1.为什么需要网关…

“实现数字化转型:探索会议OA项目的高级技术与创新应用“

文章目录 引言&#xff1a;1.项目背景和需求分析&#xff1a;2.技术选型和架构设计&#xff1a;3.项目实现和功能亮点&#xff1a;3.0 layui实现登录及注册3.1 会议管理模块3.1.1 会议发布3.1.2 我的会议3.1.3 我的审批3.1.4 会议通知3.1.5 待开会议3.1.6 历史会议3.1.7 所有会…

【Leetcode】层次遍历||树深度||队列

step by step. 题目&#xff1a; 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例 2&#xff1a; 输入&am…

【LeetCode】数据结构题解(13)[设计循环链表]

设计循环链表 &#x1f609; 1.题目来源&#x1f440;2.题目描述&#x1f914;3.解题思路&#x1f973;4.代码展示 所属专栏&#xff1a;玩转数据结构题型❤️ &#x1f680; >博主首页&#xff1a;初阳785❤️ &#x1f680; >代码托管&#xff1a;chuyang785❤️ &…

WordPress使用【前端投稿】功能时为用户怎么添加插入文章标签

在使用Wordpress做前端投稿功能的时候&#xff0c;可能需要用户填写文章标签&#xff0c;在插入文章的时候很多人不知道怎么把这些标签插入进去&#xff0c;下面这篇文章来为大家带来WordPress使用前端投稿功能时插入文章标签方法。 在Wordpress里 wp_insert_post 此函数的作…

Vue3核心笔记

文章目录 1.Vue3简介2.Vue3带来了什么1.性能的提升2.源码的升级3.拥抱TypeScript4.新的特性 一、创建Vue3.0工程1.使用 vue-cli 创建2.使用 vite 创建 二、常用 Composition API1.拉开序幕的setup2.ref函数3.reactive函数4.Vue3.0中的响应式原理vue2.x的响应式Vue3.0的响应式 5…

冠达管理:夜盘是什么意思?

跟着现代社会的快节奏开展&#xff0c;股票商场的运营也因而发生了相应的改变。夜盘是指在正常的买卖时刻外特别敞开的买卖商场&#xff0c;为出资者供给更多的买卖时刻和机会。夜盘的概念在我国自2002年引入以来逐渐被越来越多的出资者所熟知。那么夜盘究竟是什么&#xff1f;…

Collection 中的简单命令

一、list 集合常用的方法 LIst&#xff1a;元素存取有序、可以保存重复元素、有下标。可以存储 null 值。 ArrayList&#xff1a;底层数组结构&#xff0c;有 index&#xff0c;查询快 LinkList&#xff1a; 底层链表结构&#xff0c;增删快 添加 //确保此集合包含指定的元素…

操作系统—进程管理

这里写目录标题 进程特点并行和并发进程的状态进程的控制结构PCB包含什么信息 进程上下文切换进程上下文切换发生的场景 进程的通信方式 线程什么是线程线程的优缺点进程线程的区别线程的上下文切换线程的实现用户线程和内核线程的对应关系 用户线程如何理解、优缺点内核线程如…

在Echarts中的tooltip上添加点击按钮

需求&#xff1a; 在Echarts的tooltips中添加点击按钮并可以鼠标悬停点击该按钮 功能实现&#xff1a; 在option中的tooltip添加enterable: true的属性&#xff0c;表示鼠标可以移入tooltip中再在formatter中添加 <button onclick"onTooltipsFun()" stylecursor:…

protobuf 2定义string常量

背景 protobuf 2中定义的enum枚举值必须为数字类型&#xff0c;故不支持string类型&#xff0c;但有些业务场景又确实需要定义string常量。 目标 在protobuf 2中定义string常量。 方案 思路&#xff1a;通optional default实现string常量。 细节&#xff1a; 1、protobu…

直击HDC展区现场,华为天气开启天气+生活新方式

备受瞩目的HDC 2023 大会正式举办&#xff0c;华为天气携气象领域最新成果展现在大会现场&#xff0c;经过在线下展区实际体验过后&#xff0c;我被天气服务卡强势“种草”了。 天气与人们生活息息相关&#xff0c;面对日益复杂的天气场景&#xff0c;简单的“打开APP看天气”…

手搓单链表

文章目录 前言一、链表和顺序表的区别二、什么是单链表单链表分类单链表的结构 三、带头不循环单链表1.单链表的结构体2.带头不循环单链表的初始化和销毁3.带头不循环单链表的头插&#xff0c;尾插和打印4.带头不循环单链表的头删和尾删5.带头不循环单链表的查找&#xff0c;指…

冠达管理:创新药概念强势拉升,康希诺大涨超15%

立异药概念9日盘中强势拉升&#xff0c;到发稿&#xff0c;昊帆生物“20cm”涨停&#xff0c;康希诺大涨超15%&#xff0c;翰宇药业涨近13%&#xff0c;德展健康涨停&#xff0c;泰格医药、药石科技涨超7%。 康希诺昨日晚间公告&#xff0c;8月7日&#xff0c;公司与 AstraZene…

使用go获取链上数据之主动拉取-搭建环境(一)

使用go获取链上数据之主动拉取-搭建环境&#xff08;一&#xff09; 1、配置文件1.1、新建配置文件1.2、新建setting.go文件1.3、新建config.go文件 2、全局变量配置2.1、新建global.go2.2、初始化配置2.3、验证配置 在我们实际开发项目中&#xff0c;很多时候都需要从链上获取…

基于Selenium模块实现无界面模式 执行JS脚本

此篇文章主要介绍如何使用 Selenium 模块实现 无界面模式 & 执行JS脚本(把滚动条拉到底部)&#xff0c;并以具体的示例进行展示。 1、Selenium 设置无界面模式 创建浏览器对象之前&#xff0c;创建 options 功能对象 &#xff1a;options webdriver.ChromeOptions() 添加…

SciencePub学术 | 传感器类重点SCIE征稿中

SciencePub学术 刊源推荐: 传感器类重点SCIE征稿中&#xff01;信息如下&#xff0c;录满为止&#xff1a; 一、期刊概况&#xff1a; 传感器类重点SCIE 【期刊简介】IF&#xff1a;2.0-2.5&#xff0c;JCR3区&#xff0c;中科院4区&#xff1b; 【版面类型】正刊&#xff1…

【UE4 RTS】03-Camera Zoom

前言 本篇通过对CameraPawn的弹簧臂组件的长度进行增减&#xff0c;从而实现了视角的远近缩放控制。 效果 步骤 1. 在项目设置中添加如下操作映射 2. 打开玩家控制器“RTS_PlayerController_BP”&#xff0c;在上一篇中我们已经实现了CameraPawn的移动功能&#xff1a; 这里…