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
|
|||
|
|
}
|