/** * Navigation Information Display Panel for Moving Map package * @author Robert J Morton * @version 19 December 1997 */ /* To display the map for the moving map package. Provides main canvas on which to display the map plus an image of the map. The image is updated unseen, then copied to the canvas once each cyclic update of the map has been completed. */ import javax.swing.*; // Java Swing GUI utilities import java.awt.*; // Java Abstract Windowing Toolkit class airmap extends JPanel implements navconst { // What the hell this is for, I don't know! private static final long serialVersionUID = 2013L; private static airmap am; // reference to air map object private aircraft ac; // reference to current aircraft object private movmap MM; // reference to current instance of movmap applet class private loader ld; // reference to the routes and waypoints data loader private selmap sm; // reference to the map selector object private waypnt pw, // reference to previous waypoint object wc, // reference to current waypoint object nw; // reference to next waypoint object private boolean podePaint = false, // can paint/repaint map when true ToFlag, // true if aircraft is approaching current waypoint // true when current waypoint is out of "radio" range of aircraft wcOutOfRange = false; private int MNVW = 100, // maximum number of visible waypoints allowed on map X = 150, Y = 150, // maximum x and y from centre of canvas CX = X + 115, // x co-ordinate of the centre of the compass cross CY = Y + 115, // y co-ordinate of the centre of the compass cross X1 = X >> 3, // To test if city name should be displayed X2 = X + X1, // on the left or right of the 'city'. x = 100, y = 100, // x & y pixel coords of current geographic feature Xx[] = new int[MNVW], // array for those of all visible features Yy[] = new int[MNVW], // array for those of all visible features vw = 0, // number of waypoints currently visible vwc = 0, // index number of current waypoint z = 7, hz = 3, // diameter of city blob, and half of it fh, // font height (total height of a line of text) fa, // half font ascent (height of letters above base line) Nw; // half-width of letter 'N' for compass cross private double Scale, // map scale in pixels per earth-radian InM = 50 / KPR, // length of standard outbound radial line in radians OutM = 30 / KPR, // length of standard outbound radial line in radians h, // screen x-pixel of a geographic feature v, // screen y-pixel of a geographic feature H[] = new double[MNVW], // array for h of all visible waypoints V[] = new double[MNVW], // array for h of all visible waypoints Dst, // aircraft's current distance from current waypoint ah, // aircraft's current heading angle, // screen angle of outbound radial line pwBrg, // bearing (rBrg) from aircraft to prev waypoint wcBrg, // bearing (rBrg) from aircraft to current waypoint nwBrg, // bearing (rBrg) from aircraft to next waypoint wcInRad, // screen angle of inbound radial line acCM, // distance from waypoint to where approach corridor ends wcPWD, // half dist from prev waypoint (where approach corridor starts) wcDL, // corridor width wcDST, // half the distance to next waypoint in radians acTCR, // radius of aircraft's minimum comfortable turning circle [km] MapSize; // get the appropriate angular increment for drawing the circles // Array for the names of the currently visible waypoints private String WN[] = new String[MNVW]; // Paint graphics ref used universally throughout this class private Graphics g; private Font font; // font for on-map lettering private FontMetrics fm; // letter dimensions etc for the above font private Color GCC, // create special bright green for circle RCC, // create special bright red for circle SRC, // create special colour for selected radial line fg; // foreground colour fpr text airmap(movmap MM, Color fg, aircraft ac, selmap sm) { this.MM = MM; // reference to current instance of movmap am = this; // reference to airmap object this.fg = fg; // foreground colour for text this.ac = ac; // reference to (current) aircraft object this.sm = sm; // reference to the map selector radio-buttons object /* Create a general font for screen lettering, get its letter and leading dimensions and its ascent (ht above baseline). */ font = new Font("Serif",Font.BOLD,12); fm = getFontMetrics(font); fa = fm.getAscent() >> 1; /* Create special bright green for circle, special bright red for circle and special colour for selected radial line. */ GCC = new java.awt.Color(127,255,127); RCC = new java.awt.Color(255,127,127); SRC = new java.awt.Color(255,255,255); // Half the width of the letter 'N' for the compass. Nw = fm.stringWidth("N") / 2; } void setLoader(loader ld) {this.ld = ld;} /* This method is executed by the run() thread. It updates local var- viables containing all the data needed to draw the map. This must be done independently of the paint() method so that the paint() mehtod always has a valid set of data from which to redraw the map in the event of the applet being eclipsed by a drop-down from a choice menu or by another the open window. This method is executed on the run() thread. */ void atualizar() { /* Get the current value of the map scaling factor and the appropriate angular increment for drawing the guide circles. */ Scale = sm.getScale(); MapSize = sm.getMapSize(); /* Get the aircraft's current heading 'ah', the distance 'acCM' from the waypoint to where waypoint's approach corridor ends and the radius 'acTCR' of aircraft's minimum comfortable turning circle in kilometres. */ ah = ac.getHdg(); acCM = ac.getCM(); acTCR = ac.getTCR() * Scale; /* Get the reference 'wc' of current waypoint object and use it to get the bearing 'wcBrg' from aircraft to current waypoint, the screen angle 'wcInRad' of inbound radial line, the semi-distance 'wcPWD' from the previous waypoint (where approach corridor starts), the width 'wcDL' of the approach corridor and the semi-distance 'wcDST' to the next waypoint (in Earth-radians]. */ wc = waypnt.getCurrent(); wcBrg = wc.getrBrg() - ah; wcInRad = wc.getInRad() - ah; wcPWD = wc.getPWD() / 2; wcDL = wc.getDL() * Scale * 2; wcDST = wc.getDST() / 2; /* The 'ToFlag' is TRUE if aircraft is approaching current waypoint and FALSE if it is receding from the current waypoint. */ ToFlag = wpenc.getCurrent().getToFlag(); /* Get the reference 'pw' of the previous waypoint's object and use it to compute the distance and bearing to next waypoint in order to get the bearing 'pwBrg' to previous waypoint (behind aircraft). */ pw = wc.getPrev(); pw.DandB(); pwBrg = pw.getrBrg() - ah; /* Get the reference 'nw' of the next waypoint's object and use it to compute the distance and bearing to previous waypoint in order to get the bearing 'nwBrg' to next waypoint (ahead of aircraft). */ nw = wc.getNext(); nw.DandB(); nwBrg = nw.getrBrg() - ah; angle = wc.getOutRad() - ah; // screen angle of outbound radial line /* Set total number of visible waypoints to zero and set the index number of the current waypoint 'vwc' to '-1' to avoid painting the guide circles when the current waypoint is off-screen. */ vw = 0; vwc = -1; int I = waypnt.getTotal(); // number of waypoints in the database for(int k = 0 ; k < I ; k++) { // For each geographic feature in database waypnt w = waypnt.getRef(k); // get reference of next waypoint if(DBtoXY(w)) { // If feature within range of map window WN[vw] = w.getName(); // get the waypoint's name and Xx[vw] = x; // save its x & y coordinates. H[vw] = h; Yy[vw] = y; V[vw] = v; if(w == wc) { // If it is the current waypoint vwc = vw; // note its index number. /* If current waypoint is out of view (beyond the map boundary) then set flag to indicate this, otherwise it is within range so mark it as not out of range to indicate that current way- point is not on screen. */ if(Dst > w.getRange()) wcOutOfRange = true; else wcOutOfRange = false; } vw++; // increment index of visible waypoints } } repaint(); // schedule a call to paint() on the event dispatching thread } /* COMPUTE X,Y SCREEN CO-ORDINATES FROM RANGE AND BEARING: Assumes logical co-ordinates are at centre of 300 by 300 pixel canvas. Returns false if feature out of screen range. (executed on the run() thread) */ // COMPUTE X,Y PIXEL CO-ORDS FROM DISTANCE & BREARING private boolean DBtoXY(waypnt w) { if(!w.DandB()) return false; // abort if waypoint is out of range if((Dst = w.getDst()) == 0) { // If aircraft is at the waypoint, x = X; // set 'int' x and y co-ordinates y = Y; // and also their 'double' versions h = 0; // to centre of map v = 0; // and return that return true; // the aircraft is within range } double d = Dst * Scale, // Distance of waypoint in kilometres b = w.getrBrg() - ah; // bearing of waypoint in radians // less ah, the aircraft heading h = d * Math.sin(b); //screen x-pixel in double float v = d * Math.cos(b); //screen y-pixel in double float x = (int)(Math.rint(h)); //rounded interger screen x-pixel y = (int)(Math.rint(v)); //rounded interger screen y-pixel // Exit if waypoint is beyond screen range. if(Math.abs(x) > X || Math.abs(y) > Y) return false; x += X; // relate co-ordinates to centre of canvas y = Y - y; return true; // return that it is within screen range } /* RE-DRAW THE PANEL IN THE EVENT OF IT BEING ECLIPSED OR UPDATED: The map can be painted only after the background image, the route names and the waypoints of the default route have all been loaded. Painting before this time would mean painting data from variables within class instances that had not yet been created. DefaultsLoaded() returns true when both route names and a full set of default waypoints exist within the system. The local flag podePaint is set true once DefaultsLoaed() has returned true for the first time. If it goes false later during the loading of another route's way- points, repainting of the map can still take place without any problems. This method is executed on the event dispatching thread. */ public void paint(Graphics g) { if(!podePaint) { if(!ld.DefaultsLoaded()) // Exit if routes and default return; // waypoints not yet loaded. podePaint = true; } /* Draw the "Clouds" map area backdrop photo and set the letter font for the waypoint names and compass. */ g.drawImage(movmap.IMG2,0,0,this); g.setFont(font); // For each currently-visible geographic feature... for(int k = 0 ; k < vw ; k++) { x = Xx[k]; // Get its x and y screen coordinates y = Yy[k]; // in both integer and h = H[k]; // floating-point double forms. v = V[k]; if(k == vwc) { // If current waypoint, draw navigation constructs /* SHOW GREEN AND RED GUIDE CIRCLES AT CURRENT WAYPOINT Get the sine and cosine of the screen angle of the outbound radial line and the x and y off-sets of the centre of the turning circle perpendicular to OutRad. */ double S = Math.sin(angle), C = Math.cos(angle), rS = acTCR * S, rC = acTCR * C; // For every degree around a guide circle... for(double a = 0; a < Twoπ; a += MapSize) { /* Absolute co-ordinates of one point on a generic guide circle which is centred on the waypoint. */ double xw = acTCR * Math.sin(a), yw = acTCR * Math.cos(a); /* Compute the screen coordinates of the right-hand guide circle and plot its next point in green. */ int p = x + (int)(Math.rint(xw + rC)), q = y - (int)(Math.rint(yw - rS)); g.setColor(GCC); g.drawLine(p,q,p,q); /* Compute the screen coordinates of the left-hand guide circle and plot its next point in red. */ p = x + (int)(Math.rint(xw - rC)); q = y - (int)(Math.rint(yw + rS)); g.setColor(RCC); g.drawLine(p,q,p,q); } // MARK OUTBOUND RADIAL FROM CURRENT WAYPOINT double r = wcDST; // half the distance to next waypoint in radians if(r > OutM) // Impose an upper limit on length r = OutM; // of the displayed line. r *= Scale; // adjust it to currently selected map scale g.setColor(SRC); // colour for the radial lines g.drawLine( // draw the outbound radial: x, // x coord of start of radial line y, // y coord of start of radial line X + (int)(Math.rint(h + r * S)), // x coord of end Y - (int)(Math.rint(v + r * C)) // y coord of end ); //MARK CURRENT WAYPOINT'S APPROACH CORRIDOR /* Get the sine and cosine of the aircraft's heading error (InRad - aircraft heading). */ S = Math.sin(wcInRad); C = Math.cos(wcInRad); /* Get the distance from the waypoint to where the approach corridor ends and half the distance from the previous way- point (where approach corridor starts). */ double m = acCM; r = wcPWD; if(r > InM) // limit the displayed length r = InM; // of inbound corridor /* , . */ if(r > m) { // If there is space for an approach corridor r *= Scale; // adjust its length to the m *= Scale; // currently selected map scale double lx = r * S, // Get the x and y components ly = r * C, rx = m * S, // of the start and end of the corridor. ry = m * C; g.drawLine( // draw the inbound radial line X + (int)(Math.rint(h + rx)), Y - (int)(Math.rint(v + ry)), X + (int)(Math.rint(h + lx)), Y - (int)(Math.rint(v + ly)) ); double w = wcDL, // Corridor width wx = w * C, // and its x and y components. wy = w * S; g.drawLine( // draw the other boundary of the approach corridor X + (int)(Math.rint(h + rx - wx)), Y - (int)(Math.rint(v + ry + wy)), X + (int)(Math.rint(h + lx - wx)), Y - (int)(Math.rint(v + ly + wy)) ); } } //-------------------------------- end of 'if(k == vwc) {' /* Draw the white blob to represent the visible way- point (city) and annotate it with its geographic name. */ g.setColor(fg); // Set the colour for the blob, g.fillOval(x - hz,y - hz,z,z); // draw it on the map if(x < X2) // If waypoint less that 9/16 the way across the map x += hz + 3; // put its name on the right else // otherwise put its name on the left x -= hz + 2 + fm.stringWidth(s); g.drawString(s, x, y + fa); // display the feature's name } /*---------end of the 'for(int k = 0 ; k < vw ; k++) {' loop that draws all visible waypoints. */ /* DRAW THE GPS/INERTIAL GUIDANCE BARS: Displays a magenta bar pointing from aircraft to the waypoint it is currently approaching and an orange bar pointing from aircraft back towards the waypoint it has just left. */ if(wcOutOfRange) { // If aircraft beyond radio range of current waypoint double // create and zero variables for its bearing 'rBrg' bf = 0, // to the next waypoint bb = 0; // to the previous waypoint. if(ToFlag) { // If aircraft is approaching current waypoint bb = pwBrg; // get the bearing to previous waypoint (behind aircraft) bf = wcBrg; // bearing to the current waypoint (ahead of aircraft) } else { // else if receding from the current waypoint bb = wcBrg; // get bearing to current waypoint (behind aircraft) bf = nwBrg; // and bearing to next waypoint (ahead of aircraft). } g.setColor(Color.magenta); // Draw the forward-pointing g.drawLine( // and backward-pointing lines X, // each in its respective colour Y, X + (int)(100 * Math.sin(bf)), Y - (int)(100 * Math.cos(bf)) ); g.setColor(Color.orange); g.drawLine( X, Y, X + (int)(100 * Math.sin(bb)), Y - (int)(100 * Math.cos(bb)) ); } //DISPLAY CENTRAL CROSS-HAIRS AND EDGE LINES ON MAP g.setColor(Color.white); // set colour for central cross-hairs g.drawLine(140,150,160,150); // draw the horizontal hair g.drawLine(150,140,150,160); // draw the vertical hair g.drawLine(150,0,150,10); // draw the top vertical hair g.drawLine(150,290,150,300); // draw the bottom vertical hair g.drawLine(0,150,10,150); // draw the left horizontal hair g.drawLine(290,150,300,150); // draw the right horizontal hair //SHOW LITTLE COMPASS CROSS AT BOTTOM RIGHT double // Sine and cosine of the aircraft's heading a = Math.sin(ah), b = Math.cos(ah); //draw the letter 'N' to indicate North g.drawString("N",CX - (int)(23 * a) - Nw,CY - (int)(23 * b) + fa); int // vertical & horizontal off-sets of the points of the compass cross c = (int)(15 * a), d = (int)(15 * b); g.drawLine(CX - c,CY - d,CX + c,CY + d); // draw north-south line g.drawLine(CX + d,CY - c,CX - d,CY + c); // draw east-west line } }