import java.awt.*; import java.awt.event.*; import java.lang.reflect.*; import javax.swing.*; import com.genlogic.*; ////////////////////////////////////////////////////////////////////////// // GIS demo with a map displayed using the GLG Map Server. // This demo uses Glg as a bean and may be used in a browser or stand-alone. ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// public class GlgGISDemo extends GlgJBean implements ActionListener { ////////////////////////////////////////////////////////////////////////// // The main demo class ////////////////////////////////////////////////////////////////////////// static final long serialVersionUID = 0; String map_name = "gis_demo.g"; double PlaneSpeed = 0.005; // Relative units double MaxZoomSpeed = 5.; // In XY Coordinates int USZoomDelay1 = 3000; // Delay to zoom to the US area to show details. int USZoomDelay2 = 1000; // Delay to remove US zooming message. // If supplied, overrides the URL of the GIS object in the drawing static String SuppliedMapServerURL = null; GlgObject Drawing, PositionObject, PositionArea, NodeTemplate[] = new GlgObject[ 2 ], PlaneTemplate[] = new GlgObject[ 2 ], Map[] = new GlgObject[ 2 ], GISObject[] = new GlgObject[ 2 ], NodeGroup[] = new GlgObject[ 2 ], PlaneGroup[] = new GlgObject[ 2 ]; final double // Plane size constants SMALL_SIZE = 0.6, MEDIUM_SIZE = 0.8, BIG_SIZE = 1.0; double PlaneSize = MEDIUM_SIZE, // Dimensions of the map viewport windows window_width[] = new double[ 2 ], window_height[] = new double[ 2 ]; int NumNodes, NumPlanes = 10, MapServer, MapProjection[] = new int[ 2 ]; // If true, pan the map to make the selected plane visible in the current // zoomed area boolean LockSelectedPlane; boolean CityLabels = true, StateDisplay = true; GlgPoint // Store initial extent and center, used to reset InitExtent[] = new GlgPoint[ 2 ], InitCenter[] = new GlgPoint[ 2 ], // Temp vars: allocate once lat_lon = new GlgPoint(), last_lat_lon = new GlgPoint(), old_position = new GlgPoint(), point = new GlgPoint(), rect_point = new GlgPoint(), util_point = new GlgPoint(); static boolean StandAlone = false; boolean MapIsReady = false; boolean TopMapIsReady = false; boolean PanMode = false; Timer timer = null; Timer zoom_timer = null; boolean DoUpdate = true; PlaneData PlaneArray[]; PlaneData SelectedPlane; // Array of icons to place on the map as GLG objects in addition to the // icons defined in GIS server's data. The icons that use GLG objects may // be selected with the mouse and their attributes can be changed // dynamically, based on data. When the mouse moves over an icon, it may // be highlighted with a different color or a tooltip may be displayed. // NodeData NodeArray[] = { new NodeData( "Boston", -71.01789, 42.33602 ), new NodeData( "New York", -73.97213, 40.77436 ), new NodeData( "San Francisco", -122.55478, 37.79325 ), new NodeData( "Miami", -80.21084, 25.77566 ), new NodeData( "Seattle", -122.35032, 47.62180 ), new NodeData( "Houston", -95.38672, 29.76870 ), new NodeData( "Denver", -104.87265, 39.76803 ), new NodeData( "Minneapolis", -93.26684, 44.96185 ), new NodeData( "Chicago", -87.68496, 41.83705 ), new NodeData( "Dallas", -96.76524, 32.79415 ) }; ////////////////////////////////////////////////////////////////////////// public GlgGISDemo() { super(); SetDResource( "$config/GlgSwingUsage", 1. ); SetDResource( "$config/GlgMouseTooltipTimeout", 0.05 ); // Don't expand selection area for exact tooltips. SetDResource( "$config/GlgPickResolution", 0. ); // Activate Trace callback. AddListener( GlgObject.TRACE_CB, this ); } ////////////////////////////////////////////////////////////////////////// // main() method for using as a stand-alone java demo ////////////////////////////////////////////////////////////////////////// public static void main( final String arg[] ) { SwingUtilities. invokeLater( new Runnable(){ public void run() { Main( arg ); } } ); } ////////////////////////////////////////////////////////////////////////// public static void Main( final String arg[] ) { class DemoQuit extends WindowAdapter { public void windowClosing( WindowEvent e ) { System.exit( 0 ); } } GlgGISDemo.StandAlone = true; // Map server URL from the command line if( Array.getLength( arg ) != 0 ) GlgGISDemo.SuppliedMapServerURL = arg[ 0 ]; JFrame frame = new JFrame(); frame.setResizable( true ); frame.setSize( 800, 650 ); frame.setLocation( 200, 20 ); GlgGISDemo map_demo = new GlgGISDemo(); // Use getContentPane() for GlgJBean frame.getContentPane().add( map_demo ); frame.addWindowListener( new DemoQuit() ); frame.setVisible( true ); // The ReadyCallback will start updates when the drawing is loaded. map_demo.SetDrawingName( map_demo.map_name ); } ////////////////////////////////////////////////////////////////////////// // Invoked before the hierarchy setup. ////////////////////////////////////////////////////////////////////////// public void HCallback( GlgObject viewport ) { Drawing = viewport; Map[ 0 ] = Drawing.GetResourceObject( "TopMap" ); // Thumbnail map Map[ 1 ] = Drawing.GetResourceObject( "Map" ); // Detailed map // Display thumbnail map in a separate window. It is kept as a child // window in the drawing for the convinience of editing. Map[ 0 ].SetDResource( "ShellType", (double) GlgObject.DIALOG_SHELL ); if( !StandAlone ) { String param = getParameter( "MapServerURL" ); if( param != null ) SuppliedMapServerURL = param; } Init(); } ////////////////////////////////////////////////////////////////////////// // Initializes icons in the drawing ////////////////////////////////////////////////////////////////////////// void Init() { GlgObject resource; int i, j; for( i=0; i<2; ++i ) // For each map window { // Get IDs of the GIS map objects in each of the map viewports. GISObject[ i ] = Map[ i ].GetResourceObject( "GISObject" ); if( SuppliedMapServerURL != null ) // Override URL in the drawing GISObject[ i ].SetSResource( "GISMapServerURL", SuppliedMapServerURL ); // Query and store the GIS projection (ORTHOGRAPHIC or RECTANGULAR) // used to render the map. MapProjection[ i ] = (int) GetDResource( GISObject[ i ], "GISProjection" ); // Set GIS Zoom mode: generate a new map request for a new area on // zoom/pan. Map[ i ].SetGISZoom( null, GISObject[ i ], null ); // Store initial map extent for resetting after zooming. InitExtent[i] = GISObject[ i ].GetGResource( "GISExtent" ); InitCenter[i] = GISObject[ i ].GetGResource( "GISCenter" ); } // Get the palette containing templates for plane and node icons. GlgObject palette = Drawing.GetResourceObject( "Palette" ); // Delete it from the drawing Drawing.DeleteObject( palette ); // Get node and plane templates from the palette. Two sets of templates // are used: smaller icons for the thumbnail view and more elaborate // ones for the detailed map. for( i=0; i<2; ++i ) { NodeTemplate[ i ] = palette.GetResourceObject( i == 0 ? "Node1" : "Node2" ); PlaneTemplate[ i ] = palette.GetResourceObject( i == 0 ? "Plane1" : "Plane2" ); // If the icon is not a marker (Scale resource exists), set the icon's // size. resource = PlaneTemplate[ i ].GetResourceObject( "Scale" ); if( resource != null ) resource.SetDResource( null, PlaneSize ); } NumNodes = Array.getLength( NodeArray ); // Create and initialize plane structures used for simulation. PlaneArray = new PlaneData[ NumPlanes ]; for( i =0; i < NumPlanes; ++i ) { PlaneArray[ i ] = new PlaneData(); PlaneArray[ i ].name = Integer.toString( i ); StartPlane( PlaneArray[ i ], true ); } // Add node and plane icons to both thumbnail and detailed map. for( i=0; i<2; ++i ) { CreateAirportIcons( i ); // Add airport icons CreatePlaneIcons( i ); // Add plane icons // Check if the icon has an angle to indicate its direction. if( PlaneTemplate[ i ].GetResourceObject( "Angle" ) != null ) for( j=0; j < NumPlanes; ++j ) PlaneArray[ j ].has_angle[ i ] = true; } // Selected area annotates the currently viewed area of the detailed map // in the thumbnail map view. Reorder SelectedArea on top of icons // (last in the array). GlgObject selected_area = Map[ 0 ].GetResourceObject( "SelectedArea" ); Map[ 0 ].ReorderElement( Map[ 0 ].GetIndex( selected_area ), Map[ 0 ].GetSize() - 1 ); // Set state display on the thumbnail map. Airport labels on the detailed // map are handled by HandleZoomLevel(). SetGISLayers( 0 ); // Demos starts with the whole word view, then zooms to the US area // in a few seconds to show more details. Set initial parameters // for the whole world view. HandleZoomLevel(); InitSelection(); // Zoom on the US and select some plane. // Store objects used to display lat/lon on mouse move. PositionArea = Drawing.GetResourceObject( "PositionArea" ); PositionObject = Drawing.GetResourceObject( "PositionLabel/String" ); PositionObject.SetSResource( null, "" ); // Set US zooming message to OFF initially. Drawing.SetDResource( "Map/USZoomingMessage/Visibility", 0. ); } ////////////////////////////////////////////////////////////////////////// // Invoked after the hierarchy setup. ////////////////////////////////////////////////////////////////////////// public void VCallback( GlgObject viewport ) { String message = "Loading map, please wait...."; // Position icons before showing them. UpdateObjectsOnMap( 0, message ); UpdateObjectsOnMap( 1, message ); } ////////////////////////////////////////////////////////////////////////// // Initializes the drawing and starts updates. ////////////////////////////////////////////////////////////////////////// public void ReadyCallback( GlgObject viewport ) { super.ReadyCallback(); StartUpdates(); // Zoom to the US area after a few seconds to show details. zoom_timer = new Timer( USZoomDelay1, new ZoomPerformer( this ) ); zoom_timer.setRepeats( false ); zoom_timer.start(); } ////////////////////////////////////////////////////////////////////////// // Creates NodeGroup to hold airport icons and adds the icons to it. ////////////////////////////////////////////////////////////////////////// void CreateAirportIcons( int map ) { NodeGroup[ map ] = new GlgDynArray( GlgObject.GLG_OBJECT, 0, 0 ); NodeGroup[ map ].SetSResource( "Name", "NodeGroup" ); // Add city/node icons for( int i = 0; i < NumNodes; ++i ) AddNode( NodeArray[ i ], map, i ); Map[ map ].AddObjectToBottom( NodeGroup[ map ] ); } ////////////////////////////////////////////////////////////////////////// // Creates PlaneGroup to hold plane icons and adds the icons to it. ////////////////////////////////////////////////////////////////////////// void CreatePlaneIcons( int map ) { PlaneGroup[ map ] = new GlgDynArray( GlgObject.GLG_OBJECT, 0, 0 ); PlaneGroup[ map ].SetSResource( "Name", "PlaneGroup" ); // Add plane icons for( int i=0; i < NumPlanes; ++i ) AddPlane( PlaneArray[ i ], map, i ); Map[ map ].AddObjectToBottom( PlaneGroup[ map ] ); } ////////////////////////////////////////////////////////////////////////// void AddNode( NodeData node_data, int map, int index ) { // Create a copy of a node. GlgObject node = NodeTemplate[ map ].CloneObject( GlgObject.STRONG_CLONE ); node.SetSResource( "Name", node_data.name ); // Set object name // Index for direct access node.SetDResource( "DataIndex", (double)index ); if( map == 1 ) // On the detailed map, show node name label. node.SetSResource( "LabelString", node_data.name ); String tooltip; if( map == 0 ) // On the thumbnail map, show node name in the tooltip. tooltip = node_data.name; else // On the detailed map, include lat/lon into the tooltip. tooltip = node_data.name + ", " + CreateLocationString( node_data.lat_lon ); node.SetSResource( "TooltipString", tooltip ); node_data.graphics[ map ] = node; // The node will be positioned after the GIS object is setup. // PositionNode( node_data, map ); NodeGroup[ map ].AddObjectToBottom( node ); } ////////////////////////////////////////////////////////////////////////// // Adds a plane icon, fills labels, tooltips, etc. ////////////////////////////////////////////////////////////////////////// void AddPlane( PlaneData plane_data, int map, int index ) { // Create a copy of a node. GlgObject plane = PlaneTemplate[ map ].CloneObject( GlgObject.STRONG_CLONE ); plane.SetSResource( "Name", plane_data.name ); // Object name // Index for direct access plane.SetDResource( "DataIndex", (double)index ); if( map == 1 ) // On detailed map, show the flight number as icon label plane.SetSResource( "LabelString", "Flight " + plane_data.flight_number ); // Set the tooltip, created by StartPlane method. plane.SetSResource( "TooltipString", plane_data.tooltip[ map ] ); plane_data.graphics[ map ] = plane; // The plane will be positioned after the GIS object is setup. // PositionPlane( plane_data, map ); PlaneGroup[ map ].AddObjectToBottom( plane ); } ////////////////////////////////////////////////////////////////////////// void PositionNode( NodeData node, int map ) { if( node.graphics == null ) return; // Converts node position from lat/lon to GLG coordinates. GetNodePosition( node, map ); // Update node's icon in the drawing node.graphics[ map ].SetGResource( "Position", node.adj_xyz[ map ] ); } ////////////////////////////////////////////////////////////////////////// void PositionPlane( PlaneData plane, int map ) { if( plane.graphics[ map ] == null || plane.from_node == null || plane.to_node == null ) return; // Converts plane position (simulated or real data) from lat/lon to // GLG coordinates. GetPlanePosition( plane, map, true ); // Update plane's icon in the drawing plane.graphics[ map ].SetGResource( "Position", plane.adj_xyz[ map ] ); // Update icon's direction angle is necessary if( plane.has_angle[ map ] ) plane.graphics[ map ].SetDResource( "Angle", plane.angle[ map ] ); } ////////////////////////////////////////////////////////////////////////// // Converts node's position from lat/lon to X/Y in GLG world coordinates. ////////////////////////////////////////////////////////////////////////// void GetNodePosition( NodeData node, int map ) { // Converts lat/lon to X/Y using GIS object's current projection. GISObject[ map ].GISConvert( null, GlgObject.OBJECT_COORD, /* Lat/Lon to XY */ false, node.lat_lon, node.xyz[ map ] ); // Prevent wrap-around errors under big zoom factors. Also handles // visibility of hidden nodes on another side of the globe // (z < 0 in ORTHOGRAPHIC projection ). if( node.xyz[ map ].z < 0. || !GetVisibility( node.xyz[ map ], 1.1 ) ) { // Not visible. Use smaller coordinates just outside the visible area // to prevent wrap-around errors. We could remove the node from the // drawing and show only the visible ones. node.adj_xyz[ map ].x = 2000.; node.adj_xyz[ map ].y = 2000.; } else node.adj_xyz[ map ].CopyFrom( node.xyz[ map ] ); } ////////////////////////////////////////////////////////////////////////// // Converts plane's position from lat/lon to X/Y in GLG world coordinates. ////////////////////////////////////////////////////////////////////////// void GetPlanePosition( PlaneData plane, int map, boolean get_angle ) { // Gets the new plane position, simulated or from real data. GetPlaneLatLon( plane ); // Converts lat/lon to X/Y using GIS object's current projection. GISObject[ map ].GISConvert( null, GlgObject.OBJECT_COORD, /* Lat/Lon to XY */ false, plane.lat_lon, plane.xyz[ map ] ); // Prevent wrap-around errors under big zoom factors. Also handles // visibility of hidden planes on another side of the globe // (z < 0 in ORTHOGRAPHIC projection ). if( plane.xyz[ map ].z < 0. || !GetVisibility( plane.xyz[ map ], 1.1 ) ) { plane.adj_xyz[ map ].x = 2000.; plane.adj_xyz[ map ].y = 2000.; plane.angle[ map ] = 0.; } else { plane.adj_xyz[ map ].CopyFrom( plane.xyz[ map ] ); if( get_angle && plane.has_angle[ map ] ) plane.angle[ map ] = GetPlaneAngle( plane, map ); } } ////////////////////////////////////////////////////////////////////////// // Calculates plane icon's directional angle. ////////////////////////////////////////////////////////////////////////// double GetPlaneAngle( PlaneData plane, int map ) { double angle; if( MapProjection[ map ] == GlgObject.RECTANGULAR_PROJECTION || plane.path_position == plane.path_position_last ) // Just started { // Rectangular projection preserves straight lines, we can use the // angle of the line connecting the start and end nodes. For the // orthographic projection, use this case if the plane has just // started and there is no previous position stored. angle = GetAngle( plane.from_node.xyz[ map ], plane.to_node.xyz[ map ] ); } else // In orthographic projection straight lines are drawn as curves. // Use the angle of the line connecting the current and last // positions of the plane. { double stored_position; stored_position = plane.path_position; // Store current position. // Get the coordinates of the plane's previous position plane.path_position = plane.path_position_last; GetPlaneLatLon( plane ); GISObject[ map ].GISConvert( null, GlgObject.OBJECT_COORD, /* Lat/Lon to XY */ false, plane.lat_lon, last_lat_lon ); // Get angle of a line connecting the previous and current position. angle = GetAngle( last_lat_lon, plane.xyz[ map ] ); // Restore the plane's current position plane.path_position = stored_position; GetPlaneLatLon( plane ); } return angle; } ////////////////////////////////////////////////////////////////////////// // Checks if the object is visible in the current zoom region. // This prevents wrap-around errors under big zoom factors. ////////////////////////////////////////////////////////////////////////// boolean GetVisibility( GlgPoint position, double adj ) { // Use adj as a gap return position.x > -1000. * adj && position.x < 1000. * adj && position.y > -1000. * adj && position.y < 1000. * adj; } ////////////////////////////////////////////////////////////////////////// // FOR SIMULATION: Performs one step of the simulation to move the planes. // Change this code to use real data. ////////////////////////////////////////////////////////////////////////// void UpdatePlanes() { if( !DoUpdate ) return; for( int i = 0; i < NumPlanes; ++i ) UpdatePlane( PlaneArray[ i ] ); if( LockSelectedPlane ) UpdateLocking(); Update(); } ////////////////////////////////////////////////////////////////////////// // FOR SIMULATION: Calculates new plane position. ////////////////////////////////////////////////////////////////////////// void UpdatePlane( PlaneData plane ) { if( plane.graphics == null || plane.from_node == null || plane.to_node == null ) return; if( plane.path_position == 1. ) StartPlane( plane, false ); // Finished old path, start a new one. else { double speed = PlaneSpeed; // Slow the selected plane down when zoomed on it for a nice // demo effect. if( plane == SelectedPlane && LockSelectedPlane ) { double stored_position = plane.path_position; GetPlanePosition( plane, 1, false ); old_position.CopyFrom( plane.xyz[ 1 ] ); plane.path_position += plane.speed * speed; GetPlanePosition( plane, 1, false ); // Distance between the old and current position of the plane double dist = GetLength( old_position, plane.xyz[ 1 ] ); if( dist > MaxZoomSpeed ) { double slow_down = dist / MaxZoomSpeed; speed /= slow_down; } plane.path_position = stored_position; // restore } // Store last position for calculating angle in ORTHO projection. plane.path_position_last = plane.path_position; plane.path_position += plane.speed * speed; if( plane.path_position > 1. ) plane.path_position = 1.; } for( int i =0; i<2; ++i ) PositionPlane( plane, i ); // Position the plane on both maps } ////////////////////////////////////////////////////////////////////////// // In the lock mode, pans the map to keep selected plane visible when the // plane moves out of the detailed map area. ////////////////////////////////////////////////////////////////////////// void UpdateLocking() { int map = 1; // Checking is done on the detailed map UpdateStatus(); // Update selected plane lat/lon display GetPlanePosition( SelectedPlane, map, false ); // If selected plane goes on another side of the globe or // off the visible portion of the map, pan to re-center // on the selected plane if( MapProjection[ map ] == GlgObject.ORTHOGRAPHIC_PROJECTION && SelectedPlane.xyz[ 1 ].z < 0.1 || !GetVisibility( SelectedPlane.xyz[ 1 ], 0.9 ) ) { String message = "Loading new map to keep the selected plane in sight, " + "please wait..."; CenterOnPlane( SelectedPlane, 1 ); UpdateObjectsOnMap( 1, message ); if( MapProjection[ map ] == GlgObject.ORTHOGRAPHIC_PROJECTION ) { CenterOnPlane( SelectedPlane, 0 ); UpdateObjectsOnMap( 0, message ); } } } ////////////////////////////////////////////////////////////////////////// // Compensate plane icons for X/Y stretch. Screen coord. extent may be // used instead. ////////////////////////////////////////////////////////////////////////// void AdjustPlane( int map ) { GlgObject first_plane = PlaneArray[ 0 ].graphics[ map ]; GlgObject scale_x = first_plane.GetResourceObject( "ScaleX" ); GlgObject scale_y = first_plane.GetResourceObject( "ScaleY" ); if( scale_x != null && scale_y != null ) { double ratio_half; double wh_ratio = window_width[ map ] / window_height[ map ]; if( wh_ratio > 1. ) { ratio_half = Math.sqrt( wh_ratio ); scale_y.SetDResource( null, ratio_half ); scale_x.SetDResource( null, 1. / ratio_half ); } else { ratio_half = Math.sqrt( 1. / wh_ratio ); scale_y.SetDResource( null, 1. / ratio_half ); scale_x.SetDResource( null, ratio_half ); } } } ////////////////////////////////////////////////////////////////////////// // Reposition icons when a new map is displayed. ////////////////////////////////////////////////////////////////////////// void UpdateObjectsOnMap( int map, String message ) { int i; // GlgSetupHierarchy causes the new map to be generated if necessary, // so display the wait message while the map is being generated. SetStatus( message ); // Update the GIS object with new extent but don't draw it yet: // we want to update objects on the map first. Map[ map ].SetupHierarchy(); for( i = 0; i < NumNodes; ++i ) PositionNode( NodeArray[ i ], map ); for( i = 0; i < NumPlanes; ++i ) PositionPlane( PlaneArray[ i ], map ); SetStatus( "" ); // Adjust selected region on the thumbnail map to match detailed map. SetSelectedArea(); } ////////////////////////////////////////////////////////////////////////// // Handle user interaction. ////////////////////////////////////////////////////////////////////////// public void InputCallback( GlgObject vp, GlgObject message_obj ) { String origin, format, action, subaction; super.InputCallback( vp, message_obj ); origin = message_obj.GetSResource( "Origin" ); format = message_obj.GetSResource( "Format" ); action = message_obj.GetSResource( "Action" ); subaction = message_obj.GetSResource( "SubAction" ); // Handle window closing. if( format.equals( "Window" ) ) { if( action.equals( "DeleteWindow" ) ) { if( origin.equals( "SelectionDialog" ) ) { // Close selection dialog Drawing.SetDResource( "SelectionDialog/Visibility", 0. ); Update(); return; } else if( origin.equals( "TopMap" ) ) { // Close top map window Drawing.SetDResource( "TopMap/Visibility", 0. ); Update(); return; } else // Closing main window when stand-alone: exit. System.exit( 0 ); return; } else if( action.equals( "FirstExposure" ) ) { origin = message_obj.GetSResource( "Origin" ); if( origin.equals( "Map" ) ) MapIsReady = true; else if( origin.equals( "TopMap" ) ) TopMapIsReady = true; } } if( format.equals( "Button" ) ) { if( !action.equals( "Activate" ) ) return; PanMode = false; // Abort Pan mode if( origin.equals( "CloseDialog" ) ) { Drawing.SetDResource( "SelectionDialog/Visibility", 0. ); Update(); } else if( origin.equals( "ToggleLock" ) ) { SetLocking( !LockSelectedPlane ); Update(); } else if( origin.equals( "ZoomIn" ) ) { // Allow selected plane to be outside of the visible area: we // are zooming on different area. SetLocking( false ); Zoom( 'i', 2. ); HandleZoomLevel(); Update(); } else if( origin.equals( "ZoomOut" ) ) { Zoom( 'o', 2. ); HandleZoomLevel(); Update(); } else if( origin.equals( "ZoomReset" ) ) { SetLocking( false ); Zoom( 'n', 0. ); HandleZoomLevel(); Update(); } else if( origin.equals( "ZoomTo" ) ) { SetLocking( false ); Map[ 1 ].SetZoom( null, 't', 0. ); // Start Zoom op SetStatus( "Define a rectangular area to zoom to." ); Update(); } else if( origin.equals( "Pan" ) ) { Map[ 1 ].SetZoom( null, 'e', 0. ); // Abort ZoomTo mode PanMode = true; SetLocking( false ); SetStatus( "Click to define a new center." ); Update(); } else if( origin.equals( "Up" ) ) { // Allow selected plane to be outside of the visible area: we // are panning out. */ SetLocking( false ); Zoom( 'u', 0. ); Update(); } else if( origin.equals( "Down" ) ) { SetLocking( false ); Zoom( 'd', 0. ); Update(); } else if( origin.equals( "Left" ) ) { SetLocking( false ); Zoom( 'l', 0. ); Update(); } else if( origin.equals( "Right" ) ) { SetLocking( false ); Zoom( 'r', 0. ); Update(); } else if( origin.equals( "AirportLabels" ) ) { CityLabels = !CityLabels; SetGISLayers( 1 ); Update(); } else if( origin.equals( "Planes" ) ) { ToggleResource( Map[ 0 ], "PlaneGroup/Visibility" ); ToggleResource( Map[ 1 ], "PlaneGroup/Visibility" ); Update(); } else if( origin.equals( "ValueDisplay" ) ) { // Visibility of all labels is constrained, set just one. ToggleResource( PlaneArray[ 0 ].graphics[ 1 ], "Label/Visibility" ); Update(); } else if( origin.equals( "ToggleStates" ) ) { StateDisplay = !StateDisplay; SetGISLayers( 0 ); // Thumbnail map SetGISLayers( 1 ); // Detailed map Update(); } else if( origin.equals( "Update" ) ) { DoUpdate = !DoUpdate; } else if( origin.equals( "PlaneSize" ) ) { // Change plane icon's size. if( PlaneSize == SMALL_SIZE ) PlaneSize = MEDIUM_SIZE; else if( PlaneSize == MEDIUM_SIZE ) PlaneSize = BIG_SIZE; else // BIG_SIZE PlaneSize = SMALL_SIZE; SetPlaneSize(); Update(); } } else if( action.equals( "Zoom" ) && subaction.equals( "End" ) ) { // Update icon positions after zooming. UpdateObjectsOnMap( 1, "Zooming or panning, please wait..." ); Map[ 1 ].Update(); // Get the center of the detailed map. GlgPoint center = GISObject[ 1 ].GetGResource( "GISCenter" ); // Rotate the thumbnail globe to show the same area. GISObject[ 0 ].SetGResource( "GISCenter", center ); HandleZoomLevel(); // Updates icons and selected area display on the thumbnail globe. UpdateObjectsOnMap( 0, "Zooming, please wait..." ); Update(); } else if( format.equals( "CustomEvent" ) && action.equals( "MouseClick" ) ) { if( Map[ 1 ].GetDResource( "ZoomToMode" ).intValue() != 0 ) return; // Don't handle selection in ZoomTo mode. if( message_obj.GetDResource( "ButtonIndex" ).intValue() != 1 ) return; // Ignore middle and right mouse button clicks String custom_event = message_obj.GetSResource( "EventLabel" ); if( custom_event.equals( "Plane" ) ) { int data_index = message_obj.GetDResource( "Object/DataIndex" ).intValue(); if( SelectedPlane != PlaneArray[ data_index ] ) { SelectPlane( SelectedPlane, 0 ); // Unhighlight old SelectedPlane = PlaneArray[ data_index ]; SelectPlane( SelectedPlane, 1 ); // Highlight new Update(); } } } } ////////////////////////////////////////////////////////////////////////// void Zoom( char type, double value ) { switch( type ) { default: Map[ 1 ].SetZoom( null, type, value ); UpdateObjectsOnMap( 1, "Zooming, please wait..." ); // After "1:1" zoom reset, the maps' centers differ, sync the centers // when zooming in the first time. */ switch( type ) { case 'i': case 'u': case 'd': case 'l': case 'r': // Get the center of the detailed map. GlgPoint center = GISObject[ 1 ].GetGResource( "GISCenter" ); // Get the center of the thumbnail globe. GlgPoint globe_center = GISObject[ 0 ].GetGResource( "GISCenter" ); // First time: centers differ, sync up. if( globe_center.x != center.x || globe_center.y != center.y || globe_center.z != center.z ) { // Rotate the thumbnail globe to show the same area GISObject[ 0 ].SetGResource( "GISCenter", center ); UpdateObjectsOnMap( 0, "Zooming, please wait..." ); } } break; case 'n': if( MapProjection[ 0 ] == GlgObject.ORTHOGRAPHIC_PROJECTION ) { // Reset thumbnail globe to initial position. GISObject[ 0 ].SetGResource( "GISCenter", InitCenter[0] ); GISObject[ 0 ].SetGResource( "GISExtent", InitExtent[0] ); UpdateObjectsOnMap( 0, "Reloading map, please wait..." ); } // Reset detailed map to initial extent. GISObject[ 1 ].SetGResource( "GISCenter", InitCenter[1] ); GISObject[ 1 ].SetGResource( "GISExtent", InitExtent[1] ); UpdateObjectsOnMap( 1, "Reloading map, please wait..." ); // Make selected area rectangle invisible when no zoom Map[ 0 ].SetDResource( "SelectedArea/Visibility", 0. ); break; } } ////////////////////////////////////////////////////////////////////////// // Used to obtain coordinates of the mouse click. ////////////////////////////////////////////////////////////////////////// public void TraceCallback( GlgObject viewport, GlgTraceData trace_info ) { int map; if( !MapIsReady || !TopMapIsReady ) return; int event_type = trace_info.event.getID(); // Use the Map area events only. if( trace_info.viewport == Map[ 0 ] ) map = 0; else if( trace_info.viewport == Map[ 1 ] ) map = 1; else { // Erase the current postion display when the mouse moves outside // of the map. switch( event_type ) { case MouseEvent.MOUSE_MOVED: case MouseEvent.MOUSE_DRAGGED: PositionObject.SetSResource( null, "" ); PositionArea.Update(); break; } return; } switch( event_type ) { case MouseEvent.MOUSE_MOVED: // Report lat/lon position under the mouse. case MouseEvent.MOUSE_DRAGGED: point.x = (double) ((MouseEvent)trace_info.event).getX(); point.y = (double) ((MouseEvent)trace_info.event).getY(); point.z = 0.; // Converts X/Y to lat/lon using GIS object's current projection, // handles both maps. GISObject[ map ]. GISConvert( null, GlgObject.SCREEN_COORD, /* X/Y to Lat/Lon */ true, point, lat_lon ); PositionObject.SetSResource( null, CreateLocationString( lat_lon ) ); PositionArea.Update(); break; case MouseEvent.MOUSE_PRESSED: // Handle paning: set the new map center to the location of the click. // Handles paning on both maps. if( !PanMode ) return; if( GetButton( trace_info.event ) != 1 ) return; // Use the left button clicks only. PanMode = false; point.x = (double) ((MouseEvent)trace_info.event).getX(); point.y = (double) ((MouseEvent)trace_info.event).getY(); point.z = 0.; // Converts X/Y to lat/lon using GIS object's current projection, // handles clicks on either map. GISObject[ map ].GISConvert( null, GlgObject.SCREEN_COORD, /* X/Y to Lat/Lon */ true, point, lat_lon ); if( MapProjection[ 0 ] == GlgObject.ORTHOGRAPHIC_PROJECTION ) { // Pan/Rotate globe on the thumbnail map as well // Don't do anything for the rectangular projection: the whole // world is displayed anyway. GISObject[ 0 ].SetGResource( "GISCenter", lat_lon ); UpdateObjectsOnMap( 0, "Paning, please wait..." ); Map[ 0 ].Update(); } // Pan detailed map GISObject[ 1 ].SetGResource( "GISCenter", lat_lon ); UpdateObjectsOnMap( 1, "Paning the map..." ); Update(); break; case ComponentEvent.COMPONENT_RESIZED: Component widget = (Component) Map[ map ].GetResource( "Widget" ); int width = widget.getSize().width; int height = widget.getSize().height; if( width == window_width[ map ] && height == window_height[ map ] ) return; window_width[ map ] = width; window_height[ map ] = height; /* Adjust icons to maintain X/Y ratio when the window is resized. */ AdjustPlane( map ); // No need to adjust icon positions if the GIS object has Stretch=YES break; default: return; } } ////////////////////////////////////////////////////////////////////////// // Adjust selected region on the thumbnail map to match detailed map. ////////////////////////////////////////////////////////////////////////// void SetSelectedArea() { GlgObject point_obj[] = new GlgObject[ 16 ]; GlgPoint lat_lon[] = new GlgPoint[ 16 ]; int i; // Set the coordinates of the SelectedArea polygon. GlgObject rect = Map[ 0 ].GetResourceObject( "SelectedArea" ); GlgPoint extent = GetExtentDegrees( 1 ); if( extent.x >= 120. ) { // Big area: don't need to show. rect.SetDResource( "Visibility", 0. ); } else { rect.SetDResource( "Visibility", 1. ); // Get polygon points for( i=0; i<16; ++i ) point_obj[ i ] = (GlgObject) rect.GetElement( i ); // Get lat/lon on detailed map lat_lon[ 0 ] = GetLatLon( -1000., -1000., 1 ); lat_lon[ 1 ] = GetLatLon( -1000., -500., 1 ); lat_lon[ 2 ] = GetLatLon( -1000., 0., 1 ); lat_lon[ 3 ] = GetLatLon( -1000., 500., 1 ); lat_lon[ 4 ] = GetLatLon( -1000., 1000., 1 ); lat_lon[ 5 ] = GetLatLon( -500., 1000., 1 ); lat_lon[ 6 ] = GetLatLon( 0., 1000., 1 ); lat_lon[ 7 ] = GetLatLon( 500., 1000., 1 ); lat_lon[ 8 ] = GetLatLon( 1000., 1000., 1 ); lat_lon[ 9 ] = GetLatLon( 1000., 500., 1 ); lat_lon[ 10 ] = GetLatLon( 1000., 0., 1 ); lat_lon[ 11 ] = GetLatLon( 1000., -500., 1 ); lat_lon[ 12 ] = GetLatLon( 1000., -1000., 1 ); lat_lon[ 13 ] = GetLatLon( 500., -1000., 1 ); lat_lon[ 14 ] = GetLatLon( 0., -1000., 1 ); lat_lon[ 15 ] = GetLatLon( -500., -1000., 1 ); for( i=0; i<16; ++i ) { // Converts lat/lon on the detailed map to X/Y on the thumbnail map // using GIS object's current projection. GISObject[ 0 ].GISConvert( null, GlgObject.OBJECT_COORD, /* Lat/Lon to X/Y */ false, lat_lon[ i ], rect_point ); point_obj[ i ].SetGResource( null, rect_point ); } } } ////////////////////////////////////////////////////////////////////////// // FOR SIMULATION ONLY: Starts simulation for a plane, selects its start // and end nodes. ////////////////////////////////////////////////////////////////////////// void StartPlane( PlaneData plane, boolean init ) { if( NumNodes < 2 ) { System.out.println( "Less then two nodes: can't start planes." ); return; } int to_index; int from_index = (int) Rand( 0, NumNodes - 0.001 ); do { to_index = (int) Rand( 0, NumNodes - 0.001 ); } while( to_index == from_index ); plane.from_node = NodeArray[ from_index ]; plane.to_node = NodeArray[ to_index ]; plane.flight_number = (int) Rand( 101., 1999. ); plane.speed = Rand( 0.4, 1. ); // Vary plane speed if( init ) { plane.path_position = Rand( 0.1, 0.2 ); plane.path_position_last = plane.path_position - 0.05; // For angle } else { plane.path_position = 0.; plane.path_position_last = 0.; } plane.tooltip[ 0 ] = "Flight " + plane.flight_number; // On the detailed map, add from/to node info to the tooltip. plane.tooltip[ 1 ] = plane.tooltip[ 0 ] + " from " + plane.from_node.name + " to " + plane.to_node.name; for( int i = 0; i < 2; ++i ) if( plane.graphics[ i ] != null ) plane.graphics[ i ].SetSResource( "TooltipString", plane.tooltip[ i ] ); // Stop tracking the selected flight when it reaches destination if( plane == SelectedPlane ) SetLocking( false ); } ////////////////////////////////////////////////////////////////////////// // FOR SIMULATION ONLY: Calculates plane's lat/lon using simulated data. // // The simulation moves the plane from the start to the end node/city // as controlled by the path_position parameter. The path_position changes // in the range from from 0 (start node) to 1 (end node). ////////////////////////////////////////////////////////////////////////// void GetPlaneLatLon( PlaneData plane ) { plane.lat_lon.x = RELATIVE_TO_NEW_RANGE( plane.from_node.lat_lon.x, plane.to_node.lat_lon.x, plane.path_position ); plane.lat_lon.y = RELATIVE_TO_NEW_RANGE( plane.from_node.lat_lon.y, plane.to_node.lat_lon.y, plane.path_position ); } ////////////////////////////////////////////////////////////////////////// // FOR SIMULATION ONLY: select a plane to zoom on on the initial appearance. ////////////////////////////////////////////////////////////////////////// void InitSelection() { // Select the first plane SelectedPlane = PlaneArray[ 0 ]; SelectPlane( SelectedPlane, 1 ); // Lock on the selected plane, pan the map to keep it visible. SetLocking( true ); // Rotate thumbnail globe too to show the same location. if( MapProjection[ 0 ] == GlgObject.ORTHOGRAPHIC_PROJECTION ) GISObject[ 0 ].SetGResource( "GISCenter", -95.35, 37.37, 0. ); } ////////////////////////////////////////////////////////////////////////// // UTILITY FUNCTION: Calculates an angle between the line defined by two // points and the X axis. ////////////////////////////////////////////////////////////////////////// double GetAngle( GlgPoint pt1, GlgPoint pt2 ) { double length, angle; length = GetLength( pt1, pt2 ); if( length == 0. ) angle = 0.; else { angle = Math.acos( ( pt2.x - pt1.x ) / length ); if( pt2.y - pt1.y < 0. ) // ScreenSpace Z axis points to the user. angle = - angle; } return RadToDeg( angle ); } ////////////////////////////////////////////////////////////////////////// // UTILITY FUNCTION: Calculates a distance between two points in 2D. ////////////////////////////////////////////////////////////////////////// double GetLength( GlgPoint pt1, GlgPoint pt2 ) { return Math.sqrt( ( pt2.x - pt1.x ) * ( pt2.x - pt1.x ) + ( pt2.y - pt1.y ) * ( pt2.y - pt1.y ) ); } ////////////////////////////////////////////////////////////////////////// // Turns plane icons' labels ON or OFF on the detailed map. ////////////////////////////////////////////////////////////////////////// void SetPlaneLabels( boolean on ) { GlgObject label = PlaneArray[ 0 ].graphics[ 1 ].GetResourceObject( "Label" ); if( label != null ) label.SetDResource( "Visibility", on ? 1. : 0. ); } ////////////////////////////////////////////////////////////////////////// // Sets locking mode ON or OFF. If locking is ON, the map is automatically // scrolled to keep the selected plane icon in view. void SetLocking( boolean lock ) { LockSelectedPlane = lock; UpdateStatus(); } ////////////////////////////////////////////////////////////////////////// // Displays locking status. ////////////////////////////////////////////////////////////////////////// void UpdateStatus() { String message; if( LockSelectedPlane ) { GetPlaneLatLon( SelectedPlane ); message = "Locked on Flight " + SelectedPlane.flight_number + " " + CreateLocationString( SelectedPlane.lat_lon ); } else message = "Selected Plane Locking is off."; Drawing.SetSResource( "StatusLabel/String", message ); } ////////////////////////////////////////////////////////////////////////// // Displays a message in the status area. ////////////////////////////////////////////////////////////////////////// void SetStatus( String message ) { Drawing.SetSResource( "StatusLabel/String", message ); Drawing.GetResourceObject( "StatusArea" ).Update(); } ////////////////////////////////////////////////////////////////////////// // Centers the map on the selected plane, used when locking mode is ON. ////////////////////////////////////////////////////////////////////////// void CenterOnPlane( PlaneData plane, int map ) { GetPlaneLatLon( plane ); // Center the map on the plane GISObject[ map ].SetGResource( "GISCenter", plane.lat_lon ); } ////////////////////////////////////////////////////////////////////////// // Highlights the selected plane on both maps by changing its // SelectedIndex value. ////////////////////////////////////////////////////////////////////////// void SelectPlane( PlaneData plane, int selected ) { for( int i=0; i < 2; ++i ) if( plane.graphics[ i ] != null ) plane.graphics[ i ].SetDResource( "SelectedIndex", (double)selected ); } ////////////////////////////////////////////////////////////////////////// void SetPlaneSize() { for( int i=0; i<2; ++i ) { GlgObject resource = PlaneTemplate[ i ].GetResourceObject( "Scale" ); if( resource != null ) resource.SetDResource( null, PlaneSize ); } } ////////////////////////////////////////////////////////////////////////// // Toggle resource between 0 and 1. ////////////////////////////////////////////////////////////////////////// void ToggleResource( GlgObject object, String res_name ) { double value = object.GetDResource( res_name ).doubleValue(); object.SetDResource( res_name, value != 0. ? 0. : 1. ); } //////////////////////////////////////////////////////////////////////// // Toggle map layers: airport/city labels and states. //////////////////////////////////////////////////////////////////////// void SetGISLayers( int map ) { String layers; // Airport labels should be visible only when city labels are off. NodeTemplate[1].SetDResource( "Label/Visibility", CityLabels ? 0. : 1. ); layers = "default_gis"; // Add city layers if they are on on the detailed map. if( map == 1 ) if( CityLabels ) layers = layers + ",us_cities"; else layers = layers + ",-us_cities"; if( StateDisplay ) // Add states layer if it is on. // Enable states regardless of the default. layers = layers + ",states"; else // Disable state outline display. layers = layers + ",-states"; GISObject[ map ].SetSResource( "GISLayers", layers ); } //////////////////////////////////////////////////////////////////////// // Convenience wrapper //////////////////////////////////////////////////////////////////////// GlgPoint GetLatLon( double x, double y, int map ) { GlgPoint lat_lon = new GlgPoint(); util_point.x = x; util_point.y = y; util_point.z = 0.; GISObject[ map ].GISConvert( null, GlgObject.OBJECT_COORD, /* X/Y to Lat/Lon */ true, util_point, lat_lon ); return lat_lon; } //////////////////////////////////////////////////////////////////////// int GetButton( AWTEvent event ) { if( ! ( event instanceof InputEvent ) ) return 0; InputEvent input_event = (InputEvent) event; int modifiers = input_event.getModifiers(); if( ( modifiers & InputEvent.BUTTON3_MASK ) != 0 ) return 3; else if( ( modifiers & InputEvent.BUTTON2_MASK ) != 0 ) return 2; else return 1; } ////////////////////////////////////////////////////////////////////////// double Rand( double low, double high ) { return low + ( high - low ) * Math.random(); } ////////////////////////////////////////////////////////////////////////// double RELATIVE_TO_NEW_RANGE( double low, double high, double rel_value ) { return ( (low) + ((high) - (low)) * rel_value ); } ////////////////////////////////////////////////////////////////////////// double VALUE_TO_RELATIVE( double low, double high, double value ) { return ( high - low != 0. ? ((value) - (low)) / ((high) - (low)) : 0. ); } ////////////////////////////////////////////////////////////////////////// double DegToRad( double angle ) { return angle / 180. * Math.PI; } ////////////////////////////////////////////////////////////////////////// double RadToDeg( double angle ) { return angle / Math.PI * 180.; } ////////////////////////////////////////////////////////////////////////// // Stops updates. ////////////////////////////////////////////////////////////////////////// public void stop() { StopUpdates(); if( zoom_timer != null ) { zoom_timer.stop(); zoom_timer = null; } MapIsReady = false; TopMapIsReady = false; super.stop(); } ////////////////////////////////////////////////////////////////////////// void StopUpdates() { if( timer != null ) { timer.stop(); timer = null; } } ////////////////////////////////////////////////////////////////////////// void StartUpdates() { if( timer == null ) { timer = new Timer( 30, this ); timer.setRepeats( true ); timer.start(); } } ////////////////////////////////////////////////////////////////////////// // ActionListener method to use the bean as update timer's ActionListener. ////////////////////////////////////////////////////////////////////////// public void actionPerformed( ActionEvent e ) { if( timer != null && MapIsReady && TopMapIsReady ) UpdatePlanes(); } ////////////////////////////////////////////////////////////////////////// // Turn airport and plane labels on or off depending on the zoom level and // adjust plane icon size. ////////////////////////////////////////////////////////////////////////// void HandleZoomLevel() { GlgPoint extent; boolean high_zoom; extent = GetExtentDegrees( 1 ); high_zoom = ( extent.x < 100. && extent.y < 50. ); SetPlaneLabels( high_zoom ); // Plane labels. CityLabels = true; SetGISLayers( 1 ); /* Airport labels. */ // Plane icons size. PlaneSize = ( high_zoom ? MEDIUM_SIZE : SMALL_SIZE ); SetPlaneSize(); } ////////////////////////////////////////////////////////////////////////// // Gets extent in lat/lon. // For the ortho projection, roughly converts from meters to lat/lon. ////////////////////////////////////////////////////////////////////////// GlgPoint GetExtentDegrees( int map ) { GlgPoint extent = GetGResource( GISObject[ map ], "GISExtent" ); if( MapProjection[ map ] == GlgObject.ORTHOGRAPHIC_PROJECTION ) { extent.x = extent.x / GlgObject.EQUATOR_RADIUS * 90.; extent.y = extent.y / GlgObject.POLAR_RADIUS * 90.; } return extent; } ////////////////////////////////////////////////////////////////////////// // Generate a location info string by converting +- sign info into the // N/S, E/W suffixes, and decimal fraction to deg, min, sec. ////////////////////////////////////////////////////////////////////////// String CreateLocationString( GlgPoint point ) { int x_deg, y_deg, x_min, y_min, x_sec, y_sec; char char_x, char_y; double lat, lon; if( point.z < 0. ) return ""; lon = point.x; lat = point.y; if( lon < 0. ) { lon = -lon; char_x = 'W'; } else if( lon >= 360. ) { lon -= 360.; char_x = 'E'; } else if( lon >= 180. ) { lon = 180. - ( lon - 180. ); char_x = 'W'; } else char_x = 'E'; if( lat < 0. ) { lat = -lat; char_y = 'S'; } else char_y = 'N'; x_deg = (int) lon; x_min = (int) ( ( lon - x_deg ) * 60. ); x_sec = (int) ( ( lon - x_deg - x_min / 60. ) * 3600. ); y_deg = (int) lat; y_min = (int) ( ( lat - y_deg ) * 60. ); y_sec = (int) ( ( lat - y_deg - y_min / 60. ) * 3600. ); return "Lon=" + x_deg + "\u00B0" + x_min + "\'" + x_sec + "\"" + char_x + " Lat=" + y_deg + "\u00B0" + y_min + "\'" + y_sec + "\"" + char_y; } ////////////////////////////////////////////////////////////////////////// // Show zoom message. ////////////////////////////////////////////////////////////////////////// void ZoomToUSStart() { Drawing.SetDResource( "Map/USZoomingMessage/Visibility", 1. ); Drawing.Update(); } ////////////////////////////////////////////////////////////////////////// // Zoom to the US area after a few seconds to show details. ////////////////////////////////////////////////////////////////////////// void ZoomToUS() { // Zoom to the US boundaries on detailed map. GISObject[ 1 ].SetGResource( "GISCenter", -95.35, 37.37, 0. ); GISObject[ 1 ].SetGResource( "GISExtent", 69.71, 34.85, 0. ); if( MapProjection[ 0 ] == GlgObject.ORTHOGRAPHIC_PROJECTION ) // Rotate thumbnail globe too to show the same location. GISObject[ 0 ].SetGResource( "GISCenter", -95.35, 37.37, 0. ); HandleZoomLevel(); // Update icon positions after zooming. UpdateObjectsOnMap( 1, "Zooming, please wait..." ); UpdateObjectsOnMap( 0, "Zooming, please wait..." ); // Reorder US zoom message to top, otherwise airplane icons // would be flying on top of it. GlgObject florida_message = Drawing.GetResourceObject( "Map/USZoomingMessage" ); Map[1].ReorderElement( Map[1].GetIndex( florida_message ), Map[1].GetSize() - 1 ); Drawing.Update(); } ////////////////////////////////////////////////////////////////////////// // Remove the US zooming message after a few seconds. ////////////////////////////////////////////////////////////////////////// void ZoomToUSEnd() { Drawing.SetDResource( "Map/USZoomingMessage/Visibility", 0. ); Drawing.Update(); } class NodeData { String name; GlgPoint lat_lon; GlgObject graphics[] = new GlgObject[ 2 ]; // Position in GLG world coords on both maps GlgPoint xyz[] = new GlgPoint[ 2 ]; // Adjusted position to avoid overflow and wrap-around errors. GlgPoint adj_xyz[] = new GlgPoint[ 2 ]; NodeData( String name_p, double lon, double lat ) { name = name_p; lat_lon = new GlgPoint( lon, lat, 0. ); for( int i=0; i<2; ++i ) { xyz[ i ] = new GlgPoint(); adj_xyz[ i ] = new GlgPoint(); } } } class PlaneData { String name; GlgPoint lat_lon; int flight_number; String tooltip[] = new String[ 2 ]; GlgObject graphics[] = new GlgObject[ 2 ]; NodeData from_node; NodeData to_node; double path_position; double path_position_last; double speed; // Position in GLG world coords on both maps GlgPoint xyz[] = new GlgPoint[ 2 ]; // Adjusted position to avoid overflow and wrap-around errors. GlgPoint adj_xyz[] = new GlgPoint[ 2 ]; boolean has_angle[] = new boolean[ 2 ]; double angle[] = new double[ 2 ]; PlaneData() { lat_lon = new GlgPoint(); for( int i=0; i<2; ++i ) { xyz[ i ] = new GlgPoint(); adj_xyz[ i ] = new GlgPoint(); } } } class ZoomPerformer implements ActionListener { GlgGISDemo bean; int stage; ZoomPerformer( GlgGISDemo bean_p ) { bean = bean_p; stage = 0; } public void actionPerformed( ActionEvent e ) { switch( stage ) { case 0: // Display zoom message, yield to let event thread draw it. bean.ZoomToUSStart(); stage = 1; bean.zoom_timer.setInitialDelay( 1 ); bean.zoom_timer.start(); break; case 1: // Zoom to the US area bean.ZoomToUS(); stage = 2; bean.zoom_timer.setInitialDelay( USZoomDelay2 ); bean.zoom_timer.start(); break; case 2: // Erase zoom message after a delay bean.ZoomToUSEnd(); zoom_timer = null; break; } } } }