/** * 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 { private static final long serialVersionUID = 14L; // what the hell this is for, I don't know! 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 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 wcOutOfRange = false; /* true when current waypoint is out of "radio" range of aircraft */ private int MNVW = 100, // max number of visible waypoints allowed on map X = 150, // maximum x from centre of canvas Y = 150, // maximum 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, // pixel x-coordinate of current geographic feature y = 100, // pixel y-coordinate 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, // diameter of city blob, hz = 3, // radius of city blob, 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 appropriate angular increment for drawing the circles private String WN[] = new String[MNVW]; // names of currently visible waypoints private Graphics g; // paint graphics ref used throughout this class private Font font; // font for on-map lettering private FontMetrics fm; // letter dimensions etc for the above font private Color GCC = new java.awt.Color(127,255,127), // bright green for right circle RCC = new java.awt.Color(255,127,127), // bright red for left circle SRC = new java.awt.Color(255,255,255), // white 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 // general font for screen lettering font = new Font("Serif",Font.BOLD,12); // letter dimensions etc for the above font fm = getFontMetrics(font); // get the font's ascent (ht above baseline) fa = fm.getAscent() >> 1; // 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 variables containing all the data needed to draw the map. This must be done independently of the paint() method so that the paint() method 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. */ void atualizar() { // this is executed on the run() thread Scale = sm.getScale(); // current value of map scaling factor // appropriate angular increment for drawing the circles MapSize = sm.getMapSize(); ah = ac.getHdg(); // get aircraft's current heading // distance from waypoint to where approach corridor ends acCM = ac.getCM(); // radius of aircraft's minimum comfortable turning circle in km acTCR = ac.getTCR() * Scale; wc = waypnt.getCurrent(); // reference to current waypoint object // bearing (rBrg) from aircraft to current waypoint wcBrg = wc.getrBrg() - ah; wcInRad = wc.getInRad() - ah; // screen angle of inbound radial line // half dist from prev waypoint (where approach corridor starts) wcPWD = wc.getPWD() / 2; wcDL = wc.getDL()*Scale*2; // corridor width wcDST = wc.getDST()/2; // half distance to next waypoint in radians // true if aircraft is approaching current waypoint ToFlag = wpenc.getCurrent().getToFlag(); pw = wc.getPrev(); // reference to previous waypoint's object pw.DandB(); // distance and bearing to previous waypoint pwBrg = pw.getrBrg()-ah; // bearing to prev waypoint (behind aircraft) nw = wc.getNext(); // reference to next waypoint's object nw.DandB(); // distance and bearing of next waypoint nwBrg = nw.getrBrg()-ah; // bearing to next waypoint (ahead of aircraft) angle = wc.getOutRad()-ah; // screen angle of outbound radial line vw = 0; // set total number of visible waypoints to zero // to avoid painting nav circles when current waypoint is off-screen 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); // reference the next waypoint if(DBtoXY(w)) { //if feature is within map window range WN[vw] = w.getName(); //get waypoint's name Xx[vw] = x; H[vw] = h; //save screen x-coordinate of waypoint Yy[vw] = y; V[vw] = v; //save screen y-coordinate of waypoint if(w == wc) { //if it is current waypoint, vwc = vw; /note its index number // if current waypoint is out of view (beyond map boundary) if(Dst > w.getRange()) wcOutOfRange = true; // set flag to indicate this /* Otherwise it is within range, so mark it as not out of range to indicate that current waypoint is on screen. */ else wcOutOfRange = false; } vw++; // increment index of visible waypoints } } repaint(); // schedule a call to paint() on 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()) // if waypoint is out of range return false; // abort and exit // if aircraft is at the waypoint if((Dst = w.getDst()) == 0) { x = X; // set 'int' x co-ordinate to map centre y = Y; // set 'int' y co-ordinate to map centre h = 0; // and also their 'double' versions v = 0; return true; // and return that it is in range } double d = Dst * Scale, // distance of waypoint in km b = w.getrBrg() - ah; /* bearing of waypoint in radians, less 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 // If waypoint is beyond the safe limits of the screen, abort and exit. if(Math.abs(x) > X || Math.abs(y) > Y) return false; x += X; y = Y - y; // relate co-ordinates to centre of canvas 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 waypoints, repainting of the map can still take place without any problems. */ public void paint(Graphics g) { // executed on event dispatching thread if(!podePaint) { if(!ld.DefaultsLoaded()) // routes and default waypoints return; // not yet loaded podePaint = true; } g.drawImage(movmap.IMG2,0,0,this); // photo behind map area g.setFont(font); // set font for name and compass for(int k = 0; k < vw; k++) { /* for each currently visible geographic feature... */ x = Xx[k]; // get its xscreen coordinate and y = Yy[k]; // its y screen coordinate in both h = H[k]; // in both integer and double-float form v = V[k]; if(k == vwc) { /* if it is the current waypoint, then draw the navigation constructs. */ // SHOW GREEN AND RED GUIDE CIRCLES AT CURRENT WAYPOINT // sine and cosine of screen angle of outbound radial line double S = Math.sin(angle), C = Math.cos(angle); /* x and y off-sets of centre of turn- ing circle perpendicular to OutRad */ 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); int p = x + (int)(Math.rint(xw + rC)), q = y - (int)(Math.rint(yw - rS)); g.setColor(GCC); // colour for the green (right-hand) circle g.drawLine(p,q,p,q); // plot next point on green circle p = x + (int)(Math.rint(xw - rC)); q = y - (int)(Math.rint(yw + rS)); g.setColor(RCC); // colour for the red (left-land) circle g.drawLine(p,q,p,q); // plot next point on red circle } // MARK OUTBOUND RADIAL FROM CURRENT WAYPOINT double r = wcDST; // half the distance to next waypoint in radians // impose an upper limit on length of displayed line if(r > OutM) r = OutM; r *= Scale; // adjust it to currently selected map scale g.setColor(SRC); // colour for the radial lines /* draw the outbound radial: the final two arguments are the x and y co-ordinates of the end of the outbound radial line. */ g.drawLine( x, y, X + (int)(Math.rint(h + r * S)), Y - (int)(Math.rint(v + r * C)) ); // MARK CURRENT WAYPOINT'S APPROACH CORRIDOR // sine and cosinr of (InRad - aircraft heading) S = Math.sin(wcInRad); C = Math.cos(wcInRad); // distance from waypoint to where the approach corridor ends double m = acCM; // half dist from prev waypoint (where approach corridor starts) r = wcPWD; if(r > InM) // Limit the displayed length r = InM; // of the inbound corridor. if(r > m) { // If there is space for an approach corridor r *= Scale; // adjust its length to the currently m *= Scale; // selected map scale. double lx = r * S, // x component of corridor start ly = r * C, // y component of corridor start rx = m * S, // x component of corridor end ry = m * C; // y component of corridor end 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, // x component of corridor width wy = w * S; // y component of corridor width g.drawLine( // draw other boundary of 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 waypoint (city) and annotate it with its geographic name */ g.setColor(fg); // set colour for waypoint blob g.fillOval(x-hz,y-hz,z,z); // draw it on the map String s = WN[k]; // annotate it with its name if(x < X2) // if it is less that 9/16 the way across the map x += hz+3; // put its name on the right else // otherwise x -= hz+2+fm.stringWidth(s); // put its name on the left 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 the aircraft to the waypoint it is curr- ently approaching and an orange bar pointing from the air- craft back towards the waypoint it has just left. */ if(wcOutOfRange) { // if beyond radio range of current waypoint double bf = 0, // for bearings (rBrg) to front and back waypoints bb = 0; if(ToFlag) { // if aircraft is approaching current waypoint bb = pwBrg; // get bearing to previous waypoint (behind aircraft) bf = wcBrg; // get bearing to current waypoint (ahead of aircraft) } else { // else if receding from current waypoint bb = wcBrg; // get bearing to current waypoint (behind aircraft) bf = nwBrg; // get bearing to next waypoint (ahead of aircraft) } g.setColor(Color.magenta); // colour for pointer to waypoint ahead g.drawLine(X,Y,X+(int)(100*Math.sin(bf)),Y-(int)(100*Math.cos(bf))); g.setColor(Color.orange); // colour for pointer to waypoint behind 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 a = Math.sin(ah), // sine and cosine of aircraft's heading b = Math.cos(ah); g.setColor(SRC); // colour in which to display compass cross g.drawString("N", // draw the letter 'N' for North CX - (int)(23 * a) - Nw, CY - (int)(23 * b) + fa ); int c = (int)(15 * a), // vertical & horizontal off-sets of the d = (int)(15 * b); // points of the compass cross 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 } }