地球+卫星运动轨迹

由于工作中, 需要追踪卫星, 记录一下最近的工作成果, 分别有俩个方案, 一个是基于市面上主流的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. 理论上可以通过逆向编码获取卫星所在的城市
相关推荐
Justinc.10 小时前
HTML5新增属性
前端·html·html5
LilyCoder20 小时前
HTML5二十四节气网站源码
前端·javascript·html·html5
浊浪载清辉2 天前
基于HTML5与Tailwind CSS的现代运势抽签系统技术解析
前端·css·html5·随机运签·样式技巧
合作小小程序员小小店2 天前
web网站开发,在线%射击比赛成绩管理%系统开发demo,基于html,css,jquery,python,django,model,orm,mysql数据库
python·mysql·django·jquery·html5
chinahcp20083 天前
CSS保持元素宽高比,固定元素宽高比
前端·css·html·css3·html5
veminhe4 天前
html5语义元素
前端·html·html5
艾小码4 天前
HTML5 & CSS3 从入门到精通:构建现代Web的艺术与科学
前端·css3·html5
LilyCoder5 天前
HTML5中华美食网站源码
前端·html·html5
黑椒牛肉焖饭6 天前
第三次作业
前端·css·html5
鹏多多.10 天前
flutter-使用AnimatedDefaultTextStyle实现文本动画
android·前端·css·flutter·ios·html5·web