Files
b612 16c62a97d5 feat: 完善时标与天象几何计算并扩展输出接口
- 新增时标、ΔT 模型、质心时间与 UT1 支持
- 改进日月食、月掩、行星事件及路径边界计算
- 完善恒星三维自行与动态距离传播
- 扩展 SVG、GeoJSON、KML 输出与底层距离换算工具
- 整理中英文手册、示例资源及回归测试
2026-09-23 18:55:12 +08:00

52 KiB
Raw Permalink Blame History

日食与月食

English | 返回 README

本手册的完整示例以仓库根目录为工作目录执行,生成的图片写入 doc/img/。

目录

简单示例:2009长江大日食,上海某地见食情况

package main

import (
	"fmt"
	"time"

	"b612.me/astro/eclipse"
)

func main() {
	cst := time.FixedZone("CST", 8*3600)
	date := time.Date(2009, 7, 22, 0, 0, 0, 0, cst)
	info, ok := eclipse.LocalSolarEclipseOnDate(date, 121.9850, 30.6167, 0)
	if !ok {
		fmt.Println("no local solar eclipse")
		return
	}
	fmt.Println(info.Type)
	fmt.Printf("%+v\n", info)
}

ok=false 表示该日期或地点没有命中所查询的日食,此时不应读取结果字段。全局事件、地方接触时刻和全球路径分别由下面的接口计算。

API 参考

SVG 片段使用导入别名 eclipsesvg "b612.me/astro/eclipse/svg";月掩图使用 moonsvg "b612.me/astro/moon/svg"。日期和时区沿用首例。

名称 用途 备注
LocalSolarEclipseOnDate / LocalSolarEclipseOnDateNASABulletinSplitK 站心日食查询 返回 (info, bool);默认 NASA Split-K 口径
LunarEclipseOnDate / LunarEclipseOnDateDanjon / LunarEclipseOnDateChauvenet 月食查询 同上;后缀强制影半径模型
SearchLocalCentralSolarEclipse / SolarEclipseCandidates 跨年搜索中心食 / 候选时刻表 前者带 status.Exhausted
SolarEclipseCentralPath / SolarEclipsePartialFootprints 中心线和偏食足迹几何 选项见 SolarEclipsePathOptions / SolarEclipsePartialFootprintOptions
SolarEclipseBesselianElements / SolarEclipseBesselianMuForPublishedTable 贝塞尔根数与已发布表换算 用来对表
eclipsesvg.SolarEclipseMapSVG / LunarEclipseMapSVG 全球见食图 / 月食可见区图 返回 (string, bool)
eclipsesvg.LocalSolarEclipseSVG / LunarEclipseSVG / LunarEclipseDetailedSVG 站心视圆图 / 穿影图 / 详细版式 同上
eclipsesvg.SolarEclipseMapSVGOptions / LunarEclipseDetailedSVGOptions 出图选项(投影、图层、画布) 图层开关见「全球见食图与月食出图」
astro.TimeScaleUT1 UT1 口径出图 此时 Location 必须为 UTC
SolarEclipseInfoInUT1 等 ...InUT1 系列 把结果里的民用时刻换成同一物理时刻的 UT1 读数 零值时刻原样保留

常用场景

本地有没有日食或月食

solar, ok := eclipse.LocalSolarEclipseOnDate(date, 121.9850, 30.6167, 0)
fmt.Println(ok, solar.Type)
lunar, ok2 := eclipse.LunarEclipseOnDate(time.Date(2029, 1, 1, 0, 0, 0, 0, cst))
fmt.Println(ok2, lunar.Type)
true total
true total
  • LocalSolarEclipseOnDate 一次给出食型、各阶段时刻、食分、遮掩比例与食甚太阳高度;只关心"有没有"时看第二个返回值。
  • 要跨年份找"下一次中心食",用 SearchLocalCentralSolarEclipse(返回 status.Exhausted 区分"跨度内确实没有");只要候选时刻表用 SolarEclipseCandidates。
  • 月食侧对应 LunarEclipseOnDate 与带 Danjon / Chauvenet 后缀的强制模型入口。

全球见食图与月食图

solar, ok := eclipsesvg.SolarEclipseMapSVG(date, eclipsesvg.SolarEclipseMapSVGOptions{Width: 1200, Height: 800, Location: cst})
local, ok2 := eclipsesvg.LocalSolarEclipseSVG(date, 121.9850, 30.6167, 0, eclipsesvg.LocalSolarEclipseSVGOptions{Width: 920, Height: 720, Step: 5 * time.Minute, Location: cst})
lunar, ok3 := eclipsesvg.LunarEclipseSVG(time.Date(2029, 1, 1, 0, 0, 0, 0, cst), eclipsesvg.LunarEclipseSVGOptions{Width: 960, Height: 620, Step: 10 * time.Minute, Location: cst})
detailed, ok4 := eclipsesvg.LunarEclipseDetailedSVG(time.Date(2029, 1, 1, 0, 0, 0, 0, cst), eclipsesvg.LunarEclipseDetailedSVGOptions{Location: cst})
fmt.Println(ok, len(solar), ok2, len(local), ok3, len(lunar), ok4, len(detailed))
true 265827 true 13625 true 19831 true 319721

投影切换、图层开关(半影/本影轮廓、食分等值线、等时线)与画布下限都在全球见食图与月食出图里;正射球面与极区版式的取舍见地图投影。

中心线与偏食足迹

partial, ok := eclipse.SolarEclipsePartialFootprints(date,
	eclipse.SolarEclipsePartialFootprintOptions{Step: 10 * time.Minute, BoundaryPoints: 180})
central, hasCentral := eclipse.SolarEclipseCentralPath(date,
	eclipse.SolarEclipsePathOptions{Step: time.Minute, TargetSpacingKM: 20})
fmt.Println(ok, hasCentral, len(partial.CentralBandFootprints), len(central.CenterLine))
true true 42 1117
  • 只要几何、不要图(例如自己写数据管线或传给 geojson)时用这两个入口;TargetSpacingKM 控制中心线加密上限。
  • 偏食足迹是瞬时足迹的并集,Step 小于两分钟会夹到两分钟;结果里的 data-source 会标注实际几何来源。

贝塞尔根数与沙罗

elements, ok := eclipse.SolarEclipseBesselianElements(2460409.262835,
	eclipse.SolarEclipseBesselianElementsOptions{DeltaTSeconds: 70.6, ReferenceJDE: 2460409.25})
t := 0.5
fmt.Printf("X=%.6f Y=%.6f D=%.6f L1=%.6f L2=%.6f\n",
	elements.X.At(t), elements.Y.At(t), elements.D.At(t), elements.L1.At(t), elements.L2.At(t))
fmt.Println(info.HasSaros, info.Saros)
X=-0.062351 Y=0.355150 D=7.593613 L1=0.535842 L2=-0.010245
true {136 37 71 true}
  • 显式给 DeltaTSeconds 与 ReferenceJDE 时可直接与已发布的贝塞尔根数表逐项对表;已发布表用的是 T0 本身的恒星时,需要先用 SolarEclipseBesselianMuForPublishedTable 换算。
  • 与 NASA 目录/图页的 UT 时刻比较前要先扣掉它们出版时采用的 ΔT 口径,方法见与 NASA 资料的时间对照;TD 层(不含 ΔT)才是能单独检验星历与几何的一层。

日食

图内与图注的时标声明见时标声明。

日食计算统一放在 eclipse 包;SVG 生成功能放在 eclipse/svg 包。月亮半径默认采用 NASA bulletin Split-K 口径(半影与偏食 k = 0.2724880,本影与反本影 k = 0.2722810);需要 IAU 单一 k = 0.2725076 时,调用同名的 ...IAUSingleK 接口。

太阳半径有两个口径:标准档(1 AU 处 959.639″,与已发布星历表、目录一致,默认)与边缘档(1 AU 处 959.95″,由全食边缘的光变曲线实测得到的食半径),两者相差 0.31″。以 2024-04-08 全食为例,改用边缘档会让全食带每侧收窄约 0.6 千米、中心食时长缩短约 1.5 秒;2023-10-14 环食则带变宽约 1.3 千米、时长延长约 2.1 秒;偏食食分变化约 1e-4。

可据此评估食限与中心时长对太阳半径的敏感性。

两个口径都可以选:eclipse.SolarEclipseOptions{SunRadiusModel: ...} 配合 SolarEclipseOnDateWithOptions、LocalSolarEclipseOnDateWithOptions,以及搜索与面板的 LastSolarEclipseWithOptions / NextSolarEclipseWithOptions / ClosestSolarEclipseWithOptions / SolarEclipseGeocentricPanelWithOptions,或 basic 侧的 SolarEclipseWithOptions / LocalSolarEclipseWithOptions 与各 ...Options 结构里的 SunRadiusModel 字段;结果里的 SunRadiusModel 记录实际使用的口径,basic.SolarEclipseSunSemidiameter 与面板里的“视半径 S.D.”都按同一口径计算。

常用接口:

  • SolarEclipseOnDate:判断某个当地日期附近是否有全局日食
  • LastSolarEclipse / NextSolarEclipse / ClosestSolarEclipse:搜索全局日食
  • LocalSolarEclipseOnDate:判断某地当天是否能看到站心日食
  • LastLocalSolarEclipse / NextLocalSolarEclipse / ClosestLocalSolarEclipse:搜索某地可见的站心日食
  • LastLocalTotalSolarEclipse / NextLocalTotalSolarEclipse / ClosestLocalTotalSolarEclipse:搜索某地可见的日全食,返回 (info, ok)
  • LastLocalAnnularSolarEclipse / NextLocalAnnularSolarEclipse / ClosestLocalAnnularSolarEclipse:搜索某地可见的日环食,返回 (info, ok)
  • SolarEclipseCentralPath:计算中心线、南北界和食甚点
  • SolarEclipsePartialFootprints:计算偏食半影在地球表面的足迹;可选采样本影/反本影瞬时轮廓
  • SolarEclipseBesselianElements:计算一次日食的多项式贝塞尔根数(X/Y/D/L1/L2/Mu 的三次系数与 TanF1/TanF2),窗口内无日食时返回 (零值, false)
  • eclipse/svg.LocalSolarEclipseSVG:生成某地的日面视圆 SVG

SolarEclipseBesselianElements 给出一张与已发布根数表同形的多项式表:T0JDE 是参考时刻,t = (jde - T0JDE) * 24 是自它起算的 TT 小时数,X/Y/D/L1/L2/Mu 都是 t 的三次多项式(D 与 Mu 用度,其余用地球赤道半径),TanF1/TanF2 在本次日食内为常数。

L1/L2 用 Explanatory Supplement 的含 1/cos f 形式,本影 L2 为负表示月心尚未越过本影锥顶点。默认 T0 取食甚所在 TT 小时的下整、窗口半径 3 小时、窗口内 5 个等距时刻做三次最小二乘,与已发布表的拟合口径一致。Model、SunRadiusModel、PenumbralK、UmbralK、DeltaTSeconds 五个字段记录决定这些数值的口径,随结果一起返回。

Mu 的时间自变量与已发布表不同,必须换算后才能对表。 已发布表用 T0 本身的恒星时,本库用 UT = TT - ΔT,两者相差 ΔT × 15.041067/3600 度;Mu 在窗口内保持连续、不折回 [0,360),SolarEclipseBesselianMuForPublishedTable 只平移常数项。本库取真实格林时角,是为了让 Mu 与本库自己的食甚经度、中心线和接触时刻自洽;直接拿已发布表的 Mu 配上正确的恒星时,会得到约 0.3° 的经度偏差(2024-04-08 的食甚纬度上约 30 km)。

// 贝塞尔根数表,T0 与 ΔT 显式指定时可直接与已发布表对表。
elements, ok := eclipse.SolarEclipseBesselianElements(
	2460409.262835, // 2024-04-08 日全食附近的 TT 儒略日
	eclipse.SolarEclipseBesselianElementsOptions{DeltaTSeconds: 70.6, ReferenceJDE: 2460409.25},
)
if ok {
	t := 0.5                                                          // 自 T0 起 0.5 TT 小时
	fmt.Println(elements.X.At(t), elements.Y.At(t), elements.D.At(t)) // 基本面坐标与影轴赤纬
	fmt.Println(elements.L1.At(t), elements.L2.At(t))                 // 半影、本影半径
	// 已发布表用 T0 本身的恒星时,换算后才能对表。
	fmt.Println(eclipse.SolarEclipseBesselianMuForPublishedTable(elements.Mu, elements.DeltaTSeconds).At(t))
}

SolarEclipsePartialFootprintsInfo 还给出影锥与地球的全球接触:P1/P4 是半影外切,P2/P3 是半影内切;U1/U4 是本影或反本影外切,U2/U3 是内切。某次日食不存在的接触保持 time.Time 零值。CentralBeginOnEarth / CentralEndOnEarth 仍表示影轴进入和离开地球,不等同于 U1/U4。

需要结构化的瞬时中心影轮廓时,可在 SolarEclipsePartialFootprintOptions 中设置 CentralShadowStep;结果写入 CentralShadowFootprints。零值关闭该额外计算;SVG 入口同样只在正值时采样(小于一分钟按一分钟),零值或负值都不会构造。

需要在数据层直接取等时线时,可在同一个 SolarEclipsePartialFootprintOptions 中设置 GreatestTimeValues 或 GreatestTimeStep。GreatestTimeValues []time.Time 是食甚时刻取值,按绝对时刻使用(其 Location 不参与换算),最多保留 64 条:重复的时刻取值与偏食可见窗口之外的时刻取值会被跳过,其余按时间先后排序,超出时保留最早的 64 条;没有可用支路的时刻取值不会出现在结果里。它为空时改用 GreatestTimeStep 按间隔生成,间隔只在为正值时生效,且对齐到 UTC 整刻度;要按展示时区对齐,请自行生成时刻后传给 GreatestTimeValues。

结果写入 SolarEclipsePartialFootprintsInfo.GreatestTimeContours:JDE 是对应的力学时儒略日,Time 是该时刻取值在输入时区下的时刻(显式传入的时刻取值原样回显,按步长生成时由 JDE 换算并抹到毫秒,避免往返把整分截断成前一分钟),Segments 是该时刻的等时线支路。等时线只出现在日月盘面确有重叠且太阳在几何地平以上(不含蒙气差与半径修正)的地方,两端止于地平线或偏食可见域边界;纬度 ±88° 以上不再延拓,同一时刻可能有多条互不相连的支路。不请求时既有输出完全不变。

日食结果 SolarEclipseInfo、LocalSolarEclipseInfo,以及 SolarEclipsePath / SolarEclipsePartialFootprintsInfo 里的 Eclipse 字段还会附带沙罗序列信息:

  • HasSaros:是否成功匹配到沙罗序列
  • Saros.Series:Verified=true 时为 NASA 沙罗系列号,否则为推算的暂定系列号
  • Saros.Member:这次日食在该系列中的第几个成员,从 1 开始
  • Saros.Count:该沙罗系列的总成员数
  • Saros.Verified:是否已与内置权威目录锚点核验;扩展表或范围外推算结果为 false

说明:

  • 沙罗周期约为 6585.321 天,也就是 223 个朔望月、约 18 年 11 天 8 小时;系列成员按此周期排列。
  • 沙罗系列是一组按沙罗周期连续排列的日食事件;Series 标识该组,Member / Count 表示当前事件在该组中的序号和总数。
  • 沙罗序列属于整场日食事件,不随观测地点改变,所以全局日食、站心日食、中心路径和偏食足迹中的对应值应当一致。
  • 内置 NASA 锚点优先使用正式编号;天文年份 -3000 至 +6000 年内(含首尾年,0 年为公元前 1 年)未被锚点覆盖的事件使用预计算扩展表,范围外才实时演算外推。预计算与实时推算结果的 Verified 都是 false,不应视为实际已发布编号。
  • 扩展编号沿用 NASA 的 Saros/Inex 编号关系,成员按 Split-K 模型计算,计数覆盖完整系列,不在预计算年份边界截断。3288-11-15 的推算结果为系列 202、第 1/71 个成员。
  • 例如 2024-04-08 北美日全食属于 日食沙罗序列139 的第 30/71 个成员。

与 NASA 资料的时间对照

日食时间分两类:

  • 全局日食:关注整次日食的食甚 UT、食分、Gamma、食甚点经纬度和食带宽度。当前用 NASA GSFC 的日食搜索/贝塞尔根数资料对照了 2023-04-20 全环食、2024-04-08 日全食、2024-10-02 日环食、2025-03-29 日偏食。
  • 站心日食:关注某个观测点看到的初亏、食甚、复圆和中心食持续时间。当前用 NASA GSFC 的 local circumstances / Google map 日食资料对照了芝加哥偏食、2024 日全食食甚点、2024 日环食食甚点。
时标类型 比较项 作用 已知量级
TT / TD 层(力学时,去掉 ΔT) 几何与星历:贝塞尔根数、影轴位置、接触时刻的 TD 值 星历与影子几何本身 4 个回归样例中位差约 0.2 s;1901–2100 与 NASA 配对 195 次的中位差 0.40 s、p99 2.37 s、max 2.51 s
UT 层(民用时刻,含 ΔT 口径) 上面再叠一段地球自转换算 还额外含出版方选的 ΔT 系统差 4.8–5.9 s,是口径差不是几何误差

UT 层的差异来自 ΔT 口径,不是几何:NASA 目录/图页的 UT 时刻用其出版时采用的 ΔT 从 TD 换算(2024 年 74 s、2026 年 75 s),而本库实测 ΔT 约 69.1–69.2 s,两者相差 4.8–5.9 s,会整体进入任何 UT 层比较。

换算成地面量级用 basic.DeltaTGroundShiftKM(ΔΔT, 纬度)(即 ≈0.4651·|ΔΔT|·cos(纬度) km,ΔT 只改自转相位、不动 TT 几何):实测 DeltaTGroundShiftKM(5, 36) = 1.881 km、DeltaTGroundShiftKM(5.9, 24) = 2.507 km。

分类对比:

对照类型 样例 时间项 时标层级 对照结果
全局日食 4 次现代日食 食甚 UT(包含ΔT 口径) UT 层,8 s 阈值已含口径差;扣掉后见下一行 秒级对齐,在 8 s 阈值内(其中 4.8–5.9 s 是 ΔT 口径)
全局日食 同上扣掉口径后 食甚 TD(本库 TT 对 NASA TD) TT 层 中位差约 0.2 s
站心日食 3 个本地观测点 食甚、初亏、复圆 UT 层,但公开值多为整分钟 与公开分钟值一致(分辨率限制,不按秒级解读)
站心日食 2 个中心食点 全食/环食持续时间 TT 层:两端时刻相减,ΔT 自动抵消 秒级对齐,在 5 s 阈值内
  • 日食持续时间反应模型层面精度:它是两个接触时刻之差,ΔT 口径自动抵消,所以 5 s 阈值反映的是几何与星历(对食带边缘最敏感),与 ΔT 无关。

  • UT 世界时时刻的 8 s 差异主要是口径差异:不能当成几何精度,它的构成是 4.8–5.9 s(ΔT 口径)+ 不到 1 s(TD 层几何残差)+ NASA 整秒打印的舍入,所以是个宽松上界而不是精度指标。

  • 更大范围的一致性对比结果:1901–2100 与 NASA 五千年目录配对 195 次(NASA 页面缺 1986–2000、2088–2100 两段),类型普查 A145/T139/H13/P155 与 NASA 逐一相等、类型不一致 0;食甚 TD 差中位 0.40 s、p99 2.37 s、max 2.51 s;gamma 差中位 3.2e-5、食分差中位 4.2e-5、带宽差中位 0.5 km(max 8.3 km)、中心食时长差中位 0.25 s(max 0.59 s)。

  • 复现NASA的 UT 值:用 astro.SetDeltaT 注入NASA采用的 ΔT(例如 2024 年 74 s)后取 UT,就能把ΔT差值从比较里去掉;注入只影响换算,不改变 TT 几何。

  • 此外,全局日食资料通常给到秒,适合直接做秒级对照;很多站心日食页面的初亏、复圆和本地食甚只公开到整分钟,因此这类资料只按分钟级核对,公开资料舍入造成的残差不按秒级误差解读。

  • 下面的 2009 上海洋山港和 2012 厦门示例只展示接口调用与 SVG 输出,未承诺地方接触时刻的发布级精度;需要逐项核对时可与 NASA/IMCCE local circumstances 比对。

2009 年长江大日食:上海洋山附近

2009-07-22 “长江大日食”。下面示例选用上海东南方长江口洋山附近的观测点,接近中心线,全食持续约 5 分 57 秒。

package main

import (
	"fmt"
	"time"

	"b612.me/astro/eclipse"
)

func main() {
	cst := time.FixedZone("CST", 8*3600)
	date := time.Date(2009, 7, 22, 12, 0, 0, 0, cst)

	// 上海洋山附近,东经为正,北纬为正,椭球高取 0 米。
	info, ok := eclipse.LocalSolarEclipseOnDate(date, 121.9850, 30.6167, 0)
	fmt.Println(ok, info.Type)                                                                                        // 是否命中本地日食;食型
	fmt.Println(info.HasSaros, info.Saros)                                                                            // 是否匹配沙罗序列;系列号、系列内序号、总成员数
	fmt.Println(info.PartialStart)                                                                                    // 初亏
	fmt.Println(info.CentralStart)                                                                                    // 全食开始
	fmt.Println(info.GreatestEclipse)                                                                                 // 食甚
	fmt.Println(info.CentralEnd)                                                                                      // 全食结束
	fmt.Println(info.PartialEnd)                                                                                      // 复圆
	fmt.Println(info.CentralEnd.Sub(info.CentralStart))                                                               // 全食阶段持续时间
	fmt.Printf("magnitude=%.6f obscuration=%.6f altitude=%.3f\n", info.Magnitude, info.Obscuration, info.SunAltitude) // 食分、遮掩比例、食甚太阳高度

	// 同一天的中心路径,包含食甚点、中心线和南北界。
	path, _ := eclipse.SolarEclipseCentralPath(
		date,
		eclipse.SolarEclipsePathOptions{Step: time.Minute, TargetSpacingKM: 100},
	)
	fmt.Printf("greatest lon=%.4f lat=%.4f width=%.1fkm center=%d\n",
		path.Greatest.Longitude,
		path.Greatest.Latitude,
		path.Greatest.WidthKM,
		len(path.CenterLine),
	)
}

输出结果:

true total // 洋山站点当天命中日食,食型为日全食
true {136 37 71 true} // Solar Saros 136,第 37/71 个成员,已核验
2009-07-22 08:23:55.092397034 +0800 CST // 初亏
2009-07-22 09:37:23.088684976 +0800 CST // 全食开始
2009-07-22 09:40:20.87983489 +0800 CST // 食甚
2009-07-22 09:43:19.723805487 +0800 CST // 全食结束
2009-07-22 11:03:13.914820253 +0800 CST // 复圆
5m56.635120511s // 全食持续时间
magnitude=1.076997 obscuration=1.000000 altitude=57.293 // 食分、遮掩比例、食甚太阳高度
greatest lon=144.1167 lat=24.2193 width=258.3km center=289 // 全局食甚点经纬度、食带宽度、中心线采样点数

2012 年日环食:厦门示例

2012-05-21 日环食在我国东南沿海可见。下面用厦门做一个本地日环食示例,食甚时太阳高度约 9.6 度,环食阶段持续约 4 分 19 秒。

package main

import (
	"fmt"
	"time"

	"b612.me/astro/eclipse"
)

func main() {
	cst := time.FixedZone("CST", 8*3600)
	date := time.Date(2012, 5, 21, 12, 0, 0, 0, cst)

	info, ok := eclipse.LocalSolarEclipseOnDate(date, 118.0894, 24.4798, 0)
	fmt.Println(ok, info.Type)                                                                                        // 是否命中本地日食;食型
	fmt.Println(info.HasSaros, info.Saros)                                                                            // 是否匹配沙罗序列;系列号、系列内序号、总成员数
	fmt.Println(info.PartialStart)                                                                                    // 初亏
	fmt.Println(info.CentralStart)                                                                                    // 环食开始
	fmt.Println(info.GreatestEclipse)                                                                                 // 食甚
	fmt.Println(info.CentralEnd)                                                                                      // 环食结束
	fmt.Println(info.PartialEnd)                                                                                      // 复圆
	fmt.Println(info.CentralEnd.Sub(info.CentralStart))                                                               // 环食阶段持续时间
	fmt.Printf("magnitude=%.6f obscuration=%.6f altitude=%.3f\n", info.Magnitude, info.Obscuration, info.SunAltitude) // 食分、遮掩比例、食甚太阳高度
}

输出结果:

true annular // 厦门站点当天命中日食,食型为日环食
true {128 58 73 true} // Solar Saros 128,第 58/73 个成员,已核验
2012-05-21 05:08:12.878718674 +0800 CST // 初亏
2012-05-21 06:08:15.561088621 +0800 CST // 环食开始
2012-05-21 06:10:25.180663168 +0800 CST // 食甚
2012-05-21 06:12:34.80941087 +0800 CST // 环食结束
2012-05-21 07:20:54.806806147 +0800 CST // 复圆
4m19.248322249s // 环食持续时间
magnitude=0.933289 obscuration=0.872354 altitude=9.565 // 食分、遮掩比例、食甚太阳高度

生成日食 SVG

现代城市观测示例可以使用 2035-09-02 北京日全食。按北京市区近似坐标(东经 116.4074,北纬 39.9042)计算,这次事件属于 Solar Saros 145 的第 23/77 个成员,全食阶段持续约 1m33s。

默认日食 SVG 头部会自动带上沙罗序列和全食/环食历时;更多文案可通过 LocalSolarEclipseSVGOptions 覆写:

  • Title:主标题
  • SummaryText / GreatestText / MetaText:标题下三行摘要
  • OverviewTitle / PhasePanelsTitle / ContactsTitle:总览、阶段视圆、接触时刻三个分区标题
  • DirectionText / FooterNote:底部方向说明和补充说明
package main

import (
	"fmt"
	"os"
	"time"

	"b612.me/astro/eclipse"
	eclipsesvg "b612.me/astro/eclipse/svg"
)

func main() {
	cst := time.FixedZone("CST", 8*3600)

	// 2009 长江口洋山附近日全食图。
	totalSVG, ok := eclipsesvg.LocalSolarEclipseSVG(
		time.Date(2009, 7, 22, 12, 0, 0, 0, cst),
		121.9850, 30.6167, 0,
		eclipsesvg.LocalSolarEclipseSVGOptions{
			Width:    920,
			Height:   720,
			Step:     5 * time.Minute,
			Location: cst,
		},
	)
	fmt.Println(ok, len(totalSVG)) // 是否生成成功;SVG 字节长度
	if ok {
		_ = os.WriteFile("doc/img/solar-eclipse-yangshan-2009.svg", []byte(totalSVG), 0o644)
	}

	// 2012 厦门日环食图。
	annularSVG, ok := eclipsesvg.LocalSolarEclipseSVG(
		time.Date(2012, 5, 21, 12, 0, 0, 0, cst),
		118.0894, 24.4798, 0,
		eclipsesvg.LocalSolarEclipseSVGOptions{
			Width:    920,
			Height:   720,
			Step:     5 * time.Minute,
			Location: cst,
		},
	)
	fmt.Println(ok, len(annularSVG)) // 是否生成成功;SVG 字节长度
	if ok {
		_ = os.WriteFile("doc/img/solar-eclipse-xiamen-2012.svg", []byte(annularSVG), 0o644)
	}

	// 2035 北京日全食图,同时打印沙罗序列号和全食持续时间。
	beijingDate := time.Date(2035, 9, 2, 12, 0, 0, 0, cst)
	beijingInfo, ok := eclipse.LocalSolarEclipseOnDate(beijingDate, 116.4074, 39.9042, 0)
	fmt.Println(ok, beijingInfo.Type)                                 // 是否命中本地日食;食型
	fmt.Println(beijingInfo.HasSaros, beijingInfo.Saros)              // 是否匹配沙罗序列;系列号、系列内序号、总成员数
	fmt.Println(beijingInfo.CentralEnd.Sub(beijingInfo.CentralStart)) // 全食持续时间

	beijingSVG, ok := eclipsesvg.LocalSolarEclipseSVG(
		beijingDate,
		116.4074, 39.9042, 0,
		eclipsesvg.LocalSolarEclipseSVGOptions{
			Width:    920,
			Height:   720,
			Step:     5 * time.Minute,
			Location: cst,
		},
	)
	fmt.Println(ok, len(beijingSVG)) // 是否生成成功;SVG 字节长度
	if ok {
		_ = os.WriteFile("doc/img/solar-eclipse-beijing-2035.svg", []byte(beijingSVG), 0o644)
	}
}

输出结果:

true 13625 // 洋山日全食 SVG 生成成功,长度 13625 字节
true 13542 // 厦门日环食 SVG 生成成功,长度 13542 字节
true total // 北京站点当天命中日食,食型为日全食
true {145 23 77 true} // Solar Saros 145,第 23/77 个成员,已核验
1m33.329527974s // 北京市区近似坐标下的全食持续时间
true 13589 // 北京日全食 SVG 生成成功,长度 13589 字节

生成效果:

2009 长江口洋山日全食

2012 厦门日环食

2035 北京日全食

月食

本库的月食判断与搜索能力统一放在 eclipse 包,返回结果会保持传入 time.Time 的时区。 常用接口:

  • LunarEclipseOnDate:判断某个当地日期是否有月食
  • LastLunarEclipse / NextLunarEclipse / ClosestLunarEclipse:搜索全局月食
  • LocalLunarEclipseOnDate:判断某地当天是否能看到可见月食
  • LastLocalLunarEclipse / NextLocalLunarEclipse / ClosestLocalLunarEclipse:搜索某地可见月食
  • LastLocalTotalLunarEclipse / NextLocalTotalLunarEclipse / ClosestLocalTotalLunarEclipse:搜索某地可见月全食,返回 (info, ok)
  • GeometricLocalLunarEclipseOnDate:判断某地当天是否发生几何月食,不做“月亮在地平线上方”的可见性过滤
  • eclipse/svg.LunarEclipseSVG:生成月食穿影图 SVG

LocalLunarEclipseInfo.Visibility 按当地中天情况分为八类:full、moonrise、moonset、rise-and-set、interrupted、penumbra-moonrise、penumbra-moonset 与 invisible。两种 penumbra-* 状态要求本影阶段始终在地平线下,判定检查整个本影区间的月球高度极值,以覆盖接触时刻之间的极区掠射窗口;纯半影月食没有本影接触,不会返回这两类。interrupted 表示半影首尾可见而中途落到地平线下,即使本影阶段始终不可见,也仍属于此类。分类使用观测点当地中天,不使用全球食甚时刻。

MarshalLunarEclipse 导出 visible-at-p1、visible-at-p4 两个瞬时可见半球,以及 visible-during-eclipse(P1 到 P4 任意时刻可见)和 visible-throughout-eclipse(P1 到 P4 全程可见)两个时间包络。包络分别使用 aggregation=union 和 aggregation=intersection;没有全程可见区域时,后者为空 MultiPolygon。包络按经度列采样,列数由 boundary_points 推导并限制在 360..720,实际值写入 longitude_points。

同一接口还导出 penumbra-moonset 与 penumbra-moonrise 两条仅见半影带,分别对应 P1 可见/P4 不可见与 P4 可见/P1 不可见、且本影阶段始终不可见的区域;每条带包含 phase=penumbral-only 和自己的接触时刻。纯半影月食没有本影接触,不导出这两条带。

MarshalLunarEclipseWithOptions 通过 LunarEclipseOptions 控制输出内容和采样精度。SkipRoles 可跳过时间包络与仅见半影带;跳过两个时间包络时不会执行包络采样。EnvelopeSweepSamples 默认 48,显式值限制在 [2, 192];EnvelopeLongitudePoints 默认 max(360, boundaryPoints),显式值限制在 [12, 720]。

MoonHorizon 和 MoonStateAt 接收 UTC 儒略日;HMoonHeight(jd, lon, lat, tz) 接收该时区的民用墙上时间儒略日,并使用小时单位的时区偏移换算。仅当 tz=0 时,jd 才是 UTC;时标换算由库内部完成。

返回结果 LunarEclipseInfo 包含:

  • 月食类型 Type
  • 沙罗序列信息 HasSaros / Saros
  • 半影食分 PenumbralMagnitude
  • 本影食分 UmbralMagnitude
  • 半影始、初亏、食既、食甚、生光、复圆、半影终等时刻

其中 Saros 的含义与日食部分相同:

  • Saros.Series:Verified=true 时为 NASA 月食沙罗系列号,否则为推算的暂定系列号
  • Saros.Member:这次月食在该系列中的第几个成员,从 1 开始
  • Saros.Count:该沙罗系列的总成员数
  • Saros.Verified:是否已与内置权威目录锚点核验;扩展表或范围外推算结果为 false

月食同样优先使用 NASA 锚点,天文年份 -3000 至 +6000 年内查扩展表,范围外才实时演算。推算成员按 Danjon 与 Chauvenet 检出的事件并集计数,因此不随调用的月食模型或观测地点改变;极浅成员可能与 NASA 目录不同,Verified 保持 false。 例如 2028-12-31 / 2029-01-01 这次跨年月全食属于 月食沙罗序列125 的第 49/72 个成员。

当前同时保留两套地影放大口径:

  • Danjon(默认):只对月球水平视差项乘 1.01,再与太阳视半径、太阳视差组合求影半径。NASA GSFC 当前月食目录与图页采用的也是这一路线,本库默认的 LunarEclipseOnDate、LastLunarEclipse、NextLunarEclipse、ClosestLunarEclipse 都使用它。
  • Chauvenet(兼容口径):先取 0.99834 × 地球赤道半径,再把整组影半径统一乘 51/50。这与传统旧历表口径更接近,适合做兼容性回归和旧结果对照。

两者的直接差异通常表现为:

  • Chauvenet 给出的半影和本影都更大,半影食分通常比 Danjon 多约 0.025,本影食分通常多约 0.005
  • 对边界月食而言,Chauvenet 更容易把结果推向“更深”的食型
  • 与 NASA 目录、现代星历软件或当前主流月食资料对照时,对应的是默认的 Danjon
  • 兼容既有历史基线时,对应的是显式调用的 Chauvenet

代码示例

package main

import (
	"b612.me/astro/eclipse"
	"fmt"
	"time"
)

func main() {
	date := time.Date(2029, 1, 1, 0, 0, 0, 0, time.UTC)

	// 默认使用 Danjon,更接近 NASA
	info := eclipse.ClosestLunarEclipse(date)
	fmt.Println(info.Type)
	fmt.Println(info.HasSaros, info.Saros)
	fmt.Println(info.Maximum)
	fmt.Println(info.PenumbralMagnitude, info.UmbralMagnitude)
	fmt.Println(info.PenumbralStart)
	fmt.Println(info.PartialStart)
	fmt.Println(info.TotalStart)
	fmt.Println(info.TotalEnd)
	fmt.Println(info.PartialEnd)
	fmt.Println(info.PenumbralEnd)

	// 如需兼容旧口径,可显式使用 Chauvenet
	legacy := eclipse.ClosestLunarEclipseChauvenet(date)
	fmt.Println(legacy.PenumbralMagnitude, legacy.UmbralMagnitude)

	// 判断某个本地自然日是否发生月食,返回时区与输入保持一致
	local := time.Date(2029, 1, 1, 12, 0, 0, 0, time.FixedZone("CST", 8*3600))
	today, ok := eclipse.LunarEclipseOnDate(local)
	fmt.Println(ok)
	fmt.Println(today.Type)
	fmt.Println(today.Maximum)
}

输出结果:

total
true {125 49 72 true}
2028-12-31 16:52:05.603753328 +0000 UTC
2.2739938633996872 1.2461094682708755
2028-12-31 14:03:54.239418804 +0000 UTC
2028-12-31 15:07:42.171904742 +0000 UTC
2028-12-31 16:16:27.306801974 +0000 UTC
2028-12-31 17:27:46.228030622 +0000 UTC
2028-12-31 18:36:32.270547151 +0000 UTC
2028-12-31 19:40:11.575520038 +0000 UTC
2.299608256177245 1.2511661731458574
true
total
2029-01-01 00:52:05.603753328 +0800 CST

与 NASA 数据对照

对照值取自 NASA GSFC 的月食目录(食甚印为 TD、食分为目录值)。UT 层的比较必须先扣掉时标口径:目录与单次图页的 UT 时刻是用其出版时采用的 ΔT 从 TD 换算的(2026 年取 75 s、2024 年取 74 s),而实测 ΔT 只有约 69.1–69.2 s,两者相差 4.8–5.9 s。所以 UT 层的逐接触比较会整体带上这个时标差,它不是月球几何误差;TD 层(不含 ΔT)才是能单独检验星历与几何的一层。

样例 模型 半影食分误差 本影食分误差 食甚 TD 差(不含 ΔT) 食甚 UT 差(含 ΔT 口径)
2026-03-03 月全食 Danjon -0.000067208 -0.000069993 +0.114 s +5.99 s
2026-03-03 月全食 Chauvenet +0.025599846 +0.004935007 +0.114 s +5.99 s
2026-08-28 月偏食 Danjon -0.000113694 -0.000033624 +0.090 s +5.93 s
2026-08-28 月偏食 Chauvenet +0.025567662 +0.004957333 +0.090 s +5.93 s
2024-03-25 半影月食 Danjon -0.000176555 见下说明 +1.012 s +5.81 s
2024-03-25 半影月食 Chauvenet +0.026044973 见下说明 +1.012 s +5.81 s

以 2026-03-03 月全食为例,当前默认 Danjon 与 NASA 的逐项差异为:

  • 食型:一致,都是 total
  • 食甚:本库 TD 11:34:52.113 vs NASA 11:34:52,差 +0.114 s;换算到 UT 后本库比 NASA 图页晚 5.99 s,其中 5.88 s 来自 NASA 采用 ΔT = 75 s、本库实测 ΔT = 69.12 s
  • 阶段历时:半影 338.67 min vs NASA 338.6、本影 207.17 min vs 207.2、全食 58.31 min vs 58.3,都落在目录 0.1 分钟的印刷精度内
  • 半影食分:2.183732792 vs NASA 2.1838,误差 -0.000067208
  • 本影食分:1.150630007 vs NASA 1.1507,误差 -0.000069993

同一例中,Chauvenet 的结果为:

  • 食型:一致,都是 total
  • 半影食分:2.209399846 vs NASA 2.1838,误差 +0.025599846
  • 本影食分:1.155635007 vs NASA 1.1507,误差 +0.004935007

Chauvenet 是保留给旧历表/旧口径兼容的影半径模型,半影和本影都会比默认 Danjon 更大;与 NASA 当前目录对照时,食分与接触时刻都会出现分钟/百分之一量级的偏移。这是模型口径差异,不代表默认月食接口的时间精度。

说明:NASA 目录的 TD 只印到整秒,因此 ±0.5 s 以内都属于印刷精度;上表 2024-03-25 的 +1.012 s 已经略超这一精度,是半影食甚这种极浅几何下两条链的差异。

说明:纯半影月食时,NASA 会给出负的 umbral magnitude,表示月面中心距本影边界还有余量;本库也保留这个负值,因此纯半影月食与 NASA 的本影食分已经属于同口径比较。

月食 SVG

LunarEclipseSVG、LunarEclipseDetailedSVG 与 LunarEclipseMapSVG 的默认模型与后缀入口口径见下文月食出图。

默认月食 SVG 头部会自动带上沙罗序列;如果需要自定义更多文字,可以通过 LunarEclipseSVGOptions 覆写:

  • Title:主标题
  • SummaryText / MaximumText / CoordinatesText / DurationText / MetaText:标题下五行信息
  • ContactsTitle:接触时刻区标题
  • DirectionText / FooterNote:底部方向说明和补充说明
package main

import (
	"fmt"
	"os"
	"time"

	eclipsesvg "b612.me/astro/eclipse/svg"
)

func main() {
	cst := time.FixedZone("CST", 8*3600)
	// 生成 2029-01-01 这次跨年月全食的穿影图。
	svg, ok := eclipsesvg.LunarEclipseSVG(
		time.Date(2029, 1, 1, 0, 0, 0, 0, cst),
		eclipsesvg.LunarEclipseSVGOptions{
			Width:    960,
			Height:   620,
			Step:     10 * time.Minute,
			Location: cst,
		},
	)
	fmt.Println(ok, len(svg))
	if ok {
		_ = os.WriteFile("doc/img/lunar-eclipse-2029-01-01.svg", []byte(svg), 0o644)
	}
}

输出结果:

true 19831

生成效果:

2029 跨年月全食穿影图

参考资料

全球见食图与月食出图

eclipse/svg 的五个入口都返回 (string, bool):第二个返回值是 false 表示这张图按当前参数画不出来(当天没有该事件、画布低于下限、或 UT1 口径配了非 UTC 时区),不是渲染错误。

入口 画面 画布建议
LocalSolarEclipseSVG 站心日面视圆图(见前文“生成日食 SVG”) 920×720 起
SolarEclipseMapSVG 全球见食图,可切四种投影 1200×800;正射球面 1000×1414
LunarEclipseSVG 月食穿影图 960×620 起
LunarEclipseMapSVG 月食世界可见区图 1200×800
LunarEclipseDetailedSVG 月食详细版式(示意与底图合成一页) 1000×1414 或 1414×1000

日食全球图包含偏食可见区、中心带与路径、阶段信息、日升日落线、太阳直射点、影轴进出地球点和接触点;定时半影及本影/反本影轮廓可按需打开。月食图绘制 P1/P4 可见半球和月出/月落过渡区,以 P1、食甚、P4 三刻地平圈近似整场可见区;它不包含 GeoJSON 的时间包络,因此两条地平线之间可能留有窄缝。

全球见食图

等经纬投影

最小可用调用只给事件日期与展示时区,其余走默认(TimeLabelStep 默认 30 分钟):

solar, ok := eclipsesvg.SolarEclipseMapSVG(
	time.Date(2009, 7, 22, 12, 0, 0, 0, cst),
	eclipsesvg.SolarEclipseMapSVGOptions{
		Width: 1414, Height: 1000, Location: cst,
		TimeLabelStep: 30 * time.Minute, GreatestTimeStep: 30 * time.Minute,
	},
)
fmt.Println(ok, len(solar))

2009 长江大日食全球见食图

另外两场等经纬示例是 2012-05-21 厦门日环食与 2035-09-02 北京日全食,两张图与长江口图同口径:不画瞬时半影/本影轮廓、画 30 分钟食甚等时线,并用空切片关闭食分等值线:

options := eclipsesvg.SolarEclipseMapSVGOptions{
	Width: 1200, Height: 800, Location: cst,
	TimeLabelStep: 30 * time.Minute, GreatestTimeStep: 30 * time.Minute,
	MagnitudeValues: []float64{},
}
annular, ok := eclipsesvg.SolarEclipseMapSVG(time.Date(2012, 5, 21, 12, 0, 0, 0, cst), options)
total, ok := eclipsesvg.SolarEclipseMapSVG(time.Date(2035, 9, 2, 12, 0, 0, 0, cst), options)
fmt.Println(ok, len(annular), len(total))

2012 厦门日环食全球见食图

2035 北京日全食全球见食图

正射球面图

EclipseMapProjectionOrthographic 给出 NASA 版式的正射球面图:视点取食甚点,只画朝向视点的半个地球,建议使用 1000×1414 画布。投影不改变底层地理结果。

globe, ok := eclipsesvg.SolarEclipseMapSVG(
	time.Date(2009, 7, 22, 12, 0, 0, 0, cst),
	eclipsesvg.SolarEclipseMapSVGOptions{
		Width: 1000, Height: 1414, Location: cst,
		Projection:    eclipsesvg.EclipseMapProjectionOrthographic,
		TimeLabelStep: 30 * time.Minute, GreatestTimeStep: 30 * time.Minute,
	},
)

2009 长江大日食正射球面图

正射球面图在竖版(如 1000×1414)下最舒展;横版(1200×800)同样能出图,只是球面会明显缩小。

南北极方位等距投影

EclipseMapProjectionNorthPolar 与 EclipseMapProjectionSouthPolar 把极点放在画布中心,适合食带整体落在高纬的事件。EclipseMapProjectionAuto(零值)会在适合时自动选极图,需要固定版式时才显式指定;投影只影响 SVG 表达,不改变底层 WGS84 地理结果。

2012-05-21 日环食的偏食可见区覆盖北极点,强制北极投影便于查看跨反经线的见食范围:

arctic, ok := eclipsesvg.SolarEclipseMapSVG(
	time.Date(2012, 5, 21, 12, 0, 0, 0, cst),
	eclipsesvg.SolarEclipseMapSVGOptions{
		Width: 1200, Height: 800, Location: cst,
		Projection:    eclipsesvg.EclipseMapProjectionNorthPolar,
		TimeLabelStep: 30 * time.Minute, GreatestTimeStep: 30 * time.Minute,
	},
)

2012 日环食北极区全球见食图

2021-12-04 日全食的食带整条落在南极区,南极投影是最自然的版式;下面这张与横版全球图同一口径,只画 30 分钟食甚等时线:

south, ok := eclipsesvg.SolarEclipseMapSVG(
	time.Date(2021, 12, 4, 12, 0, 0, 0, cst),
	eclipsesvg.SolarEclipseMapSVGOptions{
		Width: 1200, Height: 800, Location: cst,
		Projection:    eclipsesvg.EclipseMapProjectionSouthPolar,
		TimeLabelStep: 30 * time.Minute, GreatestTimeStep: 30 * time.Minute,
		MagnitudeValues: []float64{},
	},
)

2021 南极日全食南极区全球见食图

同一事件批量出四种投影

四个投影共用一份选项,只是 Projection 不同,适合一次生成后挑版式:

date := time.Date(2009, 7, 22, 12, 0, 0, 0, cst)
for _, spec := range []struct {
	name string
	p    eclipsesvg.EclipseMapProjection
}{
	{"equirectangular", eclipsesvg.EclipseMapProjectionEquirectangular},
	{"north-polar", eclipsesvg.EclipseMapProjectionNorthPolar},
	{"south-polar", eclipsesvg.EclipseMapProjectionSouthPolar},
	{"orthographic", eclipsesvg.EclipseMapProjectionOrthographic},
} {
	options := eclipsesvg.SolarEclipseMapSVGOptions{
		Width: 1200, Height: 800, Location: cst, Projection: spec.p,
	}
	svg, ok := eclipsesvg.SolarEclipseMapSVG(date, options)
	if !ok {
		continue
	}
	_ = os.WriteFile("solar-eclipse-"+spec.name+".svg", []byte(svg), 0o644)
}

图层与采样开关

选项 默认 语义
TimeLabelStep 30 分钟 中心线时刻标记间隔;负值关闭
GreatestTimeStep 关闭 食甚时刻等时线;必须显式给正值,按展示时区对齐,小于一分钟按一分钟,单次最多 64 条
MagnitudeValues 0.2/0.4/0.6/0.8(nil 时) 食分等值线电平;显式空切片关闭,非空切片按给定电平绘制
PenumbralOutlineStep 关闭 瞬时半影轮廓采样间隔;零值或负值不画,正值小于一分钟按一分钟
CentralShadowStep 关闭 瞬时本影/反本影轮廓采样间隔;同上
PartialStep 2 分钟 偏食足迹时间步长;非正值与小于两分钟的正值都夹到两分钟
BoundaryPoints 180 每个瞬时偏食足迹的角向采样数;非正值用 180,正值限制在 12..1440
CentralStep 2 分钟 中心路径时间步长;非正值用两分钟,正值小于一秒按一秒,长事件会自动放大以保持基础路径不超过 30000 个采样点
TargetSpacingKM 150 km 中心线地面间距上限;非正值用 150 km,NaN 与 +Inf 禁用加密

半影/本影轮廓默认关闭。食分等值线在 MagnitudeValues == nil 时使用表中的默认电平;显式传入空切片可关闭。厦门、北京、北极区与南极点四张图不画瞬时轮廓,只画 30 分钟食甚等时线,并显式关闭食分等值线:

options := eclipsesvg.SolarEclipseMapSVGOptions{
	Width: 1200, Height: 800, Location: cst,
	TimeLabelStep: 30 * time.Minute, GreatestTimeStep: 30 * time.Minute,
	MagnitudeValues: []float64{},
}

等时线不是逐点求食甚再描等值线,而是固定时刻后解 ∂(日月中心角距²)/∂t = 0 的零集再沿曲线延拓,成本正比于曲线长度;每条支路止于地平线或偏食可见域边界,纬度 ±88° 以上不再延拓,同一时刻可能有多条互不相连的支路。

可降级的图层用 data-source 标注实际几何来源,取值词表见 地图投影。

画布下限与回落

  • 日食图下限 800×560:宽度小于 800 或高度小于 560 时回落到 960×640;更窄的横版画布会让地图框与右栏数据网格水平重叠、面板行距压到 1 px 以下。
  • 偏食区填充由瞬时足迹的并集生成,因此 PartialStep 小于两分钟时会夹到两分钟;更密的请求不会提高结果精度。
  • 月食详细版式按 Height 推导版面:640×420 与 800×600 容不下示意图与底图的下限而返回 false,1000×1414 与 1414×1000 正常出图。

月食出图

三个入口与影半径模型

LunarEclipseSVG(穿影图)、LunarEclipseMapSVG(世界可见区)与 LunarEclipseDetailedSVG(详细版式)默认共用同一套模型:以 Danjon 为主,极浅半影按核心默认口径回退 Chauvenet,与 LunarEclipseOnDate 一致;带 Danjon / Chauvenet 后缀的入口强制指定模型,...Chauvenet 适合与采用经典影半径的旧表对表。

diagram, ok := eclipsesvg.LunarEclipseSVG(
	time.Date(2029, 1, 1, 0, 0, 0, 0, cst),
	eclipsesvg.LunarEclipseSVGOptions{
		Width: 960, Height: 620, Step: 10 * time.Minute, Location: cst,
	},
)
fmt.Println(ok, len(diagram))

2029 跨年月全食穿影图

世界可见区图

底图区分全程可见、带食月出、带食月落与不可见四类区域:全程可见要求 P1、食甚、P4 三刻月球都在地平上(两端可见推不出中途可见,高纬下中天会让月亮在食甚前后落到地平下,这段归带食月落);带食月落覆盖 P1 可见而 P4 不可见的地点,以及只在食甚前后露出地平、两端都在地平下的极区透镜;带食月出覆盖 P4 可见而 P1 不可见的地点;三刻都不见才算不可见。Projection 可切极区与正射版式;下面这张图是默认输出,半影阶段已纳入分区(口径见下):

visible, ok := eclipsesvg.LunarEclipseMapSVG(
	time.Date(2029, 1, 1, 0, 0, 0, 0, cst),
	eclipsesvg.LunarEclipseMapSVGOptions{Width: 1200, Height: 800, Location: cst},
)

2029 跨年月全食全球可见图

半影阶段默认纳入分区,画法同 NASA 月食世界图:补画 U1、U2、U3、U4 四条地平边界线,把只看得见半影的月出、月落带单独着色,图例里分列「半影月出」与「半影月落」(月出偏蓝、月落偏紫红),并在图上方的时刻行里补一行本影接触时刻。两条带排除整个本影阶段的可见区:本影区间内只要有任何时刻月亮在地平上,该地点就归带食月出/带食月落。掩膜按 15 分钟档位取样整球可见半球(圆盘分辨率约 0.035°),残余的掠射窗口深度约 0.05°(约 0.15 像素);深度更浅、只持续几分钟的窗口由站点 API 与 GeoJSON 精确判定。纯半影月食没有本影接触,开关两侧输出一致。DisablePenumbralPhase: true 退回只按三刻地平状态分的四类可见区,不画 U1–U4 与这两条半影带:

penumbral, ok := eclipsesvg.LunarEclipseMapSVG(
	time.Date(2026, 3, 3, 0, 0, 0, 0, cst),
	eclipsesvg.LunarEclipseMapSVGOptions{
		Width: 1200, Height: 800, Location: cst, DisablePenumbralPhase: true,
	},
)

LunarEclipseDetailedSVGOptions 有同名字段,控制详细版式下方那张底图,默认同样画半影阶段。

详细版式

详细版式把两类月食图合成一页:居中摘要(食甚、半影/本影食分、伽马、半影/本影半径、月距、沙罗序列)、左右两侧的日月地心坐标块、穿影示意图、历时/弧分比例尺/接触时刻三栏,以及下方的世界可见性底图与图例。地影几何由 basic.LunarEclipseShadowGeometryAt 给出,其中 Gamma 用地球赤道半径、半影/本影半径用度,换算成地球半径要乘以月球处的地球视差。

detailed, ok := eclipsesvg.LunarEclipseDetailedSVG(
	time.Date(2029, 1, 1, 0, 0, 0, 0, cst),
	eclipsesvg.LunarEclipseDetailedSVGOptions{Location: cst},
)

2029 跨年月全食详细版式

第二个事件 2026-03-03 月全食用同一批入口,穿影图与详细版式各一张:

diagram2026, ok := eclipsesvg.LunarEclipseSVG(
	time.Date(2026, 3, 3, 0, 0, 0, 0, cst),
	eclipsesvg.LunarEclipseSVGOptions{Width: 960, Height: 620, Step: 10 * time.Minute, Location: cst},
)
detailed2026, ok := eclipsesvg.LunarEclipseDetailedSVG(
	time.Date(2026, 3, 3, 12, 0, 0, 0, cst),
	eclipsesvg.LunarEclipseDetailedSVGOptions{Location: cst},
)

2026 月全食穿影图

2026 月全食详细版式

版面由 Height 推导:横版把数据块排在地图右侧两栏三行,竖版把数据块排在下方三栏两行。

时标口径与 UT1

四族图默认按 UTC 口径出图,并在图上或图注写明时标;TimeScale: astro.TimeScaleUT1 改成 UT1 读数并补上 DUT1 = UT1−UTC 差值,此时 Location 必须是 UTC,否则返回 false。几何一律按民用时刻算完再换口径,换算不会平移等时线。

要在程序里直接取 UT1 读数,用 eclipse 的 ...InUT1 系列(SolarEclipseInfoInUT1、LocalSolarEclipseInfoInUT1、LunarEclipseInfoInUT1、SolarEclipsePathInUT1、SolarEclipsePartialFootprintsInUT1、SolarEclipseGeocentricPanelInUT1、TimeLabelsInUT1),它们只改时刻字段,零值原样保留。

完整约定见时标声明。

ut1, ok := eclipsesvg.SolarEclipseMapSVG(
	time.Date(2009, 7, 22, 12, 0, 0, 0, cst),
	eclipsesvg.SolarEclipseMapSVGOptions{
		Width: 1200, Height: 800, Location: time.UTC,
		TimeScale: astro.TimeScaleUT1, TimeLabelStep: 30 * time.Minute,
	},
)

2009 长江大日食全球见食图(UT1 口径)