地球+卫星运动轨迹

由于工作中, 需要追踪卫星, 记录一下最近的工作成果, 分别有俩个方案, 一个是基于市面上主流的Threejs, 另外一个是基于Cesium, 还尝试过使用, Planetary.js, 效果其实还蛮好, 但是卫星这些不太容易往里加, 就放弃了。

下面是俩个方案的效果图, 以及原理, 由于版权是公司, 不太好贴出代码, 因此给出参考代码。

基于使用ThreeJs
原理

  1. 构建流星, 星团
  2. 使用Threejs构建旋转地球
  3. 构建卫星, 以及在帧率中控制卫星移动

参考源代码如下:

javascript 复制代码
<html><head>
    <title>Threejs实现卫星太阳板折叠</title>
    <meta charset="UTF-8">
    <!-- <script type="text/javascript" src="/js/lib/statistics.js"></script> -->
    <script type="text/javascript" src="/js/lib/three.js"></script>
    <script type="text/javascript" src="/js/lib/OrbitControls.js"></script>
    <script type="text/javascript" src="/js/lib/GLTFLoader.js"></script>
    <script type="text/javascript" src="/js/lib/dat.gui.js"></script>
    <style type="text/css">
    .dg {
/** Clear list styles */
/* Auto-place container */
/* Auto-placed GUI's */
/* Line items that don't contain folders. */
/** Folder names */
/** Hides closed items */
/** Controller row */
/** Name-half (left) */
/** Controller-half (right) */
/** Controller placement */
/** Shorter number boxes when slider is present. */
/** Ensure the entire boolean and function row shows a hand */ }
.dg ul {
list-style: none;
margin: 0;
padding: 0;
width: 100%;
clear: both; }
.dg.ac {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 0;
z-index: 0; }
.dg:not(.ac) .main {
/** Exclude mains in ac so that we don't hide close button */
overflow: hidden; }
.dg.main {
-webkit-transition: opacity 0.1s linear;
-o-transition: opacity 0.1s linear;
-moz-transition: opacity 0.1s linear;
transition: opacity 0.1s linear; }
.dg.main.taller-than-window {
  overflow-y: auto; }
  .dg.main.taller-than-window .close-button {
    opacity: 1;
    /* TODO, these are style notes */
    margin-top: -1px;
    border-top: 1px solid #2c2c2c; }
.dg.main ul.closed .close-button {
  opacity: 1 !important; }
.dg.main:hover .close-button,
.dg.main .close-button.drag {
  opacity: 1; }
.dg.main .close-button {
  /*opacity: 0;*/
  -webkit-transition: opacity 0.1s linear;
  -o-transition: opacity 0.1s linear;
  -moz-transition: opacity 0.1s linear;
  transition: opacity 0.1s linear;
  border: 0;
  position: absolute;
  line-height: 19px;
  height: 20px;
  /* TODO, these are style notes */
  cursor: pointer;
  text-align: center;
  background-color: #000; }
  .dg.main .close-button:hover {
    background-color: #111; }
.dg.a {
float: right;
margin-right: 15px;
overflow-x: hidden; }
.dg.a.has-save > ul {
  margin-top: 27px; }
  .dg.a.has-save > ul.closed {
    margin-top: 0; }
.dg.a .save-row {
  position: fixed;
  top: 0;
  z-index: 1002; }
.dg li {
-webkit-transition: height 0.1s ease-out;
-o-transition: height 0.1s ease-out;
-moz-transition: height 0.1s ease-out;
transition: height 0.1s ease-out; }
.dg li:not(.folder) {
cursor: auto;
height: 27px;
line-height: 27px;
overflow: hidden;
padding: 0 4px 0 5px; }
.dg li.folder {
padding: 0;
border-left: 4px solid rgba(0, 0, 0, 0); }
.dg li.title {
cursor: pointer;
margin-left: -4px; }
.dg .closed li:not(.title),
.dg .closed ul li,
.dg .closed ul li > * {
height: 0;
overflow: hidden;
border: 0; }
.dg .cr {
clear: both;
padding-left: 3px;
height: 27px; }
.dg .property-name {
cursor: default;
float: left;
clear: left;
width: 40%;
overflow: hidden;
text-overflow: ellipsis; }
.dg .c {
float: left;
width: 60%; }
.dg .c input[type=text] {
border: 0;
margin-top: 4px;
padding: 3px;
width: 100%;
float: right; }
.dg .has-slider input[type=text] {
width: 30%;
/*display: none;*/
margin-left: 0; }
.dg .slider {
float: left;
width: 66%;
margin-left: -5px;
margin-right: 0;
height: 19px;
margin-top: 4px; }
.dg .slider-fg {
height: 100%; }
.dg .c input[type=checkbox] {
margin-top: 9px; }
.dg .c select {
margin-top: 5px; }
.dg .cr.function,
.dg .cr.function .property-name,
.dg .cr.function *,
.dg .cr.boolean,
.dg .cr.boolean * {
cursor: pointer; }
.dg .selector {
display: none;
position: absolute;
margin-left: -9px;
margin-top: 23px;
z-index: 10; }
.dg .c:hover .selector,
.dg .selector.drag {
display: block; }
.dg li.save-row {
padding: 0; }
.dg li.save-row .button {
  display: inline-block;
  padding: 0px 6px; }
.dg.dialogue {
background-color: #222;
width: 460px;
padding: 15px;
font-size: 13px;
line-height: 15px; }

/* TODO Separate style and structure */
#dg-new-constructor {
padding: 10px;
color: #222;
font-family: Monaco, monospace;
font-size: 10px;
border: 0;
resize: none;
box-shadow: inset 1px 1px 1px #888;
word-wrap: break-word;
margin: 12px 0;
display: block;
width: 440px;
overflow-y: scroll;
height: 100px;
position: relative; }

#dg-local-explain {
display: none;
font-size: 11px;
line-height: 17px;
border-radius: 3px;
background-color: #333;
padding: 8px;
margin-top: 10px; }
#dg-local-explain code {
font-size: 10px; }

#dat-gui-save-locally {
display: none; }

/** Main type */
.dg {
color: #eee;
font: 11px 'Lucida Grande', sans-serif;
text-shadow: 0 -1px 0 #111;
/** Auto place */
/* Controller row, <li> */
/** Controllers */ }
.dg.main {
/** Scrollbar */ }
.dg.main::-webkit-scrollbar {
  width: 5px;
  background: #1a1a1a; }
.dg.main::-webkit-scrollbar-corner {
  height: 0;
  display: none; }
.dg.main::-webkit-scrollbar-thumb {
  border-radius: 5px;
  background: #676767; }
.dg li:not(.folder) {
background: #1a1a1a;
border-bottom: 1px solid #2c2c2c; }
.dg li.save-row {
line-height: 25px;
background: #dad5cb;
border: 0; }
.dg li.save-row select {
  margin-left: 5px;
  width: 108px; }
.dg li.save-row .button {
  margin-left: 5px;
  margin-top: 1px;
  border-radius: 2px;
  font-size: 9px;
  line-height: 7px;
  padding: 4px 4px 5px 4px;
  background: #c5bdad;
  color: #fff;
  text-shadow: 0 1px 0 #b0a58f;
  box-shadow: 0 -1px 0 #b0a58f;
  cursor: pointer; }
  .dg li.save-row .button.gears {
    background: #c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;
    height: 7px;
    width: 8px; }
  .dg li.save-row .button:hover {
    background-color: #bab19e;
    box-shadow: 0 -1px 0 #b0a58f; }
.dg li.folder {
border-bottom: 0; }
.dg li.title {
padding-left: 16px;
background: black url(data:image/gif;base64,R0lGODlhBQAFAJEAAPPz8yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;
cursor: pointer;
border-bottom: 1px solid rgba(255, 255, 255, 0.2); }
.dg .closed li.title {
background-image: url(data:image/gif;base64,R0lGODlhBQAFAJEAAPPz8yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==); }
.dg .cr.boolean {
border-left: 3px solid #806787; }
.dg .cr.function {
border-left: 3px solid #e61d5f; }
.dg .cr.number {
border-left: 3px solid #2fa1d6; }
.dg .cr.number input[type=text] {
  color: #2fa1d6; }
.dg .cr.string {
border-left: 3px solid #1ed36f; }
.dg .cr.string input[type=text] {
  color: #1ed36f; }
.dg .cr.function:hover, .dg .cr.boolean:hover {
background: #111; }
.dg .c input[type=text] {
background: #303030;
outline: none; }
.dg .c input[type=text]:hover {
  background: #3c3c3c; }
.dg .c input[type=text]:focus {
  background: #494949;
  color: #fff; }
.dg .c .slider {
background: #303030;
cursor: ew-resize; }
.dg .c .slider-fg {
background: #2fa1d6; }
.dg .c .slider:hover {
background: #3c3c3c; }
.dg .c .slider:hover .slider-fg {
  background: #44abda; }
</style>
    <script type="text/javascript" charset="UTF-8" src="/js/lib/Tween.min.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>
    <div id="dom"></div>
    <script type="text/javascript">
        var camera;
        var renderer;

        function init() {
            // 创建一个场景,它将包含我们所有的元素,如物体,相机和灯光。
            var scene = new THREE.Scene();
            var textureLoader = new THREE.TextureLoader();
            scene.background = textureLoader.load("assets/ba_starry.jpg");

            // 创建一个摄像机,它定义了我们正在看的地方
            camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.y = 150;
            // camera.position.z = 90;
            camera.position.x = 100;
            camera.lookAt(scene.position);
            var orbit = new THREE.OrbitControls(camera);

            // 创建一个渲染器并设置大小,WebGLRenderer将会使用电脑显卡来渲染场景
            renderer = new THREE.WebGLRenderer({
                antialias: true,
                logarithmicDepthBuffer: true,
            });
            renderer.setSize(window.innerWidth, window.innerHeight);

            var ambientLight = new THREE.AmbientLight("#ffffff", 1);
            scene.add(ambientLight);

            // 将呈现器的输出添加到HTML元素
            document.getElementById("dom").appendChild(renderer.domElement);

            // 在屏幕上显示坐标轴
            var axes = new THREE.AxesHelper(100);
            // scene.add(axes);

            // 启动动画
            renderScene();
            createWeiXing();
            createLeiDa();

            // 外层对象
            const group1 = new THREE.Group();
            group1.name = 'group1';
            // 设置偏移中心点位置
            group1.position.set(0, 0, 6);

            var cube1 = createTaiYangBan("#00aa00");
            group1.add(cube1);
            cube1.position.set(-4, 0, 0);

            // 外层对象
            const group2 = new THREE.Group();
            group2.name = 'group2';
            // 设置外层对象的中心为原本想要旋转的位置
            group2.position.set(-8, 0, 1);

            var cube2 = createTaiYangBan("#0055ff");
            group2.add(cube2);
            // 设置偏移中心点位置
            cube2.position.set(4, 0, 0);

            // 外层对象
            const group3 = new THREE.Group();
            group3.name = 'group3';
            // 设置外层对象的中心为原本想要旋转的位置
            group3.position.set(8, 0, 1);

            var cube3 = createTaiYangBan("#ff5500");
            group3.add(cube3);
            // 设置偏移中心点位置
            cube3.position.set(-4, 0, 0);

            group2.add(group3);
            group1.add(group2);
            scene.add(group1);

            // 第二个太阳板
            // 外层对象
            const group11 = new THREE.Group();
            // 设置偏移中心点位置
            group11.position.set(0, 0, -4);

            var cube11 = createTaiYangBan("#00aa00");
            group11.add(cube11);
            cube11.position.set(-4, 0, 0);

            // 外层对象
            const group12 = new THREE.Group();
            // 设置外层对象的中心为原本想要旋转的位置
            group12.position.set(-8, 0, -1);

            var cube12 = createTaiYangBan("#0055ff");
            group12.add(cube12);
            // 设置偏移中心点位置
            cube12.position.set(4, 0, 0);

            // 外层对象
            const group13 = new THREE.Group();
            // 设置外层对象的中心为原本想要旋转的位置
            group13.position.set(8, 0, -1);

            var cube13 = createTaiYangBan("#ff5500");
            group13.add(cube13);
            // 设置偏移中心点位置
            cube13.position.set(-4, 0, 0);

            group12.add(group13);
            group11.add(group12);
            scene.add(group11);

            setTimeout(function() {
                tweenComplete();
            }, 1000);

            function tweenComplete() {
                var num1 = 0,
                    num2 = 0,
                    num3 = 0,
                    num11 = 0,
                    num12 = 0,
                    num13 = 0;
                new TWEEN.Tween({
                        y1: 0,
                        y2: 0,
                        y3: 0,
                        z: 1,
                        y11: 0,
                        y12: 0,
                        y13: 0,
                        z1: -1,
                        waves: 13,
                        dia: 1,
                    })
                    .to({
                        y1: Math.PI / 2,
                        y2: -Math.PI,
                        y3: Math.PI,
                        z: 0,
                        y11: -Math.PI / 2,
                        y12: Math.PI,
                        y13: -Math.PI,
                        z1: 0,
                        waves: 50,
                        dia: 8,
                    }, 3000)
                    .easing(TWEEN.Easing.Linear.None)
                    .onUpdate(function() {
                        group2.position.z = this.z;
                        group3.position.z = this.z;
                        group1.rotateY(this.y1 - num1);
                        group2.rotateY(this.y2 - num2);
                        group3.rotateY(this.y3 - num3);


                        group12.position.z = this.z1;
                        group13.position.z = this.z1;
                        group11.rotateY(this.y11 - num11);
                        group12.rotateY(this.y12 - num12);
                        group13.rotateY(this.y13 - num13);
                        scene.rotateZ(this.y1 - num1);

                        num1 = this.y1;
                        num2 = this.y2;
                        num3 = this.y3;

                        num11 = this.y11;
                        num12 = this.y12;
                        num13 = this.y13;

                        initSatellite(this.waves, this.dia);
                    })
                    .onComplete(tweenComplete1)
                    .start();
            }

            function tweenComplete1() {
                var num1 = 0,
                    num2 = 0,
                    num3 = 0,
                    num11 = 0,
                    num12 = 0,
                    num13 = 0;
                new TWEEN.Tween({
                        y1: 0,
                        y2: 0,
                        y3: 0,
                        z: 0,
                        y11: 0,
                        y12: 0,
                        y13: 0,
                        z1: 0,
                        waves: 13,
                        dia: 1,
                    })
                    .to({
                        y1: -Math.PI / 2,
                        y2: Math.PI,
                        y3: -Math.PI,
                        z: 1,
                        y11: Math.PI / 2,
                        y12: -Math.PI,
                        y13: Math.PI,
                        z1: -1,
                        waves: 50,
                        dia: 8,
                    }, 3000)
                    .easing(TWEEN.Easing.Linear.None)
                    .onUpdate(function() {
                        group2.position.z = this.z;
                        group3.position.z = this.z;
                        group1.rotateY(this.y1 - num1);
                        group2.rotateY(this.y2 - num2);
                        group3.rotateY(this.y3 - num3);

                        group12.position.z = this.z1;
                        group13.position.z = this.z1;
                        group11.rotateY(this.y11 - num11);
                        group12.rotateY(this.y12 - num12);
                        group13.rotateY(this.y13 - num13);
                        scene.rotateY(this.y1 - num1);

                        num1 = this.y1;
                        num2 = this.y2;
                        num3 = this.y3;

                        num11 = this.y11;
                        num12 = this.y12;
                        num13 = this.y13;

                        initSatellite(this.waves, this.dia);
                    })
                    .onComplete(tweenComplete)
                    .start();
            }

            // 创建一个基础太阳板
            function createTaiYangBan(color) {
                // 创建一个立方体并设置大小
                var cubeGeometry = new THREE.BoxGeometry(8, 8, 1);
                var cubeMaterial = new THREE.MeshBasicMaterial({
                    color: color,
                });
                var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
                return cube;
            }

            // 创建一个卫星主体
            function createWeiXing() {
                // 创建一个立方体并设置大小
                var wxGeometry = new THREE.CylinderGeometry(5, 5, 20, 100, 0);
                var wxMaterial = new THREE.MeshBasicMaterial({
                    color: "#55557f",
                });
                var weixing = new THREE.Mesh(wxGeometry, wxMaterial);
                weixing.position.set(0, 0, 1);
                weixing.rotateZ(-Math.PI / 2);
                scene.add(weixing);
            }

            // 创建一个雷达
            function createLeiDa() {
                // 创建一个立方体并设置大小
                var wxGeometry = new THREE.CylinderGeometry(4, 0, 5, 100, 0);
                var wxMaterial = new THREE.MeshBasicMaterial({
                    color: "#ffaa00",
                });
                var weixing = new THREE.Mesh(wxGeometry, wxMaterial);
                weixing.position.set(10, 0, 1);
                weixing.rotateZ(-Math.PI / 2);
                scene.add(weixing);
            }
            
            // 雷达信号
            function initSatellite(positionX, starLiteRadius) {
                var group = new THREE.Group();
                var obj = scene.getObjectByName("satellite");
                scene.remove(obj);

                var wxMaterial = new THREE.MeshBasicMaterial({
                    color: "#ffffff",
                    side: THREE.DoubleSide
                });
                var signalGeometry1 = new THREE.RingGeometry(starLiteRadius, starLiteRadius + 0.1, 100, 0);
                var signal1 = new THREE.Mesh(signalGeometry1, wxMaterial);
                signal1.position.z = -1;

                var signalGeometry2 = new THREE.RingGeometry(starLiteRadius + 2, starLiteRadius + 2.1, 100, 0);
                var signal2 = new THREE.Mesh(signalGeometry2, wxMaterial);
                signal2.position.z = -10;

                var signalGeometry3 = new THREE.RingGeometry(starLiteRadius + 3, starLiteRadius + 3.1, 100, 0);
                var signal3 = new THREE.Mesh(signalGeometry3, wxMaterial);
                signal3.position.z = -20;


                group.rotateY(-Math.PI / 2);
                group.position.set(positionX, 0, 1);
                group.name = "satellite";
                group.add(signal1);
                group.add(signal2);
                group.add(signal3);
                scene.add(group);
            }

            var clock = new THREE.Clock(); //声明一个时钟对象
            function renderScene() {
                TWEEN.update();
                orbit.update();
                // 使用requestAnimationFrame函数进行渲染
                requestAnimationFrame(renderScene);
                renderer.render(scene, camera);
            }

            // 渲染的场景
            renderer.render(scene, camera);
        }
        window.onload = init;

        // 随着窗体的变化修改场景
        function onResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        }
        // 监听窗体调整大小事件
        window.addEventListener('resize', onResize, false);
    </script>


</body></html>

卫星追踪 解析Tle 高度 速度 圈数 发射时长等。
原理

  1. 基于Cesium构建地球基础元素
  2. 使用Satellite解析Tle计算卫星运动轨迹
  3. 使用算法计算出卫星速度, 高度等
  4. 理论上可以通过逆向编码获取卫星所在的城市
相关推荐
java水泥工1 天前
基于Echarts+HTML5可视化数据大屏展示-电信厅店营业效能分析
前端·echarts·html5·大屏展示
月光技术杂谈2 天前
用Deepseek 实现一个基于web的扣图应用
前端·javascript·html5·ccs·tensorflow.js·canvas api
.生产的驴3 天前
React useEffect组件渲染执行操作 组件生命周期 监视器 副作用
前端·css·react.js·ajax·前端框架·jquery·html5
ZTLJQ3 天前
植物大战僵尸HTML5游戏完整实现教程
前端·游戏·html5
Hello123网站4 天前
300多个Html5小游戏列表和下载地址
前端·html·html5
rising start4 天前
前端基础一、HTML5
前端·html·html5
tryCbest5 天前
Html5实现弹出表单
html5
xhload3d6 天前
智慧钢厂高炉冶炼仿真分析 | 图扑数字孪生
3d·智慧城市·html5·webgl·数字孪生·可视化·热力图·智慧工厂·工业互联网·工业组态·高炉炼铁·数字工厂·高炉炉体·智慧高炉·高炉
繁花与尘埃7 天前
HTML5简介与基本骨架(本文为个人学习笔记,内容整理自哔哩哔哩UP主【非学者勿扰】的公开课程。 > 所有知识点归属原作者,仅作非商业用途分享)
笔记·学习·html5
无尽夏_8 天前
HTML5(前端基础)
前端·html·html5