feat: 完善日月食与月掩几何链路并扩展历法接口
- 新增日月食中心带、偏食带、阴影足迹、等时线、食分线及升落边界计算,支持极区与混合食拓扑 - 新增日食单时刻阴影求解器、站心状态查询、批量采样和 ΔT 覆盖接口 - 重构恒星与行星月掩路径,补充有限盘面接触、站心修正、掩带宽度、极区投影及升落边界 - 扩展 SVG 与 GeoJSON 输出,支持详细面板、全球/极区/地球投影、边界闭合、时间标记和拓扑签名 - 扩展日月食候选搜索、局地搜索、沙罗序列预计算与范围外推,补充系列锚点和成员一致性校验 - 补齐古历纪年、儒略历独有闰日、多公历候选、历法改革跨日及精确日期运算接口 - 优化 ΔT、章动、恒星时、月球地平线、事件根搜索和本地星历缓存,降低重复计算开销并提升边界稳定
This commit is contained in:
+49
-16
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"b612.me/astro/basic"
|
||||
eclipsecore "b612.me/astro/eclipse"
|
||||
"b612.me/astro/internal/svgchart"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -189,8 +190,12 @@ func renderLocalSolarEclipseSVG(
|
||||
b.WriteString(`<rect width="100%" height="100%" fill="#efefed"/>`)
|
||||
fmt.Fprintf(&b, `<rect x="22" y="18" width="%.3f" height="%.3f" fill="#ffffff" stroke="#c9c9c6" stroke-width="1.2"/>`,
|
||||
width-44, height-36)
|
||||
titleText := title
|
||||
if options.Title != "" {
|
||||
titleText = svgchart.EllipsizeText(title, width-52, 26)
|
||||
}
|
||||
fmt.Fprintf(&b, `<text x="%.3f" y="44" fill="#111111" font-family="Georgia, 'Times New Roman', serif" font-size="26" font-weight="700" text-anchor="middle">%s</text>`,
|
||||
width/2, html.EscapeString(title))
|
||||
width/2, html.EscapeString(titleText))
|
||||
fmt.Fprintf(&b, `<line x1="%.3f" y1="57" x2="%.3f" y2="57" stroke="#555" stroke-width="1"/>`, width/2-78, width/2+78)
|
||||
for index, line := range headerTexts {
|
||||
fontSize := 13
|
||||
@@ -245,12 +250,23 @@ func renderLocalSolarEclipseSVG(
|
||||
}
|
||||
writeLocalSolarEclipseStagePanels(&b, info, eventFrames, options, margin, stageTop, width-2*margin, stageHeight)
|
||||
|
||||
direction := localSolarEclipseSVGDirectionTextValue(options)
|
||||
if options.DirectionText != "" {
|
||||
direction = svgchart.EllipsizeText(direction, width-80, 12)
|
||||
}
|
||||
fmt.Fprintf(&b, `<text x="%.3f" y="%.3f" fill="#333" font-family="Georgia, 'Times New Roman', serif" font-size="12">%s</text>`,
|
||||
40.0, height-54,
|
||||
html.EscapeString(localSolarEclipseSVGDirectionTextValue(options)))
|
||||
note := localSolarEclipseSVGFooterNoteText(options)
|
||||
fmt.Fprintf(&b, `<text x="%.3f" y="%.3f" fill="#555" font-family="Georgia, 'Times New Roman', serif" font-size="12">%s</text>`,
|
||||
40.0, height-34, html.EscapeString(note))
|
||||
40.0, height-54, html.EscapeString(direction))
|
||||
// 默认说明是单行;调用方文本折行后按画布底边截断,首行位置不变。
|
||||
lines := []string{localSolarEclipseSVGFooterNoteText(options)}
|
||||
if options.FooterNote != "" {
|
||||
maxWidth := width - 80
|
||||
lines = svgchart.TruncateTextLines(svgchart.WrapText(options.FooterNote, maxWidth, 12), maxWidth, 12,
|
||||
svgchart.BaselineLineLimit(12, 15, height-34, height-4))
|
||||
}
|
||||
for index, line := range lines {
|
||||
fmt.Fprintf(&b, `<text x="%.3f" y="%.3f" fill="#555" font-family="Georgia, 'Times New Roman', serif" font-size="12">%s</text>`,
|
||||
40.0, height-34+float64(index)*15, html.EscapeString(line))
|
||||
}
|
||||
writeLocalSolarEclipseContacts(&b, info, options, panelX, math.Max(154, cy-92))
|
||||
b.WriteString(`</svg>`)
|
||||
return b.String()
|
||||
@@ -264,16 +280,25 @@ func localSolarEclipseSVGTitleText(info LocalSolarEclipseInfo, options LocalSola
|
||||
}
|
||||
|
||||
func localSolarEclipseSVGHeaderTexts(info LocalSolarEclipseInfo, options LocalSolarEclipseSVGOptions) []string {
|
||||
lines := []string{
|
||||
localSolarEclipseSVGSummaryText(info, options),
|
||||
localSolarEclipseSVGGreatestTextValue(info, options),
|
||||
localSolarEclipseSVGMetaTextValue(info, options),
|
||||
values := []struct {
|
||||
text string
|
||||
custom bool
|
||||
}{
|
||||
{localSolarEclipseSVGSummaryText(info, options), options.SummaryText != ""},
|
||||
{localSolarEclipseSVGGreatestTextValue(info, options), options.GreatestText != ""},
|
||||
{localSolarEclipseSVGMetaTextValue(info, options), options.MetaText != ""},
|
||||
}
|
||||
filtered := make([]string, 0, len(lines))
|
||||
for _, line := range lines {
|
||||
if line != "" {
|
||||
filtered = append(filtered, line)
|
||||
filtered := make([]string, 0, len(values))
|
||||
for _, value := range values {
|
||||
if value.text == "" {
|
||||
continue
|
||||
}
|
||||
// 摘要行各占一个固定槽位,调用方长文本只能截断,不能折行挤压下一行。
|
||||
if value.custom {
|
||||
filtered = append(filtered, svgchart.EllipsizeText(value.text, float64(options.Width)-80, 14))
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, value.text)
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
@@ -374,9 +399,17 @@ func localSolarEclipseSVGMetaText(info LocalSolarEclipseInfo, language string) s
|
||||
parts := make([]string, 0, 2)
|
||||
if info.HasSaros {
|
||||
if language == localSolarEclipseSVGLanguageEnglish {
|
||||
parts = append(parts, fmt.Sprintf("Solar Saros %d %d/%d", info.Saros.Series, info.Saros.Member, info.Saros.Count))
|
||||
label := fmt.Sprintf("Solar Saros %d %d/%d", info.Saros.Series, info.Saros.Member, info.Saros.Count)
|
||||
if !info.Saros.Verified {
|
||||
label += " (provisional)"
|
||||
}
|
||||
parts = append(parts, label)
|
||||
} else {
|
||||
parts = append(parts, fmt.Sprintf("沙罗 %d 第 %d/%d 个成员", info.Saros.Series, info.Saros.Member, info.Saros.Count))
|
||||
label := fmt.Sprintf("沙罗 %d 第 %d/%d 个成员", info.Saros.Series, info.Saros.Member, info.Saros.Count)
|
||||
if !info.Saros.Verified {
|
||||
label += "(推算)"
|
||||
}
|
||||
parts = append(parts, label)
|
||||
}
|
||||
}
|
||||
if duration := localSolarEclipseSVGCentralDurationText(info, language); duration != "" {
|
||||
|
||||
Reference in New Issue
Block a user