Logo Search packages:      
Sourcecode: baken version File versions  Download package

detail_map.c

/*
 *   Copyright (C) 1999-2001 by Jonathan Naylor G4KLX
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <unistd.h>

#include <sys/stat.h>

#include <gtk/gtk.h>

#include "global.h"

GdkGC *DetailBackgroundGC;
GdkGC *DetailBeaconsGC;
GdkGC *DetailCitiesGC;
GdkGC *DetailLandGC;
GdkGC *DetailUserGC;
GdkGC *DetailTextBackGC;
GdkGC *DetailTextForeGC;

static int       Visible       = FALSE;

static GtkWidget *DetailWindow = NULL;
static GtkWidget *DetailMap    = NULL;
static GtkWidget *DetailText   = NULL;

static GdkFont   *MapFont;
static GdkFont   *TextFont;

static GdkPixmap *MapData;
static GdkPixmap *TextData;

static double Begin_Latitude;
static double Begin_Longitude;

static double X_Scale;
static double Y_Scale;

static void Draw_Map(void);
static void Draw_Arrow(int, int, int);

static int Normalise_X_Text(int x, char *text)
{
      int width;

      width = gdk_string_width(MapFont, text);

      if ((x + width / 2) > DETAIL_MAP_WIDTH)
            return DETAIL_MAP_WIDTH - width;

      if ((x - width / 2) < 0)
            return 0;

      return x - width / 2;
}

static int Normalise_Y_Text(int y, char *text)
{
      int height;

      height = gdk_string_height(MapFont, text) + 3;

      if ((y + height + 3) > DETAIL_MAP_HEIGHT)
            return y - 6;

      return y + height + 3;
}

static void Calc_X_Scale(void)
{
      X_Scale = (double)DETAIL_MAP_WIDTH / SQUARE_WIDTH;
}

static void Calc_Y_Scale(void)
{
      Y_Scale = (double)DETAIL_MAP_HEIGHT / SQUARE_HEIGHT;
}

static int Long_To_X(double Longitude)
{
      int x;

      if (Longitude > 180.0)  Longitude -= 360.0;
      if (Longitude < -180.0) Longitude += 360.0;

      x = (int)(X_Scale * (Longitude - Begin_Longitude) + 0.5);

      return x;
}

static int Lat_To_Y(double Latitude)
{
      int y;

      y = DETAIL_MAP_HEIGHT - (int)(Y_Scale * (Latitude - Begin_Latitude) + 0.5);

      return y;
}

static double X_To_Long(int x)
{
      double longitude;

      longitude = Begin_Longitude + (double)x / X_Scale;

      return longitude;
}

static double Y_To_Lat(int y)
{
      double latitude;

      latitude = Begin_Latitude + (double)(DETAIL_MAP_HEIGHT - y) / Y_Scale;

      return latitude;
}

static int X_In_Range(int x)
{
      return x >= 0 && x < DETAIL_MAP_WIDTH;
}

static int Y_In_Range(int y)
{
      return y >= 0 && y < DETAIL_MAP_HEIGHT;
}

static void Draw_Map(void)
{
      static int ignore = FALSE;
      char Buffer[101];
      double last_lat, last_long;
      double llat, llong;
      int last_x = 0, last_y = 0;
      int x = 0, y = 0;
      short *n, *slat, *slong;
      FILE *fp;

      sprintf(Buffer, "%s/mapfile", MAPDIR);

      if ((fp = fopen(Buffer, "r")) == NULL) {
            Error_Box("Cannot open Map file");
            return;
      }

      while (fread(Buffer, 6, 1, fp) == 1) {
            n     = (short *)(Buffer + 0);
            slat  = (short *)(Buffer + 2);
            slong = (short *)(Buffer + 4);

            llat  = (double)*slat  / 60.0;
            llong = (double)*slong / 60.0;

            if (*n >= 7000) {
                  ignore = TRUE;
            } else if (*n >= 6000) {
                  ignore = FALSE;
            } else if (*n >= 5000) {
                  ignore = FALSE;
            } else if (*n >= 4000) {
                  ignore = TRUE;
            } else if (*n >= 3000) {
                  ignore = TRUE;
            } else if (*n >= 2000) {
                  ignore = FALSE;
            } else if (*n >= 1000) {
                  ignore = FALSE;
            }

            if (ignore)
                  continue;

            x = Long_To_X(llong);
            y = Lat_To_Y(llat);

            if (*n >= 1000) {
                  last_lat  = llat;
                  last_long = llong;
                  last_x = x;
                  last_y = y;
                  continue;
            }

            if (abs(last_x - x) > DETAIL_MAP_WIDTH / 2) {
                  last_lat  = llat;
                  last_long = llong;
                  last_x = x;
                  last_y = y;
                  continue;
            }

            if ((X_In_Range(last_x) && Y_In_Range(last_y)) || (X_In_Range(x) && Y_In_Range(y)))
                  gdk_draw_line(MapData, DetailLandGC, last_x, last_y, x, y);

            last_lat  = llat;
            last_long = llong;
            last_x = x;
            last_y = y;
      }

      fclose(fp);
}

static void Draw_Home(void)
{
      int x, y;

      x = Long_To_X(User_Data.Longitude);
      y = Lat_To_Y(User_Data.Latitude);

      if (X_In_Range(x) && Y_In_Range(y)) {
            gdk_draw_rectangle(MapData, DetailUserGC, TRUE, x - 2, y - 2, 4, 4);

            x = Normalise_X_Text(x, User_Data.Callsign);
            y = Normalise_Y_Text(y, User_Data.Callsign);

            gdk_draw_string(MapData, MapFont, DetailUserGC, x, y, User_Data.Callsign);
      }
}

static void Draw_Cities(void)
{
      struct City_Struct *City_Data;
      GSList *City;
      int x, y;

      City = City_List;

      while (City != NULL) {
            City_Data = (struct City_Struct *)City->data;

            x = Long_To_X(City_Data->Longitude);
            y = Lat_To_Y(City_Data->Latitude);

            if (X_In_Range(x) && Y_In_Range(y)) {
                  gdk_draw_rectangle(MapData, DetailCitiesGC, TRUE, x - 2, y - 2, 4, 4);

                  x = Normalise_X_Text(x, City_Data->Name);
                  y = Normalise_Y_Text(y, City_Data->Name);

                  gdk_draw_string(MapData, MapFont, DetailCitiesGC, x, y, City_Data->Name);
            }

            City = g_slist_next(City);
      }
}

static void Draw_Beacons(void)
{
      struct Beacon_Struct *Beacon_Data;
      GSList *Beacon;
      int i, x, y;

      Beacon = Beacon_List;

      while (Beacon != NULL) {
            Beacon_Data = (struct Beacon_Struct *)Beacon->data;

            if (Check_Beacon(Beacon_Data)) {
                  x = Long_To_X(Beacon_Data->Longitude);
                  y = Lat_To_Y(Beacon_Data->Latitude);

                  if (X_In_Range(x) && Y_In_Range(y)) {
                        gdk_draw_rectangle(MapData, DetailBeaconsGC, TRUE, x - 3, y - 3, 6, 6);

                        for (i = 0; i < Beacon_Data->Angle_Count; i++)
                              Draw_Arrow(x, y, Beacon_Data->Angle[i]);

                        x = Normalise_X_Text(x, Beacon_Data->Callsign);
                        y = Normalise_Y_Text(y, Beacon_Data->Callsign);

                        gdk_draw_string(MapData, MapFont, DetailBeaconsGC, x, y, Beacon_Data->Callsign);
                  }
            }

            Beacon = g_slist_next(Beacon);
      }
}

static void Draw_Arrow(int x, int y, int angle)
{
      int line_end_x, line_end_y;
      int back_arrow_x, back_arrow_y;
      double sin_angle, cos_angle;
      int x_len, y_len;
      GdkPoint points[3];

      sin_angle = sin(RAD((double)angle));
      cos_angle = cos(RAD((double)angle));

      line_end_x = x + (int)(14.0 * sin_angle + 0.5);
      line_end_y = y - (int)(14.0 * cos_angle + 0.5);

      gdk_draw_line(MapData, DetailBeaconsGC, x, y, line_end_x, line_end_y);

      back_arrow_x = x + (int)(7.0 * sin_angle + 0.5);
      back_arrow_y = y - (int)(7.0 * cos_angle + 0.5);

      x_len = (int)(3.0 * cos_angle + 0.5);
      y_len = (int)(3.0 * sin_angle + 0.5);

      points[0].x = back_arrow_x - x_len;
      points[0].y = back_arrow_y - y_len;

      points[1].x = line_end_x;
      points[1].y = line_end_y;

      points[2].x = back_arrow_x + x_len;
      points[2].y = back_arrow_y + y_len;

      gdk_draw_polygon(MapData, DetailBeaconsGC, TRUE, points, 3);
}

static gint DetailMapDestroyCb(GtkWidget *w, gpointer data)
{
      gtk_widget_destroy(DetailWindow);

      gdk_pixmap_unref(MapData);
      gdk_pixmap_unref(TextData);

      MapData  = NULL;
      TextData = NULL;

      Visible = FALSE;

      return TRUE;
}

static gint DetailTextExposeCb(GtkWidget *w, GdkEventExpose *event)
{
      if (TextData == NULL) {
            TextFont = gdk_font_load("-adobe-helvetica-medium-r-normal-*-*-120-*-*-p-*-iso8859-1");

            DetailTextBackGC = gdk_gc_new(DetailText->window);
            gdk_gc_set_foreground(DetailTextBackGC, &Text_Bg_Colour);
              gdk_gc_set_font(DetailTextBackGC, TextFont);

            DetailTextForeGC = gdk_gc_new(DetailText->window);
            gdk_gc_set_foreground(DetailTextForeGC, &Text_Fg_Colour);
              gdk_gc_set_font(DetailTextForeGC, TextFont);

            TextData = gdk_pixmap_new(DetailText->window, DETAIL_MAP_WIDTH, DETAIL_MAP_TEXT_HEIGHT, -1);
            gdk_draw_rectangle(TextData, DetailTextBackGC, TRUE, 0, 0, DETAIL_MAP_WIDTH, DETAIL_MAP_TEXT_HEIGHT);
            gdk_draw_pixmap(DetailText->window, DetailTextBackGC, TextData, 0, 0, 0, 0, DETAIL_MAP_WIDTH, DETAIL_MAP_TEXT_HEIGHT);
      } else {
            gdk_draw_pixmap(DetailText->window, DetailTextBackGC, TextData, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height);
      }

      return FALSE;
}

static gint DetailMapMotionCb(GtkWidget *widget, GdkEventMotion *event)
{
      static struct Beacon_Struct *Last_Beacon;
      struct Beacon_Struct *Beacon_Data, *Best_Beacon;
      GSList *Beacon;
      double Latitude, Longitude;
      double Bearing, Distance;
      double Best_Distance;
      char buf[51], buffer[101];
      int i;

      Latitude  = Y_To_Lat(event->y);
      Longitude = X_To_Long(event->x);

      Beacon = Beacon_List;
      Best_Beacon   = NULL;
      Best_Distance = 9999.0;

      while (Beacon != NULL) {
            Beacon_Data = (struct Beacon_Struct *)Beacon->data;

            if (Check_Beacon(Beacon_Data) &&
                Beacon_Data->Latitude  >= Begin_Latitude &&
                Beacon_Data->Latitude  <= Begin_Latitude + SQUARE_HEIGHT &&
                Beacon_Data->Longitude >= Begin_Longitude &&
                Beacon_Data->Longitude <= Begin_Longitude + SQUARE_WIDTH) {
                  Calc_Distance_Bearing(Latitude, Longitude, Beacon_Data->Latitude, Beacon_Data->Longitude, &Bearing, &Distance);

                  if (Distance < Best_Distance && Distance < 20.0) {
                        Best_Beacon   = Beacon_Data;
                        Best_Distance = Distance;
                  }
            }

            Beacon = g_slist_next(Beacon);
      }

      if (Best_Beacon != Last_Beacon) {
            if (Best_Beacon != NULL) {
                  Calc_Distance_Bearing(User_Data.Latitude, User_Data.Longitude, Best_Beacon->Latitude, Best_Beacon->Longitude, &Bearing, &Distance);

                  gdk_draw_rectangle(TextData, DetailTextBackGC, TRUE, 0, 0, DETAIL_MAP_WIDTH, DETAIL_MAP_TEXT_HEIGHT);

                  sprintf(buffer, "Call: %-10s", Best_Beacon->Callsign);
                  gdk_draw_string(TextData, TextFont, DetailTextForeGC, TEXT_CALL, TEXT_LINE1, buffer);

                  sprintf(buffer, "Freq: %.3f MHz", Best_Beacon->Frequency);
                  gdk_draw_string(TextData, TextFont, DetailTextForeGC, TEXT_FREQUENCY, TEXT_LINE1, buffer);

                  sprintf(buffer, "Loc: %-6s", Best_Beacon->Locator);
                  gdk_draw_string(TextData, TextFont, DetailTextForeGC, TEXT_LOCATOR, TEXT_LINE1, buffer);

                  if (Best_Beacon->Height > 0) {
                        sprintf(buffer, "Height: %dm ASL", Best_Beacon->Height);
                        gdk_draw_string(TextData, TextFont, DetailTextForeGC, TEXT_HEIGHT, TEXT_LINE1, buffer);
                  }

                  if (strlen(Best_Beacon->QTH) > 0) {
                        sprintf(buffer, "QTH: %-20s", Best_Beacon->QTH);
                        gdk_draw_string(TextData, TextFont, DetailTextForeGC, TEXT_QTH, TEXT_LINE2, buffer);
                  }

                  buffer[0] = '\0';

                  if (strlen(Best_Beacon->Antenna) > 0) {
                        sprintf(buf, "Ant: %s", Best_Beacon->Antenna);
                        strcat(buffer, buf);
                  }

                  if (Best_Beacon->Angle_Count == 0) {
                        strcat(buffer, " - Omni");
                  } else {
                        strcat(buffer, " - ");

                        for (i = 0; i < Best_Beacon->Angle_Count; i++) {
                              sprintf(buf, "%d", Best_Beacon->Angle[i]);
                              if (i > 0) strcat(buffer, "/");
                              strcat(buffer, buf);
                        }

                        strcat(buffer, " deg");
                  }

                  if (strlen(buffer) > 0)
                        gdk_draw_string(TextData, TextFont, DetailTextForeGC, TEXT_ANTENNA, TEXT_LINE2, buffer);

                  if (strlen(Best_Beacon->Power) > 0) {
                        sprintf(buffer, "Power: %s Watts", Best_Beacon->Power);
                        gdk_draw_string(TextData, TextFont, DetailTextForeGC, TEXT_POWER, TEXT_LINE2, buffer);
                  }

                  sprintf(buffer, "Bearing: %.0f deg", Bearing);
                  gdk_draw_string(TextData, TextFont, DetailTextForeGC, TEXT_BEARING, TEXT_LINE3, buffer);

                  sprintf(buffer, "Distance: %.0f kms", Distance);
                  gdk_draw_string(TextData, TextFont, DetailTextForeGC, TEXT_DISTANCE, TEXT_LINE3, buffer);

                  if (strlen(Best_Beacon->Operator) > 0) {
                        sprintf(buffer, "Operator: %s", Best_Beacon->Operator);
                        gdk_draw_string(TextData, TextFont, DetailTextForeGC, TEXT_OPERATOR, TEXT_LINE3, buffer);
                  }

                  gdk_draw_pixmap(DetailText->window, DetailTextBackGC, TextData, 0, 0, 0, 0, DETAIL_MAP_WIDTH, DETAIL_MAP_TEXT_HEIGHT);
            } else {
                  gdk_draw_rectangle(TextData, DetailTextBackGC, TRUE, 0, 0, DETAIL_MAP_WIDTH, DETAIL_MAP_TEXT_HEIGHT);
                  gdk_draw_pixmap(DetailText->window, DetailTextBackGC, TextData, 0, 0, 0, 0, DETAIL_MAP_WIDTH, DETAIL_MAP_TEXT_HEIGHT);
            }

            Last_Beacon = Best_Beacon;
      }

      return TRUE;
}

static gint DetailMapExposeCb(GtkWidget *w, GdkEventExpose *event)
{
      if (MapData == NULL) {
            Calc_X_Scale();
            Calc_Y_Scale();

            MapFont = gdk_font_load("-adobe-helvetica-medium-r-normal-*-*-120-*-*-p-*-iso8859-1");

            DetailBackgroundGC = gdk_gc_new(DetailMap->window);
            gdk_gc_set_foreground(DetailBackgroundGC, &User_Data.Colours[COLOUR_BACKGROUND]);

            DetailBeaconsGC = gdk_gc_new(DetailMap->window);
            gdk_gc_set_foreground(DetailBeaconsGC, &User_Data.Colours[COLOUR_BEACONS]);
              gdk_gc_set_font(DetailBeaconsGC, MapFont);

            DetailCitiesGC = gdk_gc_new(DetailMap->window);
            gdk_gc_set_foreground(DetailCitiesGC, &User_Data.Colours[COLOUR_CITIES]);
              gdk_gc_set_font(DetailCitiesGC, MapFont);

            DetailLandGC = gdk_gc_new(DetailMap->window);
            gdk_gc_set_foreground(DetailLandGC, &User_Data.Colours[COLOUR_LAND]);

            DetailUserGC = gdk_gc_new(DetailMap->window);
            gdk_gc_set_foreground(DetailUserGC, &User_Data.Colours[COLOUR_USER]);
              gdk_gc_set_font(DetailUserGC, MapFont);

            MapData = gdk_pixmap_new(DetailMap->window, DETAIL_MAP_WIDTH, DETAIL_MAP_HEIGHT, -1);

            Redraw_Detail_Map();
      } else {
            gdk_draw_pixmap(DetailMap->window, DetailBackgroundGC, MapData, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height);
      }

      return FALSE;
}

void Draw_Detail_Map(double Latitude, double Longitude)
{
      GtkWidget *vbox;
      GtkWidget *separator;
      char Locator[9];
      char buffer[51];

      if (!Convert_Angles(Latitude, Longitude, Locator)) {
            g_error("baken: nonsense Lat & Long, %f & %f", Latitude, Longitude);
            return;
      }

      Locator[4] = 'A';
      Locator[5] = 'A';

      Convert_Locator(Locator, &Begin_Latitude, &Begin_Longitude);

      Locator[4] = '\0';

      Begin_Latitude  -= SUB_HEIGHT / 2.0;
      Begin_Longitude -= SUB_WIDTH / 2.0;

      if (Visible) {
            gtk_window_activate_focus(GTK_WINDOW(DetailWindow));
            Redraw_Detail_Map();
      } else {
            DetailWindow = gtk_window_new(GTK_WINDOW_DIALOG);

            gtk_signal_connect(GTK_OBJECT(DetailWindow), "destroy", (GtkSignalFunc)DetailMapDestroyCb, NULL);

            gtk_container_set_border_width(GTK_CONTAINER(DetailWindow), 0);

            vbox = gtk_vbox_new(FALSE, 1);
            gtk_container_border_width(GTK_CONTAINER(vbox), 1);
            gtk_container_add(GTK_CONTAINER(DetailWindow), vbox);

            DetailMap = gtk_drawing_area_new();
            gtk_drawing_area_size(GTK_DRAWING_AREA(DetailMap), DETAIL_MAP_WIDTH, DETAIL_MAP_HEIGHT);
            gtk_widget_set_events(DetailMap, GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK);
            gtk_signal_connect(GTK_OBJECT(DetailMap), "expose_event", (GtkSignalFunc)DetailMapExposeCb, NULL);
            gtk_signal_connect(GTK_OBJECT(DetailMap), "motion_notify_event", (GtkSignalFunc)DetailMapMotionCb, NULL);
            gtk_box_pack_start(GTK_BOX(vbox), DetailMap, FALSE, TRUE, 0);
            gtk_widget_show(DetailMap);

            separator = gtk_hseparator_new();
            gtk_box_pack_start(GTK_BOX(vbox), separator, FALSE, TRUE, 0);
            gtk_widget_show(separator);

            DetailText = gtk_drawing_area_new();
            gtk_drawing_area_size(GTK_DRAWING_AREA(DetailText), DETAIL_MAP_WIDTH, DETAIL_MAP_TEXT_HEIGHT);
            gtk_signal_connect(GTK_OBJECT(DetailText), "expose_event", (GtkSignalFunc)DetailTextExposeCb, NULL);
            gtk_box_pack_start(GTK_BOX(vbox), DetailText, FALSE, TRUE, 0);
            gtk_widget_show(DetailText);

            gtk_widget_show(vbox);
            gtk_widget_show(DetailWindow);
      }

      sprintf(buffer, "Detail of Locator %s", Locator);
      gtk_window_set_title(GTK_WINDOW(DetailWindow), buffer);

      Visible = TRUE;
}

void Redraw_Detail_Map(void)
{
      if (Visible) {
            gdk_draw_rectangle(MapData, DetailBackgroundGC, TRUE, 0, 0, DETAIL_MAP_WIDTH, DETAIL_MAP_HEIGHT);

            Draw_Map();
            Draw_Cities();
            Draw_Home();
            Draw_Beacons();

            gdk_draw_pixmap(DetailMap->window, DetailBackgroundGC, MapData, 0, 0, 0, 0, DETAIL_MAP_WIDTH, DETAIL_MAP_HEIGHT);
      }
}

Generated by  Doxygen 1.6.0   Back to index