This creates and updates the 300 by 300 pixel map area on the right hand side of the applet window. The map is double-buffered. All drawing methods draw on an image object out of sight of the user. Then, once finished, the updated map is copied onto the map canvas by the canvas's paint() method.
The map is basic, simple and sparse. It would be possible to lay in background map tiles showing greater geographic detail such as coastlines, and relief and rivers. However, in the context of this system such things are of little relevance and it is felt that they would serve only to cloud the vital information the map currently provides.
/**
* 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 java.awt.*;
class airmap extends Canvas implements navconst {
private static airmap am; //reference to air map object
private aircraft ac; //reference to current aircraft object
private waypnt wc; //reference to current waypoint object
private selmap sm; //reference to map selector panel
private int X = 150, Y = 150; //maximum x and y from centre of canvas
private int X1 = X >> 1; //To test whether city name should be displayed
private int X2 = X + X1; // on the left or right of the 'city'.
private int x = 100, y = 100; //pixel co-ordinates of current geographic feature
private int z = 7, hz = 3; //diameter of city blob, and half of it
private Image I; //reference to off-screen image used to update the map
private Graphics i; //graphics context of this off-screen image
private Font font; //font for on-map lettering
private FontMetrics fm; //letter dimensions etc for the above font
private int fh; //font height (total height of a line of text)
private int fa; //half font ascent (height of letters above base line)
private int Nw; //half-width of the letter 'N' for the compass cross
private Color GCC; //create special bright green for circle
private Color RCC; //create special bright red for circle
private Color BGC; //create special map background colour
private Color SRC; //create special colour for selected radial line
private double Scale; //map scale in pixels per kilometre
private double InM = 50; //length of standard outbound radial line in km
private double OutM = 30; //length of standard outbound radial line in km
private double h; //screen x-pixel of a geographic feature
private double v; //screen y-pixel of a geographic feature
private double Dst; //aircraft's current distance from current waypoint
private double ah; //aircraft's current heading
private int CX = X + 115; //x co-ordinate of the centre of the compass cross
private int CY = Y + 115; //y co-ordinate of the centre of the compass cross
airmap(Image a, Graphics g, Font f, FontMetrics m) {
am = this; //reference to airmap object
I = a; //set reference to off-screen image
i = g; //set graphics context of off-screen image
font = f; //set font for on-map lettering
fm = m; //set letter dimensions etc for above font
fa = fm.getAscent() >> 1; //get the font's ascent (ht above baseline)
GCC = new java.awt.Color( 72, 255, 72); //create special bright green for circle
RCC = new java.awt.Color(255, 72, 72); //create special bright red for circle
BGC = new java.awt.Color( 0, 0, 160); //create special map background colour
SRC = new java.awt.Color( 0, 255, 255); //create special colour for selected radial line
Nw = fm.stringWidth("N") / 2; //half the width of the letter 'N' for the compass
}
public void paint(Graphics g) { //Reproduce the off-screen image
g.drawImage(I, 0, 0, null); // onto the visible window canvas.
}
public void update(Graphics g) { //UPDATE THE OFF-SCREEN IMAGE
ac = aircraft.getCurrent(); //get reference to current aircraft object
i.setColor(BGC); //set background colour for map image
i.fillRect(0, 0, 300, 300); //paint image in its background colour
i.setFont(font); //set font for name and compass
ah = ac.getHdg(); //get aircraft's current heading
wc = waypnt.getCurrent(); //reference to current waypoint object
sm = selmap.getCurrent(); //get reference to map selector panel
Scale = sm.getScale(); //get the current value of the map scaling factor
int I = waypnt.getTotal(); //number of waypoints in the database
for(int k = 0 ; k < I ; k++) { //for each geographic feature in the database
waypnt w = waypnt.getRef(k); //reference the next waypoint
if(DBtoXY(w)) { //if feature is within map window range
if(w == wc) drawNavConstructs(); //if it is current waypoint, draw navigation constructs
i.setColor(Color.lightGray); //set colour for waypoint blob
i.fillOval(x - hz, y - hz, z, z); //draw it in the off-screen image
String s = w.getName(); //annotate it with its name
if(x < X2) //if it is less that 3/4 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
i.drawString(s, x, y + fa); //display the feature's name
}
if(w == wc && Dst > w.getRange()) //if beyond radio range of current waypoint
drawGuideBars(); //draw the GPS/Inertial guidance bars.
}
drawCrossHairs(); //draw aircraft cross-hairs at centre of map
drawCompass(); //draw compass in bottom right of map
paint(g); //display the 'cleared' image
}
/* 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. */
private boolean DBtoXY(waypnt w) { //COMPUTE X,Y PIXEL CO-ORDS FROM DISTANCE & BREARING
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; y = Y; //set 'int' x and y co-ordinates to map centre
h = 0; v = 0; //and also their 'double' versions
return true; //and return that it is in range
}
double d = Dst * Scale; //distance of waypoint in km
double b = w.getrBrg() - ac.getHdg(); //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(Math.abs(x) > X || Math.abs(y) > Y) //safe screen limits of x and y
return false; //waypoint is beyond screen range
x += X; y = Y - y; //relate co-ordinates to centre of canvas
return true; //return that it is within screen range
}
private void drawNavConstructs() { //DRAW THE LINES AND CIRCLES ETC ON THE MAP
double angle = wc.getOutRad() - ah; //screen angle of outbound radial line
double S = Math.sin(angle); //sine of it
double C = Math.cos(angle); //cosine of it
drawCircles(S, C); //draw the red and green guide circles
drawOutRad(S, C); //draw the outboud radial line
Corridor(); //draw the approach corridor boundary lines
}
/* Displays a magenta bar pointing from the aircraft to the waypoint it is currently approaching
and an orange bar pointing from the aircraft back towards the waypoint it has just left. */
private void drawGuideBars() { //Shows rBrg to approaching and receding waypoints
double Brg = wc.getrBrg() - ah; //bearing (rBrg) from aircraft to current waypoint
double bf = 0, bb = 0; //for bearings (rBrg) to front and back waypoints
waypnt nw = wc.getNext(), //reference to next waypoint's object
pw = wc.getPrev(); //reference to previous waypoint's object
if(wpenc.getCurrent().getToFlag()) { //if approaching current waypoint
pw.DandB(); //get distance and bearing to previous waypoint
bb = pw.getrBrg() - ah; //get bearing to previous waypoint (behind aircraft)
bf = Brg; //get bearing to current waypoint (ahead of aircraft)
} else { //else if receding from current waypoint
bb = Brg; //get bearing to current waypoint (behind aircraft)
nw.DandB(); //get distance and bearing of next waypoint
bf = nw.getrBrg() - ah; //get bearing to next waypoint (ahead of aircraft)
}
i.setColor(Color.magenta); //color for pointer to waypoint ahead
i.drawLine(X, Y, X + (int)(100 * Math.sin(bf)), Y - (int)(100 * Math.cos(bf)));
i.setColor(Color.orange); //colour for pointer to waypoint behind
i.drawLine(X, Y, X + (int)(100 * Math.sin(bb)), Y - (int)(100 * Math.cos(bb)));
}
private void drawCompass() { //SHOW LITTLE COMPASS CROSS AT BOTTOM RIGHT
double a = Math.sin(ah), //sine and cosine of aircraft's heading
b = Math.cos(ah);
i.setColor(SRC); //colour in which to display the compass cross
i.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
int d = (int)(15 * b); //points of the compass cross
i.drawLine(CX - c, CY - d, CX + c, CY + d);//draw north-south line
i.drawLine(CX + d, CY - c, CX - d, CY + c);//draw east-west line
}
private void drawCrossHairs() { //DISPLAY CENTRAL CROSS AND EDGE LINES ON MAP
i.setColor(Color.white); //set colour for central cross-hairs
i.drawLine(140, 150, 160, 150); //draw the horizontal hair
i.drawLine(150, 140, 150, 160); //draw the vertical hair
i.setColor(Color.lightGray); //set colour for edge cross-hairs
i.drawLine(150, 0, 150, 10); //draw the top vertical hair
i.drawLine(150, 290, 150, 300); //draw the bottom vertical hair
i.drawLine(0, 150, 10, 150); //draw the left horizontal hair
i.drawLine(290, 150, 300, 150); //draw the right horizontal hair
}
private void drawCircles(double S, double C) {//DISPLAY THE GREEN AND RED GUIDE CIRCLES AT THE CURRENT WAYPOINT
double r = ac.getTCR() * Scale; //radius of aircraft's minimum comfortable turning circle in km
double rS = r * S; //x off-set of centre of turning circle perpendicular to OutRad
double rC = r * C; //y off-set of centre of turning circle perpendicular to OutRad
double da = sm.getMapSize(); //get the appropriate angular increment for drawing the circles
for(double a = 0; a < TwoPi; a += da) { //for every degree or so around a guide circle...
double xw = r * Math.sin(a); //Absolute co-ordinates of one point on a generic guide circle
double yw = r * Math.cos(a); // which is centred on the waypoint.
int
p = x + (int)(Math.rint(xw + rC)),
q = y - (int)(Math.rint(yw - rS));
i.setColor(GCC); //colour for the green (right-hand) circle
i.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));
i.setColor(RCC); //colour for the red (left-land) circle
i.drawLine(p, q, p, q); //plot next point on red circle
}
}
private void drawOutRad(double S, double C) { //MARK OUTBOUND RADIAL FROM CURRENT WAYPOINT
double r = wc.getDST() / 2; //half the distance to next waypoint in km
if(r > OutM) r = OutM; //impose an upper limit on length of displayed line
r *= Scale; //adjust it to currently selected map scale
i.setColor(SRC); //colour for the radial lines
i.drawLine(x, y, //draw the outbound radial
X + (int)(Math.rint(h + r * S)), //x co-ordinate of end of outbound radial line
Y - (int)(Math.rint(v + r * C)) //y co-ordinate of end of outbound radial line
);
}
private void Corridor() { //MARK OUT CURRENT WAYPOINT'S APPROACH CORRIDOR
double a = wc.getInRad() - ac.getHdg(); //screen angle of inbound radial line
double S = Math.sin(a); //sine of InRad - aircraft heading
double C = Math.cos(a); //cosine of InRad - aircraft heading
double m = ac.getCM(); //distance from waypoint to where approach corridor ends
double r = wc.getPWD() / 2; //half dist from prev waypoint (where approach corridor starts)
if(r > InM) r = InM; //limit the displayed length of inbound corridor
if(r > m) { //if there is space for an approach corridor
r *= Scale; m *= Scale; //adjust its length to currently selected map scale
double lx = r * S; //x component of corridor start
double ly = r * C; //y component of corridor start
double rx = m * S; //x component of corridor end
double ry = m * C; //y component of corridor end
i.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 = wc.getDL() * Scale * 2; //corridor width
double wx = w * C; //x component of corridor width
double wy = w * S; //y component of corridor width
i.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))
);
}
}
static airmap getCurrent() {return am;} //return reference to current air map object
}
/* Robert J Morton, the author of this program,
is a poor but Right Honourable Member of the
Ancient and Noble Order of the Long-term Unemployed.
Offers of work please to: robmorton@clara.net */