import java.applet.*; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; import java.lang.reflect.*; import javax.swing.*; import com.genlogic.*; ////////////////////////////////////////////////////////////////////////// // 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 ////////////////////////////////////////////////////////////////////////// String map_name = "gis_demo.g"; double PlaneSpeed = 0.005; // Relative units double MaxZoomSpeed = 5.; // In XY Coordinates // If supplied, overrides the URL of the GIS object in the drawing static String SuppliedMapServerURL = null; GlgObject Drawing, 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.4, MEDIUM_SIZE = 0.6, BIG_SIZE = 0.8; 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; 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 IsReady = false; boolean PanMode = false; Timer 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 ); // 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.show(); // 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 ) { CreateNodeIcons( i ); // Add city 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 ); InitSelection(); // Zoom on the US and select some plane. } ////////////////////////////////////////////////////////////////////////// // 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(); IsReady = true; } ////////////////////////////////////////////////////////////////////////// // Creates NodeGroup to hold city/node icons and adds the icons to it. ////////////////////////////////////////////////////////////////////////// void CreateNodeIcons( 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 detailed map, show node name node.SetSResource( "LabelString", node_data.name ); String tooltip; if( map == 0 ) // On thumbnail-view, show node names only. tooltip = node_data.name; else // On detailed map, include lat/lon display into the tooltip. tooltip = node_data.name + " lat= " + node_data.lat_lon.y + " lon= " + node_data.lat_lon.x; 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, last_x, last_y; 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( "" ); // Perform this on zooming/paning of either map: may be a resize of just // one 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. ); Drawing.Update(); return; } else if( origin.equals( "TopMap" ) ) { // Close top map window Drawing.SetDResource( "TopMap/Visibility", 0. ); Drawing.Update(); return; } else // Closing main window when stand-alone: exit. System.exit( 0 ); return; } if( format.equals( "Button" ) ) { if( !action.equals( "Activate" ) ) return; PanMode = false; // Abort Pan mode if( origin.equals( "CloseDialog" ) ) { Drawing.SetDResource( "SelectionDialog/Visibility", 0. ); Drawing.Update(); } else if( origin.equals( "ToggleLock" ) ) SetLocking( !LockSelectedPlane ); else if( origin.equals( "ZoomIn" ) ) Zoom( 'i', 2. ); else if( origin.equals( "ZoomOut" ) ) Zoom( 'o', 2. ); else if( origin.equals( "ZoomReset" ) ) { PlaneSize = SMALL_SIZE; SetPlaneSize(); SetLocking( false ); SetLabels( false ); Zoom( 'n', 0. ); } else if( origin.equals( "ZoomTo" ) ) { SetLocking( false ); Map[ 1 ].SetZoom( null, 't', 0. ); // Start Zoom op SetStatus( "Define a rectangular area to zoom to." ); } 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." ); } else if( origin.equals( "Nodes" ) ) { ToggleResource( Map[ 0 ], "NodeGroup/Visibility" ); ToggleResource( Map[ 1 ], "NodeGroup/Visibility" ); } else if( origin.equals( "Planes" ) ) { ToggleResource( Map[ 0 ], "PlaneGroup/Visibility" ); ToggleResource( Map[ 1 ], "PlaneGroup/Visibility" ); } else if( origin.equals( "ValueDisplay" ) ) { // Visibility of all labels is constrained, set just one. ToggleResource( PlaneArray[ 0 ].graphics[ 1 ], "Label/Visibility" ); } else if( origin.equals( "ToggleStates" ) ) { String layers = Map[ 1 ].GetSResource( "GISObject/GISLayers" ); if( layers.equals( "default" ) ) layers = "default,states"; // Enable state outline display else layers = "default"; // Disable states display Map[ 1 ].SetSResource( "GISObject/GISLayers", layers ); } 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(); } } else if( action.equals( "Zoom" ) && subaction.equals( "End" ) ) { // Update icon positions after zooming. UpdateObjectsOnMap( 1, "Zooming, 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 ); // Updates icons and selected area display. UpdateObjectsOnMap( 0, "Zooming, please wait..." ); } 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. */ if( type == 'i' ) { // 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( !IsReady ) return; // Use the Map area events only. if( trace_info.viewport == Map[ 0 ] ) map = 0; else if( trace_info.viewport == Map[ 1 ] ) map = 1; else return; int event_type = trace_info.event.getID(); switch( event_type ) { 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; 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(); // 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..." ); Map[ 1 ].Update(); break; default: return; } } ////////////////////////////////////////////////////////////////////////// // Show the area of the detailed map on the thumbnail map. ////////////////////////////////////////////////////////////////////////// void SetSelectedArea() { GlgObject point_obj[] = new GlgObject[ 4 ]; GlgPoint lat_lon[] = new GlgPoint[ 4 ]; int i; // Set the coordinates of the SelectedArea polygon. GlgObject rect = Map[ 0 ].GetResourceObject( "SelectedArea" ); GlgPoint extent = GISObject[ 1 ].GetGResource( "GISExtent" ); if( MapProjection[ 1 ] == GlgObject.ORTHOGRAPHIC_PROJECTION && extent.x > 1.5 * GlgObject.POLAR_RADIUS && extent.y > 1.5 * GlgObject.POLAR_RADIUS || MapProjection[ 1 ] == GlgObject.RECTANGULAR_PROJECTION && extent.x > 300. && extent.y > 130. ) { // Big area: don't need to show. rect.SetDResource( "Visibility", 0. ); } else { rect.SetDResource( "Visibility", 1. ); // Get polygon points for( i=0; i<4; ++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., 1000., 1 ); lat_lon[ 2 ] = GetLatLon( 1000., 1000., 1 ); lat_lon[ 3 ] = GetLatLon( 1000., -1000., 1 ); for( i=0; i<4; ++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 ); } } rect.SetGResource( "EdgeColor", 0.9, 0.9, 0.9 ); // Light color if( IsReady ) Map[ 0 ].Update(); } ////////////////////////////////////////////////////////////////////////// // 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 ); PlaneSize = MEDIUM_SIZE; SetPlaneSize(); // 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. ); // Turn labels on on the detailed view: zoomed on US. SetLabels( true ); } ////////////////////////////////////////////////////////////////////////// // 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 SetLabels( 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 + " lat= " + SelectedPlane.lat_lon.y + " lon= " + SelectedPlane.lat_lon.x; } 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. ); } //////////////////////////////////////////////////////////////////////// // 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(); IsReady = 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 ) UpdatePlanes(); } 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(); } } } }