feat: 完善星表查询与跨版本回归验证
- 将星表名称数据收敛为 TSV/GZIP 单一数据源,补充中文名、别名、英文常用名及宿/垣区域字段 - 增加星表名称表加载校验、旧详情字段一致性校验和 GZIP 漂移检查 - 新增 StarDataByName、StarDataByNameAll、StarDataByNameIn ,统一中英文查询与重名消歧 - 优化日月食半影带、升落曲线和可见性回归测试,补充极区、拓扑及低精度场景覆盖 - 增加 Go 1.16/386 兼容门禁和测试案例,处理旧工具链的分配、拓扑、SVG字节及 ULP 差异 - 补充星表、坐标、日月食和低精度回归用例,完善测试文件与缓存忽略规则
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
package basic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// star_catalog_names.tsv.gz 由同名 .tsv 压缩生成,测试校验两者一致。
|
||||
//
|
||||
//go:embed star_catalog_names.tsv.gz
|
||||
var starCatalogNamesCompressed []byte
|
||||
|
||||
// starCatalogNameHeader 单一星名表的列顺序,生成端与加载端必须一致。
|
||||
var starCatalogNameHeader = []string{"hr", "chinese", "aliases", "bayer_zh", "common", "cst", "cst_zh", "region"}
|
||||
|
||||
type starNameRecord struct {
|
||||
chinese string
|
||||
aliases []string
|
||||
bayerZH string
|
||||
common string
|
||||
cst string
|
||||
cstZH string
|
||||
region string
|
||||
}
|
||||
|
||||
func decodeStarNameTable(compressed []byte, starCount int) (map[uint16]starNameRecord, error) {
|
||||
reader, err := gzip.NewReader(bytes.NewReader(compressed))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open star name table: %w", err)
|
||||
}
|
||||
defer reader.Close()
|
||||
text, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read star name table: %w", err)
|
||||
}
|
||||
lines := strings.Split(string(text), "\n")
|
||||
if len(lines) == 0 || strings.TrimSpace(lines[0]) == "" {
|
||||
return nil, errors.New("empty star name table")
|
||||
}
|
||||
header := strings.Split(strings.TrimSuffix(lines[0], "\r"), "\t")
|
||||
if len(header) != len(starCatalogNameHeader) {
|
||||
return nil, fmt.Errorf("star name header has %d columns, want %d", len(header), len(starCatalogNameHeader))
|
||||
}
|
||||
for i, want := range starCatalogNameHeader {
|
||||
if header[i] != want {
|
||||
return nil, fmt.Errorf("star name header column %d = %q, want %q", i+1, header[i], want)
|
||||
}
|
||||
}
|
||||
names := make(map[uint16]starNameRecord)
|
||||
for index, line := range lines[1:] {
|
||||
line = strings.TrimSuffix(line, "\r")
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
fields := strings.Split(line, "\t")
|
||||
if len(fields) != len(starCatalogNameHeader) {
|
||||
return nil, fmt.Errorf("star name line %d has %d columns, want %d", index+2, len(fields), len(starCatalogNameHeader))
|
||||
}
|
||||
hr, err := strconv.ParseUint(fields[0], 10, 16)
|
||||
if err != nil || hr == 0 || int(hr) > starCount {
|
||||
return nil, fmt.Errorf("star name line %d has invalid HR %q", index+2, fields[0])
|
||||
}
|
||||
key := uint16(hr)
|
||||
if _, ok := names[key]; ok {
|
||||
return nil, fmt.Errorf("star name line %d repeats HR %d", index+2, key)
|
||||
}
|
||||
chinese := fields[1]
|
||||
region := fields[7]
|
||||
rec := starNameRecord{
|
||||
chinese: chinese,
|
||||
aliases: splitStarAliases(fields[2]),
|
||||
bayerZH: fields[3],
|
||||
common: fields[4],
|
||||
cst: fields[5],
|
||||
cstZH: fields[6],
|
||||
region: region,
|
||||
}
|
||||
if chinese == "" && len(rec.aliases) == 0 && rec.bayerZH == "" && rec.common == "" &&
|
||||
rec.cst == "" && rec.cstZH == "" && rec.region == "" {
|
||||
continue
|
||||
}
|
||||
names[key] = rec
|
||||
}
|
||||
if len(names) == 0 {
|
||||
return nil, errors.New("empty star name table")
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func splitStarAliases(value string) []string {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
return nil
|
||||
}
|
||||
parts := strings.FieldsFunc(value, func(r rune) bool { return r == ';' || r == ';' })
|
||||
out := make([]string, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
if part = strings.TrimSpace(part); part != "" {
|
||||
out = append(out, part)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
Reference in New Issue
Block a user