1 /*
2   Gpredict: Real-time satellite tracking and orbit prediction program
3 
4   Copyright (C)  2001-2017  Alexandru Csete, OZ9AEC.
5   Copyright (C)  2006-2007  William J Beksi, KC2EXL.
6   Copyright (C)  2013       Charles Suprin,  AA1VS.
7 
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12 
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17 
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, visit http://www.fsf.org/
20 */
21 #ifdef HAVE_CONFIG_H
22 #include <build-config.h>
23 #endif
24 
25 #include <goocanvas.h>
26 #include <gtk/gtk.h>
27 #include <glib/gi18n.h>
28 #include <math.h>
29 #include <string.h>
30 
31 #include "compat.h"
32 #include "config-keys.h"
33 #include "gpredict-utils.h"
34 #include "gtk-sat-data.h"
35 #include "gtk-sat-map-popup.h"
36 #include "gtk-sat-map-ground-track.h"
37 #include "gtk-sat-map.h"
38 #include "locator.h"
39 #include "map-tools.h"
40 #include "mod-cfg-get-param.h"
41 #include "orbit-tools.h"
42 #include "predict-tools.h"
43 #include "sat-cfg.h"
44 #include "sat-info.h"
45 #include "sat-log.h"
46 #include "sgpsdp/sgp4sdp4.h"
47 #include "time-tools.h"
48 
49 #define MARKER_SIZE_HALF    1
50 
51 /* Update terminator every 30 seconds */
52 #define TERMINATOR_UPDATE_INTERVAL (15.0/86400.0)
53 
54 static void     gtk_sat_map_class_init(GtkSatMapClass * class);
55 static void     gtk_sat_map_init(GtkSatMap * polview);
56 static void     gtk_sat_map_destroy(GtkWidget * widget);
57 static void     size_allocate_cb(GtkWidget * widget,
58                                  GtkAllocation * allocation, gpointer data);
59 static void     update_map_size(GtkSatMap * satmap);
60 static void     update_sat(gpointer key, gpointer value, gpointer data);
61 static void     plot_sat(gpointer key, gpointer value, gpointer data);
62 static void     lonlat_to_xy(GtkSatMap * m, gdouble lon, gdouble lat,
63                              gfloat * x, gfloat * y);
64 static void     xy_to_lonlat(GtkSatMap * m, gfloat x, gfloat y, gfloat * lon,
65                              gfloat * lat);
66 static gboolean on_motion_notify(GooCanvasItem * item, GooCanvasItem * target,
67                                  GdkEventMotion * event, gpointer data);
68 static void     on_item_created(GooCanvas * canvas, GooCanvasItem * item,
69                                 GooCanvasItemModel * model, gpointer data);
70 static void     on_canvas_realized(GtkWidget * canvas, gpointer data);
71 static gboolean on_button_press(GooCanvasItem * item,
72                                 GooCanvasItem * target,
73                                 GdkEventButton * event, gpointer data);
74 static gboolean on_button_release(GooCanvasItem * item,
75                                   GooCanvasItem * target,
76                                   GdkEventButton * event, gpointer data);
77 static void     clear_selection(gpointer key, gpointer val, gpointer data);
78 static void     load_map_file(GtkSatMap * satmap, float clon);
79 static GooCanvasItemModel *create_canvas_model(GtkSatMap * satmap);
80 static gdouble  arccos(gdouble, gdouble);
81 static gboolean pole_is_covered(sat_t * sat);
82 static gboolean north_pole_is_covered(sat_t * sat);
83 static gboolean south_pole_is_covered(sat_t * sat);
84 static gboolean mirror_lon(sat_t * sat, gdouble rangelon, gdouble * mlon,
85                            gdouble mapbreak);
86 static guint    calculate_footprint(GtkSatMap * satmap, sat_t * sat);
87 static void     split_points(GtkSatMap * satmap, sat_t * sat, gdouble sspx);
88 static void     sort_points_x(GtkSatMap * satmap, sat_t * sat,
89                               GooCanvasPoints * points, gint num);
90 static void     sort_points_y(GtkSatMap * satmap, sat_t * sat,
91                               GooCanvasPoints * points, gint num);
92 static gint     compare_coordinates_x(gconstpointer a, gconstpointer b,
93                                       gpointer data);
94 static gint     compare_coordinates_y(gconstpointer a, gconstpointer b,
95                                       gpointer data);
96 static void     update_selected(GtkSatMap * satmap, sat_t * sat);
97 static void     draw_grid_lines(GtkSatMap * satmap, GooCanvasItemModel * root);
98 static void     redraw_grid_lines(GtkSatMap * satmap);
99 static void     draw_terminator(GtkSatMap * satmap, GooCanvasItemModel * root);
100 static void     redraw_terminator(GtkSatMap * satmap);
101 static gchar   *aoslos_time_to_str(GtkSatMap * satmap, sat_t * sat);
102 static void     gtk_sat_map_load_showtracks(GtkSatMap * map);
103 static void     gtk_sat_map_store_showtracks(GtkSatMap * satmap);
104 static void     gtk_sat_map_load_hide_coverages(GtkSatMap * map);
105 static void     gtk_sat_map_store_hidecovs(GtkSatMap * satmap);
106 static void     reset_ground_track(gpointer key, gpointer value,
107                                    gpointer user_data);
108 
109 static GtkVBoxClass *parent_class = NULL;
110 static GooCanvasPoints *points1;
111 static GooCanvasPoints *points2;
112 
113 
gtk_sat_map_get_type()114 GType gtk_sat_map_get_type()
115 {
116     static GType    gtk_sat_map_type = 0;
117 
118     if (!gtk_sat_map_type)
119     {
120         static const GTypeInfo gtk_sat_map_info = {
121             sizeof(GtkSatMapClass),
122             NULL,               /* base init */
123             NULL,               /* base finalize */
124             (GClassInitFunc) gtk_sat_map_class_init,
125             NULL,               /* class finalize */
126             NULL,               /* class data */
127             sizeof(GtkSatMap),
128             5,                  /* n_preallocs */
129             (GInstanceInitFunc) gtk_sat_map_init,
130             NULL
131         };
132 
133         gtk_sat_map_type = g_type_register_static(GTK_TYPE_BOX,
134                                                   "GtkSatMap",
135                                                   &gtk_sat_map_info, 0);
136     }
137 
138     return gtk_sat_map_type;
139 }
140 
gtk_sat_map_class_init(GtkSatMapClass * class)141 static void gtk_sat_map_class_init(GtkSatMapClass * class)
142 {
143     GtkWidgetClass *widget_class;
144 
145     widget_class = (GtkWidgetClass *) class;
146     widget_class->destroy = gtk_sat_map_destroy;
147     parent_class = g_type_class_peek_parent(class);
148 }
149 
gtk_sat_map_init(GtkSatMap * satmap)150 static void gtk_sat_map_init(GtkSatMap * satmap)
151 {
152     satmap->sats = NULL;
153     satmap->qth = NULL;
154     satmap->obj = NULL;
155     satmap->showtracks = g_hash_table_new_full(g_int_hash, g_int_equal,
156                                                NULL, NULL);
157     satmap->hidecovs = g_hash_table_new_full(g_int_hash, g_int_equal,
158                                              NULL, NULL);
159     satmap->naos = 0.0;
160     satmap->ncat = 0;
161     satmap->tstamp = 2458849.5;
162     satmap->x0 = 0;
163     satmap->y0 = 0;
164     satmap->width = 0;
165     satmap->height = 0;
166     satmap->refresh = 0;
167     satmap->counter = 0;
168     satmap->show_terminator = FALSE;
169     satmap->qthinfo = FALSE;
170     satmap->eventinfo = FALSE;
171     satmap->cursinfo = FALSE;
172     satmap->showgrid = FALSE;
173     satmap->keepratio = FALSE;
174     satmap->resize = FALSE;
175 }
176 
gtk_sat_map_destroy(GtkWidget * widget)177 static void gtk_sat_map_destroy(GtkWidget * widget)
178 {
179     gtk_sat_map_store_showtracks(GTK_SAT_MAP(widget));
180     gtk_sat_map_store_hidecovs(GTK_SAT_MAP(widget));
181     (*GTK_WIDGET_CLASS(parent_class)->destroy) (widget);
182 }
183 
gtk_sat_map_new(GKeyFile * cfgdata,GHashTable * sats,qth_t * qth)184 GtkWidget      *gtk_sat_map_new(GKeyFile * cfgdata, GHashTable * sats,
185                                 qth_t * qth)
186 {
187     GtkSatMap      *satmap;
188     GooCanvasItemModel *root;
189     guint32         col;
190 
191     satmap = g_object_new(GTK_TYPE_SAT_MAP, NULL);
192 
193     satmap->cfgdata = cfgdata;
194     satmap->sats = sats;
195     satmap->qth = qth;
196 
197     satmap->obj = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, NULL);
198 
199     satmap->refresh = mod_cfg_get_int(cfgdata,
200                                       MOD_CFG_MAP_SECTION,
201                                       MOD_CFG_MAP_REFRESH,
202                                       SAT_CFG_INT_MAP_REFRESH);
203     satmap->counter = 1;
204 
205     satmap->qthinfo = mod_cfg_get_bool(cfgdata,
206                                        MOD_CFG_MAP_SECTION,
207                                        MOD_CFG_MAP_SHOW_QTH_INFO,
208                                        SAT_CFG_BOOL_MAP_SHOW_QTH_INFO);
209 
210     satmap->eventinfo = mod_cfg_get_bool(cfgdata,
211                                          MOD_CFG_MAP_SECTION,
212                                          MOD_CFG_MAP_SHOW_NEXT_EVENT,
213                                          SAT_CFG_BOOL_MAP_SHOW_NEXT_EV);
214 
215     satmap->cursinfo = mod_cfg_get_bool(cfgdata,
216                                         MOD_CFG_MAP_SECTION,
217                                         MOD_CFG_MAP_SHOW_CURS_TRACK,
218                                         SAT_CFG_BOOL_MAP_SHOW_CURS_TRACK);
219 
220     satmap->showgrid = mod_cfg_get_bool(cfgdata,
221                                         MOD_CFG_MAP_SECTION,
222                                         MOD_CFG_MAP_SHOW_GRID,
223                                         SAT_CFG_BOOL_MAP_SHOW_GRID);
224 
225     satmap->keepratio = mod_cfg_get_bool(cfgdata,
226                                          MOD_CFG_MAP_SECTION,
227                                          MOD_CFG_MAP_KEEP_RATIO,
228                                          SAT_CFG_BOOL_MAP_KEEP_RATIO);
229     col = mod_cfg_get_int(cfgdata,
230                           MOD_CFG_MAP_SECTION,
231                           MOD_CFG_MAP_INFO_BGD_COL,
232                           SAT_CFG_INT_MAP_INFO_BGD_COL);
233 
234     satmap->infobgd = rgba2html(col);
235 
236     satmap->canvas = goo_canvas_new();
237     g_object_set(G_OBJECT(satmap->canvas), "has-tooltip", TRUE, NULL);
238 
239     float           clon = (float)mod_cfg_get_int(cfgdata,
240                                                   MOD_CFG_MAP_SECTION,
241                                                   MOD_CFG_MAP_CENTER,
242                                                   SAT_CFG_INT_MAP_CENTER);
243 
244     load_map_file(satmap, clon);
245 
246     /* Initial size request should be based on map size
247        but if we do this we can not shrink the canvas below this size
248        even though the map shrinks => better to set default size of
249        main container window.
250      */
251     /*  gtk_widget_set_size_request (satmap->canvas, */
252     /*  gdk_pixbuf_get_width (satmap->origmap), */
253     /*  gdk_pixbuf_get_height (satmap->origmap)); */
254 
255     goo_canvas_set_bounds(GOO_CANVAS(satmap->canvas), 0, 0,
256                           gdk_pixbuf_get_width(satmap->origmap),
257                           gdk_pixbuf_get_height(satmap->origmap));
258 
259     g_signal_connect(satmap->canvas, "size-allocate",
260                      G_CALLBACK(size_allocate_cb), satmap);
261     g_signal_connect(satmap->canvas, "item_created",
262                      (GCallback) on_item_created, satmap);
263     g_signal_connect_after(satmap->canvas, "realize",
264                            (GCallback) on_canvas_realized, satmap);
265 
266     gtk_widget_show(satmap->canvas);
267 
268     root = create_canvas_model(satmap);
269     goo_canvas_set_root_item_model(GOO_CANVAS(satmap->canvas), root);
270     g_object_unref(root);
271 
272     gtk_sat_map_load_showtracks(satmap);
273     gtk_sat_map_load_hide_coverages(satmap);
274     g_hash_table_foreach(satmap->sats, plot_sat, satmap);
275 
276     gtk_box_pack_start(GTK_BOX(satmap), satmap->canvas, TRUE, TRUE, 0);
277 
278     return GTK_WIDGET(satmap);
279 }
280 
create_canvas_model(GtkSatMap * satmap)281 static GooCanvasItemModel *create_canvas_model(GtkSatMap * satmap)
282 {
283     GooCanvasItemModel *root;
284     gchar          *buff;
285     gfloat          x, y;
286     guint32         col;
287 
288     root = goo_canvas_group_model_new(NULL, NULL);
289 
290     /* map dimensions */
291     satmap->width = 200;        // was: gdk_pixbuf_get_width (satmap->origmap);
292     satmap->height = 100;       // was: gdk_pixbuf_get_height (satmap->origmap);
293     satmap->x0 = 0;
294     satmap->y0 = 0;
295 
296     /* background map */
297     satmap->map = goo_canvas_image_model_new(root,
298                                              satmap->origmap,
299                                              satmap->x0, satmap->y0, NULL);
300 
301     goo_canvas_item_model_lower(satmap->map, NULL);
302     draw_grid_lines(satmap, root);
303 
304     if (satmap->show_terminator)
305         draw_terminator(satmap, root);
306 
307     /* QTH mark */
308     col = mod_cfg_get_int(satmap->cfgdata,
309                           MOD_CFG_MAP_SECTION,
310                           MOD_CFG_MAP_QTH_COL, SAT_CFG_INT_MAP_QTH_COL);
311     lonlat_to_xy(satmap, satmap->qth->lon, satmap->qth->lat, &x, &y);
312     satmap->qthmark = goo_canvas_rect_model_new(root,
313                                                 x - MARKER_SIZE_HALF,
314                                                 y - MARKER_SIZE_HALF,
315                                                 2 * MARKER_SIZE_HALF,
316                                                 2 * MARKER_SIZE_HALF,
317                                                 "fill-color-rgba", col,
318                                                 "stroke-color-rgba", col,
319                                                 NULL);
320     satmap->qthlabel = goo_canvas_text_model_new(root, satmap->qth->name,
321                                                  x, y + 2, -1,
322                                                  GOO_CANVAS_ANCHOR_NORTH,
323                                                  "font", "Sans 8",
324                                                  "fill-color-rgba", col, NULL);
325 
326     /* QTH info */
327     col = mod_cfg_get_int(satmap->cfgdata,
328                           MOD_CFG_MAP_SECTION,
329                           MOD_CFG_MAP_INFO_COL, SAT_CFG_INT_MAP_INFO_COL);
330 
331     satmap->locnam = goo_canvas_text_model_new(root, "",
332                                                satmap->x0 + 2, satmap->y0 + 1,
333                                                -1,
334                                                GOO_CANVAS_ANCHOR_NORTH_WEST,
335                                                "font", "Sans 8",
336                                                "fill-color-rgba", col,
337                                                "use-markup", TRUE, NULL);
338 
339     /* set text only if QTH info is enabled */
340     if (satmap->qthinfo)
341     {
342         /* For now only QTH name and location.
343            It would be nice with coordinates (remember NWSE setting)
344            and Maidenhead locator, when using hamlib.
345 
346            Note: I used pango markup to set the background color, I didn't find any
347            other obvious ways to get the text height in pixels to draw rectangle.
348          */
349         buff =
350             g_strdup_printf("<span background=\"#%s\"> %s \302\267 %s </span>",
351                             satmap->infobgd, satmap->qth->name,
352                             satmap->qth->loc);
353         g_object_set(satmap->locnam, "text", buff, NULL);
354         g_free(buff);
355     }
356 
357     /* next event */
358     satmap->next = goo_canvas_text_model_new(root, "",
359                                              satmap->x0 + satmap->width - 2,
360                                              satmap->y0 + 1, -1,
361                                              GOO_CANVAS_ANCHOR_NORTH_EAST,
362                                              "font", "Sans 8",
363                                              "fill-color-rgba", col,
364                                              "use-markup", TRUE, NULL);
365 
366     /* set text only if QTH info is enabled */
367     if (satmap->eventinfo)
368     {
369         buff = g_strdup_printf("<span background=\"#%s\"> ... </span>",
370                                satmap->infobgd);
371         g_object_set(satmap->next, "text", buff, NULL);
372         g_free(buff);
373     }
374 
375     /* cursor track */
376     satmap->curs = goo_canvas_text_model_new(root, "",
377                                              satmap->x0 + 2,
378                                              satmap->y0 + satmap->height - 1,
379                                              -1,
380                                              GOO_CANVAS_ANCHOR_SOUTH_WEST,
381                                              "font", "Sans 8",
382                                              "fill-color-rgba", col,
383                                              "use-markup", TRUE, NULL);
384 
385     /* info about a selected satellite */
386     satmap->sel = goo_canvas_text_model_new(root, "",
387                                             satmap->x0 + satmap->width - 2,
388                                             satmap->y0 + satmap->height - 1,
389                                             -1,
390                                             GOO_CANVAS_ANCHOR_SOUTH_EAST,
391                                             "font", "Sans 8",
392                                             "fill-color-rgba", col,
393                                             "use-markup", TRUE, NULL);
394 
395     return root;
396 }
397 
size_allocate_cb(GtkWidget * widget,GtkAllocation * allocation,gpointer data)398 static void size_allocate_cb(GtkWidget * widget, GtkAllocation * allocation,
399                              gpointer data)
400 {
401     (void)widget;
402     (void)allocation;
403     GTK_SAT_MAP(data)->resize = TRUE;
404 }
405 
update_map_size(GtkSatMap * satmap)406 static void update_map_size(GtkSatMap * satmap)
407 {
408     GtkAllocation   allocation;
409     GdkPixbuf      *pbuf;
410     gfloat          x, y;
411     gfloat          ratio;      /* ratio between map width and height */
412     gfloat          size;       /* size = min (alloc.w, ratio*alloc.h) */
413 
414     if (gtk_widget_get_realized(GTK_WIDGET(satmap)))
415     {
416         /* get graph dimensions */
417         gtk_widget_get_allocation(GTK_WIDGET(satmap), &allocation);
418 
419         if (satmap->keepratio)
420         {
421             /* Use allocation->width and allocation->height to calculate
422              *  new X0 Y0 width and height. Map proportions must be kept.
423              */
424             ratio = gdk_pixbuf_get_width(satmap->origmap) /
425                 gdk_pixbuf_get_height(satmap->origmap);
426 
427             size = MIN(allocation.width, ratio * allocation.height);
428 
429             satmap->width = (guint) size;
430             satmap->height = (guint) (size / ratio);
431 
432             satmap->x0 = (allocation.width - satmap->width) / 2;
433             satmap->y0 = (allocation.height - satmap->height) / 2;
434 
435             /* rescale pixbuf */
436             pbuf = gdk_pixbuf_scale_simple(satmap->origmap,
437                                            satmap->width,
438                                            satmap->height,
439                                            GDK_INTERP_BILINEAR);
440         }
441         else
442         {
443             satmap->x0 = 0;
444             satmap->y0 = 0;
445             satmap->width = allocation.width;
446             satmap->height = allocation.height;
447 
448             /* rescale pixbuf */
449             pbuf = gdk_pixbuf_scale_simple(satmap->origmap,
450                                            satmap->width,
451                                            satmap->height,
452                                            GDK_INTERP_BILINEAR);
453         }
454 
455         /* set canvas bounds to match new size */
456         goo_canvas_set_bounds(GOO_CANVAS(GTK_SAT_MAP(satmap)->canvas), 0, 0,
457                               satmap->width, satmap->height);
458 
459 
460         /* redraw static elements */
461         g_object_set(satmap->map,
462                      "pixbuf", pbuf,
463                      "x", (gdouble) satmap->x0,
464                      "y", (gdouble) satmap->y0, NULL);
465         g_object_unref(pbuf);
466 
467         redraw_grid_lines(satmap);
468 
469         if (satmap->show_terminator)
470             redraw_terminator(satmap);
471 
472         lonlat_to_xy(satmap, satmap->qth->lon, satmap->qth->lat, &x, &y);
473         g_object_set(satmap->qthmark,
474                      "x", x - MARKER_SIZE_HALF,
475                      "y", y - MARKER_SIZE_HALF, NULL);
476         g_object_set(satmap->qthlabel, "x", x, "y", y + 2, NULL);
477 
478         g_object_set(satmap->locnam,
479                      "x", (gdouble) satmap->x0 + 2,
480                      "y", (gdouble) satmap->y0 + 1, NULL);
481 
482         g_object_set(satmap->next,
483                      "x", (gdouble) satmap->x0 + satmap->width - 2,
484                      "y", (gdouble) satmap->y0 + 1, NULL);
485 
486         g_object_set(satmap->curs,
487                      "x", (gdouble) satmap->x0 + 2,
488                      "y", (gdouble) satmap->y0 + satmap->height - 1, NULL);
489 
490         g_object_set(satmap->sel,
491                      "x", (gdouble) satmap->x0 + satmap->width - 2,
492                      "y", (gdouble) satmap->y0 + satmap->height - 1, NULL);
493 
494         g_hash_table_foreach(satmap->sats, update_sat, satmap);
495         satmap->resize = FALSE;
496     }
497 }
498 
on_canvas_realized(GtkWidget * canvas,gpointer data)499 static void on_canvas_realized(GtkWidget * canvas, gpointer data)
500 {
501     GtkSatMap      *satmap = GTK_SAT_MAP(data);
502 
503     (void)canvas;
504 
505     goo_canvas_item_model_raise(satmap->sel, NULL);
506     goo_canvas_item_model_raise(satmap->locnam, NULL);
507     goo_canvas_item_model_raise(satmap->next, NULL);
508     goo_canvas_item_model_raise(satmap->curs, NULL);
509 }
510 
511 /* Update the GtkSatMap widget. Called periodically from GtkSatModule. */
gtk_sat_map_update(GtkWidget * widget)512 void gtk_sat_map_update(GtkWidget * widget)
513 {
514     GtkSatMap      *satmap = GTK_SAT_MAP(widget);
515     sat_t          *sat = NULL;
516     gdouble         number, now;
517     gchar          *buff;
518     gint           *catnr;
519     guint           h, m, s;
520     gchar          *ch, *cm, *cs;
521     gfloat          x, y;
522     gdouble         oldx, oldy;
523 
524     /* check whether there are any pending resize requests */
525     if (satmap->resize)
526         update_map_size(satmap);
527 
528     /* check if qth has moved significantly, if so move it */
529     lonlat_to_xy(satmap, satmap->qth->lon, satmap->qth->lat, &x, &y);
530     g_object_get(satmap->qthmark, "x", &oldx, "y", &oldy, NULL);
531 
532     if ((fabs(oldx - x) >= 2 * MARKER_SIZE_HALF) ||
533         (fabs(oldy - y) >= 2 * MARKER_SIZE_HALF))
534     {
535         g_object_set(satmap->qthmark,
536                      "x", x - MARKER_SIZE_HALF,
537                      "y", y - MARKER_SIZE_HALF, NULL);
538         g_object_set(satmap->qthlabel, "x", x, "y", y + 2, NULL);
539         g_object_set(satmap->locnam,
540                      "x", (gdouble) satmap->x0 + 2,
541                      "y", (gdouble) satmap->y0 + 1, NULL);
542         satmap->counter = satmap->refresh;
543     }
544 
545     /* check refresh rate and refresh sats/qth if time */
546     /* FIXME add location check */
547     if (satmap->counter < satmap->refresh)
548     {
549         satmap->counter++;
550     }
551     else
552     {
553         /* reset data */
554         satmap->counter = 1;
555         satmap->naos = 0.0;
556         satmap->ncat = 0;
557 
558         lonlat_to_xy(satmap, satmap->qth->lon, satmap->qth->lat, &x, &y);
559         g_object_set(satmap->qthmark,
560                      "x", x - MARKER_SIZE_HALF,
561                      "y", y - MARKER_SIZE_HALF, NULL);
562         g_object_set(satmap->qthlabel, "x", x, "y", y + 2, NULL);
563         g_object_set(satmap->locnam,
564                      "x", (gdouble) satmap->x0 + 2,
565                      "y", (gdouble) satmap->y0 + 1, NULL);
566 
567         g_hash_table_foreach(satmap->sats, update_sat, satmap);
568 
569         /* Update the Solar Terminator if necessary */
570         if (satmap->show_terminator &&
571             fabs(satmap->tstamp - satmap->terminator_last_tstamp) >
572             TERMINATOR_UPDATE_INTERVAL)
573         {
574             satmap->terminator_last_tstamp = satmap->tstamp;
575             redraw_terminator(satmap);
576         }
577 
578         if (satmap->eventinfo)
579         {
580             if (satmap->ncat > 0)
581             {
582                 catnr = g_try_new0(gint, 1);
583                 *catnr = satmap->ncat;
584                 sat = SAT(g_hash_table_lookup(satmap->sats, catnr));
585                 g_free(catnr);
586 
587                 /* last desperate sanity check */
588                 if (sat != NULL)
589                 {
590                     now = satmap->tstamp;       //get_current_daynum ();
591                     number = satmap->naos - now;
592 
593                     /* convert julian date to seconds */
594                     s = (guint) (number * 86400);
595 
596                     /* extract hours */
597                     h = (guint) floor(s / 3600);
598                     s -= 3600 * h;
599 
600                     /* leading zero */
601                     if ((h > 0) && (h < 10))
602                         ch = g_strdup("0");
603                     else
604                         ch = g_strdup("");
605 
606                     /* extract minutes */
607                     m = (guint) floor(s / 60);
608                     s -= 60 * m;
609 
610                     /* leading zero */
611                     if (m < 10)
612                         cm = g_strdup("0");
613                     else
614                         cm = g_strdup("");
615 
616                     /* leading zero */
617                     if (s < 10)
618                         cs = g_strdup(":0");
619                     else
620                         cs = g_strdup(":");
621 
622                     if (h > 0)
623                         buff =
624                             g_markup_printf_escaped(_
625                                                     ("<span background=\"#%s\"> "
626                                                      "Next: %s in %s%d:%s%d%s%d </span>"),
627                                                     satmap->infobgd,
628                                                     sat->nickname, ch, h, cm,
629                                                     m, cs, s);
630                     else
631                         buff =
632                             g_markup_printf_escaped(_
633                                                     ("<span background=\"#%s\"> "
634                                                      "Next: %s in %s%d%s%d </span>"),
635                                                     satmap->infobgd,
636                                                     sat->nickname, cm, m, cs,
637                                                     s);
638 
639                     g_object_set(satmap->next, "text", buff, NULL);
640 
641                     g_free(buff);
642                     g_free(ch);
643                     g_free(cm);
644                     g_free(cs);
645                 }
646                 else
647                 {
648                     sat_log_log(SAT_LOG_LEVEL_ERROR,
649                                 _("%s: Can not find NEXT satellite."),
650                                 __func__);
651                     g_object_set(satmap->next, "text", _("Next: ERR"), NULL);
652                 }
653             }
654             else
655             {
656                 g_object_set(satmap->next, "text", _("Next: N/A"), NULL);
657             }
658         }
659         else
660         {
661             g_object_set(satmap->next, "text", "", NULL);
662         }
663     }
664 }
665 
666 /* Assumes that -180 <= lon <= 180 and -90 <= lat <= 90 */
lonlat_to_xy(GtkSatMap * p,gdouble lon,gdouble lat,gfloat * x,gfloat * y)667 static void lonlat_to_xy(GtkSatMap * p, gdouble lon, gdouble lat, gfloat * x,
668                          gfloat * y)
669 {
670     *x = p->x0 + (lon - p->left_side_lon) * p->width / 360.0;
671     *y = p->y0 + (90.0 - lat) * p->height / 180.0;
672     while (*x < 0)
673     {
674         *x += p->width;
675     }
676     while (*x > p->width)
677     {
678         *x -= p->width;
679     }
680 }
681 
xy_to_lonlat(GtkSatMap * p,gfloat x,gfloat y,gfloat * lon,gfloat * lat)682 static void xy_to_lonlat(GtkSatMap * p, gfloat x, gfloat y, gfloat * lon,
683                          gfloat * lat)
684 {
685     *lat = 90.0 - (180.0 / p->height) * (y - p->y0);
686     *lon = (360.0 / p->width) * (x - p->x0) + p->left_side_lon;
687     while (*lon > 180)
688     {
689         *lon -= 360;
690     }
691     while (*lon < -180)
692     {
693         *lon += 360;
694     }
695 }
696 
on_motion_notify(GooCanvasItem * item,GooCanvasItem * target,GdkEventMotion * event,gpointer data)697 static gboolean on_motion_notify(GooCanvasItem * item,
698                                  GooCanvasItem * target,
699                                  GdkEventMotion * event, gpointer data)
700 {
701     GtkSatMap      *satmap = GTK_SAT_MAP(data);
702     gfloat          lat, lon;
703     gchar          *text;
704 
705     (void)target;
706     (void)item;
707 
708     /* set text only if QTH info is enabled */
709     if (satmap->cursinfo)
710     {
711         xy_to_lonlat(satmap, event->x, event->y, &lon, &lat);
712 
713         /* cursor track */
714         text = g_strdup_printf("<span background=\"#%s\"> "
715                                "LON:%.0f\302\260 LAT:%.0f\302\260 </span>",
716                                satmap->infobgd, lon, lat);
717 
718         g_object_set(satmap->curs, "text", text, NULL);
719         g_free(text);
720     }
721 
722     return TRUE;
723 }
724 
725 /*
726  * Finish canvas item setup.
727  *
728  * This function is called when a canvas item is created. Its purpose is to connect
729  * the corresponding signals to the created items.
730  *
731  * The root item, ie the background is connected to motion notify event, while other
732  * items (sats) are connected to mouse click events.
733  *
734  * @bug Should filter out QTH or do we want to click on the QTH?
735  */
on_item_created(GooCanvas * canvas,GooCanvasItem * item,GooCanvasItemModel * model,gpointer data)736 static void on_item_created(GooCanvas * canvas,
737                             GooCanvasItem * item,
738                             GooCanvasItemModel * model, gpointer data)
739 {
740     (void)canvas;
741     if (!goo_canvas_item_model_get_parent(model))
742     {
743         /* root item / canvas */
744         g_signal_connect(item, "motion_notify_event",
745                          (GCallback) on_motion_notify, data);
746     }
747     else if (!g_object_get_data(G_OBJECT(item), "skip-signal-connection"))
748     {
749         g_signal_connect(item, "button_press_event",
750                          (GCallback) on_button_press, data);
751         g_signal_connect(item, "button_release_event",
752                          (GCallback) on_button_release, data);
753     }
754 }
755 
on_button_press(GooCanvasItem * item,GooCanvasItem * target,GdkEventButton * event,gpointer data)756 static gboolean on_button_press(GooCanvasItem * item,
757                                 GooCanvasItem * target, GdkEventButton * event,
758                                 gpointer data)
759 {
760     GooCanvasItemModel *model = goo_canvas_item_get_model(item);
761     GtkSatMap      *satmap = GTK_SAT_MAP(data);
762     gint            catnum =
763         GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model), "catnum"));
764     gint           *catpoint = NULL;
765     sat_t          *sat = NULL;
766 
767     (void)target;
768 
769     switch (event->button)
770     {
771         /* double-left-click */
772     case 1:
773         if (event->type == GDK_2BUTTON_PRESS)
774         {
775             catpoint = g_try_new0(gint, 1);
776             *catpoint = catnum;
777 
778             sat = SAT(g_hash_table_lookup(satmap->sats, catpoint));
779             if (sat != NULL)
780             {
781                 show_sat_info(sat, gtk_widget_get_toplevel(GTK_WIDGET(data)));
782             }
783             else
784             {
785                 /* double-clicked on map */
786             }
787         }
788         g_free(catpoint);
789         break;
790 
791         /* pop-up menu */
792     case 3:
793         catpoint = g_try_new0(gint, 1);
794         *catpoint = catnum;
795         sat = SAT(g_hash_table_lookup(satmap->sats, catpoint));
796         if (sat != NULL)
797         {
798             gtk_sat_map_popup_exec(sat, satmap->qth, satmap, event,
799                                    gtk_widget_get_toplevel(GTK_WIDGET
800                                                            (satmap)));
801         }
802         else
803         {
804             /* clicked on map -> map pop-up in the future */
805         }
806         g_free(catpoint);
807         break;
808     default:
809         break;
810     }
811 
812     return TRUE;
813 }
814 
on_button_release(GooCanvasItem * item,GooCanvasItem * target,GdkEventButton * event,gpointer data)815 static gboolean on_button_release(GooCanvasItem * item,
816                                   GooCanvasItem * target,
817                                   GdkEventButton * event, gpointer data)
818 {
819     GooCanvasItemModel *model = goo_canvas_item_get_model(item);
820     GtkSatMap      *satmap = GTK_SAT_MAP(data);
821     gint            catnum =
822         GPOINTER_TO_INT(g_object_get_data(G_OBJECT(model), "catnum"));
823     gint           *catpoint = NULL;
824     sat_map_obj_t  *obj = NULL;
825     guint32         col;
826 
827     (void)target;
828 
829     catpoint = g_try_new0(gint, 1);
830     *catpoint = catnum;
831 
832     switch (event->button)
833     {
834         /* Select / de-select satellite */
835     case 1:
836         obj = SAT_MAP_OBJ(g_hash_table_lookup(satmap->obj, catpoint));
837         if (obj == NULL)
838         {
839             sat_log_log(SAT_LOG_LEVEL_ERROR,
840                         _
841                         ("%s:%d: Can not find clicked object (%d) in hash table"),
842                         __FILE__, __LINE__, catnum);
843         }
844         else
845         {
846             obj->selected = !obj->selected;
847 
848             if (obj->selected)
849             {
850                 col = mod_cfg_get_int(satmap->cfgdata,
851                                       MOD_CFG_MAP_SECTION,
852                                       MOD_CFG_MAP_SAT_SEL_COL,
853                                       SAT_CFG_INT_MAP_SAT_SEL_COL);
854             }
855             else
856             {
857                 col = mod_cfg_get_int(satmap->cfgdata,
858                                       MOD_CFG_MAP_SECTION,
859                                       MOD_CFG_MAP_SAT_COL,
860                                       SAT_CFG_INT_MAP_SAT_COL);
861                 *catpoint = 0;
862 
863                 g_object_set(satmap->sel, "text", "", NULL);
864             }
865 
866             g_object_set(obj->marker,
867                          "fill-color-rgba", col,
868                          "stroke-color-rgba", col, NULL);
869             g_object_set(obj->label,
870                          "fill-color-rgba", col,
871                          "stroke-color-rgba", col, NULL);
872             g_object_set(obj->range1, "stroke-color-rgba", col, NULL);
873 
874             if (obj->oldrcnum == 2)
875                 g_object_set(obj->range2, "stroke-color-rgba", col, NULL);
876 
877             /* clear other selections */
878             g_hash_table_foreach(satmap->obj, clear_selection, catpoint);
879         }
880         break;
881     default:
882         break;
883     }
884 
885     g_free(catpoint);
886 
887     return TRUE;
888 }
889 
clear_selection(gpointer key,gpointer val,gpointer data)890 static void clear_selection(gpointer key, gpointer val, gpointer data)
891 {
892     gint           *old = key;
893     gint           *new = data;
894     sat_map_obj_t  *obj = SAT_MAP_OBJ(val);
895     guint32         col;
896 
897     if ((*old != *new) && (obj->selected))
898     {
899         obj->selected = FALSE;
900 
901         /** FIXME: this is only global default; need the satmap here! */
902         col = sat_cfg_get_int(SAT_CFG_INT_MAP_SAT_COL);
903 
904         g_object_set(obj->marker,
905                      "fill-color-rgba", col, "stroke-color-rgba", col, NULL);
906         g_object_set(obj->label,
907                      "fill-color-rgba", col, "stroke-color-rgba", col, NULL);
908         g_object_set(obj->range1, "stroke-color-rgba", col, NULL);
909 
910         if (obj->oldrcnum == 2)
911             g_object_set(obj->range2, "stroke-color-rgba", col, NULL);
912     }
913 }
914 
gtk_sat_map_select_sat(GtkWidget * satmap,gint catnum)915 void gtk_sat_map_select_sat(GtkWidget * satmap, gint catnum)
916 {
917     GtkSatMap      *smap = GTK_SAT_MAP(satmap);
918     gint           *catpoint = NULL;
919     sat_map_obj_t  *obj = NULL;
920     guint32         col;
921 
922     catpoint = g_try_new0(gint, 1);
923     *catpoint = catnum;
924 
925     obj = SAT_MAP_OBJ(g_hash_table_lookup(smap->obj, catpoint));
926     if (obj == NULL)
927     {
928         sat_log_log(SAT_LOG_LEVEL_ERROR,
929                     _("%s: Can not find clicked object (%d) in hash table"),
930                     __func__, catnum);
931     }
932     else
933     {
934         obj->selected = TRUE;
935 
936         col = mod_cfg_get_int(smap->cfgdata,
937                               MOD_CFG_MAP_SECTION,
938                               MOD_CFG_MAP_SAT_SEL_COL,
939                               SAT_CFG_INT_MAP_SAT_SEL_COL);
940 
941         g_object_set(obj->marker,
942                      "fill-color-rgba", col, "stroke-color-rgba", col, NULL);
943         g_object_set(obj->label,
944                      "fill-color-rgba", col, "stroke-color-rgba", col, NULL);
945         g_object_set(obj->range1, "stroke-color-rgba", col, NULL);
946 
947         if (obj->oldrcnum == 2)
948             g_object_set(obj->range2, "stroke-color-rgba", col, NULL);
949 
950         /* clear other selections */
951         g_hash_table_foreach(smap->obj, clear_selection, catpoint);
952     }
953 
954     g_free(catpoint);
955 }
956 
957 /*
958  * Reconfigure map.
959  *
960  * This function should eventually reload all configuration for the GtkSatMap.
961  * Currently this function is not implemented for any of the views. Reconfiguration
962  * is done by recreating the whole module.
963  */
gtk_sat_map_reconf(GtkWidget * widget,GKeyFile * cfgdat)964 void gtk_sat_map_reconf(GtkWidget * widget, GKeyFile * cfgdat)
965 {
966     (void)widget;
967     (void)cfgdat;
968 }
969 
970 /*
971  * Safely load a map file.
972  *
973  * @param satmap The GtkSatMap widget
974  * @param clon The longitude that should be the center of the map
975  *
976  * This function is called shortly after the canvas has been created. Its purpose
977  * is to load a mapfile into satmap->origmap.
978  *
979  * The function ensures that satmap->origmap will contain a valid GdkPixpuf, by
980  * using the following logic:
981  *
982  *   - Get either module specific or global map file using mod_cfg_get_str
983  *   - If the returned file does not exist try sat_cfg_get_str_def
984  *   - If loading of default map does not succeed, create a dummy GdkPixbuf
985  *     (and raise all possible alarms)
986  *
987  * @note satmap->cfgdata should contain a valid GKeyFile.
988  *
989  */
load_map_file(GtkSatMap * satmap,float clon)990 static void load_map_file(GtkSatMap * satmap, float clon)
991 {
992     gchar          *buff;
993     gchar          *mapfile;
994     GError         *error = NULL;
995     GdkPixbuf      *tmpbuf;
996 
997     /* get local, global or default map file */
998     buff = mod_cfg_get_str(satmap->cfgdata,
999                            MOD_CFG_MAP_SECTION,
1000                            MOD_CFG_MAP_FILE, SAT_CFG_STR_MAP_FILE);
1001 
1002     if (g_path_is_absolute(buff))
1003     {
1004         /* map is user specific, ie. in $HOME/.gpredict2/maps/ */
1005         mapfile = g_strdup(buff);
1006     }
1007     else
1008     {
1009         /* build complete path */
1010         mapfile = map_file_name(buff);
1011     }
1012     g_free(buff);
1013 
1014     sat_log_log(SAT_LOG_LEVEL_DEBUG,
1015                 _("%s:%d: Loading map file %s"), __FILE__, __LINE__, mapfile);
1016 
1017     /* check that file exists, if not get the default */
1018     if (g_file_test(mapfile, G_FILE_TEST_EXISTS))
1019     {
1020         sat_log_log(SAT_LOG_LEVEL_DEBUG,
1021                     _("%s:%d: Map file found"), __FILE__, __LINE__);
1022     }
1023     else
1024     {
1025         sat_log_log(SAT_LOG_LEVEL_ERROR,
1026                     _("%s:%d: Could not find map file %s"),
1027                     __FILE__, __LINE__, mapfile);
1028 
1029         /* get default map file */
1030         g_free(mapfile);
1031         mapfile = sat_cfg_get_str_def(SAT_CFG_STR_MAP_FILE);
1032 
1033         sat_log_log(SAT_LOG_LEVEL_ERROR,
1034                     _("%s:%d: Using default map: %s"),
1035                     __FILE__, __LINE__, mapfile);
1036     }
1037 
1038     /* try to load the map file */
1039     tmpbuf = gdk_pixbuf_new_from_file(mapfile, &error);
1040 
1041     if (error != NULL)
1042     {
1043         sat_log_log(SAT_LOG_LEVEL_ERROR,
1044                     _("%s:%d: Error loading map file (%s)"),
1045                     __FILE__, __LINE__, error->message);
1046         g_clear_error(&error);
1047 
1048         /* create a dummy GdkPixbuf to avoid crash */
1049         tmpbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 400, 200);
1050         gdk_pixbuf_fill(tmpbuf, 0x0F0F0F0F);
1051     }
1052 
1053     /* create a blank map with same parameters as tmpbuf */
1054     satmap->origmap = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
1055                                      FALSE,
1056                                      gdk_pixbuf_get_bits_per_sample(tmpbuf),
1057                                      gdk_pixbuf_get_width(tmpbuf),
1058                                      gdk_pixbuf_get_height(tmpbuf));
1059 
1060     map_tools_shift_center(tmpbuf, satmap->origmap, clon);
1061     g_object_unref(tmpbuf);
1062     g_free(mapfile);
1063 
1064     /* Calculate longitude at the left side (-180 deg if center is at 0 deg longitude) */
1065     satmap->left_side_lon = -180.0;
1066     if (clon > 0.0)
1067         satmap->left_side_lon += clon;
1068     else if (clon < 0.0)
1069         satmap->left_side_lon = 180.0 + clon;
1070 }
1071 
arccos(gdouble x,gdouble y)1072 static gdouble arccos(gdouble x, gdouble y)
1073 {
1074     if (x && y)
1075     {
1076         if (y > 0.0)
1077             return acos(x / y);
1078         else if (y < 0.0)
1079             return pi + acos(x / y);
1080     }
1081 
1082     return 0.0;
1083 }
1084 
1085 /* Check whether the footprint covers the North or South pole. */
pole_is_covered(sat_t * sat)1086 static gboolean pole_is_covered(sat_t * sat)
1087 {
1088     if (north_pole_is_covered(sat) || south_pole_is_covered(sat))
1089         return TRUE;
1090     else
1091         return FALSE;
1092 }
1093 
1094 /* Check whether the footprint covers the North pole. */
north_pole_is_covered(sat_t * sat)1095 static gboolean north_pole_is_covered(sat_t * sat)
1096 {
1097     int             ret1;
1098     gdouble         qrb1, az1;
1099 
1100     ret1 = qrb(sat->ssplon, sat->ssplat, 0.0, 90.0, &qrb1, &az1);
1101     if (ret1 != RIG_OK)
1102     {
1103         sat_log_log(SAT_LOG_LEVEL_ERROR,
1104                     _("%s: Bad data measuring distance to North Pole %f %f."),
1105                     __func__, sat->ssplon, sat->ssplat);
1106     }
1107     if (qrb1 <= 0.5 * sat->footprint)
1108     {
1109         return TRUE;
1110     }
1111     return FALSE;
1112 }
1113 
1114 /* Check whether the footprint covers the South pole. */
south_pole_is_covered(sat_t * sat)1115 static gboolean south_pole_is_covered(sat_t * sat)
1116 {
1117     int             ret1;
1118     gdouble         qrb1, az1;
1119 
1120     ret1 = qrb(sat->ssplon, sat->ssplat, 0.0, -90.0, &qrb1, &az1);
1121     if (ret1 != RIG_OK)
1122     {
1123         sat_log_log(SAT_LOG_LEVEL_ERROR,
1124                     _("%s: Bad data measuring distance to South Pole %f %f."),
1125                     __func__, sat->ssplon, sat->ssplat);
1126     }
1127     if (qrb1 <= 0.5 * sat->footprint)
1128     {
1129         return TRUE;
1130     }
1131     return FALSE;
1132 }
1133 
1134 /* Mirror the footprint longitude. */
mirror_lon(sat_t * sat,gdouble rangelon,gdouble * mlon,gdouble mapbreak)1135 static gboolean mirror_lon(sat_t * sat, gdouble rangelon, gdouble * mlon,
1136                            gdouble mapbreak)
1137 {
1138     gdouble         diff;
1139     gboolean        warped = FALSE;
1140 
1141     /* make it so rangelon is on left of ssplon */
1142     diff = (sat->ssplon - rangelon);
1143     while (diff < 0)
1144         diff += 360;
1145     while (diff > 360)
1146         diff -= 360;
1147 
1148     *mlon = sat->ssplon + fabs(diff);
1149     while (*mlon > 180)
1150         *mlon -= 360;
1151     while (*mlon < -180)
1152         *mlon += 360;
1153     //printf("Something %s %f %f %f\n",sat->nickname, sat->ssplon, rangelon,mapbreak);
1154     if (((sat->ssplon >= mapbreak) && (sat->ssplon < mapbreak + 180)) ||
1155         ((sat->ssplon < mapbreak - 180) && (sat->ssplon >= mapbreak - 360)))
1156     {
1157         if (((rangelon >= mapbreak) && (rangelon < mapbreak + 180)) ||
1158             ((rangelon < mapbreak - 180) && (rangelon >= mapbreak - 360)))
1159         {
1160         }
1161         else
1162         {
1163             warped = TRUE;
1164             //printf ("sat %s warped for first \n",sat->nickname);
1165         }
1166     }
1167     else
1168     {
1169         if (((*mlon >= mapbreak) && (*mlon < mapbreak + 180)) ||
1170             ((*mlon < mapbreak - 180) && (*mlon >= mapbreak - 360)))
1171         {
1172             warped = TRUE;
1173             //printf ("sat %s warped for second \n",sat->nickname);
1174         }
1175     }
1176 
1177     return warped;
1178 }
1179 
1180 /**
1181  * Calculate satellite footprint and coverage area.
1182  *
1183  * @param satmap TheGtkSatMap widget.
1184  * @param sat The satellite.
1185  * @param points1 Initialised GooCanvasPoints structure with 360 points.
1186  * @param points2 Initialised GooCanvasPoints structure with 360 points.
1187  * @return The number of range circle parts.
1188  *
1189  * This function calculates the "left" side of the range circle and mirrors
1190  * the points in longitude to create the "right side of the range circle, too.
1191  * In order to be able to use the footprint points to create a set of subsequent
1192  * lines conencted to each other (poly-lines) the function may have to perform
1193  * one of the following three actions:
1194  *
1195  * 1. If the footprint covers the North or South pole, we need to sort the points
1196  *    and add two extra points: One to begin the range circle (e.g. -180,90) and
1197  *    one to end the range circle (e.g. 180,90). This is necessary to create a
1198  *    complete and consistent set of points suitable for a polyline. The addition
1199  *    of the extra points is done by the sort_points function.
1200  *
1201  * 2. Else if parts of the range circle is on one side of the map, while parts of
1202  *    it is on the right side of the map, i.e. the range circle runs off the border
1203  *    of the map, it calls the split_points function to split the points into two
1204  *    complete and consistent sets of points that are suitable to create two
1205  *    poly-lines.
1206  *
1207  * 3. Else nothing needs to be done since the points are already suitable for
1208  *    a polyline.
1209  *
1210  * The function will re-initialise points1 and points2 according to its needs. The
1211  * total number of points will always be 360, even with the addition of the two
1212  * extra points.
1213  */
calculate_footprint(GtkSatMap * satmap,sat_t * sat)1214 static guint calculate_footprint(GtkSatMap * satmap, sat_t * sat)
1215 {
1216     guint           azi;
1217     gfloat          sx, sy, msx, msy, ssx, ssy;
1218     gdouble         ssplat, ssplon, beta, azimuth, num, dem;
1219     gdouble         rangelon, rangelat, mlon;
1220     gboolean        warped = FALSE;
1221     guint           numrc = 1;
1222 
1223     /* Range circle calculations.
1224      * Borrowed from gsat 0.9.0 by Xavier Crehueras, EB3CZS
1225      * who borrowed from John Magliacane, KD2BD.
1226      * Optimized by Alexandru Csete and William J Beksi.
1227      */
1228     ssplat = sat->ssplat * de2ra;
1229     ssplon = sat->ssplon * de2ra;
1230     beta = (0.5 * sat->footprint) / xkmper;
1231 
1232     for (azi = 0; azi < 180; azi++)
1233     {
1234         azimuth = de2ra * (double)azi;
1235         rangelat = asin(sin(ssplat) * cos(beta) + cos(azimuth) *
1236                         sin(beta) * cos(ssplat));
1237         num = cos(beta) - (sin(ssplat) * sin(rangelat));
1238         dem = cos(ssplat) * cos(rangelat);
1239 
1240         if (azi == 0 && north_pole_is_covered(sat))
1241             rangelon = ssplon + pi;
1242         else if (fabs(num / dem) > 1.0)
1243             rangelon = ssplon;
1244         else
1245         {
1246             if ((180.0 - azi) >= 0)
1247                 rangelon = ssplon - arccos(num, dem);
1248             else
1249                 rangelon = ssplon + arccos(num, dem);
1250         }
1251 
1252         while (rangelon < -pi)
1253             rangelon += twopi;
1254 
1255         while (rangelon > (pi))
1256             rangelon -= twopi;
1257 
1258         rangelat = rangelat / de2ra;
1259         rangelon = rangelon / de2ra;
1260 
1261         /* mirror longitude */
1262         if (mirror_lon(sat, rangelon, &mlon, satmap->left_side_lon))
1263             warped = TRUE;
1264 
1265         lonlat_to_xy(satmap, rangelon, rangelat, &sx, &sy);
1266         lonlat_to_xy(satmap, mlon, rangelat, &msx, &msy);
1267 
1268         points1->coords[2 * azi] = sx;
1269         points1->coords[2 * azi + 1] = sy;
1270 
1271         /* Add mirrored point */
1272         points1->coords[718 - 2 * azi] = msx;
1273         points1->coords[719 - 2 * azi] = msy;
1274     }
1275 
1276     /* points1 now contains 360 pairs of map-based XY coordinates.
1277        Check whether actions 1, 2 or 3 have to be performed.
1278      */
1279 
1280     /* pole is covered => sort points1 and add additional points */
1281     if (pole_is_covered(sat))
1282     {
1283 
1284         sort_points_x(satmap, sat, points1, 360);
1285         numrc = 1;
1286     }
1287 
1288     /* pole not covered but range circle has been warped
1289        => split points */
1290     else if (warped == TRUE)
1291     {
1292 
1293         lonlat_to_xy(satmap, sat->ssplon, sat->ssplat, &ssx, &ssy);
1294         split_points(satmap, sat, ssx);
1295         numrc = 2;
1296 
1297     }
1298     else
1299     {
1300         /* the nominal condition => points1 is adequate */
1301         numrc = 1;
1302     }
1303 
1304     return numrc;
1305 }
1306 
1307 /**
1308  * Split and sort polyline points.
1309  *
1310  * @param satmap The GtkSatMap structure.
1311  * @param points1 GooCanvasPoints containing the footprint points.
1312  * @param points2 A GooCanvasPoints structure containing the second set of points.
1313  * @param sspx Canvas based x-coordinate of SSP.
1314  * @bug We should ensure that the endpoints in points1 have x=x0, while in
1315  *      the endpoints in points2 should have x=x0+width (TBC).
1316  *
1317  * @note This function works on canvas-based coordinates rather than lat/lon
1318  * @note DO NOT USE this function when the footprint covers one of the poles
1319  *       (the end result may freeze the X-server requiring a hard-reset!)
1320  */
split_points(GtkSatMap * satmap,sat_t * sat,gdouble sspx)1321 static void split_points(GtkSatMap * satmap, sat_t * sat, gdouble sspx)
1322 {
1323     GooCanvasPoints *tps1, *tps2;
1324     gint            n, n1, n2, ns, i, j, k;
1325 
1326     /* initialize parameters */
1327     n = points1->num_points;
1328     n1 = 0;
1329     n2 = 0;
1330     i = 0;
1331     j = 0;
1332     k = 0;
1333     ns = 0;
1334     tps1 = goo_canvas_points_new(n);
1335     tps2 = goo_canvas_points_new(n);
1336 
1337     //if ((sspx >= (satmap->x0 + satmap->width - 0.6)) ||
1338     //    (sspx >= (satmap->x0 - 0.6))) {
1339     //if ((sspx == (satmap->x0 + satmap->width)) ||
1340     //    (sspx == (satmap->x0))) {
1341     if ((sat->ssplon >= 179.4) || (sat->ssplon <= -179.4))
1342     {
1343         /* sslon = +/-180 deg.
1344            - copy points with (x > satmap->x0+satmap->width/2) to tps1
1345            - copy points with (x < satmap->x0+satmap->width/2) to tps2
1346            - sort tps1 and tps2
1347          */
1348         for (i = 0; i < n; i++)
1349         {
1350             if (points1->coords[2 * i] > (satmap->x0 + satmap->width / 2))
1351             {
1352                 tps1->coords[2 * n1] = points1->coords[2 * i];
1353                 tps1->coords[2 * n1 + 1] = points1->coords[2 * i + 1];
1354                 n1++;
1355             }
1356             else
1357             {
1358                 tps2->coords[2 * n2] = points1->coords[2 * i];
1359                 tps2->coords[2 * n2 + 1] = points1->coords[2 * i + 1];
1360                 n2++;
1361             }
1362         }
1363 
1364         sort_points_y(satmap, sat, tps1, n1);
1365         sort_points_y(satmap, sat, tps2, n2);
1366     }
1367     else if (sspx < (satmap->x0 + satmap->width / 2))
1368     {
1369         /* We are on the left side of the map.
1370            Scan through points1 until we get to x > sspx (i=ns):
1371 
1372            - copy the points forwards until x < (x0+w/2) => tps2
1373            - continue to copy until the end => tps1
1374            - copy the points from i=0 to i=ns => tps1.
1375 
1376            Copy tps1 => points1 and tps2 => points2
1377          */
1378         while (points1->coords[2 * i] <= sspx)
1379         {
1380             i++;
1381         }
1382         ns = i - 1;
1383 
1384         while (points1->coords[2 * i] > (satmap->x0 + satmap->width / 2))
1385         {
1386             tps2->coords[2 * j] = points1->coords[2 * i];
1387             tps2->coords[2 * j + 1] = points1->coords[2 * i + 1];
1388             i++;
1389             j++;
1390             n2++;
1391         }
1392 
1393         while (i < n)
1394         {
1395             tps1->coords[2 * k] = points1->coords[2 * i];
1396             tps1->coords[2 * k + 1] = points1->coords[2 * i + 1];
1397             i++;
1398             k++;
1399             n1++;
1400         }
1401 
1402         for (i = 0; i <= ns; i++)
1403         {
1404             tps1->coords[2 * k] = points1->coords[2 * i];
1405             tps1->coords[2 * k + 1] = points1->coords[2 * i + 1];
1406             k++;
1407             n1++;
1408         }
1409     }
1410     else
1411     {
1412         /* We are on the right side of the map.
1413            Scan backwards through points1 until x < sspx (i=ns):
1414 
1415            - copy the points i=ns,i-- until x >= x0+w/2  => tps2
1416            - copy the points until we reach i=0          => tps1
1417            - copy the points from i=n to i=ns            => tps1
1418 
1419          */
1420         i = n - 1;
1421         while (points1->coords[2 * i] >= sspx)
1422         {
1423             i--;
1424         }
1425         ns = i + 1;
1426 
1427         while (points1->coords[2 * i] < (satmap->x0 + satmap->width / 2))
1428         {
1429             tps2->coords[2 * j] = points1->coords[2 * i];
1430             tps2->coords[2 * j + 1] = points1->coords[2 * i + 1];
1431             i--;
1432             j++;
1433             n2++;
1434         }
1435 
1436         while (i >= 0)
1437         {
1438             tps1->coords[2 * k] = points1->coords[2 * i];
1439             tps1->coords[2 * k + 1] = points1->coords[2 * i + 1];
1440             i--;
1441             k++;
1442             n1++;
1443         }
1444 
1445         for (i = n - 1; i >= ns; i--)
1446         {
1447             tps1->coords[2 * k] = points1->coords[2 * i];
1448             tps1->coords[2 * k + 1] = points1->coords[2 * i + 1];
1449             k++;
1450             n1++;
1451         }
1452     }
1453 
1454     //g_print ("NS:%d  N1:%d  N2:%d\n", ns, n1, n2);
1455 
1456     /* free points and copy new contents */
1457     goo_canvas_points_unref(points1);
1458     goo_canvas_points_unref(points2);
1459 
1460     points1 = goo_canvas_points_new(n1);
1461     for (i = 0; i < n1; i++)
1462     {
1463         points1->coords[2 * i] = tps1->coords[2 * i];
1464         points1->coords[2 * i + 1] = tps1->coords[2 * i + 1];
1465     }
1466     goo_canvas_points_unref(tps1);
1467 
1468     points2 = goo_canvas_points_new(n2);
1469     for (i = 0; i < n2; i++)
1470     {
1471         points2->coords[2 * i] = tps2->coords[2 * i];
1472         points2->coords[2 * i + 1] = tps2->coords[2 * i + 1];
1473     }
1474     goo_canvas_points_unref(tps2);
1475 
1476     /* stretch end points to map borders */
1477     if (points1->coords[0] > (satmap->x0 + satmap->width / 2))
1478     {
1479         points1->coords[0] = satmap->x0 + satmap->width;
1480         points1->coords[2 * (n1 - 1)] = satmap->x0 + satmap->width;
1481         points2->coords[0] = satmap->x0;
1482         points2->coords[2 * (n2 - 1)] = satmap->x0;
1483     }
1484     else
1485     {
1486         points2->coords[0] = satmap->x0 + satmap->width;
1487         points2->coords[2 * (n2 - 1)] = satmap->x0 + satmap->width;
1488         points1->coords[0] = satmap->x0;
1489         points1->coords[2 * (n1 - 1)] = satmap->x0;
1490     }
1491 }
1492 
1493 /**
1494  * Sort points according to X coordinates.
1495  *
1496  * @param satmap The GtkSatMap structure.
1497  * @param sat The satellite data structure.
1498  * @param points The points to sort.
1499  * @param num The number of points. By specifying it as parameter we can
1500  *            sort incomplete arrays.
1501  *
1502  * This function sorts the points in ascending order with respect
1503  * to their x value. After sorting the function adds two extra points
1504  * to the array using the following algorithms:
1505  *
1506  *   move point at position 0 to position 1
1507  *   move point at position N to position N-1
1508  *   if (ssplat > 0)
1509  *         insert (x0,y0) into position 0
1510  *         insert (x0+width,y0) into position N
1511  *   else
1512  *         insert (x0,y0+height) into position 0
1513  *         insert (x0+width,y0+height) into position N
1514  *
1515  * This way we loose the points at position 1 and N-1, but that does not
1516  * make any big difference anyway, since we have 360 points in total.
1517  *
1518  */
sort_points_x(GtkSatMap * satmap,sat_t * sat,GooCanvasPoints * points,gint num)1519 static void sort_points_x(GtkSatMap * satmap, sat_t * sat,
1520                           GooCanvasPoints * points, gint num)
1521 {
1522     gsize           size = 2 * sizeof(double);
1523 
1524     /* call g_qsort_with_data, which warps the qsort function
1525        from stdlib */
1526     g_qsort_with_data(points->coords, num, size, compare_coordinates_x, NULL);
1527 
1528     /* move point at position 0 to position 1 */
1529     points->coords[2] = satmap->x0;
1530     points->coords[3] = points->coords[1];
1531 
1532     /* move point at position N to position N-1 */
1533     points->coords[716] = satmap->x0 + satmap->width;   //points->coords[718];
1534     points->coords[717] = points->coords[719];
1535 
1536     if (sat->ssplat > 0.0)
1537     {
1538         /* insert (x0-1,y0) into position 0 */
1539         points->coords[0] = satmap->x0;
1540         points->coords[1] = satmap->y0;
1541 
1542         /* insert (x0+width,y0) into position N */
1543         points->coords[718] = satmap->x0 + satmap->width;
1544         points->coords[719] = satmap->y0;
1545     }
1546     else
1547     {
1548         /* insert (x0,y0+height) into position 0 */
1549         points->coords[0] = satmap->x0;
1550         points->coords[1] = satmap->y0 + satmap->height;
1551 
1552         /* insert (x0+width,y0+height) into position N */
1553         points->coords[718] = satmap->x0 + satmap->width;
1554         points->coords[719] = satmap->y0 + satmap->height;
1555     }
1556 }
1557 
1558 /**
1559  * Sort points according to Y coordinates.
1560  *
1561  * @param satmap The GtkSatMap structure.
1562  * @param sat The satellite data structure.
1563  * @param points The points to sort.
1564  * @param num The number of points. By specifying it as parameter we can
1565  *            sort incomplete arrays.
1566  *
1567  * This function sorts the points in ascending order with respect
1568  * to their y value.
1569  */
sort_points_y(GtkSatMap * satmap,sat_t * sat,GooCanvasPoints * points,gint num)1570 static void sort_points_y(GtkSatMap * satmap, sat_t * sat,
1571                           GooCanvasPoints * points, gint num)
1572 {
1573     gsize           size;
1574 
1575     (void)satmap;
1576     (void)sat;
1577     size = 2 * sizeof(double);
1578 
1579     /* call g_qsort_with_data, which warps the qsort function
1580        from stdlib */
1581     g_qsort_with_data(points->coords, num, size, compare_coordinates_y, NULL);
1582 }
1583 
1584 /**
1585  * Compare two X coordinates.
1586  *
1587  * @param a Pointer to one coordinate (x,y) both double.
1588  * @param b Pointer to the second coordinate (x,y) both double.
1589  * @param data User data; always NULL.
1590  * @return Negative value if a < b; zero if a = b; positive value if a > b.
1591  *
1592  * This function is used by the g_qsort_with_data function to compare two
1593  * elements in the coordinate array of the GooCanvasPoints structure. We have
1594  * to remember that GooCanvasPoints->coords is an array of XY values, i.e.
1595  * [X0,Y0,X1,Y1,...,Xn,Yn], but we only want to sort according to the X
1596  * coordinate. Therefore, we let the g_qsort_with_data believe that the whole
1597  * pair is one element (which is OK even according to the API docs) but we
1598  * cast the pointers to an array with two elements and compare only the first
1599  * one (x).
1600  *
1601  */
compare_coordinates_x(gconstpointer a,gconstpointer b,gpointer data)1602 static gint compare_coordinates_x(gconstpointer a, gconstpointer b,
1603                                   gpointer data)
1604 {
1605     double         *ea = (double *)a;
1606     double         *eb = (double *)b;
1607 
1608     (void)data;
1609 
1610     if (ea[0] < eb[0])
1611     {
1612         return -1;
1613     }
1614     else if (ea[0] > eb[0])
1615     {
1616         return 1;
1617     }
1618 
1619     return 0;
1620 }
1621 
1622 /**
1623  * Compare two Y coordinates.
1624  *
1625  * @param a Pointer to one coordinate (x,y) both double.
1626  * @param b Pointer to the second coordinate (x,y) both double.
1627  * @param data User data; always NULL.
1628  * @return Negative value if a < b; zero if a = b; positive value if a > b.
1629  *
1630  * This function is used by the g_qsort_with_data function to compare two
1631  * elements in the coordinate array of the GooCanvasPoints structure. We have
1632  * to remember that GooCanvasPoints->coords is an array of XY values, i.e.
1633  * [X0,Y0,X1,Y1,...,Xn,Yn], but we only want to sort according to the Y
1634  * coordinate. Therefore, we let the g_qsort_with_data believe that the whole
1635  * pair is one element (which is OK even according to the API docs) but we
1636  * cast the pointers to an array with two elements and compare only the second
1637  * one (y).
1638  *
1639  */
compare_coordinates_y(gconstpointer a,gconstpointer b,gpointer data)1640 static gint compare_coordinates_y(gconstpointer a, gconstpointer b,
1641                                   gpointer data)
1642 {
1643     double         *ea = (double *)a;
1644     double         *eb = (double *)b;
1645 
1646     (void)data;
1647 
1648     if (ea[1] < eb[1])
1649     {
1650         return -1;
1651     }
1652 
1653     else if (ea[1] > eb[1])
1654     {
1655         return 1;
1656     }
1657 
1658     return 0;
1659 }
1660 
1661 /**
1662  * Plot a satellite.
1663  *
1664  * @param key The hash table key.
1665  * @param value Pointer to the satellite.
1666  * @param data Pointer to the GtkSatMap widget.
1667  *
1668  * This function creates and initializes the canvas objects (rectangle, label,
1669  * footprint) for a satellite. The function is called as a g_hash_table_foreach
1670  * callback.
1671  */
plot_sat(gpointer key,gpointer value,gpointer data)1672 static void plot_sat(gpointer key, gpointer value, gpointer data)
1673 {
1674     GtkSatMap      *satmap = GTK_SAT_MAP(data);
1675     sat_map_obj_t  *obj = NULL;
1676     sat_t          *sat = SAT(value);
1677     GooCanvasItemModel *root;
1678     gint           *catnum;
1679     guint32         col, covcol, shadowcol;
1680     gfloat          x, y;
1681     gchar          *tooltip;
1682 
1683     (void)key;
1684 
1685     if (decayed(sat))
1686     {
1687         return;
1688     }
1689     /* get satellite and SSP */
1690     catnum = g_new0(gint, 1);
1691     *catnum = sat->tle.catnr;
1692 
1693     lonlat_to_xy(satmap, sat->ssplon, sat->ssplat, &x, &y);
1694 
1695     /* create and initialize a sat object */
1696     obj = g_try_new(sat_map_obj_t, 1);
1697 
1698     if (obj == NULL)
1699     {
1700         sat_log_log(SAT_LOG_LEVEL_ERROR,
1701                     _("%s: Cannot allocate memory for satellite %d."),
1702                     __func__, sat->tle.catnr);
1703         return;
1704     }
1705 
1706     obj->selected = FALSE;
1707 
1708     if (!g_hash_table_lookup_extended(satmap->showtracks, catnum, NULL, NULL))
1709     {
1710         obj->showtrack = FALSE;
1711     }
1712     else
1713     {
1714         obj->showtrack = TRUE;
1715     }
1716 
1717     if (!g_hash_table_lookup_extended(satmap->hidecovs, catnum, NULL, NULL))
1718     {
1719         obj->showcov = TRUE;
1720     }
1721     else
1722     {
1723         obj->showcov = FALSE;
1724     }
1725     obj->istarget = FALSE;
1726     obj->oldrcnum = 0;
1727     obj->newrcnum = 0;
1728     obj->track_data.latlon = NULL;
1729     obj->track_data.lines = NULL;
1730     obj->track_orbit = 0;
1731 
1732     root = goo_canvas_get_root_item_model(GOO_CANVAS(satmap->canvas));
1733 
1734     /* satellite color */
1735     col = mod_cfg_get_int(satmap->cfgdata,
1736                           MOD_CFG_MAP_SECTION,
1737                           MOD_CFG_MAP_SAT_COL, SAT_CFG_INT_MAP_SAT_COL);
1738 
1739     /* area coverage colour */
1740     covcol = mod_cfg_get_int(satmap->cfgdata,
1741                              MOD_CFG_MAP_SECTION,
1742                              MOD_CFG_MAP_SAT_COV_COL,
1743                              SAT_CFG_INT_MAP_SAT_COV_COL);
1744     /* coverage color */
1745     if (obj->showcov)
1746     {
1747         covcol = mod_cfg_get_int(satmap->cfgdata,
1748                                  MOD_CFG_MAP_SECTION,
1749                                  MOD_CFG_MAP_SAT_COV_COL,
1750                                  SAT_CFG_INT_MAP_SAT_COV_COL);
1751     }
1752     else
1753     {
1754         covcol = 0x00000000;
1755     }
1756 
1757 
1758     /* shadow colour (only alpha channel) */
1759     shadowcol = mod_cfg_get_int(satmap->cfgdata,
1760                                 MOD_CFG_MAP_SECTION,
1761                                 MOD_CFG_MAP_SHADOW_ALPHA,
1762                                 SAT_CFG_INT_MAP_SHADOW_ALPHA);
1763 
1764     /* create tooltip */
1765     tooltip = g_markup_printf_escaped("<b>%s</b>\n"
1766                                       "Lon: %5.1f\302\260\n"
1767                                       "Lat: %5.1f\302\260\n"
1768                                       " Az: %5.1f\302\260\n"
1769                                       " El: %5.1f\302\260",
1770                                       sat->nickname,
1771                                       sat->ssplon, sat->ssplat,
1772                                       sat->az, sat->el);
1773 
1774     /* create satellite marker and label + shadows. We create shadows first */
1775     obj->shadowm = goo_canvas_rect_model_new(root,
1776                                              x - MARKER_SIZE_HALF + 1,
1777                                              y - MARKER_SIZE_HALF + 1,
1778                                              2 * MARKER_SIZE_HALF,
1779                                              2 * MARKER_SIZE_HALF,
1780                                              "fill-color-rgba", 0x00,
1781                                              "stroke-color-rgba", shadowcol,
1782                                              NULL);
1783     obj->marker = goo_canvas_rect_model_new(root,
1784                                             x - MARKER_SIZE_HALF,
1785                                             y - MARKER_SIZE_HALF,
1786                                             2 * MARKER_SIZE_HALF,
1787                                             2 * MARKER_SIZE_HALF,
1788                                             "fill-color-rgba", col,
1789                                             "stroke-color-rgba", col,
1790                                             "tooltip", tooltip, NULL);
1791 
1792     obj->shadowl = goo_canvas_text_model_new(root, sat->nickname,
1793                                              x + 1,
1794                                              y + 3,
1795                                              -1,
1796                                              GOO_CANVAS_ANCHOR_NORTH,
1797                                              "font", "Sans 8",
1798                                              "fill-color-rgba", shadowcol,
1799                                              NULL);
1800     obj->label = goo_canvas_text_model_new(root, sat->nickname,
1801                                            x,
1802                                            y + 2,
1803                                            -1,
1804                                            GOO_CANVAS_ANCHOR_NORTH,
1805                                            "font", "Sans 8",
1806                                            "fill-color-rgba", col,
1807                                            "tooltip", tooltip, NULL);
1808 
1809     g_free(tooltip);
1810 
1811     g_object_set_data(G_OBJECT(obj->marker), "catnum",
1812                       GINT_TO_POINTER(*catnum));
1813     g_object_set_data(G_OBJECT(obj->label), "catnum",
1814                       GINT_TO_POINTER(*catnum));
1815 
1816     /* initialize points for footprint */
1817     points1 = goo_canvas_points_new(360);
1818     points2 = goo_canvas_points_new(360);
1819 
1820     /* calculate footprint */
1821     obj->newrcnum = calculate_footprint(satmap, sat);
1822     obj->oldrcnum = obj->newrcnum;
1823 
1824     /* invisible footprint for decayed sats (STS fix) */
1825     /*     if (sat->otype == ORBIT_TYPE_DECAYED) { */
1826     /*         col = 0x00000000; */
1827     /*     } */
1828 
1829     /* always create first part of range circle */
1830     obj->range1 = goo_canvas_polyline_model_new(root, FALSE, 0,
1831                                                 "points", points1,
1832                                                 "line-width", 1.0,
1833                                                 "fill-color-rgba", covcol,
1834                                                 "stroke-color-rgba", col,
1835                                                 "line-cap",
1836                                                 CAIRO_LINE_CAP_SQUARE,
1837                                                 "line-join",
1838                                                 CAIRO_LINE_JOIN_MITER, NULL);
1839     g_object_set_data(G_OBJECT(obj->range1), "catnum",
1840                       GINT_TO_POINTER(*catnum));
1841 
1842     /* create second part if available */
1843     if (obj->newrcnum == 2)
1844     {
1845         //g_print ("N1:%d  N2:%d\n", points1->num_points, points2->num_points);
1846 
1847         obj->range2 = goo_canvas_polyline_model_new(root, FALSE, 0,
1848                                                     "points", points2,
1849                                                     "line-width", 1.0,
1850                                                     "fill-color-rgba", covcol,
1851                                                     "stroke-color-rgba", col,
1852                                                     "line-cap",
1853                                                     CAIRO_LINE_CAP_SQUARE,
1854                                                     "line-join",
1855                                                     CAIRO_LINE_JOIN_MITER,
1856                                                     NULL);
1857         g_object_set_data(G_OBJECT(obj->range2), "catnum",
1858                           GINT_TO_POINTER(*catnum));
1859 
1860     }
1861 
1862     goo_canvas_points_unref(points1);
1863     goo_canvas_points_unref(points2);
1864 
1865     /* add sat to hash table */
1866     g_hash_table_insert(satmap->obj, catnum, obj);
1867 }
1868 
1869 /** Update a given satellite. */
update_sat(gpointer key,gpointer value,gpointer data)1870 static void update_sat(gpointer key, gpointer value, gpointer data)
1871 {
1872     gint           *catnum;
1873     GtkSatMap      *satmap = GTK_SAT_MAP(data);
1874     sat_map_obj_t  *obj = NULL;
1875     sat_t          *sat = SAT(value);
1876     gfloat          x, y;
1877     gdouble         oldx, oldy;
1878     gdouble         now;        // = get_current_daynum ();
1879     GooCanvasItemModel *root;
1880     gint            idx;
1881     guint32         col, covcol;
1882     gchar          *tooltip;
1883     gchar          *aosstr;
1884 
1885     //gdouble sspla,ssplo;
1886 
1887     root = goo_canvas_get_root_item_model(GOO_CANVAS(satmap->canvas));
1888 
1889     catnum = g_new0(gint, 1);
1890     *catnum = sat->tle.catnr;
1891 
1892     now = satmap->tstamp;
1893 
1894     /* update next AOS */
1895     if (sat->aos > now)
1896     {
1897         if ((sat->aos < satmap->naos) || (satmap->naos == 0.0))
1898         {
1899             satmap->naos = sat->aos;
1900             satmap->ncat = sat->tle.catnr;
1901         }
1902     }
1903 
1904     obj = SAT_MAP_OBJ(g_hash_table_lookup(satmap->obj, catnum));
1905 
1906     /* get rid of a decayed satellite */
1907     if (decayed(sat) && obj != NULL)
1908     {
1909         idx = goo_canvas_item_model_find_child(root, obj->marker);
1910         if (idx != -1)
1911             goo_canvas_item_model_remove_child(root, idx);;
1912         idx = goo_canvas_item_model_find_child(root, obj->shadowm);
1913         if (idx != -1)
1914             goo_canvas_item_model_remove_child(root, idx);;
1915         idx = goo_canvas_item_model_find_child(root, obj->label);
1916         if (idx != -1)
1917             goo_canvas_item_model_remove_child(root, idx);
1918         idx = goo_canvas_item_model_find_child(root, obj->shadowl);
1919         if (idx != -1)
1920             goo_canvas_item_model_remove_child(root, idx);
1921         idx = goo_canvas_item_model_find_child(root, obj->range1);
1922         if (idx != -1)
1923             goo_canvas_item_model_remove_child(root, idx);
1924         idx = goo_canvas_item_model_find_child(root, obj->range2);
1925         if (idx != -1)
1926             goo_canvas_item_model_remove_child(root, idx);
1927         g_hash_table_remove(satmap->obj, catnum);
1928         if (obj->showtrack)
1929             ground_track_update(satmap, sat, satmap->qth, obj, TRUE);
1930         g_free(obj);
1931 
1932         g_hash_table_remove(satmap->obj, catnum);
1933         return;
1934     }
1935 
1936     if (obj == NULL)
1937     {
1938         if (decayed(sat))
1939         {
1940             return;
1941         }
1942         else
1943         {
1944             /* satellite was decayed now is visible
1945                time controller backed up time */
1946             plot_sat(key, value, data);
1947             return;
1948         }
1949     }
1950 
1951     if (obj->selected)
1952     {
1953         /* update satmap->sel */
1954         update_selected(satmap, sat);
1955     }
1956 
1957     g_object_set(obj->label, "text", sat->nickname, NULL);
1958     g_object_set(obj->shadowl, "text", sat->nickname, NULL);
1959 
1960     aosstr = aoslos_time_to_str(satmap, sat);
1961     tooltip = g_markup_printf_escaped("<b>%s</b>\n"
1962                                       "Lon: %5.1f\302\260\n"
1963                                       "Lat: %5.1f\302\260\n"
1964                                       " Az: %5.1f\302\260\n"
1965                                       " El: %5.1f\302\260\n"
1966                                       "%s",
1967                                       sat->nickname,
1968                                       sat->ssplon, sat->ssplat,
1969                                       sat->az, sat->el, aosstr);
1970     g_object_set(obj->marker, "tooltip", tooltip, NULL);
1971     g_object_set(obj->label, "tooltip", tooltip, NULL);
1972     g_free(tooltip);
1973     g_free(aosstr);
1974 
1975     lonlat_to_xy(satmap, sat->ssplon, sat->ssplat, &x, &y);
1976 
1977     /* update only if satellite has moved at least
1978        2 * MARKER_SIZE_HALF (no need to drain CPU all the time)
1979      */
1980     g_object_get(obj->marker, "x", &oldx, "y", &oldy, NULL);
1981 
1982     if ((fabs(oldx - x) >= 2 * MARKER_SIZE_HALF) ||
1983         (fabs(oldy - y) >= 2 * MARKER_SIZE_HALF))
1984     {
1985         g_object_set(obj->marker,
1986                      "x", (gdouble) (x - MARKER_SIZE_HALF),
1987                      "y", (gdouble) (y - MARKER_SIZE_HALF), NULL);
1988         g_object_set(obj->shadowm,
1989                      "x", (gdouble) (x - MARKER_SIZE_HALF + 1),
1990                      "y", (gdouble) (y - MARKER_SIZE_HALF + 1), NULL);
1991 
1992         if (x < 50)
1993         {
1994             g_object_set(obj->label,
1995                          "x", (gdouble) (x + 3),
1996                          "y", (gdouble) (y), "anchor", GOO_CANVAS_ANCHOR_WEST,
1997                          NULL);
1998             g_object_set(obj->shadowl, "x", (gdouble) (x + 3 + 1), "y",
1999                          (gdouble) (y + 1), "anchor", GOO_CANVAS_ANCHOR_WEST,
2000                          NULL);
2001         }
2002         else if ((satmap->width - x) < 50)
2003         {
2004             g_object_set(obj->label,
2005                          "x", (gdouble) (x - 3),
2006                          "y", (gdouble) (y), "anchor", GOO_CANVAS_ANCHOR_EAST,
2007                          NULL);
2008             g_object_set(obj->shadowl, "x", (gdouble) (x - 3 + 1), "y",
2009                          (gdouble) (y + 1), "anchor", GOO_CANVAS_ANCHOR_EAST,
2010                          NULL);
2011         }
2012         else if ((satmap->height - y) < 25)
2013         {
2014             g_object_set(obj->label,
2015                          "x", (gdouble) (x),
2016                          "y", (gdouble) (y - 2),
2017                          "anchor", GOO_CANVAS_ANCHOR_SOUTH, NULL);
2018             g_object_set(obj->shadowl,
2019                          "x", (gdouble) (x + 1),
2020                          "y", (gdouble) (y - 2 + 1),
2021                          "anchor", GOO_CANVAS_ANCHOR_SOUTH, NULL);
2022         }
2023         else
2024         {
2025             g_object_set(obj->label,
2026                          "x", (gdouble) (x),
2027                          "y", (gdouble) (y + 2),
2028                          "anchor", GOO_CANVAS_ANCHOR_NORTH, NULL);
2029             g_object_set(obj->shadowl,
2030                          "x", (gdouble) (x + 1),
2031                          "y", (gdouble) (y + 2 + 1),
2032                          "anchor", GOO_CANVAS_ANCHOR_NORTH, NULL);
2033         }
2034 
2035         /* initialize points for footprint */
2036         points1 = goo_canvas_points_new(360);
2037         points2 = goo_canvas_points_new(360);
2038 
2039         /* calculate footprint */
2040         obj->newrcnum = calculate_footprint(satmap, sat);
2041 
2042         /* always update first part */
2043         g_object_set(obj->range1, "points", points1, NULL);
2044 
2045         if (obj->newrcnum == 2)
2046         {
2047             if (obj->oldrcnum == 1)
2048             {
2049                 /* we need to create the second part */
2050                 if (obj->selected)
2051                 {
2052                     col = mod_cfg_get_int(satmap->cfgdata,
2053                                           MOD_CFG_MAP_SECTION,
2054                                           MOD_CFG_MAP_SAT_SEL_COL,
2055                                           SAT_CFG_INT_MAP_SAT_SEL_COL);
2056                 }
2057                 else
2058                 {
2059                     col = mod_cfg_get_int(satmap->cfgdata,
2060                                           MOD_CFG_MAP_SECTION,
2061                                           MOD_CFG_MAP_SAT_COL,
2062                                           SAT_CFG_INT_MAP_SAT_COL);
2063                 }
2064                 /* coverage color */
2065                 if (obj->showcov)
2066                 {
2067                     covcol = mod_cfg_get_int(satmap->cfgdata,
2068                                              MOD_CFG_MAP_SECTION,
2069                                              MOD_CFG_MAP_SAT_COV_COL,
2070                                              SAT_CFG_INT_MAP_SAT_COV_COL);
2071                 }
2072                 else
2073                 {
2074                     covcol = 0x00000000;
2075                 }
2076                 obj->range2 = goo_canvas_polyline_model_new(root, FALSE, 0,
2077                                                             "points", points2,
2078                                                             "line-width", 1.0,
2079                                                             "fill-color-rgba",
2080                                                             covcol,
2081                                                             "stroke-color-rgba",
2082                                                             col, "line-cap",
2083                                                             CAIRO_LINE_CAP_SQUARE,
2084                                                             "line-join",
2085                                                             CAIRO_LINE_JOIN_MITER,
2086                                                             NULL);
2087                 g_object_set_data(G_OBJECT(obj->range2), "catnum",
2088                                   GINT_TO_POINTER(*catnum));
2089             }
2090             else
2091             {
2092                 /* just update the second part */
2093                 g_object_set(obj->range2, "points", points2, NULL);
2094             }
2095         }
2096         else
2097         {
2098             if (obj->oldrcnum == 2)
2099             {
2100                 /* remove second part */
2101                 idx = goo_canvas_item_model_find_child(root, obj->range2);
2102                 if (idx != -1)
2103                 {
2104                     goo_canvas_item_model_remove_child(root, idx);
2105                 }
2106             }
2107         }
2108 
2109         /* update rc-number */
2110         obj->oldrcnum = obj->newrcnum;
2111 
2112         goo_canvas_points_unref(points1);
2113         goo_canvas_points_unref(points2);
2114     }
2115 
2116     /* if ground track is visible check whether we have passed into a
2117        new orbit, in which case we need to recalculate the ground track
2118      */
2119     if (obj->showtrack)
2120     {
2121         if (obj->track_orbit != sat->orbit)
2122         {
2123             ground_track_update(satmap, sat, satmap->qth, obj, TRUE);
2124         }
2125         /* otherwise we may be in a map rescale process */
2126         else if (satmap->resize)
2127         {
2128             ground_track_update(satmap, sat, satmap->qth, obj, FALSE);
2129         }
2130     }
2131 
2132     g_free(catnum);
2133 }
2134 
2135 /**
2136  * Update information about the selected satellite.
2137  *
2138  * @param satmap Pointer to the GtkSatMap widget.
2139  * @param sat Pointer to the selected satellite
2140  */
update_selected(GtkSatMap * satmap,sat_t * sat)2141 static void update_selected(GtkSatMap * satmap, sat_t * sat)
2142 {
2143     guint           h, m, s;
2144     gchar          *ch, *cm, *cs;
2145     gchar          *alsstr, *text;
2146     gdouble         number, now;
2147     gboolean        isgeo = FALSE;      /* set to TRUE if satellite appears to be GEO */
2148 
2149     now = satmap->tstamp;       //get_current_daynum ();
2150 
2151     if (sat->el > 0.0)
2152     {
2153         if (sat->los > 0.0)
2154         {
2155             number = sat->los - now;
2156             alsstr = g_strdup("LOS");
2157         }
2158         else
2159         {
2160             isgeo = TRUE;
2161         }
2162     }
2163     else
2164     {
2165         if (sat->aos > 0.0)
2166         {
2167             number = sat->aos - now;
2168             alsstr = g_strdup("AOS");
2169         }
2170         else
2171         {
2172             isgeo = TRUE;
2173         }
2174     }
2175 
2176     /* if satellite appears to be GEO don't attempt to show AOS/LOS */
2177     if (isgeo)
2178     {
2179         if (sat->el > 0.0)
2180         {
2181             text =
2182                 g_markup_printf_escaped
2183                 ("<span background=\"#%s\"> %s: Always in range </span>",
2184                  satmap->infobgd, sat->nickname);
2185         }
2186         else
2187         {
2188             text =
2189                 g_markup_printf_escaped
2190                 ("<span background=\"#%s\"> %s: Always out of range </span>",
2191                  satmap->infobgd, sat->nickname);
2192         }
2193     }
2194     else
2195     {
2196         /* convert julian date to seconds */
2197         s = (guint) (number * 86400);
2198 
2199         /* extract hours */
2200         h = (guint) floor(s / 3600);
2201         s -= 3600 * h;
2202 
2203         /* leading zero */
2204         if ((h > 0) && (h < 10))
2205             ch = g_strdup("0");
2206         else
2207             ch = g_strdup("");
2208 
2209         /* extract minutes */
2210         m = (guint) floor(s / 60);
2211         s -= 60 * m;
2212 
2213         /* leading zero */
2214         if (m < 10)
2215             cm = g_strdup("0");
2216         else
2217             cm = g_strdup("");
2218 
2219         /* leading zero */
2220         if (s < 10)
2221             cs = g_strdup(":0");
2222         else
2223             cs = g_strdup(":");
2224 
2225         if (h > 0)
2226         {
2227             text = g_markup_printf_escaped("<span background=\"#%s\"> "
2228                                            "%s %s in %s%d:%s%d%s%d </span>",
2229                                            satmap->infobgd, sat->nickname,
2230                                            alsstr, ch, h, cm, m, cs, s);
2231         }
2232         else
2233         {
2234             text = g_markup_printf_escaped("<span background=\"#%s\"> "
2235                                            "%s %s in %s%d%s%d </span>",
2236                                            satmap->infobgd, sat->nickname,
2237                                            alsstr, cm, m, cs, s);
2238         }
2239 
2240         g_free(ch);
2241         g_free(cm);
2242         g_free(cs);
2243         g_free(alsstr);
2244     }
2245 
2246     /* update info text */
2247     g_object_set(satmap->sel, "text", text, NULL);
2248     g_free(text);
2249 }
2250 
draw_grid_lines(GtkSatMap * satmap,GooCanvasItemModel * root)2251 static void draw_grid_lines(GtkSatMap * satmap, GooCanvasItemModel * root)
2252 {
2253     gdouble         xstep, ystep;
2254     gfloat          lon, lat;
2255     guint32         col;
2256     guint           i;
2257     gchar          *buf, hmf = ' ';
2258 
2259     /* initialize algo parameters */
2260     col = mod_cfg_get_int(satmap->cfgdata,
2261                           MOD_CFG_MAP_SECTION,
2262                           MOD_CFG_MAP_GRID_COL, SAT_CFG_INT_MAP_GRID_COL);
2263 
2264     xstep = (gdouble) (30.0 * satmap->width / 360.0);
2265     ystep = (gdouble) (30.0 * satmap->height / 180.0);
2266 
2267 #define MKLINE goo_canvas_polyline_model_new_line
2268 
2269     /* horizontal grid */
2270     for (i = 0; i < 5; i++)
2271     {
2272         /* line */
2273         satmap->gridh[i] = MKLINE(root,
2274                                   (gdouble) satmap->x0,
2275                                   (gdouble) (satmap->y0 + (i + 1) * ystep),
2276                                   (gdouble) (satmap->x0 + satmap->width),
2277                                   (gdouble) (satmap->y0 + (i + 1) * ystep),
2278                                   "stroke-color-rgba", col,
2279                                   "line-cap", CAIRO_LINE_CAP_SQUARE,
2280                                   "line-join", CAIRO_LINE_JOIN_MITER,
2281                                   "line-width", 0.5, NULL);
2282 
2283         /* FIXME: Use dotted line pattern? */
2284 
2285         /* label */
2286         xy_to_lonlat(satmap, satmap->x0, satmap->y0 + (i + 1) * ystep, &lon,
2287                      &lat);
2288         if (sat_cfg_get_bool(SAT_CFG_BOOL_USE_NSEW))
2289         {
2290             if (lat < 0.00)
2291             {
2292                 lat = -lat;
2293                 hmf = 'S';
2294             }
2295             else
2296             {
2297                 hmf = 'N';
2298             }
2299         }
2300         buf = g_strdup_printf("%.0f\302\260%c", lat, hmf);
2301         satmap->gridhlab[i] = goo_canvas_text_model_new(root,
2302                                                         buf,
2303                                                         (gdouble) (satmap->x0 +
2304                                                                    15),
2305                                                         (gdouble) (satmap->y0 +
2306                                                                    (i +
2307                                                                     1) *
2308                                                                    ystep), -1,
2309                                                         GOO_CANVAS_ANCHOR_NORTH,
2310                                                         "font", "Sans 8",
2311                                                         "fill-color-rgba", col,
2312                                                         NULL);
2313         g_free(buf);
2314 
2315         /* lower items to be just above the background map
2316            or below it if lines are invisible
2317          */
2318         if (satmap->showgrid)
2319         {
2320             goo_canvas_item_model_raise(satmap->gridh[i], satmap->map);
2321             goo_canvas_item_model_raise(satmap->gridhlab[i], satmap->map);
2322         }
2323         else
2324         {
2325             goo_canvas_item_model_lower(satmap->gridh[i], satmap->map);
2326             goo_canvas_item_model_lower(satmap->gridhlab[i], satmap->map);
2327         }
2328     }
2329 
2330     /* vertical grid */
2331     for (i = 0; i < 11; i++)
2332     {
2333         /* line */
2334         satmap->gridv[i] = MKLINE(root,
2335                                   (gdouble) (satmap->x0 + (i + 1) * xstep),
2336                                   (gdouble) satmap->y0,
2337                                   (gdouble) (satmap->x0 + (i + 1) * xstep),
2338                                   (gdouble) (satmap->y0 + satmap->height),
2339                                   "stroke-color-rgba", col,
2340                                   "line-cap", CAIRO_LINE_CAP_SQUARE,
2341                                   "line-join", CAIRO_LINE_JOIN_MITER,
2342                                   "line-width", 0.5, NULL);
2343 
2344         /* label */
2345         xy_to_lonlat(satmap, satmap->x0 + (i + 1) * xstep, satmap->y0, &lon,
2346                      &lat);
2347         if (sat_cfg_get_bool(SAT_CFG_BOOL_USE_NSEW))
2348         {
2349             if (lon < 0.00)
2350             {
2351                 lon = -lon;
2352                 hmf = 'W';
2353             }
2354             else
2355             {
2356                 hmf = 'E';
2357             }
2358         }
2359         buf = g_strdup_printf("%.0f\302\260%c", lon, hmf);
2360         satmap->gridvlab[i] = goo_canvas_text_model_new(root,
2361                                                         buf,
2362                                                         (gdouble) (satmap->x0 +
2363                                                                    (i +
2364                                                                     1) *
2365                                                                    xstep),
2366                                                         (gdouble) (satmap->y0 +
2367                                                                    satmap->height
2368                                                                    - 5), -1,
2369                                                         GOO_CANVAS_ANCHOR_EAST,
2370                                                         "font", "Sans 8",
2371                                                         "fill-color-rgba", col,
2372                                                         NULL);
2373         g_free(buf);
2374 
2375         /* lower items to be just above the background map
2376            or below it if lines are invisible
2377          */
2378         if (satmap->showgrid)
2379         {
2380             goo_canvas_item_model_raise(satmap->gridv[i], satmap->map);
2381             goo_canvas_item_model_raise(satmap->gridvlab[i], satmap->map);
2382         }
2383         else
2384         {
2385             goo_canvas_item_model_lower(satmap->gridv[i], satmap->map);
2386             goo_canvas_item_model_lower(satmap->gridvlab[i], satmap->map);
2387         }
2388     }
2389 }
2390 
draw_terminator(GtkSatMap * satmap,GooCanvasItemModel * root)2391 static void draw_terminator(GtkSatMap * satmap, GooCanvasItemModel * root)
2392 {
2393     guint32         terminator_col;
2394     guint32         globe_shadow_col;
2395 
2396     /* initialize algo parameters */
2397     terminator_col = mod_cfg_get_int(satmap->cfgdata,
2398                                      MOD_CFG_MAP_SECTION,
2399                                      MOD_CFG_MAP_TERMINATOR_COL,
2400                                      SAT_CFG_INT_MAP_TERMINATOR_COL);
2401 
2402     globe_shadow_col = mod_cfg_get_int(satmap->cfgdata,
2403                                        MOD_CFG_MAP_SECTION,
2404                                        MOD_CFG_MAP_GLOBAL_SHADOW_COL,
2405                                        SAT_CFG_INT_MAP_GLOBAL_SHADOW_COL);
2406 
2407     /* We do not set any polygon vertices here, but trust that the redraw_terminator
2408        will be called in due course to do the job. */
2409 
2410     satmap->terminator = goo_canvas_polyline_model_new(root, FALSE, 0,
2411                                                        "line-width", 1.0,
2412                                                        "fill-color-rgba",
2413                                                        globe_shadow_col,
2414                                                        "stroke-color-rgba",
2415                                                        terminator_col,
2416                                                        "line-cap",
2417                                                        CAIRO_LINE_CAP_SQUARE,
2418                                                        "line-join",
2419                                                        CAIRO_LINE_JOIN_MITER,
2420                                                        NULL);
2421 
2422     goo_canvas_item_model_raise(satmap->terminator, satmap->map);
2423 
2424     satmap->terminator_last_tstamp = satmap->tstamp;
2425 }
2426 
redraw_grid_lines(GtkSatMap * satmap)2427 static void redraw_grid_lines(GtkSatMap * satmap)
2428 {
2429     GooCanvasPoints *line;
2430     gdouble         xstep, ystep;
2431     guint           i;
2432 
2433     xstep = (gdouble) 30.0 *((gdouble) satmap->width) / 360.0;
2434     ystep = (gdouble) 30.0 *((gdouble) satmap->height) / 180.0;
2435 
2436     /* horizontal grid */
2437     for (i = 0; i < 5; i++)
2438     {
2439 
2440         /* update line */
2441         line = goo_canvas_points_new(2);
2442         line->coords[0] = (gdouble) satmap->x0;
2443         line->coords[1] = (gdouble) (satmap->y0 + (i + 1) * ystep);
2444         line->coords[2] = (gdouble) (satmap->x0 + satmap->width);
2445         line->coords[3] = (gdouble) (satmap->y0 + (i + 1) * ystep);
2446         g_object_set(satmap->gridh[i], "points", line, NULL);
2447         goo_canvas_points_unref(line);
2448 
2449         /* update label */
2450         g_object_set(satmap->gridhlab[i],
2451                      "x", (gdouble) (satmap->x0 + 15),
2452                      "y", (gdouble) (satmap->y0 + (i + 1) * ystep), NULL);
2453 
2454         /* lower items to be just above the background map
2455            or below it if lines are invisible
2456          */
2457         if (satmap->showgrid)
2458         {
2459             goo_canvas_item_model_raise(satmap->gridh[i], satmap->map);
2460             goo_canvas_item_model_raise(satmap->gridhlab[i], satmap->map);
2461         }
2462         else
2463             goo_canvas_item_model_lower(satmap->gridh[i], satmap->map);
2464     }
2465 
2466     /* vertical grid */
2467     for (i = 0; i < 11; i++)
2468     {
2469         /* update line */
2470         line = goo_canvas_points_new(2);
2471         line->coords[0] = (gdouble) (satmap->x0 + (i + 1) * xstep);
2472         line->coords[1] = (gdouble) satmap->y0;
2473         line->coords[2] = (gdouble) (satmap->x0 + (i + 1) * xstep);
2474         line->coords[3] = (gdouble) (satmap->y0 + satmap->height);
2475         g_object_set(satmap->gridv[i], "points", line, NULL);
2476         goo_canvas_points_unref(line);
2477 
2478         /* update label */
2479         g_object_set(satmap->gridvlab[i],
2480                      "x", (gdouble) (satmap->x0 + (i + 1) * xstep),
2481                      "y", (gdouble) (satmap->y0 + satmap->height - 5), NULL);
2482 
2483         /* lower items to be just above the background map
2484            or below it if lines are invisible
2485          */
2486         if (satmap->showgrid)
2487         {
2488             goo_canvas_item_model_raise(satmap->gridv[i], satmap->map);
2489             goo_canvas_item_model_raise(satmap->gridvlab[i], satmap->map);
2490         }
2491         else
2492         {
2493             goo_canvas_item_model_lower(satmap->gridv[i], satmap->map);
2494             goo_canvas_item_model_lower(satmap->gridvlab[i], satmap->map);
2495         }
2496     }
2497 }
2498 
sgn(gdouble const t)2499 static inline gdouble sgn(gdouble const t)
2500 {
2501     return t < 0.0 ? -1.0 : 1.0;
2502 }
2503 
redraw_terminator(GtkSatMap * satmap)2504 static void redraw_terminator(GtkSatMap * satmap)
2505 {
2506     /* Set of (x, y) points along the terminator, one on each line of longitude in
2507        increments of longitudinal degrees. */
2508     GooCanvasPoints *line;
2509 
2510     /* A variable which iterates over the longitudes. */
2511     int             longitude;
2512 
2513     /* Working variables in the computation of the terminator coordinates. */
2514     gfloat          x, y;
2515 
2516     /* Vector normal to plane containing a line of longitude (z component is
2517        always zero). */
2518     /* Note: our coordinates have z along the Earth's axis, x pointing through the
2519        intersection of the Greenwich Meridian and the Equator, and y right-handedly
2520        perpendicular to both. */
2521     gdouble         lx, ly;
2522 
2523     /* The position of the sun as latitude, longitude. */
2524     geodetic_t      geodetic;
2525 
2526     /* Vector which points from the centre of the Earth to the Sun in inertial
2527        coordinates. */
2528     vector_t        sun_;
2529 
2530     /* The same vector in geodesic coordinates. */
2531     gdouble         sx, sy, sz;
2532 
2533     /* Vector cross-product of (lx,ly,lz) and sun vector. */
2534     gdouble         rx, ry, rz;
2535 
2536     line = goo_canvas_points_new(363);
2537 
2538     Calculate_Solar_Position(satmap->tstamp, &sun_);
2539     Calculate_LatLonAlt(satmap->tstamp, &sun_, &geodetic);
2540 
2541     sx = cos(geodetic.lat) * cos(geodetic.lon);
2542     sy = cos(geodetic.lat) * sin(-geodetic.lon);
2543     sz = sin(geodetic.lat);
2544 
2545 
2546     for (longitude = -180; longitude <= 180; ++longitude)
2547     {
2548 
2549         lx = cos(de2ra * (longitude + sgn(sz) * 90));
2550         ly = sin(de2ra * (longitude + sgn(sz) * 90));
2551         /* lz = 0.0; */
2552 
2553         rx = ly * sz /* -lz*sy */ ;
2554         ry = /* lz*sx */ -lx * sz;
2555         rz = -lx * sy - ly * sx;
2556 
2557         gdouble         length = sqrt(rx * rx + ry * ry + rz * rz);
2558 
2559         lonlat_to_xy(satmap,
2560                      longitude, asin(rz / length) * (1.0 / de2ra), &x, &y);
2561 
2562         line->coords[2 * (longitude + 181)] = x;
2563         line->coords[2 * (longitude + 181) + 1] = y;
2564     }
2565 
2566     lonlat_to_xy(satmap, -180.0, sz < 0.0 ? 90.0 : -90.0, &x, &y);
2567     line->coords[0] = x;
2568     line->coords[1] = y;
2569 
2570     lonlat_to_xy(satmap, 180.0, sz < 0.0 ? 90.0 : -90.0, &x, &y);
2571     line->coords[724] = x;
2572     line->coords[725] = y;
2573 
2574     g_object_set(satmap->terminator, "points", line, NULL);
2575     goo_canvas_points_unref(line);
2576 }
2577 
gtk_sat_map_lonlat_to_xy(GtkSatMap * m,gdouble lon,gdouble lat,gdouble * x,gdouble * y)2578 void gtk_sat_map_lonlat_to_xy(GtkSatMap * m,
2579                               gdouble lon, gdouble lat,
2580                               gdouble * x, gdouble * y)
2581 {
2582     gfloat          fx, fy;
2583 
2584     fx = (gfloat) * x;
2585     fy = (gfloat) * y;
2586 
2587     lonlat_to_xy(m, lon, lat, &fx, &fy);
2588 
2589     *x = (gdouble) fx;
2590     *y = (gdouble) fy;
2591 }
2592 
gtk_sat_map_reload_sats(GtkWidget * satmap,GHashTable * sats)2593 void gtk_sat_map_reload_sats(GtkWidget * satmap, GHashTable * sats)
2594 {
2595     GTK_SAT_MAP(satmap)->sats = sats;
2596     GTK_SAT_MAP(satmap)->naos = 0.0;
2597     GTK_SAT_MAP(satmap)->ncat = 0;
2598 
2599     /* reset ground track orbit to force repaint */
2600     g_hash_table_foreach(GTK_SAT_MAP(satmap)->obj, reset_ground_track, NULL);
2601 }
2602 
reset_ground_track(gpointer key,gpointer value,gpointer user_data)2603 static void reset_ground_track(gpointer key, gpointer value,
2604                                gpointer user_data)
2605 {
2606     sat_map_obj_t  *obj = (sat_map_obj_t *) value;
2607     (void) key;
2608     (void) user_data;
2609 
2610     obj->track_orbit = 0;
2611 }
2612 
aoslos_time_to_str(GtkSatMap * satmap,sat_t * sat)2613 static gchar   *aoslos_time_to_str(GtkSatMap * satmap, sat_t * sat)
2614 {
2615     guint           h, m, s;
2616     gdouble         number, now;
2617     gchar          *text = NULL;
2618 
2619 
2620     now = satmap->tstamp;       //get_current_daynum ();
2621     if (sat->el > 0.0)
2622     {
2623         number = sat->los - now;
2624     }
2625     else
2626     {
2627         number = sat->aos - now;
2628     }
2629 
2630     /* convert julian date to seconds */
2631     s = (guint) (number * 86400);
2632 
2633     /* extract hours */
2634     h = (guint) floor(s / 3600);
2635     s -= 3600 * h;
2636 
2637     /* extract minutes */
2638     m = (guint) floor(s / 60);
2639     s -= 60 * m;
2640 
2641     if (sat->el > 0.0)
2642         text = g_strdup_printf(_("LOS in %d minutes"), m + 60 * h);
2643     else
2644         text = g_strdup_printf(_("AOS in %d minutes"), m + 60 * h);
2645 
2646     return text;
2647 }
2648 
2649 /** Load the satellites that we should show tracks for */
gtk_sat_map_load_showtracks(GtkSatMap * satmap)2650 static void gtk_sat_map_load_showtracks(GtkSatMap * satmap)
2651 {
2652     mod_cfg_get_integer_list_boolean(satmap->cfgdata,
2653                                      MOD_CFG_MAP_SECTION,
2654                                      MOD_CFG_MAP_SHOWTRACKS,
2655                                      satmap->showtracks);
2656 
2657 }
2658 
2659 /** Save the satellites that we should not show ground tracks */
gtk_sat_map_store_showtracks(GtkSatMap * satmap)2660 static void gtk_sat_map_store_showtracks(GtkSatMap * satmap)
2661 {
2662     mod_cfg_set_integer_list_boolean(satmap->cfgdata,
2663                                      satmap->showtracks,
2664                                      MOD_CFG_MAP_SECTION,
2665                                      MOD_CFG_MAP_SHOWTRACKS);
2666 }
2667 
2668 /** Save the satellites that we should not highlight coverage */
gtk_sat_map_store_hidecovs(GtkSatMap * satmap)2669 static void gtk_sat_map_store_hidecovs(GtkSatMap * satmap)
2670 {
2671     mod_cfg_set_integer_list_boolean(satmap->cfgdata,
2672                                      satmap->hidecovs,
2673                                      MOD_CFG_MAP_SECTION,
2674                                      MOD_CFG_MAP_HIDECOVS);
2675 }
2676 
2677 /** Load the satellites that we should not highlight coverage */
gtk_sat_map_load_hide_coverages(GtkSatMap * satmap)2678 static void gtk_sat_map_load_hide_coverages(GtkSatMap * satmap)
2679 {
2680     mod_cfg_get_integer_list_boolean(satmap->cfgdata,
2681                                      MOD_CFG_MAP_SECTION,
2682                                      MOD_CFG_MAP_HIDECOVS, satmap->hidecovs);
2683 }
2684