1 /*
2  * This file is part of Siril, an astronomy image processor.
3  * Copyright (C) 2005-2011 Francois Meyer (dulle at free.fr)
4  * Copyright (C) 2012-2021 team free-astro (see more in AUTHORS file)
5  * Reference site is https://free-astro.org/index.php/Siril
6  *
7  * Siril is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Siril is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Siril. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <math.h>
22 
23 #include "core/siril.h"
24 #include "core/proto.h"
25 #include "core/siril_world_cs.h"
26 #include "core/siril_app_dirs.h"
27 #include "core/siril_log.h"
28 #include "gui/utils.h"
29 #include "algos/siril_wcs.h"
30 #include "gui/image_display.h"
31 
32 #include "annotate.h"
33 
34 static GSList *siril_catalogue_list = NULL;
35 static gboolean show_catalog(const gchar *catalog);
36 
37 /* set a tolerance for "same object" test, in degree */
38 #define TOLERANCE 20.0 / 3600.0;
39 
40 static const gchar *cat[] = {
41 		"messier.txt",
42 		"ngc.txt",
43 		"ic.txt",
44 		"ldn.txt",
45 		"sh2.txt",
46 		"stars.txt"
47 };
48 
49 struct _CatalogObjects {
50 	gchar *code;
51 	gdouble ra;
52 	gdouble dec;
53 	gdouble radius;
54 	gchar *name;
55 	const gchar *catalogue;
56 };
57 
new_catalog_object(gchar * code,gdouble ra,gdouble dec,gdouble radius,gchar * name,const gchar * catalogue)58 static CatalogObjects *new_catalog_object(gchar *code, gdouble ra, gdouble dec, gdouble radius, gchar *name, const gchar *catalogue) {
59 	CatalogObjects *object = g_new(CatalogObjects, 1);
60 	object->code = g_strdup(code);
61 	object->ra = ra;
62 	object->dec = dec;
63 	object->radius = radius;
64 	object->name = g_strdup(name);
65 	object->catalogue = catalogue;
66 	return object;
67 }
68 
is_inside(double circle_x,double circle_y,double rad,double x,double y)69 gboolean is_inside(double circle_x, double circle_y, double rad, double x, double y) {
70 	// Compare radius of circle with distance
71 	// of its center from given point
72 	if ((x - circle_x) * (x - circle_x) + (y - circle_y) * (y - circle_y)
73 			<= rad * rad)
74 		return TRUE;
75 	else
76 		return FALSE;
77 }
78 
already_exist(GSList * list,CatalogObjects * obj)79 static gboolean already_exist(GSList *list, CatalogObjects *obj) {
80 	/* we exclude from the check the star catalogue */
81 	if (!g_strcmp0(obj->catalogue, "stars.txt") || (obj->catalogue == NULL)) {
82 		return FALSE;
83 	}
84 	for (GSList *l = list; l; l = l->next) {
85 		gdouble cur_dec = ((CatalogObjects*) l->data)->dec;
86 		gdouble cur_ra = ((CatalogObjects*) l->data)->ra;
87 
88 		double minDec = cur_dec - TOLERANCE;
89 		double maxDec = cur_dec + TOLERANCE;
90 
91 		double minRa = cur_ra - TOLERANCE;
92 		double maxRa = cur_ra + TOLERANCE;
93 
94 		/* compare */
95 		if (obj->dec > minDec && obj->dec < maxDec && obj->ra > minRa
96 				&& obj->ra < maxRa) {
97 			return TRUE;
98 		}
99 	}
100 	return FALSE;
101 }
102 
load_catalog(const gchar * catalogue)103 static GSList *load_catalog(const gchar *catalogue) {
104 	GFile *file;
105 	gchar *line;
106 	GSList *list = NULL;
107 	GError *error = NULL;
108 
109 	file = g_file_new_build_filename(siril_get_system_data_dir(), "catalogue", catalogue, NULL);
110 	GInputStream *input_stream = (GInputStream *)g_file_read(file, NULL, &error);
111 
112 	if (input_stream == NULL) {
113 		if (error != NULL) {
114 			g_clear_error(&error);
115 			siril_log_message(_("File [%s] does not exist\n"), g_file_peek_path(file));
116 		}
117 		g_object_unref(file);
118 		return NULL;
119 	}
120 
121 	GDataInputStream *data_input = g_data_input_stream_new(input_stream);
122 	while ((line = g_data_input_stream_read_line_utf8(data_input, NULL,
123 				NULL, NULL))) {
124 		if (g_str_has_prefix (line, "Code")) {
125 			g_free(line);
126 			continue;
127 		}
128 		gchar **token = g_strsplit(line, "\t", -1);
129 
130 		CatalogObjects *object = new_catalog_object(
131 				g_strdup(token[0]),
132 				g_ascii_strtod(token[1], NULL) * 15.0,
133 				g_strcmp0(token[2], "-") ? g_ascii_strtod(token[3], NULL) :	g_ascii_strtod(token[3], NULL) * -1.0,
134 				g_ascii_strtod(token[4], NULL) * 0.5,
135 				g_strdup(token[6]),
136 				catalogue);
137 
138 		list = g_slist_prepend(list, (gpointer) object);
139 
140 		g_strfreev(token);
141 		g_free(line);
142 	}
143 	list = g_slist_reverse(list);
144 
145 	g_object_unref(data_input);
146 	g_object_unref(input_stream);
147 	g_object_unref(file);
148 	return list;
149 }
150 
load_all_catalogues()151 static void load_all_catalogues() {
152 	for (int i = 0; i < G_N_ELEMENTS(cat); i++) {
153 		siril_catalogue_list = g_slist_concat(siril_catalogue_list, load_catalog(cat[i]));
154 	}
155 }
156 
get_siril_catalogue_list()157 static GSList *get_siril_catalogue_list() {
158 	return siril_catalogue_list;
159 }
160 
is_catalogue_loaded()161 static gboolean is_catalogue_loaded() {
162 	return siril_catalogue_list != NULL;
163 }
164 
find_objects(fits * fit)165 GSList *find_objects(fits *fit) {
166 	if (!has_wcs(fit)) return NULL;
167 	GSList *targets = NULL;
168 	gdouble x1, y1, x2, y2;
169 	double *crval;
170 	double resolution;
171 
172 	crval = get_wcs_crval(fit);
173 	resolution = get_wcs_image_resolution(fit);
174 
175 	if (crval == NULL) return NULL;
176 	if (crval[0] == 0.0 && crval[1] == 0.0) return NULL;
177 	if (resolution <= 0.0) return NULL;
178 
179 	/* get center of the image */
180 	x1 = crval[0];
181 	y1 = crval[1];
182 
183 	/* get radius of the fov */
184 	x2 = x1 + fit->rx * resolution;
185 	y2 = y1 + fit->ry * resolution;
186 
187 	if (!is_catalogue_loaded())
188 		load_all_catalogues();
189 	GSList *list = get_siril_catalogue_list();
190 
191 	for (GSList *l = list; l; l = l->next) {
192 		CatalogObjects *cur = (CatalogObjects *)l->data;
193 		if (cur->catalogue && !show_catalog(cur->catalogue)) continue;
194 
195 		/* Search for objects in the circle of radius defined by the image */
196 		if (is_inside(x1, y1, sqrt(pow((x2 - x1), 2) + pow((y2 - y1), 2)),
197 				cur->ra, cur->dec)) {
198 			if (!already_exist(targets, cur)) {
199 				CatalogObjects *new_object = new_catalog_object(cur->code, cur->ra, cur->dec, cur->radius, cur->name, cur->catalogue);
200 				targets = g_slist_prepend(targets, new_object);
201 			}
202 		}
203 	}
204 
205 	if (targets) {
206 		targets = g_slist_reverse(targets);
207 	}
208 	return targets;
209 }
210 
add_object_in_catalogue(gchar * code,SirilWorldCS * wcs)211 void add_object_in_catalogue(gchar *code, SirilWorldCS *wcs) {
212 	if (!is_catalogue_loaded())
213 		load_all_catalogues();
214 
215 	CatalogObjects *new_object = new_catalog_object(code,
216 			siril_world_cs_get_alpha(wcs), siril_world_cs_get_delta(wcs), 0,
217 			NULL, NULL);
218 	/* We need to add it at the end of the list, if not double rejection could reject it */
219 	siril_catalogue_list = g_slist_append(siril_catalogue_list, new_object);
220 }
221 
get_catalogue_object_code(CatalogObjects * object)222 gchar *get_catalogue_object_code(CatalogObjects *object) {
223 	return object->code;
224 }
225 
get_catalogue_object_name(CatalogObjects * object)226 gchar *get_catalogue_object_name(CatalogObjects *object) {
227 	return object->name;
228 }
229 
get_catalogue_object_ra(CatalogObjects * object)230 gdouble get_catalogue_object_ra(CatalogObjects *object) {
231 	return object->ra;
232 }
233 
get_catalogue_object_dec(CatalogObjects * object)234 gdouble get_catalogue_object_dec(CatalogObjects *object) {
235 	return object->dec;
236 }
237 
get_catalogue_object_radius(CatalogObjects * object)238 gdouble get_catalogue_object_radius(CatalogObjects *object) {
239 	return object->radius;
240 }
241 
free_object(CatalogObjects * object)242 void free_object(CatalogObjects *object) {
243 	g_free(object->code);
244 	g_free(object->name);
245 	g_free(object);
246 }
247 
force_to_refresh_catalogue_list()248 void force_to_refresh_catalogue_list() {
249 	if (has_wcs(&gfit)) {
250 		if (com.found_object) {
251 			g_slist_free_full(com.found_object, (GDestroyNotify) free_object);
252 		}
253 		com.found_object = find_objects(&gfit);
254 	}
255 }
256 
257 /*** UI callbacks **/
258 
show_catalog(const gchar * catalog)259 static gboolean show_catalog(const gchar *catalog) {
260 	gchar *name = remove_ext_from_filename(catalog);
261 	gchar *widget = g_strdup_printf("check_button_%s", name);
262 	gboolean show = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lookup_widget(widget)));
263 
264 	g_free(name);
265 	g_free(widget);
266 
267 	return show;
268 }
269 
initialize_wcs_toggle_button()270 void initialize_wcs_toggle_button() {
271 	gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(lookup_widget("annotate_button")), FALSE);
272 }
273