1 /*
2   Gpredict: Real-time satellite tracking and orbit prediction program
3 
4   Copyright (C)  2001-2017  Alexandru Csete, OZ9AEC.
5 
6   Comments, questions and bugreports should be submitted via
7   http://sourceforge.net/projects/gpredict/
8   More details can be found at the project home page:
9 
10   http://gpredict.oz9aec.net/
11 
12   This program is free software; you can redistribute it and/or modify
13   it under the terms of the GNU General Public License as published by
14   the Free Software Foundation; either version 2 of the License, or
15   (at your option) any later version.
16 
17   This program is distributed in the hope that it will be useful,
18   but WITHOUT ANY WARRANTY; without even the implied warranty of
19   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20   GNU General Public License for more details.
21 
22   You should have received a copy of the GNU General Public License
23   along with this program; if not, visit http://www.fsf.org/
24 */
25 /**
26  * Polar Plot Widget.
27  * @ingroup widgets
28  *
29  * GtkPolarPlot is a graphical widget that can display a satellite pass
30  * in an Az/El polar plot. The widget was originally created to display
31  * a single satellite pass in the detailed pass predicition dialog.
32  *
33  * Later, a few utility functions were added in order to make the GtkPolarPlot
34  * more dynamic and useful in other contexts too. In addition to a satellite
35  * pass, GtkPolarPlot can show a target object (small square), a target
36  * position marker (thread), and a current position marker (small circle).
37  * These three objects are very useful in the rotator control window.
38  */
39 #ifdef HAVE_CONFIG_H
40 #include <build-config.h>
41 #endif
42 #include <glib/gi18n.h>
43 #include <goocanvas.h>
44 #include <gtk/gtk.h>
45 
46 #include "config-keys.h"
47 #include "gpredict-utils.h"
48 #include "gtk-polar-plot.h"
49 #include "gtk-sat-data.h"
50 #include "sat-cfg.h"
51 #include "sat-log.h"
52 #include "sgpsdp/sgp4sdp4.h"
53 #include "time-tools.h"
54 
55 #define POLV_DEFAULT_SIZE 200
56 #define POLV_DEFAULT_MARGIN 20
57 
58 /* extra size for line outside 0 deg circle (inside margin) */
59 #define POLV_LINE_EXTRA 5
60 
61 #define MARKER_SIZE_HALF 2
62 
63 
64 static GtkVBoxClass *parent_class = NULL;
65 
gtk_polar_plot_init(GtkPolarPlot * polview)66 static void gtk_polar_plot_init(GtkPolarPlot * polview)
67 {
68     polview->qth = NULL;
69     polview->pass = NULL;
70     polview->size = 0;
71     polview->r = 0;
72     polview->cx = 0;
73     polview->cy = 0;
74     polview->swap = 0;
75     polview->qthinfo = FALSE;
76     polview->cursinfo = FALSE;
77     polview->extratick = FALSE;
78     polview->target = NULL;
79 }
80 
gtk_polar_plot_destroy(GtkWidget * widget)81 static void gtk_polar_plot_destroy(GtkWidget * widget)
82 {
83     if (GTK_POLAR_PLOT(widget)->pass != NULL)
84     {
85         free_pass(GTK_POLAR_PLOT(widget)->pass);
86         GTK_POLAR_PLOT(widget)->pass = NULL;
87     }
88 
89     (*GTK_WIDGET_CLASS(parent_class)->destroy) (widget);
90 }
91 
gtk_polar_plot_class_init(GtkPolarPlotClass * class)92 static void gtk_polar_plot_class_init(GtkPolarPlotClass * class)
93 {
94     GtkWidgetClass *widget_class = (GtkWidgetClass *) class;
95     parent_class = g_type_class_peek_parent(class);
96     widget_class->destroy = gtk_polar_plot_destroy;
97 }
98 
gtk_polar_plot_get_type()99 GType gtk_polar_plot_get_type()
100 {
101     static GType    gtk_polar_plot_type = 0;
102 
103     if (!gtk_polar_plot_type)
104     {
105         static const GTypeInfo gtk_polar_plot_info = {
106             sizeof(GtkPolarPlotClass),
107             NULL,               /* base init */
108             NULL,               /* base finalise */
109             (GClassInitFunc) gtk_polar_plot_class_init,
110             NULL,               /* class finalise */
111             NULL,               /* class data */
112             sizeof(GtkPolarPlot),
113             5,                  /* n_preallocs */
114             (GInstanceInitFunc) gtk_polar_plot_init,
115             NULL
116         };
117 
118         gtk_polar_plot_type = g_type_register_static(GTK_TYPE_BOX,
119                                                      "GtkPolarPlot",
120                                                      &gtk_polar_plot_info, 0);
121     }
122 
123     return gtk_polar_plot_type;
124 }
125 
create_time_tick(GtkPolarPlot * pv,gdouble time,gfloat x,gfloat y)126 static GooCanvasItemModel *create_time_tick(GtkPolarPlot * pv, gdouble time,
127                                             gfloat x, gfloat y)
128 {
129     GooCanvasItemModel *item;
130     GooCanvasAnchorType     anchor;
131     GooCanvasItemModel *root;
132     guint32         col;
133     gchar           buff[6];
134 
135     root = goo_canvas_get_root_item_model(GOO_CANVAS(pv->canvas));
136 
137     col = sat_cfg_get_int(SAT_CFG_INT_POLAR_TRACK_COL);
138 
139     daynum_to_str(buff, 6, "%H:%M", time);
140 
141     if (x > pv->cx)
142     {
143         anchor = GOO_CANVAS_ANCHOR_EAST;
144         x -= 5;
145     }
146     else
147     {
148         anchor = GOO_CANVAS_ANCHOR_WEST;
149         x += 5;
150     }
151 
152     item = goo_canvas_text_model_new(root, buff,
153                                      (gdouble) x, (gdouble) y,
154                                      -1, anchor,
155                                      "font", "Sans 7",
156                                      "fill-color-rgba", col, NULL);
157 
158     return item;
159 }
160 
161 /** Convert Az/El to canvas based XY coordinates. */
azel_to_xy(GtkPolarPlot * p,gdouble az,gdouble el,gfloat * x,gfloat * y)162 static void azel_to_xy(GtkPolarPlot * p, gdouble az, gdouble el,
163                        gfloat * x, gfloat * y)
164 {
165     gdouble         rel;
166 
167     if (el < 0.0)
168     {
169         /* FIXME: generate bug report */
170         *x = 0.0;
171         *y = 0.0;
172 
173         return;
174     }
175 
176     /* convert angles to radians */
177     az = de2ra * az;
178     el = de2ra * el;
179 
180     /* radius @ el */
181     rel = p->r - (2 * p->r * el) / M_PI;
182 
183     switch (p->swap)
184     {
185     case POLAR_PLOT_NWSE:
186         az = 2 * M_PI - az;
187         break;
188 
189     case POLAR_PLOT_SENW:
190         az = M_PI - az;
191         break;
192 
193     case POLAR_PLOT_SWNE:
194         az = M_PI + az;
195         break;
196 
197     default:
198         break;
199     }
200 
201     *x = (gfloat) (p->cx + rel * sin(az));
202     *y = (gfloat) (p->cy - rel * cos(az));
203 }
204 
205 /** Convert canvas based coordinates to Az/El. */
xy_to_azel(GtkPolarPlot * p,gfloat x,gfloat y,gfloat * az,gfloat * el)206 static void xy_to_azel(GtkPolarPlot * p, gfloat x, gfloat y,
207                        gfloat * az, gfloat * el)
208 {
209     gfloat          rel;
210 
211     /* distance from center to cursor */
212     rel = p->r - sqrt((x - p->cx) * (x - p->cx) + (y - p->cy) * (y - p->cy));
213 
214     /* scale according to p->r = 90 deg */
215     *el = 90.0 * rel / p->r;
216 
217     if (x >= p->cx)
218     {
219         /* 1. and 2. quadrant */
220         *az = atan2(x - p->cx, p->cy - y) / de2ra;
221     }
222     else
223     {
224         /* 3 and 4. quadrant */
225         *az = 360 + atan2(x - p->cx, p->cy - y) / de2ra;
226     }
227 
228     /* correct for orientation */
229     switch (p->swap)
230     {
231     case POLAR_PLOT_NWSE:
232         *az = 360.0 - *az;
233         break;
234 
235     case POLAR_PLOT_SENW:
236         if (*az <= 180)
237             *az = 180.0 - *az;
238         else
239             *az = 540.0 - *az;
240         break;
241 
242     case POLAR_PLOT_SWNE:
243         if (*az >= 180.0)
244             *az = *az - 180.0;
245         else
246             *az = 180.0 + *az;
247         break;
248 
249     default:
250         break;
251     }
252 }
253 
create_track(GtkPolarPlot * pv)254 static void create_track(GtkPolarPlot * pv)
255 {
256     guint           i;
257     GooCanvasItemModel *root;
258     pass_detail_t  *detail;
259     guint           num;
260     GooCanvasPoints *points;
261     gfloat          x, y;
262     guint32         col;
263     guint           tres, ttidx;
264 
265     root = goo_canvas_get_root_item_model(GOO_CANVAS(pv->canvas));
266 
267     /* create points */
268     num = g_slist_length(pv->pass->details);
269 
270     /* time resolution for time ticks; we need
271        3 additional points to AOS and LOS ticks.
272      */
273     tres = (num - 2) / (TRACK_TICK_NUM - 1);
274 
275     points = goo_canvas_points_new(num);
276 
277     /* first point should be (aos_az,0.0) */
278     azel_to_xy(pv, pv->pass->aos_az, 0.0, &x, &y);
279     points->coords[0] = (double)x;
280     points->coords[1] = (double)y;
281     pv->trtick[0] = create_time_tick(pv, pv->pass->aos, x, y);
282 
283     ttidx = 1;
284 
285     for (i = 1; i < num - 1; i++)
286     {
287         detail = PASS_DETAIL(g_slist_nth_data(pv->pass->details, i));
288         if (detail->el >= 0.0)
289             azel_to_xy(pv, detail->az, detail->el, &x, &y);
290         points->coords[2 * i] = (double)x;
291         points->coords[2 * i + 1] = (double)y;
292 
293         if (!(i % tres))
294         {
295             if (ttidx < TRACK_TICK_NUM)
296                 /* create a time tick */
297                 pv->trtick[ttidx] = create_time_tick(pv, detail->time, x, y);
298             ttidx++;
299         }
300     }
301 
302     /* last point should be (los_az, 0.0)  */
303     azel_to_xy(pv, pv->pass->los_az, 0.0, &x, &y);
304     points->coords[2 * (num - 1)] = (double)x;
305     points->coords[2 * (num - 1) + 1] = (double)y;
306 
307     /* create poly-line */
308     col = sat_cfg_get_int(SAT_CFG_INT_POLAR_TRACK_COL);
309 
310     pv->track = goo_canvas_polyline_model_new(root, FALSE, 0,
311                                               "points", points,
312                                               "line-width", 1.0,
313                                               "stroke-color-rgba", col,
314                                               "line-cap",
315                                               CAIRO_LINE_CAP_SQUARE,
316                                               "line-join",
317                                               CAIRO_LINE_JOIN_MITER, NULL);
318     goo_canvas_points_unref(points);
319 }
320 
321 /**
322  * Transform pole coordinates.
323  *
324  * This function transforms the pols coordinates (x,y) taking into account
325  * the orientation of the polar plot.
326  */
327 static void
correct_pole_coor(GtkPolarPlot * polv,polar_plot_pole_t pole,gfloat * x,gfloat * y,GooCanvasAnchorType * anch)328 correct_pole_coor(GtkPolarPlot * polv,
329                   polar_plot_pole_t pole,
330                   gfloat * x, gfloat * y, GooCanvasAnchorType * anch)
331 {
332     switch (pole)
333     {
334     case POLAR_PLOT_POLE_N:
335         if ((polv->swap == POLAR_PLOT_SENW) || (polv->swap == POLAR_PLOT_SWNE))
336         {
337             /* North and South are swapped */
338             *y = *y + POLV_LINE_EXTRA;
339             *anch = GOO_CANVAS_ANCHOR_NORTH;
340         }
341         else
342         {
343             *y = *y - POLV_LINE_EXTRA;
344             *anch = GOO_CANVAS_ANCHOR_SOUTH;
345         }
346         break;
347 
348     case POLAR_PLOT_POLE_E:
349         if ((polv->swap == POLAR_PLOT_NWSE) || (polv->swap == POLAR_PLOT_SWNE))
350         {
351             /* East and West are swapped */
352             *x = *x - POLV_LINE_EXTRA;
353             *anch = GOO_CANVAS_ANCHOR_EAST;
354         }
355         else
356         {
357             *x = *x + POLV_LINE_EXTRA;
358             *anch = GOO_CANVAS_ANCHOR_WEST;
359         }
360         break;
361 
362     case POLAR_PLOT_POLE_S:
363         if ((polv->swap == POLAR_PLOT_SENW) || (polv->swap == POLAR_PLOT_SWNE))
364         {
365             /* North and South are swapped */
366             *y = *y - POLV_LINE_EXTRA;
367             *anch = GOO_CANVAS_ANCHOR_SOUTH;
368         }
369         else
370         {
371             *y = *y + POLV_LINE_EXTRA;
372             *anch = GOO_CANVAS_ANCHOR_NORTH;
373         }
374         break;
375 
376     case POLAR_PLOT_POLE_W:
377         if ((polv->swap == POLAR_PLOT_NWSE) || (polv->swap == POLAR_PLOT_SWNE))
378         {
379             /* East and West are swapped */
380             *x = *x + POLV_LINE_EXTRA;
381             *anch = GOO_CANVAS_ANCHOR_WEST;
382         }
383         else
384         {
385             *x = *x - POLV_LINE_EXTRA;
386             *anch = GOO_CANVAS_ANCHOR_EAST;
387         }
388         break;
389 
390     default:
391         sat_log_log(SAT_LOG_LEVEL_ERROR,
392                     _("%s:%d: Incorrect polar plot orientation."),
393                     __FILE__, __LINE__);
394         break;
395     }
396 }
397 
create_canvas_model(GtkPolarPlot * polv)398 static GooCanvasItemModel *create_canvas_model(GtkPolarPlot * polv)
399 {
400     GooCanvasItemModel *root;
401     gfloat          x, y;
402     guint32         col;
403     GooCanvasAnchorType     anch = GOO_CANVAS_ANCHOR_CENTER;
404 
405     root = goo_canvas_group_model_new(NULL, NULL);
406 
407     polv->bgd = goo_canvas_rect_model_new(root, 0.0, 0.0,
408                                           POLV_DEFAULT_SIZE, POLV_DEFAULT_SIZE,
409                                           "fill-color-rgba", 0xFFFFFFFF,
410                                           "stroke-color-rgba", 0xFFFFFFFF,
411                                           NULL);
412 
413     /* graph dimensions */
414     polv->size = POLV_DEFAULT_SIZE;
415     polv->r = (polv->size / 2) - POLV_DEFAULT_MARGIN;
416     polv->cx = POLV_DEFAULT_SIZE / 2;
417     polv->cy = POLV_DEFAULT_SIZE / 2;
418 
419     col = sat_cfg_get_int(SAT_CFG_INT_POLAR_AXIS_COL);
420 
421     /* Add elevation circles at 0, 30 and 60 deg */
422     polv->C00 = goo_canvas_ellipse_model_new(root,
423                                              polv->cx, polv->cy,
424                                              polv->r, polv->r,
425                                              "line-width", 1.0,
426                                              "stroke-color-rgba", col, NULL);
427 
428     polv->C30 = goo_canvas_ellipse_model_new(root,
429                                              polv->cx, polv->cy,
430                                              0.6667 * polv->r,
431                                              0.6667 * polv->r, "line-width",
432                                              1.0, "stroke-color-rgba", col,
433                                              NULL);
434 
435     polv->C60 = goo_canvas_ellipse_model_new(root,
436                                              polv->cx, polv->cy,
437                                              0.333 * polv->r, 0.3333 * polv->r,
438                                              "line-width", 1.0,
439                                              "stroke-color-rgba", col, NULL);
440 
441     /* add horixontal and vertical guidance lines */
442     polv->hl = goo_canvas_polyline_model_new_line(root,
443                                                   polv->cx - polv->r -
444                                                   POLV_LINE_EXTRA, polv->cy,
445                                                   polv->cx + polv->r +
446                                                   POLV_LINE_EXTRA, polv->cy,
447                                                   "stroke-color-rgba", col,
448                                                   "line-width", 1.0, NULL);
449     polv->vl =
450         goo_canvas_polyline_model_new_line(root, polv->cx,
451                                            polv->cy - polv->r -
452                                            POLV_LINE_EXTRA, polv->cx,
453                                            polv->cy + polv->r +
454                                            POLV_LINE_EXTRA,
455                                            "stroke-color-rgba", col,
456                                            "line-width", 1.0, NULL);
457 
458     /* N, S, E and W labels.  */
459     col = sat_cfg_get_int(SAT_CFG_INT_POLAR_TICK_COL);
460     azel_to_xy(polv, 0.0, 0.0, &x, &y);
461     correct_pole_coor(polv, POLAR_PLOT_POLE_N, &x, &y, &anch);
462     polv->N = goo_canvas_text_model_new(root, _("N"),
463                                         x,
464                                         y,
465                                         -1,
466                                         anch,
467                                         "font", "Sans 8",
468                                         "fill-color-rgba", col, NULL);
469 
470     azel_to_xy(polv, 180.0, 0.0, &x, &y);
471     correct_pole_coor(polv, POLAR_PLOT_POLE_S, &x, &y, &anch);
472     polv->S = goo_canvas_text_model_new(root, _("S"),
473                                         x,
474                                         y,
475                                         -1,
476                                         anch,
477                                         "font", "Sans 8",
478                                         "fill-color-rgba", col, NULL);
479 
480     azel_to_xy(polv, 90.0, 0.0, &x, &y);
481     correct_pole_coor(polv, POLAR_PLOT_POLE_E, &x, &y, &anch);
482     polv->E = goo_canvas_text_model_new(root, _("E"),
483                                         x,
484                                         y,
485                                         -1,
486                                         anch,
487                                         "font", "Sans 8",
488                                         "fill-color-rgba", col, NULL);
489 
490     azel_to_xy(polv, 270.0, 0.0, &x, &y);
491     correct_pole_coor(polv, POLAR_PLOT_POLE_W, &x, &y, &anch);
492     polv->W = goo_canvas_text_model_new(root, _("W"),
493                                         x,
494                                         y,
495                                         -1,
496                                         anch,
497                                         "font", "Sans 8",
498                                         "fill-color-rgba", col, NULL);
499 
500     /* cursor text */
501     col = sat_cfg_get_int(SAT_CFG_INT_POLAR_INFO_COL);
502     polv->curs = goo_canvas_text_model_new(root, "",
503                                            polv->cx - polv->r -
504                                            2 * POLV_LINE_EXTRA,
505                                            polv->cy + polv->r +
506                                            POLV_LINE_EXTRA, -1, GOO_CANVAS_ANCHOR_W,
507                                            "font", "Sans 8", "fill-color-rgba",
508                                            col, NULL);
509 
510     /* location info */
511     polv->locnam = goo_canvas_text_model_new(root, polv->qth->name,
512                                              polv->cx - polv->r -
513                                              2 * POLV_LINE_EXTRA,
514                                              polv->cy - polv->r -
515                                              POLV_LINE_EXTRA, -1,
516                                              GOO_CANVAS_ANCHOR_SW, "font", "Sans 8",
517                                              "fill-color-rgba", col, NULL);
518 
519     return root;
520 }
521 
522 /** Update sky track drawing after size allocate. */
update_track(GtkPolarPlot * pv)523 static void update_track(GtkPolarPlot * pv)
524 {
525     guint           num, i;
526     GooCanvasPoints *points;
527     gfloat          x, y;
528     pass_detail_t  *detail;
529     guint           tres, ttidx;
530 
531     /* create points */
532     num = g_slist_length(pv->pass->details);
533 
534     points = goo_canvas_points_new(num);
535 
536     /* first point should be (aos_az,0.0) */
537     azel_to_xy(pv, pv->pass->aos_az, 0.0, &x, &y);
538     points->coords[0] = (double)x;
539     points->coords[1] = (double)y;
540 
541     /* time tick 0 */
542     g_object_set(pv->trtick[0], "x", (gdouble) x, "y", (gdouble) y, NULL);
543 
544     /* time resolution for time ticks; we need
545        3 additional points to AOS and LOS ticks.
546      */
547     tres = (num - 2) / (TRACK_TICK_NUM - 1);
548     ttidx = 1;
549 
550     for (i = 1; i < num - 1; i++)
551     {
552         detail = PASS_DETAIL(g_slist_nth_data(pv->pass->details, i));
553         if (detail->el >= 0.0)
554             azel_to_xy(pv, detail->az, detail->el, &x, &y);
555         points->coords[2 * i] = (double)x;
556         points->coords[2 * i + 1] = (double)y;
557 
558         if (!(i % tres))
559         {
560             /* make room between text and track */
561             if (x > pv->cx)
562                 x -= 5;
563             else
564                 x += 5;
565 
566             /* update time tick */
567             if (ttidx < TRACK_TICK_NUM)
568             {
569                 g_object_set(pv->trtick[ttidx],
570                              "x", (gdouble) x, "y", (gdouble) y, NULL);
571             }
572             ttidx++;
573         }
574     }
575 
576     /* last point should be (los_az, 0.0)  */
577     azel_to_xy(pv, pv->pass->los_az, 0.0, &x, &y);
578     points->coords[2 * (num - 1)] = (double)x;
579     points->coords[2 * (num - 1) + 1] = (double)y;
580 
581     g_object_set(pv->track, "points", points, NULL);
582 
583     goo_canvas_points_unref(points);
584 }
585 
586 /**
587  * Manage new size allocation.
588  *
589  * This function is called when the canvas receives a new size allocation,
590  * e.g. when the container is re-sized. The function re-calculates the graph
591  * dimensions based on the new canvas size.
592  */
size_allocate_cb(GtkWidget * widget,GtkAllocation * allocation,gpointer data)593 static void size_allocate_cb(GtkWidget * widget, GtkAllocation * allocation,
594                              gpointer data)
595 {
596     GtkPolarPlot   *polv;
597     GooCanvasPoints *prec;
598     gfloat          x, y;
599     GooCanvasAnchorType     anch = GOO_CANVAS_ANCHOR_CENTER;
600 
601     if (gtk_widget_get_realized(widget))
602     {
603         /* get graph dimensions */
604         polv = GTK_POLAR_PLOT(data);
605 
606         polv->size = MIN(allocation->width, allocation->height);
607         polv->r = (polv->size / 2) - POLV_DEFAULT_MARGIN;
608         polv->cx = allocation->width / 2;
609         polv->cy = allocation->height / 2;
610 
611         goo_canvas_set_bounds(GOO_CANVAS(GTK_POLAR_PLOT(polv)->canvas), 0, 0,
612                               allocation->width, allocation->height);
613 
614         /* background item */
615         g_object_set(polv->bgd, "width", (gdouble) allocation->width,
616                      "height", (gdouble) allocation->height, NULL);
617 
618         /* update coordinate system */
619         g_object_set(polv->C00,
620                      "center-x", (gdouble) polv->cx,
621                      "center-y", (gdouble) polv->cy,
622                      "radius-x", (gdouble) polv->r,
623                      "radius-y", (gdouble) polv->r, NULL);
624         g_object_set(polv->C30,
625                      "center-x", (gdouble) polv->cx,
626                      "center-y", (gdouble) polv->cy,
627                      "radius-x", (gdouble) 0.6667 * polv->r,
628                      "radius-y", (gdouble) 0.6667 * polv->r, NULL);
629         g_object_set(polv->C60,
630                      "center-x", (gdouble) polv->cx,
631                      "center-y", (gdouble) polv->cy,
632                      "radius-x", (gdouble) 0.333 * polv->r,
633                      "radius-y", (gdouble) 0.333 * polv->r, NULL);
634 
635         /* horizontal line */
636         prec = goo_canvas_points_new(2);
637         prec->coords[0] = polv->cx - polv->r - POLV_LINE_EXTRA;
638         prec->coords[1] = polv->cy;
639         prec->coords[2] = polv->cx + polv->r + POLV_LINE_EXTRA;
640         prec->coords[3] = polv->cy;
641         g_object_set(polv->hl, "points", prec, NULL);
642 
643         /* vertical line */
644         prec->coords[0] = polv->cx;
645         prec->coords[1] = polv->cy - polv->r - POLV_LINE_EXTRA;
646         prec->coords[2] = polv->cx;
647         prec->coords[3] = polv->cy + polv->r + POLV_LINE_EXTRA;
648         g_object_set(polv->vl, "points", prec, NULL);
649 
650         /* free memory */
651         goo_canvas_points_unref(prec);
652 
653         /* N/E/S/W */
654         azel_to_xy(polv, 0.0, 0.0, &x, &y);
655         correct_pole_coor(polv, POLAR_PLOT_POLE_N, &x, &y, &anch);
656         g_object_set(polv->N, "x", x, "y", y, NULL);
657 
658         azel_to_xy(polv, 90.0, 0.0, &x, &y);
659         correct_pole_coor(polv, POLAR_PLOT_POLE_E, &x, &y, &anch);
660         g_object_set(polv->E, "x", x, "y", y, NULL);
661 
662         azel_to_xy(polv, 180.0, 0.0, &x, &y);
663         correct_pole_coor(polv, POLAR_PLOT_POLE_S, &x, &y, &anch);
664         g_object_set(polv->S, "x", x, "y", y, NULL);
665 
666         azel_to_xy(polv, 270.0, 0.0, &x, &y);
667         correct_pole_coor(polv, POLAR_PLOT_POLE_W, &x, &y, &anch);
668         g_object_set(polv->W, "x", x, "y", y, NULL);
669 
670         /* cursor track */
671         g_object_set(polv->curs,
672                      "x", (gfloat) (polv->cx - polv->r - 2 * POLV_LINE_EXTRA),
673                      "y", (gfloat) (polv->cy + polv->r + POLV_LINE_EXTRA),
674                      NULL);
675 
676         /* location name */
677         g_object_set(polv->locnam,
678                      "x", (gfloat) (polv->cx - polv->r - 2 * POLV_LINE_EXTRA),
679                      "y", (gfloat) (polv->cy - polv->r - POLV_LINE_EXTRA),
680                      NULL);
681 
682         /* sky track */
683         if (polv->pass != NULL)
684             update_track(polv);
685     }
686 }
687 
688 /**
689  * Manage canvas realise signals.
690  *
691  * This function is used to re-initialise the graph dimensions when
692  * the graph is realized, i.e. displayed for the first time. This is
693  * necessary in order to compensate for missing "re-allocate" signals for
694  * graphs that have not yet been realised, e.g. when opening several module
695  */
on_canvas_realized(GtkWidget * canvas,gpointer data)696 static void on_canvas_realized(GtkWidget * canvas, gpointer data)
697 {
698     GtkAllocation   aloc;
699 
700     gtk_widget_get_allocation(canvas, &aloc);
701     size_allocate_cb(canvas, &aloc, data);
702 }
703 
704 /** Manage mouse motion events. */
on_motion_notify(GooCanvasItem * item,GooCanvasItem * target,GdkEventMotion * event,gpointer data)705 static gboolean on_motion_notify(GooCanvasItem * item, GooCanvasItem * target,
706                                  GdkEventMotion * event, gpointer data)
707 {
708     GtkPolarPlot   *polv = GTK_POLAR_PLOT(data);
709     gfloat          az, el;
710     gchar          *text;
711 
712     (void)item;
713     (void)target;
714 
715     if (polv->cursinfo)
716     {
717         xy_to_azel(polv, event->x, event->y, &az, &el);
718 
719         if (el > 0.0)
720         {
721             /* cursor track */
722             text = g_strdup_printf("AZ %.0f\302\260\nEL %.0f\302\260", az, el);
723             g_object_set(polv->curs, "text", text, NULL);
724             g_free(text);
725         }
726         else
727         {
728             g_object_set(polv->curs, "text", "", NULL);
729         }
730     }
731 
732     return TRUE;
733 }
734 
735 /**
736  * Finish canvas item setup.
737  *
738  * This function is called when a canvas item is created. Its purpose is to connect
739  * the corresponding signals to the created items.
740  */
on_item_created(GooCanvas * canvas,GooCanvasItem * item,GooCanvasItemModel * model,gpointer data)741 static void on_item_created(GooCanvas * canvas, GooCanvasItem * item,
742                             GooCanvasItemModel * model, gpointer data)
743 {
744     (void)canvas;
745     if (!goo_canvas_item_model_get_parent(model))
746     {
747         /* root item / canvas */
748         g_signal_connect(item, "motion_notify_event",
749                          (GCallback) on_motion_notify, data);
750     }
751 }
752 
753 /**
754  * Create a new GtkPolarPlot widget.
755  *
756  * @param qth  Pointer to the QTH.
757  * @param pass Pointer to the satellite pass to display. If NULL no
758  *             pass will be displayed.
759  *
760  */
gtk_polar_plot_new(qth_t * qth,pass_t * pass)761 GtkWidget *gtk_polar_plot_new(qth_t * qth, pass_t * pass)
762 {
763     GtkPolarPlot   *polv;
764     GooCanvasItemModel *root;
765 
766     polv = GTK_POLAR_PLOT(g_object_new(GTK_TYPE_POLAR_PLOT, NULL));
767 
768     polv->qth = qth;
769 
770     if (pass != NULL)
771         polv->pass = copy_pass(pass);
772 
773     /* get settings */
774     polv->swap = sat_cfg_get_int(SAT_CFG_INT_POLAR_ORIENTATION);
775     polv->qthinfo = sat_cfg_get_bool(SAT_CFG_BOOL_POL_SHOW_QTH_INFO);
776     polv->extratick = sat_cfg_get_bool(SAT_CFG_BOOL_POL_SHOW_EXTRA_AZ_TICKS);
777     polv->cursinfo = TRUE;
778 
779     /* create the canvas */
780     polv->canvas = goo_canvas_new();
781     gtk_widget_set_size_request(polv->canvas,
782                                 POLV_DEFAULT_SIZE, POLV_DEFAULT_SIZE);
783     goo_canvas_set_bounds(GOO_CANVAS(polv->canvas), 0, 0,
784                           POLV_DEFAULT_SIZE, POLV_DEFAULT_SIZE);
785 
786     /* connect size-request signal */
787     g_signal_connect(polv->canvas, "size-allocate",
788                      G_CALLBACK(size_allocate_cb), polv);
789     g_signal_connect(polv->canvas, "item_created",
790                      (GCallback) on_item_created, polv);
791     g_signal_connect_after(polv->canvas, "realize",
792                            (GCallback) on_canvas_realized, polv);
793 
794     gtk_widget_show(polv->canvas);
795 
796     /* Create the canvas model */
797     root = create_canvas_model(polv);
798     goo_canvas_set_root_item_model(GOO_CANVAS(polv->canvas), root);
799     g_object_unref(root);
800 
801     if (polv->pass != NULL)
802         create_track(polv);
803 
804     gtk_box_pack_start(GTK_BOX(polv), polv->canvas, TRUE, TRUE, 0);
805 
806     return GTK_WIDGET(polv);
807 }
808 
809 /**
810  * Set new pass
811  *
812  * @param plot Pointer to the GtkPolarPlot widget.
813  * @param pass Pointer to the new pass data. Use NULL to disable
814  *             display of pass.
815  */
gtk_polar_plot_set_pass(GtkPolarPlot * plot,pass_t * pass)816 void gtk_polar_plot_set_pass(GtkPolarPlot * plot, pass_t * pass)
817 {
818     GooCanvasItemModel *root;
819     gint            idx, i;
820 
821     /* remove sky track, time ticks and the pass itself */
822     if (plot->pass != NULL)
823     {
824         /* remove sat from canvas */
825         root = goo_canvas_get_root_item_model(GOO_CANVAS(plot->canvas));
826         idx = goo_canvas_item_model_find_child(root, plot->track);
827 
828         if (idx != -1)
829             goo_canvas_item_model_remove_child(root, idx);
830 
831         for (i = 0; i < TRACK_TICK_NUM; i++)
832         {
833             idx = goo_canvas_item_model_find_child(root, plot->trtick[i]);
834             if (idx != -1)
835                 goo_canvas_item_model_remove_child(root, idx);
836         }
837 
838         free_pass(plot->pass);
839         plot->pass = NULL;
840     }
841 
842     if (pass != NULL)
843     {
844         plot->pass = copy_pass(pass);
845         create_track(plot);
846     }
847 }
848 
849 /**
850  * Set target object position
851  *
852  * @param plot Pointer to the GtkPolarPlot widget
853  * @param az Azimuth of the target object
854  * @param el Elevation of the target object
855  *
856  * If either az or el are negative the target object will be hidden
857  */
gtk_polar_plot_set_target_pos(GtkPolarPlot * plot,gdouble az,gdouble el)858 void gtk_polar_plot_set_target_pos(GtkPolarPlot * plot, gdouble az, gdouble el)
859 {
860     GooCanvasItemModel *root;
861     gint            idx;
862     gfloat          x, y;
863     guint32         col;
864 
865     if (plot == NULL)
866         return;
867 
868     root = goo_canvas_get_root_item_model(GOO_CANVAS(plot->canvas));
869 
870     if ((az < 0.0) || (el < 0.0))
871     {
872         if (plot->target != NULL)
873         {
874             /* the target object is visible; delete it */
875             idx = goo_canvas_item_model_find_child(root, plot->target);
876             if (idx != -1)
877                 goo_canvas_item_model_remove_child(root, idx);
878 
879             plot->target = NULL;
880         }
881         /* else the target object is not visible; nothing to do */
882     }
883     else
884     {
885         /* we need to either update or create the object */
886         azel_to_xy(plot, az, el, &x, &y);
887 
888         if (plot->target != NULL)
889         {
890             /* the target object already exists; move it */
891             g_object_set(plot->target,
892                          "x", x - MARKER_SIZE_HALF,
893                          "y", y - MARKER_SIZE_HALF, NULL);
894         }
895         else
896         {
897             /* the target object does not exist; create it */
898             col = sat_cfg_get_int(SAT_CFG_INT_POLAR_SAT_COL);
899             plot->target = goo_canvas_rect_model_new(root,
900                                                      x - MARKER_SIZE_HALF,
901                                                      y - MARKER_SIZE_HALF,
902                                                      2 * MARKER_SIZE_HALF,
903                                                      2 * MARKER_SIZE_HALF,
904                                                      "fill-color-rgba", col,
905                                                      "stroke-color-rgba", col,
906                                                      NULL);
907         }
908     }
909 }
910 
911 /**
912  * Set controller object position
913  *
914  * @param plot Pointer to the GtkPolarPlot widget
915  * @param az Azimuth of the controller object
916  * @param el Elevation of the controller object
917  *
918  * If either az or el are negative the controller object will be hidden
919  */
gtk_polar_plot_set_ctrl_pos(GtkPolarPlot * plot,gdouble az,gdouble el)920 void gtk_polar_plot_set_ctrl_pos(GtkPolarPlot * plot, gdouble az, gdouble el)
921 {
922     GooCanvasItemModel *root;
923     gint            idx;
924     gfloat          x, y;
925     guint32         col;
926 
927     if (plot == NULL)
928         return;
929 
930     root = goo_canvas_get_root_item_model(GOO_CANVAS(plot->canvas));
931 
932     if ((az < 0.0) || (el < 0.0))
933     {
934         if (plot->ctrl != NULL)
935         {
936             /* the target object is visible; delete it */
937             idx = goo_canvas_item_model_find_child(root, plot->ctrl);
938             if (idx != -1)
939                 goo_canvas_item_model_remove_child(root, idx);
940 
941             plot->ctrl = NULL;
942         }
943         /* else the target object is not visible; nothing to do */
944     }
945     else
946     {
947         /* we need to either update or create the object */
948         azel_to_xy(plot, az, el, &x, &y);
949 
950         if (plot->ctrl != NULL)
951         {
952             /* the target object already exists; move it */
953             g_object_set(plot->ctrl, "center_x", x, "center_y", y, NULL);
954         }
955         else
956         {
957             /* the target object does not exist; create it */
958             col = sat_cfg_get_int(SAT_CFG_INT_POLAR_SAT_COL);
959             plot->ctrl = goo_canvas_ellipse_model_new(root,
960                                                       x, y, 7, 7,
961                                                       "fill-color-rgba",
962                                                       0xFF00000F,
963                                                       "stroke-color-rgba", col,
964                                                       "line-width", 0.8, NULL);
965         }
966     }
967 }
968 
969 /**
970  * Set rotator object position
971  *
972  * @param plot Pointer to the GtkPolarPlot widget
973  * @param az Azimuth of the rotator object
974  * @param el Elevation of the rotator object
975  *
976  * If either az or el are negative the controller object will be hidden
977  */
gtk_polar_plot_set_rotor_pos(GtkPolarPlot * plot,gdouble az,gdouble el)978 void gtk_polar_plot_set_rotor_pos(GtkPolarPlot * plot, gdouble az, gdouble el)
979 {
980     GooCanvasItemModel *root;
981     GooCanvasPoints *prec;
982     gint            idx;
983     gfloat          x, y;
984     guint32         col;
985 
986     if (plot == NULL)
987         return;
988 
989     root = goo_canvas_get_root_item_model(GOO_CANVAS(plot->canvas));
990 
991     if ((az < 0.0) || (el < 0.0))
992     {
993         if (plot->rot1 != NULL)
994         {
995             /* the target object is visible; delete it */
996             idx = goo_canvas_item_model_find_child(root, plot->rot1);
997             if (idx != -1)
998                 goo_canvas_item_model_remove_child(root, idx);
999 
1000             plot->rot1 = NULL;
1001         }
1002         if (plot->rot2 != NULL)
1003         {
1004             /* the target object is visible; delete it */
1005             idx = goo_canvas_item_model_find_child(root, plot->rot2);
1006             if (idx != -1)
1007                 goo_canvas_item_model_remove_child(root, idx);
1008 
1009             plot->rot2 = NULL;
1010         }
1011         if (plot->rot3 != NULL)
1012         {
1013             /* the target object is visible; delete it */
1014             idx = goo_canvas_item_model_find_child(root, plot->rot3);
1015             if (idx != -1)
1016                 goo_canvas_item_model_remove_child(root, idx);
1017 
1018             plot->rot3 = NULL;
1019         }
1020         if (plot->rot4 != NULL)
1021         {
1022             /* the target object is visible; delete it */
1023             idx = goo_canvas_item_model_find_child(root, plot->rot4);
1024             if (idx != -1)
1025                 goo_canvas_item_model_remove_child(root, idx);
1026 
1027             plot->rot4 = NULL;
1028         }
1029     }
1030     else
1031     {
1032         /* we need to either update or create the object */
1033         azel_to_xy(plot, az, el, &x, &y);
1034         col = sat_cfg_get_int(SAT_CFG_INT_POLAR_SAT_COL);
1035 
1036         if (plot->rot1 != NULL)
1037         {
1038             /* the target object already exists; move it */
1039             prec = goo_canvas_points_new(2);
1040             prec->coords[0] = x;
1041             prec->coords[1] = y - 4;
1042             prec->coords[2] = x;
1043             prec->coords[3] = y - 14;
1044             g_object_set(plot->rot1, "points", prec, NULL);
1045             goo_canvas_points_unref(prec);
1046         }
1047         else
1048         {
1049             /* the target object does not exist; create it */
1050             plot->rot1 = goo_canvas_polyline_model_new_line(root,
1051                                                             x, y - 4, x,
1052                                                             y - 14,
1053                                                             "fill-color-rgba",
1054                                                             col,
1055                                                             "stroke-color-rgba",
1056                                                             col, "line-width",
1057                                                             1.0, NULL);
1058         }
1059         if (plot->rot2 != NULL)
1060         {
1061             /* the target object already exists; move it */
1062             prec = goo_canvas_points_new(2);
1063             prec->coords[0] = x + 4;
1064             prec->coords[1] = y;
1065             prec->coords[2] = x + 14;
1066             prec->coords[3] = y;
1067             g_object_set(plot->rot2, "points", prec, NULL);
1068             goo_canvas_points_unref(prec);
1069         }
1070         else
1071         {
1072             /* the target object does not exist; create it */
1073             plot->rot2 = goo_canvas_polyline_model_new_line(root,
1074                                                             x + 4, y, x + 14,
1075                                                             y,
1076                                                             "fill-color-rgba",
1077                                                             col,
1078                                                             "stroke-color-rgba",
1079                                                             col, "line-width",
1080                                                             1.0, NULL);
1081         }
1082         if (plot->rot3 != NULL)
1083         {
1084             /* the target object already exists; move it */
1085             prec = goo_canvas_points_new(2);
1086             prec->coords[0] = x;
1087             prec->coords[1] = y + 4;
1088             prec->coords[2] = x;
1089             prec->coords[3] = y + 14;
1090             g_object_set(plot->rot3, "points", prec, NULL);
1091             goo_canvas_points_unref(prec);
1092         }
1093         else
1094         {
1095             /* the target object does not exist; create it */
1096             plot->rot3 = goo_canvas_polyline_model_new_line(root,
1097                                                             x, y + 4, x,
1098                                                             y + 14,
1099                                                             "fill-color-rgba",
1100                                                             col,
1101                                                             "stroke-color-rgba",
1102                                                             col, "line-width",
1103                                                             1.0, NULL);
1104         }
1105         if (plot->rot4 != NULL)
1106         {
1107             /* the target object already exists; move it */
1108             prec = goo_canvas_points_new(2);
1109             prec->coords[0] = x - 4;
1110             prec->coords[1] = y;
1111             prec->coords[2] = x - 14;
1112             prec->coords[3] = y;
1113             g_object_set(plot->rot4, "points", prec, NULL);
1114             goo_canvas_points_unref(prec);
1115         }
1116         else
1117         {
1118             /* the target object does not exist; create it */
1119             plot->rot4 = goo_canvas_polyline_model_new_line(root,
1120                                                             x - 4, y, x - 14,
1121                                                             y,
1122                                                             "fill-color-rgba",
1123                                                             col,
1124                                                             "stroke-color-rgba",
1125                                                             col, "line-width",
1126                                                             1.0, NULL);
1127         }
1128     }
1129 }
1130 
1131 /**
1132  * Show/hide time tick
1133  *
1134  * @param plot Pointer to the GtkPolarPlot widget
1135  * @param show TRUE => show tick. FALSE => don't show
1136  *
1137  */
gtk_polar_plot_show_time_ticks(GtkPolarPlot * plot,gboolean show)1138 void gtk_polar_plot_show_time_ticks(GtkPolarPlot * plot, gboolean show)
1139 {
1140     (void)plot;
1141     (void)show;
1142     g_print("NOT IMPLEMENTED %s\n", __func__);
1143 }
1144