跳到主要内容
Python 月相可视化系统:天文计算与 Web 界面实现 | 极客日志
Python 大前端 算法
Python 月相可视化系统:天文计算与 Web 界面实现 综述由AI生成 本项目基于 Python 结合 Matplotlib 与 Web 技术,实现了高精度的月相计算与可视化展示。核心包含朔望月周期算法、动态阴影渲染及 HTML 交互界面生成。通过数学建模映射时间差至月相值,利用图形库绘制不同阶段的月亮形态,并自动生成包含星空特效的 HTML 报告。方案解决了中文字体回退、内存泄漏等工程细节,适合天文爱好者进行数据可视化实践。
机器人 发布于 2026/3/16 更新于 2026/4/24 2 浏览项目概述
本方案构建了一个功能完整的月相可视化系统,核心目标是将天文数据转化为直观的图形界面。主要特性包括基于朔望月周期的高精度算法、自动生成 HTML 报告以及丰富的视觉特效(如星空背景、月亮光晕)。
技术架构解析
依赖环境
pip install numpy matplotlib datetime base64 io warnings
项目结构
moon-phase-visualizer/
├── moon_calculator.py
├── generate_html.py
└── moon_phase_2025_mid_autumn.html
实现思路
月相计算核心
月相变化周期约为 29.53 天。我们选定一个参考新月日期(例如 2025 年 9 月 25 日),通过计算目标日期与参考点的时间差,结合朔望月周期进行数学建模。算法的关键在于将连续时间变化映射到 0-1 的月相值:前半周期对应新月至满月,后半周期对应满月至新月。
可视化难点处理
Matplotlib 在中文显示上常遇到字体问题,这里采用多字体回退策略:微软雅黑 → 黑体 → 其他系统字体。月亮绘制使用圆形叠加椭圆阴影来模拟盈亏,阴影宽度根据月相值动态计算。中秋节特效则通过多层光晕和星星装饰增强氛围。
核心模块设计
moon_calculator.py - 核心计算引擎
这个类负责所有天文数据的计算。注意设置非交互式后端以避免 GUI 冲突,并优化中文字体配置。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from datetime import datetime, timedelta
import math
import base64
import io
import warnings
from matplotlib import rcParams
warnings.filterwarnings('ignore' )
plt.switch_backend('Agg' )
plt.rcParams['font.sans-serif' ] = ['Microsoft YaHei' , 'SimHei' , 'Arial Unicode MS' , 'DejaVu Sans' ]
plt.rcParams['axes.unicode_minus' ] =
:
( ):
.lunar_cycle =
.reference_new_moon = datetime( , , )
( ):
days_since_new_moon = (date - .reference_new_moon).total_seconds() / ( * )
cycle_position = (days_since_new_moon % .lunar_cycle) / .lunar_cycle
cycle_position <= :
phase = cycle_position *
:
phase = - (cycle_position * )
phase
( ):
days_since_new_moon = (date - .reference_new_moon).total_seconds() / ( * )
days_since_new_moon % .lunar_cycle
( ):
mid_autumn_2025 = datetime( , , )
((date - mid_autumn_2025).days) ==
( ):
year == :
datetime( , , )
year == :
datetime( , , )
year == :
datetime( , , )
:
datetime( , , )
False
class
MoonPhaseCalculator
"""月相计算器类"""
def
__init__
self
self
29.530588853
self
2025
9
25
def
get_moon_phase
self, date
"""计算指定日期的月相"""
self
24
3600
self
self
if
0.5
2
else
2
2
return
def
get_moon_age
self, date
"""计算月龄"""
self
24
3600
return
self
def
is_mid_autumn_festival
self, date
"""判断是否为中秋节(2025 年 10 月 6 日)"""
2025
10
6
return
abs
0
def
get_mid_autumn_date
self, year=2025
"""获取指定年份的中秋节日期"""
if
2025
return
2025
10
6
elif
2024
return
2024
9
17
elif
2026
return
2026
9
25
else
return
2025
10
6
lunar_cycle 设定为精确的朔望月天数。
get_moon_phase() 利用模运算处理周期性,确保数值在 0-2 之间归一化。
使用 total_seconds() 保证时间差计算的浮点精度。
可视化渲染类 class WebMoonVisualizer :
"""Web 版月相可视化器"""
def __init__ (self ):
self .calculator = MoonPhaseCalculator()
def draw_moon (self, ax, phase, size=1.0 , position=(0 , 0 ), is_mid_autumn=False ):
"""绘制月亮形状"""
x, y = position
circle = patches.Circle((x, y), size, facecolor='lightyellow' , edgecolor='gold' , linewidth=2 )
ax.add_patch(circle)
if phase < 0.95 :
shadow_width = size * 2 * (1 - phase)
if shadow_width > 0.1 :
shadow = patches.Ellipse((x + size * 0.1 , y), shadow_width, size * 2 , facecolor='darkgray' , alpha=0.6 )
ax.add_patch(shadow)
if is_mid_autumn:
for i in range (4 ):
halo = patches.Circle((x, y), size * (1.3 + i * 0.15 ), facecolor='orange' , alpha=0.12 - i * 0.03 , edgecolor='none' )
ax.add_patch(halo)
star_positions = [
(x - size * 2.0 , y + size * 1.0 ),
(x + size * 2.0 , y + size * 1.0 ),
(x - size * 1.8 , y - size * 1.3 ),
(x + size * 1.8 , y - size * 1.3 ),
]
for sx, sy in star_positions:
star = patches.RegularPolygon((sx, sy), 5 , radius=size * 0.12 , facecolor='gold' , alpha=0.9 )
ax.add_patch(star)
def get_phase_name (self, phase ):
"""获取月相名称"""
if phase < 0.1 :
return "新月"
elif phase < 0.35 :
return "蛾眉月"
elif phase < 0.65 :
return "上弦月"
elif phase < 0.9 :
return "盈凸月"
else :
return "满月"
使用 patches.Circle 构建基础形态。
阴影宽度公式 size * 2 * (1 - phase) 模拟光照遮挡效果。
中秋模式下增加多层光晕和五角星,提升节日氛围。
四种图表实现详解
时间轴图表 - 连续月相展示 此图表用于展示一段时间内的月相连续变化。关键在于调整 X 轴间距,避免月亮图标重叠。
def create_timeline_chart (self, center_date=None , days=30 ):
"""创建时间轴图表(修复重叠问题)"""
if center_date is None :
center_date = datetime(2025 , 10 , 6 )
start_date = center_date - timedelta(days=15 )
fig, ax = plt.subplots(figsize=(24 , 12 ))
for i in range (days):
current_date = start_date + timedelta(days=i)
phase = self .calculator.get_moon_phase(current_date)
is_mid_autumn = self .calculator.is_mid_autumn_festival(current_date)
x_pos = i * 3.5
y_pos = 0
self .draw_moon(ax, phase, size=1.0 , position=(x_pos, y_pos), is_mid_autumn=is_mid_autumn)
ax.text(x_pos, -3.0 , current_date.strftime('%m-%d' ), ha='center' , va='top' , fontsize=11 , fontweight='bold' )
if is_mid_autumn:
ax.text(x_pos, 3.5 , '2025 年中秋节' , ha='center' , va='bottom' , fontsize=14 , fontweight='bold' , color='red' )
x_pos = i * 3.5 确保每个日期的月亮有足够空间。
画布尺寸设为 24x12,防止长序列被压缩。
中秋节位置用红色高亮文本标注。
月相曲线图 - 数学规律可视化 通过折线图展示月相值的正弦波趋势,适合分析周期性规律。
def create_phase_chart (self ):
"""创建月相图表"""
fig, ax = plt.subplots(figsize=(12 , 8 ))
ax.set_facecolor('#001133' )
today = datetime(2025 , 10 , 6 )
dates_range = [today + timedelta(days=i - 15 ) for i in range (31 )]
phase_values = [self .calculator.get_moon_phase(d) for d in dates_range]
ax.plot(range (31 ), phase_values, 'gold' , linewidth=4 , marker='o' , markersize=6 , markerfacecolor='yellow' , markeredgecolor='orange' )
ax.fill_between(range (31 ), phase_values, alpha=0.3 , color='gold' )
ax.axhline(y=1.0 , color='red' , linestyle='--' , alpha=0.8 , linewidth=2 , label='Full Moon' )
ax.axhline(y=0.0 , color='silver' , linestyle='--' , alpha=0.8 , linewidth=2 , label='New Moon' )
ax.axvline(x=15 , color='lime' , linestyle=':' , alpha=0.8 , linewidth=3 , label='Mid-Autumn Festival' )
mid_autumn_phase = self .calculator.get_moon_phase(today)
ax.plot(15 , mid_autumn_phase, 'r*' , markersize=15 , label=f'Festival Phase({mid_autumn_phase:.2 f} )' )
深蓝背景配合金色曲线,营造夜空视觉效果。
fill_between 填充区域增强曲线的立体感。
水平/垂直参考线清晰标注了满月和节日节点。
当前月相图 def create_current_moon (self ):
"""创建当前月相图"""
fig, ax = plt.subplots(figsize=(8 , 8 ))
ax.set_facecolor('#001133' )
today = datetime(2025 , 10 , 6 )
phase_today = self .calculator.get_moon_phase(today)
is_today_mid_autumn = self .calculator.is_mid_autumn_festival(today)
self .draw_moon(ax, phase_today, size=3.0 , position=(0 , 0 ), is_mid_autumn=is_today_mid_autumn)
ax.set_xlim(-6 , 6 )
ax.set_ylim(-6 , 6 )
ax.set_aspect('equal' )
ax.axis('off' )
phase_name = self .get_phase_name(phase_today)
title = f'2025 Mid-Autumn Festival Moon Phase - {phase_name} \n{today.strftime("%Y-%m-%d" )} \nPhase Value: {phase_today:.3 f} '
if is_today_mid_autumn:
title += '\nHappy Mid-Autumn Festival!'
ax.text(0 , -5 , title, ha='center' , va='top' , fontsize=14 , color='white' , fontweight='bold' , bbox=dict (boxstyle="round,pad=0.8" , facecolor="darkblue" , alpha=0.8 ))
正方形画布保证月亮比例不失真。
size=3.0 放大主体,便于观察细节。
圆角文本框承载元数据,信息层级分明。
图像 Base64 编码 为了将图表嵌入 HTML,需要将 Matplotlib 对象转换为 Base64 字符串。
def fig_to_base64 (self, fig ):
"""将 matplotlib 图形转换为 base64 字符串"""
buffer = io.BytesIO()
fig.savefig(buffer, format ='png' , facecolor=fig.get_facecolor(), bbox_inches='tight' , dpi=150 )
buffer.seek(0 )
image_png = buffer.getvalue()
buffer.close()
plt.close(fig)
graphic = base64.b64encode(image_png)
return graphic.decode('utf-8' )
使用内存缓冲区 (BytesIO) 避免生成临时文件。
dpi=150 平衡图片质量与传输大小。
plt.close(fig) 至关重要,防止长时间运行导致内存溢出。
HTML 界面生成
generate_html.py - 界面组装器from moon_calculator import WebMoonVisualizer
from datetime import datetime
def generate_html ():
visualizer = WebMoonVisualizer()
mid_autumn_date = datetime(2025 , 10 , 6 )
timeline_fig = visualizer.create_timeline_chart(mid_autumn_date, days=30 )
timeline_img = visualizer.fig_to_base64(timeline_fig)
phase_fig = visualizer.create_phase_chart()
phase_img = visualizer.fig_to_base64(phase_fig)
current_fig = visualizer.create_current_moon()
current_img = visualizer.fig_to_base64(current_fig)
moon_info = visualizer.get_moon_info()
with open ('moon_phase_report.html' , 'w' , encoding='utf-8' ) as f:
f.write(f'<html><body>...{timeline_img} ...</body></html>' )
CSS3 特效设计
@keyframes twinkle {
0% , 100% { opacity : 0.3 ; }
50% { opacity : 1 ; }
}
@keyframes shooting {
0% { transform : rotate (-45deg ) translateX (0 ) translateY (0 ); opacity : 1 ; }
100% { transform : rotate (-45deg ) translateX (-800px ) translateY (800px ); opacity : 0 ; }
}
@keyframes float {
0% , 100% { transform : translateY (0px ); }
50% { transform : translateY (-15px ); }
}
.highlight {
background : linear-gradient (45deg , #ff6b35 , #f7931e , #ffd700 );
-webkit-background-clip : text;
-webkit-text -fill -color : transparent;
animation : gradient-shift 3s ease infinite;
}
JavaScript 交互特效
function createStars ( ) {
const starsContainer = document .getElementById ('stars' );
const numberOfStars = 120 ;
for (let i = 0 ; i < numberOfStars; i++) {
const star = document .createElement ('div' );
star.className = 'star' ;
star.style .left = Math .random () * 100 + '%' ;
star.style .top = Math .random () * 100 + '%' ;
const size = Math .random () * 3 + 1 ;
star.style .width = size + 'px' ;
star.style .height = size + 'px' ;
star.style .animationDelay = Math .random () * 3 + 's' ;
starsContainer.appendChild (star);
}
}
function createShootingStar ( ) {
const shootingStar = document .createElement ('div' );
shootingStar.className = 'shooting-star' ;
const startX = Math .random () * window .innerWidth ;
const startY = Math .random () * (window .innerHeight * 0.5 );
shootingStar.style .left = startX + 'px' ;
shootingStar.style .top = startY + 'px' ;
shootingStar.style .animation = 'shooting 1.5s linear forwards' ;
document .body .appendChild (shootingStar);
setTimeout (() => {
shootingStar.remove ();
}, 1500 );
}
随机分布的闪烁星星增加背景层次感。
流星从屏幕边缘斜向划过,自动清理 DOM 节点防止内存堆积。
动画延迟随机化,避免视觉单调。
结语 本项目展示了如何利用 Python 进行科学计算与 Web 可视化的结合。通过模块化设计,核心算法与前端展示解耦,既保证了数据的准确性,又提供了良好的用户体验。在实际应用中,可根据需求扩展更多天文参数或接入实时 API。
相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
Gemini 图片去水印 基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online