地球+卫星运动轨迹

由于工作中, 需要追踪卫星, 记录一下最近的工作成果, 分别有俩个方案, 一个是基于市面上主流的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. 理论上可以通过逆向编码获取卫星所在的城市
相关推荐
涔溪9 小时前
HTML5 实现的圣诞主题网站源码,使用了 HTML5 和 CSS3 技术,界面美观、节日氛围浓厚。
css3·html5·节日
Amy.Wang14 小时前
前端如何实现电子签名
前端·javascript·html5
veminhe19 小时前
html怎么设置html5
html·html5
失落的多巴胺1 天前
使用deepseek制作“喝什么奶茶”随机抽签小网页
javascript·css·css3·html5
veminhe11 天前
HTML5 浏览器支持
前端·html·html5
伍哥的传说12 天前
react gsap动画库使用详解之text文本动画
前端·vue.js·react.js·前端框架·vue·html5·动画
veminhe13 天前
HTML5简介
前端·html·html5
蹦极的考拉16 天前
在使用 HTML5 的 <video> 标签嵌入视频时,有时会遇到无法播放 MP4 文件的问题
前端·音视频·html5
木木黄木木16 天前
HTML5 火焰字体效果教程
前端·html·html5
于本淡17 天前
一篇文章快速学会HTML
开发语言·前端·数据结构·qt·html·json·html5