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