背景:
本次需要基于实现卫星模型扫描地球的业务。
技术栈采纳:
cesium 1.103.0
html
czml
记录:
1. 卫星轨道数据获取
地址:https://www.space-track.org/#catalog
我这里抉择获取 fengyun 的数据,能够失去一个 TLE 格局的两行数据
2. 卫星轨道数据通用转换
因为有了 tle 数据,这里也抉择去用 sgp4 来将数据转换成 czml 格式文件。
#!/usr/bin/python
# -*- coding:utf8 -*-
"""本脚本依据 TLE 文件生成每颗卫星的 CMZL 文件,用于前端 js 应用 Cesium 显示卫星轨道"""
# sgp4 算法依据二行星历计算每个工夫点卫星的地位
from sgp4.earth_gravity import wgs84
from sgp4.io import twoline2rv
import datetime
import json
# 按行读取北斗两行星历文件
# 每三行标识一个卫星,格局如下:# FENGYUN 1A
# 1 19467U 88080A 23086.12932362 -.00000103 00000-0 -36906-4 0 9992
# 2 19467 99.1890 121.7932 0014370 10.7878 349.3593 14.03214030768865
# 星历下载地址:http://celestrak.com/
with open('fengyun.txt', 'r') as f:
data_lines = f.read().split('\n')
#TLE 每颗卫星数据必须三行: 第一行名称,后两行数据 "
# 应用字典的数组 (json 文件) 保留每颗卫星的轨道六根数
orbit_info = {}
# 每三行一组,遍历所有卫星
for j in range(len(data_lines) // 3):
# 第一行名字,后两行数据
# 数据具体阐明:http://celestrak.com/columns/v04n03/
name = data_lines[j * 3].strip()
line1 = data_lines[1 + j * 3]
line2 = data_lines[2 + j * 3]
# 写轨道六根数到 json 文件
# print("卫星轨道倾斜角", line2[8:16], "度")
# print("升交点赤经", line2[17:25], "度")
# print("偏心率", line2[26:33])
# print("近地点角距", line2[34: 42], "度")
# print("平近点角", line2[43:51], "度")
# print("均匀静止(每天绕地球圈数)", line2[52:63])
orbit_info.update({
name: {'Inclination': line2[8:16],
'Right Ascension of the Ascending Node'.replace('','_'): line2[17:25],'Eccentricity': line2[26:33],'Argument of Perigee'.replace(' ','_'): line2[34: 42],'Mean Anomaly'.replace(' ','_'): line2[43:51],'Mean Motion'.replace(' ','_'): line2[52:63]
}
})
# 卫星每转 1°所经验的工夫(单位:秒)gap = 1. / float(line2[52:63]) * 24 * 60 * 60 / 360
# 调用 sgp4 算法计算星历每个时刻的地位
satellite = twoline2rv(line1, line2, wgs84)
assert satellite.error == 0
# 记录以后工夫,并在循环中计算出一个周期后的工夫
# 用于在 CZML 文件中指定 interval
now_time = datetime.datetime.now()
next_time = datetime.datetime.now()
# 保留每 1°变动后卫星的地位(x, y, z):示意具体地心的间隔(单位:km)
position_list = []
# 循环一圈
# 每次距离 gap 秒
nums = 361
for i in range(nums):
# next_time 示意每个地位对应的工夫点
next_time = now_time + datetime.timedelta(seconds=gap * (i + 1))
# 示意为字典,不便 propagate 函数的计算
next_time_str = next_time.strftime('%Y %m %d %H %M %S').split(' ')
next_time_str = [int(v) for v in next_time_str]
time_key = ['year', 'month', 'day', 'hour', 'minute', 'second']
time_map = dict(zip(time_key, next_time_str))
# 调用 sgp4 库的 propagate 函数计算对应时刻的地位
position, velocity = satellite.propagate(year=time_map['year'],
month=time_map['month'],
day=time_map['day'],
hour=time_map['hour'],
minute=time_map['minute'],
second=time_map['second']
)
# The position vector measures the satellite position in kilometers from the center of the earth.
# CZML 文件中 position 的格局为:(time, x, y, z, time, x, y, z...)
position_list.append(next_time.isoformat())
position_list.append(position[0] * 1000)
position_list.append(position[1] * 1000)
position_list.append(position[2] * 1000)
# 格式化为 ISO 工夫规范格局
begin = str(now_time.isoformat())
end = str((next_time + datetime.timedelta(seconds=gap)).isoformat())
# Write the CZML document to a file
filename = "D:/test/{}.czml".format(name)
# 初始化 CZML
# CZML 实际上是 JSON 文件,JSON 文件就是字典数组
# 所以应用字典数据结构示意每个卫星
doc = []
# 定义头部
header = {
# id 和 version 为固定格局
'id': "document",
"version": "1.0",
'name': name,
"clock": {
# interval 为无效工夫,currentTime 示意起始点,multiplier 示意时钟速度
"interval": '{}/{}'.format(begin, end),
"currentTime": begin,
"multiplier": gap
}
}
doc.append(header)
# 定义主体
body = {"id": "satellites/{}".format(name),
"availability": '{}/{}'.format(begin, end),
"label": {
# 应用 label 显示卫星名字
"font": "11pt Lucida Console",
"outlineWidth": 2,
"outlineColor": {"rgba": [0, 0, 0, 255]},
"horizontalOrigin": "LEFT",
"pixelOffset": {"cartesian2": [12, 0]},
"fillColor": {"rgba": [213, 255, 0, 255]},
"text": name
},
"path": {
# path 定义轨道的款式
"material": {
"polyline": {
"color": {"rgba": [255, 0, 255, 255]
}
}
},
"width": 1,
"resolution": 120
},
"billboard": {
# 卫星的图标,应用 base64 编码表示图片
"image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADJSURBVDhPnZHRDcMgEEMZjVEYpaNklIzSEfLfD4qNnXAJSFWfhO7w2Zc0Tf9QG2rXrEzSUeZLOGm47WoH95x3Hl3jEgilvDgsOQUTqsNl68ezEwn1vae6lceSEEYvvWNT/Rxc4CXQNGadho1NXoJ+9iaqc2xi2xbt23PJCDIB6TQjOC6Bho/sDy3fBQT8PrVhibU7yBFcEPaRxOoeTwbwByCOYf9VGp1BYI1BA+EeHhmfzKbBoJEQwn1yzUZtyspIQUha85MpkNIXB7GizqDEECsAAAAASUVORK5CYII=",
"scale": 1.5
},
"position": {# cartesian 的格局:(time, x, y, z, time, x, y, z...)
"referenceFrame": "FIXED", # 能够取 FIXED 和 INERTIAL 示意固定和惯性参考系
# 插值填补轨道
"interpolationDegree": 5,
"interpolationAlgorithm": "LAGRANGE",
"epoch": begin,
"cartesian": position_list
}
}
# 在 body 中增加 position
doc.append(body)
# 应用 JSON 写 CZML 文件
with open(filename, 'w') as f:
json.dump(doc, f)
原创作者地址:https://segmentfault.com/u/yourena_c
3.cesium 加载卫星轨道数据
<!DOCTYPE html>
<html lang="en">
<head>
<title>Cesium CZML Example</title>
<script src="https://cdn.bootcdn.net/ajax/libs/cesium/1.103.0/Cesium.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/cesium/1.103.0/Widgets/widgets.css" rel="stylesheet">
<style>
html, body, #cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="cesiumContainer"></div>
<script>
// 初始化 Cesium Viewer
var viewer = new Cesium.Viewer('cesiumContainer');
// 创立 CzmlDataSource
var dataSource = new Cesium.CzmlDataSource();
// 加载 CZML 数据
dataSource.load('./fengyun.czml').then(function() {
// 增加数据到 Viewer
viewer.dataSources.add(dataSource);
// 调整视角以查看数据
viewer.zoomTo(dataSource);
});
</script>
</body>
</html>
后续增加 gltf 卫星模型和扫描成果