package svg
import (
"fmt"
"html"
"strings"
"b612.me/astro/moon"
)
// writeOccultationGreatestTimeContours 绘制地方掩甚时刻等值线及其 HH:MM 标注。
func writeOccultationGreatestTimeContours(
b *strings.Builder,
contours []moon.OccultationGreatestTimeContour,
layout starOccultationSVGLayout,
options StarOccultationSVGOptions,
placer *occultationLabelPlacer,
) {
if len(contours) == 0 {
return
}
type isochroneLabel struct {
x, y float64
text string
}
labels := make([]isochroneLabel, 0, len(contours))
for _, contour := range contours {
for _, segment := range contour.Segments {
writeStarOccultationGeoLine(b, segment, layout,
occultationLineStyle{className: "greatest-time-isoline", color: "#1f6fb2", strokeWidth: 1.1})
}
text := contour.Time.In(options.Location).Format("15:04")
if x, y, ok := occultationGreatestTimeIsolineLabelPlacement(contour, text, layout, placer); ok {
labels = append(labels, isochroneLabel{x: x, y: y, text: text})
}
}
for _, label := range labels {
fmt.Fprintf(b, `%s`,
label.x, label.y, html.EscapeString(label.text))
}
}
// occultationGreatestTimeIsolineLabelPlacement 在最长的支路上取首个未被其它标签占用的标注点。
func occultationGreatestTimeIsolineLabelPlacement(
contour moon.OccultationGreatestTimeContour,
text string,
layout starOccultationSVGLayout,
placer *occultationLabelPlacer,
) (float64, float64, bool) {
longest := -1
for index, segment := range contour.Segments {
if longest < 0 || len(segment) > len(contour.Segments[longest]) {
longest = index
}
}
if longest < 0 {
return 0, 0, false
}
segment := contour.Segments[longest]
frame := layout.mapFrame()
middle := len(segment) / 2
for offset := 0; offset <= middle; offset++ {
for _, index := range []int{middle + offset, middle - offset} {
if index < 0 || index >= len(segment) {
continue
}
point := segment[index]
x, y, visible := frame.Project(point.Longitude, point.Latitude)
if !visible ||
x < frame.X+30 || x > frame.X+frame.Width-30 ||
y < frame.Y+18 || y > frame.Y+frame.Height-18 {
continue
}
// 优先压在等时线上方,其次下方;两者都被占用就换下一个候选点。
for _, baseline := range []float64{y - 6, y + 13} {
if placer.place(x, baseline, "middle", text, 9, 2) {
return x, baseline, true
}
}
}
}
return 0, 0, false
}