Listing 4

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

/* glg specific include files */
#include "GlgApi.h"
#include "GlgMain.h"

/* coordinate conversion utility */
#define RadToDeg( angle )   ( ( angle ) / M_PI * 180. )

void Input( GlgObject, GlgAnyType, GlgAnyType );
GlgBoolean UpdatePlane( GlgAnyType data );
void PositionPlane();

GlgObject viewport; /* the primary object displayed in the window */
GlgObject GISObject; /* an integrated GIS object displayed on screen */
GlgAppContext AppContext;
long 
  UseGrid = False,
  UpdateInterval = 30; /* update the drawing every 30 milliseconds */
/* the structure used to position and display the plane on screen */
typedef struct _plane_t
{
  GlgPoint start; /* start lat/lon coordinates */
  GlgPoint destination; /* end lat/lon coordinates */
  GlgPoint lat_lon; /* the current lat/lon coordinates */
  GlgPoint last_lat_lon;
  GlgObject graphics; /* the actual plane object in the drawing */
  double path_position; /* the percentage of the trajectory completed */
  double speed;
  GlgPoint xyz; /* the current object coordinates corresponding to lat_lon */
  double angle; /* the plane's angle, in radians */
} plane_t;

plane_t Plane;

int GlgMain( argc, argv, InitAppContext )
     int argc;
     char *argv[];
     GlgAppContext InitAppContext;
{
  AppContext = GlgInit( False, InitAppContext, argc, argv );

  /* load the drawing from a file and exit if not found */
  viewport = GlgLoadWidgetFromFile( "drawing.g" );
  if( !viewport )
    exit( 1 ); 
  /* add a callback that will handle all button click events */
  GlgAddCallback( viewport, GLG_INPUT_CB,  (GlgCallbackProc)Input,  0 );
  /* sets the size of the viewport on the screen */
  GlgSetGResource( viewport, "Point1", -700., -700., 0. );
  GlgSetGResource( viewport, "Point2", 700., 700., 0. );
  /* sets the name of the viewport that will be drawn on the title bar */
  GlgSetSResource( viewport, "ScreenName", "Map Server Demo" );

  /* set GIS zoom mode, so that all zooming and panning in viewport with the 
     GIS object zooms the GIS object itself instead of just scaling image */
  GISObject = GlgGetResourceObject( viewport, "WorldMap" );
  GlgSetGISZoom( viewport, "MapViewport", GISObject );

  /* draw the viewport for the first time */
  GlgInitialDraw( viewport );

  /* initialize a plane flying from London to San Francisco */
  Plane.speed = 0.005;
  Plane.start.x = -0.13;
  Plane.start.y = 51.50;
  Plane.lat_lon = Plane.start;
  Plane.destination.x = -122.55;
  Plane.destination.y = 37.79;
  Plane.path_position = 0.;
  Plane.last_lat_lon = Plane.lat_lon;

  /* the actual plane is the object named "Plane" in the drawing */
  Plane.graphics = GlgGetResourceObject( viewport, "Plane" );
  
  /* start the main loop to process events and handle updates */
  GlgAddTimeOut( AppContext, UpdateInterval,(GlgTimerProc)UpdatePlane, NULL );
  return GlgMainLoop( AppContext );
}
/* Input callback function handles clicking of all buttons in drawing. It uses
   GLG integrated pan and zoom functions to pan and zoom around the map */
void Input( GlgObject viewport, GlgAnyType client_data, GlgAnyType call_data )
{
   GlgObject
     message_obj;
   char
     * format,
     * action,
     * origin,
     * full_origin,
     * sub_action;
   message_obj = (GlgObject) call_data;
   /* message object received has 5 S resources which describe the event */
   GlgGetSResource( message_obj, "Format", &format );
   GlgGetSResource( message_obj, "Action", &action );
   GlgGetSResource( message_obj, "Origin", &origin );
   GlgGetSResource( message_obj, "FullOrigin", &full_origin );
   GlgGetSResource( message_obj, "SubAction", &sub_action );

   /* if we get a window close event, exit the application */
   if( strcmp( format, "Window" ) == 0 )
     {
       if( strcmp( action, "DeleteWindow" ) == 0 )
      exit(0);
     }
   /* if we get a button click event, handle it appropriately */
   else if( strcmp( format, "Button" ) == 
                                  0 && strcmp( action, "Activate" ) == 0 )
     {
       if( strcmp( origin, "QuitButton" ) == 0 )
      exit(0);
       else if( strcmp( origin, "ZoomInButton" ) == 0 )
         GlgSetZoom( viewport, "MapViewport", 'i', 2. );
       else if( strcmp( origin, "ZoomOutButton" ) == 0 )
         GlgSetZoom( viewport, "MapViewport", 'o', 2. );
       else if( strcmp( origin, "ZoomToButton" ) == 0 )
         GlgSetZoom( viewport, "MapViewport", 't', 0. );
       else if( strcmp( origin, "ResetZoomButton" ) == 0 )
         {
           /* reset the center and extent of the GIS object
              to what they originally were in the drawing */
           GlgSetGResource( GISObject, "GISCenter", -70., 40., 0. );
           GlgSetGResource( GISObject, "GISExtent",14000000.,14000000., 0. );
         }
       else if( strcmp( origin, "PanRightButton" ) == 0 )
         GlgSetZoom( viewport, "MapViewport", 'r', 0.4 );
       else if( strcmp( origin, "PanLeftButton" ) == 0 )
         GlgSetZoom( viewport, "MapViewport", 'l', 0.4 );
       else if( strcmp( origin, "ToggleGridButton" ) == 0 )
         {
           char * layers;
           /* toggle the use of the grid by changing the 
             layers which are used in the GIS object */
           if( !UseGrid )
             layers = "default,grid";
           else
             layers = "default";
           GlgSetSResource( GISObject, "GISLayers", layers );
           UseGrid = !UseGrid;
         }
     }
   /* update the viewport to make all changes visible */
   PositionPlane();
   GlgUpdate( viewport );
}
/* a periodically called function that updates the plane's position */
GlgBoolean UpdatePlane( GlgAnyType data )
{
  /* restart the plane if it has reached its destination */
  if( Plane.path_position >= 1. )
    {
      Plane.path_position = 0.;
      Plane.lat_lon = Plane.start;
      Plane.last_lat_lon = Plane.start;
    }
  else
    {
      Plane.last_lat_lon = Plane.lat_lon;
      Plane.path_position += Plane.speed;
    }
  /* get plane's lat/lon coordinates given its position on its trajectory */
  Plane.lat_lon.x = ( Plane.destination.x - Plane.start.x ) * 
                                     Plane.path_position + Plane.start.x;
  Plane.lat_lon.y = ( Plane.destination.y - Plane.start.y ) * 
                                     Plane.path_position + Plane.start.y;
  /* reposition the plane on the map */
  PositionPlane();

  /* update the viewport to reflect the changes made */
  GlgUpdate( viewport );
  GlgSync( viewport );    /* Improves interactive response */
  GlgAddTimeOut( AppContext, UpdateInterval, (GlgTimerProc)UpdatePlane, 
           NULL ); /* add a time out to re-enter this function later */
  return False;
}
void PositionPlane()
{
  GlgPoint last_xyz;
  double length, angle;
  /* convert from the plane's lat and lon coordinates to object coordinates */
  GlgGISConvert( GISObject, NULL, GLG_OBJECT_COORD,
           /* Lat/Lon to XY */ False,
           &(Plane.lat_lon), &(Plane.xyz) );
  /* if the plane is invisible, don't show it */
  if( Plane.xyz.z < 0 ||
      Plane.xyz.x < -1000. ||
      Plane.xyz.y < -1000. ||
      Plane.xyz.x >  1000. ||
      Plane.xyz.y >  1000. || 
      Plane.path_position == 0. )
    {
      GlgSetDResource( Plane.graphics, "Visibility", 0. );
      return;
    }
  /* get the plane's angle */
  GlgGISConvert( GISObject, NULL, GLG_OBJECT_COORD,
                 /* Lat/Lon to XY */ False,
                 &(Plane.last_lat_lon), &last_xyz );
  length = sqrt(( Plane.xyz.x - last_xyz.x ) * ( Plane.xyz.x - last_xyz.x ) +
                ( Plane.xyz.y - last_xyz.y ) * ( Plane.xyz.y - last_xyz.y ) );
  if( length == 0. )
    angle = 0.;
  else
    {
       angle = acos( (  Plane.xyz.x - last_xyz.x ) / length );
       if( Plane.xyz.y - last_xyz.y < 0. )
         angle = -angle;
    }
  /* make the plane visible and set its position and angle */
  GlgSetDResource( Plane.graphics, "Visibility", 1. );
  GlgSetGResource( Plane.graphics, "Position", Plane.xyz.x, Plane.xyz.y, 0. );
  GlgSetDResource( Plane.graphics, "Angle", RadToDeg( angle ) );
}