Skip to content

Commit 1ad6ee1

Browse files
feat: add IndexCommand &cobra.Command{} RunIndexer to internal indexer module in @observerly/skysolve
feat: add IndexCommand &cobra.Command{} RunIndexer to internal indexer module in @observerly/skysolve
1 parent 161aaac commit 1ad6ee1

File tree

2 files changed

+195
-0
lines changed

2 files changed

+195
-0
lines changed

indexes/.gitkeep

Whitespace-only changes.

internal/indexer/indexer.go

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/*****************************************************************************************************************/
2+
3+
// @author Michael Roberts <[email protected]>
4+
// @package @observerly/skysolve
5+
// @license Copyright © 2021-2025 observerly
6+
7+
/*****************************************************************************************************************/
8+
9+
package indexer
10+
11+
/*****************************************************************************************************************/
12+
13+
import (
14+
"encoding/json"
15+
"fmt"
16+
"log"
17+
"os"
18+
"os/signal"
19+
"path/filepath"
20+
"strings"
21+
"syscall"
22+
23+
"github.com/observerly/skysolve/pkg/catalog"
24+
"github.com/observerly/skysolve/pkg/healpix"
25+
"github.com/observerly/skysolve/pkg/index"
26+
"github.com/spf13/cobra"
27+
)
28+
29+
/*****************************************************************************************************************/
30+
31+
var (
32+
NSide int
33+
Scheme string
34+
)
35+
36+
/*****************************************************************************************************************/
37+
38+
var IndexCommand = &cobra.Command{
39+
Use: "indexer",
40+
Short: "indexer",
41+
Long: "indexer",
42+
Run: func(cmd *cobra.Command, args []string) {
43+
var scheme healpix.Scheme
44+
45+
// Extract the scheme from the user input:
46+
switch strings.ToUpper(Scheme) {
47+
case "NESTED":
48+
// Set the scheme to the nested scheme:
49+
scheme = healpix.NESTED
50+
case "RING":
51+
// Set the scheme to the ring scheme:
52+
scheme = healpix.RING
53+
default:
54+
// Set the scheme to the nested scheme by default:
55+
scheme = healpix.NESTED
56+
}
57+
58+
params := RunIndexerParams{
59+
NSide: NSide,
60+
Scheme: scheme,
61+
}
62+
63+
// Attempt to run the indexer with the given parameters:
64+
err := RunIndexer(params)
65+
if err != nil {
66+
fmt.Printf("Error: %v\n", err)
67+
return
68+
}
69+
},
70+
}
71+
72+
/*****************************************************************************************************************/
73+
74+
func init() {
75+
// Add the nside flag tp the indexer command for the number of sides for the HealPIX scheme:
76+
// example usage: --nside 256 or -n 256
77+
IndexCommand.Flags().IntVarP(
78+
&NSide,
79+
"nside",
80+
"n",
81+
2,
82+
"The number of sides for the HealPIX scheme",
83+
)
84+
IndexCommand.MarkFlagRequired("nside")
85+
86+
// Add the scheme flag to the indexer command for the HealPIX scheme:
87+
// example usage: --scheme NESTED or -s NESTED
88+
IndexCommand.Flags().StringVarP(
89+
&Scheme,
90+
"scheme",
91+
"s",
92+
"NESTED",
93+
"The HealPIX scheme to use",
94+
)
95+
}
96+
97+
/*****************************************************************************************************************/
98+
99+
// Track created files for rollback
100+
var createdFilePaths []string
101+
102+
/*****************************************************************************************************************/
103+
104+
type RunIndexerParams struct {
105+
NSide int
106+
Scheme healpix.Scheme
107+
}
108+
109+
/*****************************************************************************************************************/
110+
111+
func RunIndexer(params RunIndexerParams) error {
112+
// Create a new SIMBAD service client:
113+
service := catalog.NewCatalogService(catalog.GAIA, catalog.Params{
114+
Limit: 8, // Limit the number of records to 8
115+
Threshold: 16, // Limiting Magntiude, filter out any stars that are magnitude 16 or above (fainter)
116+
})
117+
118+
// Number of sides for the HealPIX:
119+
sides := params.NSide
120+
121+
// Create a new HealPIX instance with the given sides:
122+
healPix := healpix.NewHealPIX(sides, params.Scheme)
123+
124+
// Get the number of pixels for the given sides:
125+
pixels := healPix.GetNumberOfPixels()
126+
127+
// Setup signal handling for cleanup on interrupt:
128+
signalChannel := make(chan os.Signal, 1)
129+
130+
// Listen for interrupt signals on the SIGTERM system call, or os.Interrupt:
131+
signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM)
132+
133+
// Listen for interrupt signal in a concurrent goroutine, and rollback if received:
134+
go func() {
135+
<-signalChannel
136+
fmt.Println("\nInterrupt received. Rolling back...")
137+
rollback(createdFilePaths)
138+
os.Exit(1)
139+
}()
140+
141+
// Create a new indexer instance:
142+
indexer := index.NewIndexer(*healPix, *service)
143+
144+
// Generate quads for each pixel in the HealPIX grid for the given sides parameter:
145+
for pixel := 0; pixel <= pixels; pixel++ {
146+
// Generate quads for the given pixel:
147+
quads, err := indexer.GenerateQuadsForPixel(pixel)
148+
149+
if err != nil {
150+
fmt.Printf("failed to generate quads: %v", err)
151+
return err
152+
}
153+
154+
quadsMarshalled, err := json.Marshal(quads)
155+
if err != nil {
156+
log.Fatal(err)
157+
}
158+
159+
// Create the directory structure: indexes/<SIDES>
160+
directoryPath := filepath.Join("indexes", fmt.Sprint(sides))
161+
if err := os.MkdirAll(directoryPath, 0755); err != nil {
162+
log.Fatal("Failed to create directory:", err)
163+
}
164+
165+
// File path: indexes/<SIDES>/<PIXEL_INDEX>.json
166+
filePath := filepath.Join(directoryPath, fmt.Sprintf("%d.json", pixel))
167+
168+
// Write the JSON data to the file
169+
if err := os.WriteFile(filePath, quadsMarshalled, 0644); err != nil {
170+
log.Fatal("Failed to write file:", err)
171+
}
172+
173+
createdFilePaths = append(createdFilePaths, filePath)
174+
175+
fmt.Printf("Quads saved to %s\n", filePath)
176+
}
177+
178+
// Return nil if the indexer ran successfully:
179+
return nil
180+
}
181+
182+
/*****************************************************************************************************************/
183+
184+
// rollbackFiles deletes created files in case of failure or interruption
185+
func rollback(filepaths []string) {
186+
for _, file := range filepaths {
187+
if err := os.Remove(file); err != nil {
188+
fmt.Printf("Warning: Failed to remove file %s: %v\n", file, err)
189+
} else {
190+
fmt.Printf("Rolled back: %s\n", file)
191+
}
192+
}
193+
}
194+
195+
/*****************************************************************************************************************/

0 commit comments

Comments
 (0)