/*---------------------------------------------------------------------- | Air traffic control demo where a map is displayed suing GLG Map Server. */ import java.applet.*; import java.awt.*; import java.awt.event.*; import java.lang.*; import java.lang.reflect.*; import java.text.*; import javax.swing.*; import com.genlogic.*; ////////////////////////////////////////////////////////////////////////// public class GlgAirTrafficDemo extends GlgJBean implements ActionListener { ////////////////////////////////////////////////////////////////////////// // The main demo class ////////////////////////////////////////////////////////////////////////// String drawing_name = "airtraffic.g"; // If supplied, overrides the URL of the GIS object in the drawing static String SuppliedMapServerURL = null; static boolean StandAlone = false; boolean IsReady = false; double PlaneSpeed = 0.005; // Relative units // Define constants final int MAX_NUM_PLANES = 10000, NUM_NODE_TYPES = 2, NUM_PLANE_TYPES = 3; final double SMALL_SIZE = 0.5, MEDIUM_SIZE = 0.7, BIG_SIZE = 0.9; final int NORMAL = 0, WARNING = 1, ALARM = 2, SELECTED = 3; int MaxNumPlanes = 10000, NumNodeTypes = 2, NumPlaneTypes = 3; GlgObject Drawing, NodeTemplate[] = new GlgObject[ NumNodeTypes ], PlaneTemplate[] = new GlgObject[ NumPlaneTypes ], TrajectoryTemplate, NodePool[] = new GlgObject[ NumNodeTypes ], PlanePool[] = new GlgObject[ NumPlaneTypes ], TrajectoryPool, Map, GISObject, NodeGroup, PlaneGroup, TrajectoryGroup, DistancePolygon; // Store initial extent and center, used to reset. GlgPoint InitExtent = new GlgPoint(), InitCenter = new GlgPoint(); double PlaneSize = SMALL_SIZE, // Dimensions of the map viewport windows. window_width, window_height; int UpdateInterval = 100, // Update interval in msec. NumPlanes = 100, NumNodes, NumTrajectoryPoints, InitialMapProjection, MapProjection, PlaneType = 0, NodeType = 0, SelectedPlaneIndex = -1, NumDistancePoints; boolean OrthoOnly = false, NoFill = false, DoUpdate = true, PanMode = false, DistanceMode = false, RedoIcons = true, HasAngle = false, HasElevation = false; PlaneData PlaneArray[]; // 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 ) }; // Temporary variables: allocate once GlgPoint lat_lon = new GlgPoint(), point = new GlgPoint(), world_point = new GlgPoint(); Timer timer = null; ////////////////////////////////////////////////////////////////////////// public GlgAirTrafficDemo() { super(); SetDResource( "$config/GlgMouseTooltipTimeout", 0.05 ); // Activate Trace callback. AddListener( GlgObject.TRACE_CB, this ); } ////////////////////////////////////////////////////////////////////////// // For using as a stand-alone java demo ////////////////////////////////////////////////////////////////////////// public static void main( final String arg[] ) { SwingUtilities. invokeLater( new Runnable(){ public void run() { Main( arg ); } } ); } ////////////////////////////////////////////////////////////////////////// // Optional arguments. // -num_planes - specify the number of planes to be displayed; // default: 100 // -speed - plane speed in relative units, default: 0.005 // -ortho - use Orthographic projection only // -map_server_url - specify MapServerURL; it may be supplied as an // applet parameter when used as an applet; // default: use GIS object's MapServerURL property // from the drawing file. ////////////////////////////////////////////////////////////////////////// public static void Main( String arg[] ) { class DemoQuit extends WindowAdapter { public void windowClosing( WindowEvent e ) { System.exit( 0 ); } } JFrame frame = new JFrame(); frame.setResizable( true ); frame.setSize( 800, 700 ); frame.setLocation( 100, 20 ); GlgAirTrafficDemo air_traffic_demo = new GlgAirTrafficDemo(); frame.getContentPane().add( air_traffic_demo ); frame.addWindowListener( new DemoQuit() ); frame.show(); // Process command line arguments. int num_arg = Array.getLength( arg ); if( num_arg != 0 ) { for( int skip = 0; skip < num_arg; ++skip ) { if( arg[ skip ].equals( "-num_planes" ) ) { ++skip; if( num_arg <= skip ) air_traffic_demo.error( "No plane number.", true ); try { air_traffic_demo.NumPlanes = new Integer( arg[ skip ] ).intValue(); } catch( NumberFormatException e ) { air_traffic_demo.error( "No plane number.", true ); } if( air_traffic_demo.NumPlanes > air_traffic_demo.MAX_NUM_PLANES ) air_traffic_demo.error( "Increase plane array size and run again.", true ); } else if( arg[ skip ].equals( "-speed" ) ) { ++skip; if( num_arg <= skip ) air_traffic_demo.error( "No plane speed.", true ); try { air_traffic_demo.PlaneSpeed = new Double( arg[ skip ] ).doubleValue(); } catch( NumberFormatException e ) { air_traffic_demo.error( "No plane speed.", true ); } } else if( arg[ skip ].equals( "-ortho" ) ) { air_traffic_demo.OrthoOnly = true; } else if( arg[ skip ].equals( "-map_server_url" ) ) { ++skip; if( num_arg <= skip ) air_traffic_demo.error( "No map server URL.", true ); air_traffic_demo.SuppliedMapServerURL = arg[ skip ]; } else if( arg[ skip ].equals( "-help" ) ) { System.out.println( "Options: -num_planes -speed -ortho -map_server_url" ); System.out.println( "Defaults: -num_planes 100 -speed 0.005" ); System.exit( 0 ); } else { System.out.println ( "Invalid option. Use -help for the list of options." ); System.exit( 1 ); } } } air_traffic_demo.SetDrawingName( air_traffic_demo.drawing_name ); } ////////////////////////////////////////////////////////////////////////// // Invoked before the hierarchy setup. ////////////////////////////////////////////////////////////////////////// public void HCallback( GlgObject viewport ) { Drawing = viewport; Map = Drawing.GetResourceObject( "Map" ); GISObject = Map.GetResourceObject( "GISObject" ); if( !StandAlone ) { String param = getParameter( "MapServerURL" ); if( param != null ) SuppliedMapServerURL = param; } Init(); } ////////////////////////////////////////////////////////////////////////// // Initializes icons in the drawing ////////////////////////////////////////////////////////////////////////// void Init() { String icon_name; int i; if( SuppliedMapServerURL != null ) // Override URL in the drawing GISObject.SetSResource( "GISMapServerURL", SuppliedMapServerURL ); // Set GIS Zoom mode for the drawing. Map.SetGISZoom( null, GISObject, null ); // Query and store the GIS projection (ORTHOGRAPHIC or RECTANGULAR) int projection = GISObject.GetDResource( "GISProjection" ).intValue(); InitialMapProjection = projection; MapProjection = projection; // Query and store GIS object's Extent and Center. InitExtent = GISObject.GetGResource( "GISExtent" ); InitCenter = GISObject.GetGResource( "GISCenter" ); // Make popup dialog and distance popup invisible. Drawing.SetDResource( "FlightInfoPopup/Visibility", 0. ); Map.SetDResource( "DistancePopup/Visibility", 0. ); // Disable color button: this functionality is not supported with // the remote map sever setup. Drawing.SetDResource( "ToggleColor/HandlerDisabled", 1. ); // 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. A few sets of templates // with different level of details are used, depending on the zoom level. // Palette aproach is used to implement icon types instead of // subdrawings, since icons are dynamically added/deleted from the // drawing any way to show only the icons visible in the zoomed area. // for( i=0; i < NUM_PLANE_TYPES; ++i ) { icon_name = "Plane" + i; PlaneTemplate[ i ] = palette.GetResourceObject( icon_name ); // Turn labels on initially. if( i != 0 ) PlaneTemplate[ i ].SetDResource( "Label/Visibility", 1. ); } TrajectoryTemplate = palette.GetResourceObject( "Trajectory" ); NumTrajectoryPoints = TrajectoryTemplate.GetDResource( "Factor" ).intValue(); for( i=0; i < NUM_NODE_TYPES; ++i ) { icon_name = "Node" + i; NodeTemplate[ i ] = palette.GetResourceObject( icon_name ); } 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 ); PlaneArray[ i ].tooltip = null; PlaneArray[ i ].color_index = NORMAL; PlaneArray[ i ].graphics = null; PlaneArray[ i ].trajectory = null; PlaneArray[ i ].iteration = 0; StartPlane( PlaneArray[ i ], true ); } // Create groups to hold nodes and planes. NodeGroup = new GlgDynArray( GlgObject.GLG_OBJECT, 0, 0 ); NodeGroup.SetSResource( "Name", "NodeGroup" ); TrajectoryGroup = new GlgDynArray( GlgObject.GLG_OBJECT, 0, 0 ); TrajectoryGroup.SetSResource( "Name", "TrajectoryGroup" ); PlaneGroup = new GlgDynArray( GlgObject.GLG_OBJECT, 0, 0 ); PlaneGroup.SetSResource( "Name", "PlaneGroup" ); Map.AddObjectToBottom( NodeGroup ); Map.AddObjectToBottom( PlaneGroup ); Map.AddObjectToBottom( TrajectoryGroup ); // Create groups to keep pooled objects. for( i=0; i= 0 ? "E" : "W") + " " + lat_str + (plane.lat_lon.x >= 0 ? "N" : "S"); Drawing.SetSResource( "FlightInfoPopup/Location", buffer ); } //////////////////////////////////////////////////////////////////////// // Delete node icons and place them into the object pool. //////////////////////////////////////////////////////////////////////// void DeleteNodes() { GlgObject icon; int i; // Move node icons into the object pull int size = NodeGroup.GetSize(); for( i=0; i 0 ) for( i=0; i 0 ) for( i=0; i 0 ) for( i=0; i 0 ) // Return an icon from the pool { icon = (GlgObject) NodePool[ node_type ].GetElement( size - 1 ); NodePool[ node_type ].DeleteBottomObject(); } else // Create a new icon { icon = NodeTemplate[ node_type ].CloneObject( GlgObject.STRONG_CLONE ); } return icon; } //////////////////////////////////////////////////////////////////////// GlgObject CreatePlaneIcon( int plane_type ) { GlgObject icon; int size = PlanePool[ plane_type ].GetSize(); if( size > 0 ) // Return an icon from the pool { icon = (GlgObject) PlanePool[ plane_type ].GetElement( size - 1 ); PlanePool[ plane_type ].DeleteBottomObject(); } else // Create a new icon { icon = PlaneTemplate[ plane_type ].CloneObject( GlgObject.STRONG_CLONE ); } return icon; } //////////////////////////////////////////////////////////////////////// GlgObject CreateTrajectoryIcon() { GlgObject icon; int size = TrajectoryPool.GetSize(); if( size > 0 ) // Return an icon from the pool { icon = (GlgObject) TrajectoryPool.GetElement( size - 1 ); TrajectoryPool.DeleteBottomObject(); } else // Create a new icon { icon = TrajectoryTemplate.CloneObject( GlgObject.STRONG_CLONE ); } return icon; } //////////////////////////////////////////////////////////////////////// void PositionPlane( PlaneData plane, int index ) { // Converts plane position (simulated or real data) from lat/lon to // GLG coordinates. GetPlanePosition( plane ); if( !IconVisible( plane.xyz ) ) { // Delete graphics and place into the pool if( plane.graphics != null ) { PlanePool[ PlaneType ].AddObjectToBottom( plane.graphics ); PlaneGroup.DeleteObject( plane.graphics ); plane.graphics = null; } // Delete trajectory and place into the pool if( plane.trajectory != null ) { TrajectoryPool.AddObjectToBottom( plane.trajectory ); TrajectoryGroup.DeleteObject( plane.trajectory ); plane.trajectory = null; } return; } if( plane.graphics == null ) AddPlaneGraphics( plane, PlaneType, index ); // Update plane's icon in the drawing plane.graphics.SetGResource( "Position", plane.xyz ); // Update icon's direction angle is necessary if( HasAngle ) { plane.angle = GetPlaneAngle( plane ); plane.graphics.SetDResource( "Angle", plane.angle ); } if( HasElevation ) { plane.elevation = GetPlaneElevation( plane ); plane.graphics.SetDResource( "Height", plane.elevation ); } if( plane.trajectory != null ) { // For small speeds, skip a few iterations to increase the // trajectory's length if( PlaneSpeed < 0.005 ) { Double n_d = new Double( 0.005 / PlaneSpeed ); int n = n_d.intValue(); if( n != 0 ) { ++plane.iteration; if( ( plane.iteration % n ) != 0 ) return; // Skip n iterations, update every n'th } } plane.trajectory.SetDResource( "VisEntryPoint", 1. ); plane.trajectory.SetGResource( "XYEntryPoint", plane.xyz ); } } //////////////////////////////////////////////////////////////////////// // Adds an airport icon, fills labels, tooltips, etc. //////////////////////////////////////////////////////////////////////// void AddNodeGraphics( NodeData node, int node_type, int index ) { GlgObject icon = CreateNodeIcon( node_type ); // Index for direct access icon.SetDResource( "DataIndex", (double)index ); if( node_type > 0 ) // More detailed icon icon.SetSResource( "LabelString", node.name ); // Format numbers. NumberFormat nf = NumberFormat.getInstance(); nf.setGroupingUsed( false ); nf.setMinimumFractionDigits( 2 ); nf.setMaximumFractionDigits( 4 ); String lon_str = nf.format( Math.abs(node.lat_lon.x) ); String lat_str = nf.format( Math.abs(node.lat_lon.y) ); String tooltip = node.name + " lon= " + lon_str + (node.lat_lon.x >= 0 ? "E" : "W") + " lat= " + lat_str + (node.lat_lon.y >= 0 ? "N" : "S"); icon.SetSResource( "TooltipString", tooltip ); node.graphics = icon; // The node will be positioned after the GIS object is setup. NodeGroup.AddObjectToBottom( icon ); } //////////////////////////////////////////////////////////////////////// // Adds a plane icon, fills labels, tooltips, etc. //////////////////////////////////////////////////////////////////////// void AddPlaneGraphics( PlaneData plane, int plane_type, int index ) { GlgObject icon = CreatePlaneIcon( plane_type ); // Index for direct access icon.SetDResource( "DataIndex", (double)index ); // Icon color icon.SetDResource( "ColorIndex", (double) plane.color_index ); if( plane_type > 0 ) // More detailed icon { // Show the flight number as icon label String label = "Flight " + plane.flight_number; icon.SetSResource( "LabelString", label ); } // Set the tooltip icon.SetSResource( "TooltipString", plane.tooltip ); plane.graphics = icon; // The plane will be positioned after the GIS object is setup. // PositionPlane( plane, map ); PlaneGroup.AddObjectToBottom( icon ); if( plane_type == 2 ) // For detailed icon, create trajectory { icon = CreateTrajectoryIcon(); plane.trajectory = icon; // Set entries invisible initially icon.SetDResource( "Marker/Visibility", 0. ); TrajectoryGroup.AddObjectToBottom( icon ); for( int i=0; i= 0. && position.x > -1000. && position.x < 1000. && position.y > -1000. && position.y < 1000.; } //////////////////////////////////////////////////////////////////////// // Converts node's position from lat/lon to X/Y in GLG world coordinates. //////////////////////////////////////////////////////////////////////// void GetNodePosition( NodeData node ) { // Converts lat/lon to X/Y using GIS object's current projection. GISObject.GISConvert( null, GlgObject.OBJECT_COORD, /* Lat/Lon to XY */ false, node.lat_lon, node.xyz ); } //////////////////////////////////////////////////////////////////////// // Converts plane's position from lat/lon to X/Y in GLG world coordinates. //////////////////////////////////////////////////////////////////////// void GetPlanePosition( PlaneData plane ) { // 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.GISConvert( null, GlgObject.OBJECT_COORD, /* Lat/Lon to XY */ false, plane.lat_lon, plane.xyz ); } //////////////////////////////////////////////////////////////////////// // Compensate plane icons for X/Y stretch. Screen coord. extent may be // used instead. //////////////////////////////////////////////////////////////////////// void AdjustPlane() { GlgObject plane, scale_x, scale_y; double wh_ratio, ratio_half; for( int i=0; i 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 ); } } } } //////////////////////////////////////////////////////////////////////// // Displays a message in the status area. //////////////////////////////////////////////////////////////////////// void SetStatus( String message ) { Drawing.SetSResource( "StatusLabel/String", message ); Drawing.GetResourceObject("StatusArea").Update(); } //////////////////////////////////////////////////////////////////////// void SetPlaneSize() { for( int i=0; i 1. ) plane.path_position = 1.; } SetPlaneColor( plane ); PositionPlane( plane, index ); // Position plane on the map } //////////////////////////////////////////////////////////////////////// // SIMULATION ONLY: Simulate data to change plane color to show warnings // and alarms. //////////////////////////////////////////////////////////////////////// void SetPlaneColor( PlaneData plane ) { int new_color_index = NORMAL; // Set random color double random_value = Rand( 0., 1. ); if( plane.color_index == NORMAL ) { if( random_value <= 0.999 ) new_color_index = NORMAL; else if( random_value > 0.9999 ) new_color_index = ALARM; else if( random_value > 0.999 ) new_color_index = WARNING; } else if( plane.color_index == WARNING ) { if( random_value > 0.99 ) new_color_index = NORMAL; else new_color_index = plane.color_index; // Keep alarm for a while } else if( plane.color_index == ALARM ) { if( random_value > 0.999 ) new_color_index = NORMAL; else new_color_index = plane.color_index; // Keep alarm for a while } if( plane.graphics != null && new_color_index != plane.color_index ) plane.graphics.SetDResource( "ColorIndex", (double)new_color_index ); plane.color_index = new_color_index; } //////////////////////////////////////////////////////////////////////// // SIMULATION ONLY: Starts simulation for a plane, selects its start // and end nodes. //////////////////////////////////////////////////////////////////////// void StartPlane( PlaneData plane, boolean init ) { int to_index, from_index; if( NumNodes < 2 ) error( "Less then two nodes: can't start planes.", true ); 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 ) // Init the demo: position randomly along the path { plane.path_position = Rand( 0.1, 0.9 ); plane.path_position_last = plane.path_position - 0.05; // For angle } else // Position at the beginning of the path { plane.path_position = 0.; plane.path_position_last = 0.; } String flight_name = "Flight " + plane.flight_number; // Add from/to node info to the tooltip. plane.tooltip = flight_name + " from " + plane.from_node.name + " to " + plane.to_node.name; // Set all trajectory points invisible, if any if( plane.trajectory != null ) plane.trajectory.SetDResource( "Marker%/Visibility", 0. ); } //////////////////////////////////////////////////////////////////////// // 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 ); } ////////////////////////////////////////////////////////////////////////// // 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( UpdateInterval, 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; GlgPoint xyz = new GlgPoint(); // Position in GLG world coords NodeData( String name_p, double lon, double lat ) { name = name_p; lat_lon = new GlgPoint( lon, lat, 0. ); } } class PlaneData { String name; GlgPoint lat_lon = new GlgPoint(); int flight_number; String tooltip; GlgObject graphics; GlgObject trajectory; NodeData from_node; NodeData to_node; double path_position; double path_position_last; double speed; GlgPoint xyz = new GlgPoint(); // Position in GLG world coords double angle; int has_angle; int color_index; double elevation; int iteration; PlaneData() { } } }