72460fa228
- 将星表名称数据收敛为 TSV/GZIP 单一数据源,补充中文名、别名、英文常用名及宿/垣区域字段 - 增加星表名称表加载校验、旧详情字段一致性校验和 GZIP 漂移检查 - 新增 StarDataByName、StarDataByNameAll、StarDataByNameIn ,统一中英文查询与重名消歧 - 优化日月食半影带、升落曲线和可见性回归测试,补充极区、拓扑及低精度场景覆盖 - 增加 Go 1.16/386 兼容门禁和测试案例,处理旧工具链的分配、拓扑、SVG字节及 ULP 差异 - 补充星表、坐标、日月食和低精度回归用例,完善测试文件与缓存忽略规则
109 lines
3.1 KiB
Go
109 lines
3.1 KiB
Go
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
|
||
}
|