/** * Route Loader Panel * @author Robert J Morton YE572246C * @version 04 December 2007 * @copyright Robert J Morton (all rights reserved) */ /* This applet conforms to API 1.1 It does the following: INITIALISATION SEQUENCE 1. Loads a list of names of air routes from a file located on the server, and places them into a "routes" Choice list. 2. Selects the first one in the list and loads the list of waypoints that make up a selected route. USER-INITIATED ACTIONS 1. User can select another route from the list. The new route's list of waypoints is then loaded from the server into the waypoints Choice list. 2. User can select a waypoint from the waypoint Choice list. 3. User can enter a word or phrase and search for it within all route names in the list of routes. If found, the first route's name is selected and its waypoints loaded. Pressing the Find/Next button again proceeds with the search onwards down the list of routes. */ import java.io.*; // file input/output handling import java.net.*; // for downloading data from the remote server class loader { // TO LOAD AND DISPLAY ROUTE NAMES AND GEOGRAPHIC FEATURES private static loader ld; // reference to current instance of loader class private boolean loadingNamesFTT = true, // first-time-through flag loadingFeatures = false, // false=loading routes, true=loading features namesLoaded = false, // route names not yet loaded featuresLoaded = false; // selected route's waypoints not yet loaded int NR = 0, // number of available routes NF[], // number of features in each route NW[], // number of these that are waypoints rn = 0, // index number of current route during loading fn = 0, // index number of current feature within current route nf = 0; // total number of geographic features in current route private int ls = 0, // load switch [see navstart.java] lp = 0, // 0=idle 1=connecting 2=loading 3=connect error 4=load error L = 0, // length of the remote item being loaded l = 0, // number of bytes of the above successfully downloaded m = 0, // message index Lang = 0; // language switch private byte B[]; // gigantic byte array to hold the downloaded index data private String MSG[][] = { {"Connecting to server...", "Couldn't find waypoints file.", "Couldn't find routes file.", "Loading waypoints...", "Loading route names...", "Couldn't load waypoints.", "Couldn't load route names.", "Flight ready to depart."}, {"Conectando ao servidor...", "Arquivo de viapontos não encontrado.", "Arquivo de rotas não encontrado.", "Carregando viapontos...", "Carregando nomes de rota...", "Viapontos não carregados.", "Nomes das rotas não carregados.", "Vôo pronto para partir."} }, loadFile = "names.txt", // name of the route names file cb; // URL path to data files private BufferedReader R; // for reading route & waypoint lists from files private InputStream I; // input stream for downloading file private movmap mm; // ref to the parent applet class private route ROUTE; // object reference of current route private msgpanel mp; // ref to instance of the message panel class private routesel rs; // ref to instance of route selector class private selwp ws; // ref to instance of waypoint selector class private butpanel bp; // ref to the button panel object private aircraft ac; // ref to the aircraft object private airmap am; // ref to airmap object private navpanel np; // ref to navigation data panel object private dandb db; // ref to distance & bearing calculation object private route r; // ref to a route object private waypnt w; // ref to a waypoint object loader(int ls, int Lang, String cb, movmap mm, msgpanel mp, routesel rs, selwp ws, aircraft ac, airmap am, navpanel np, selgeoid gs, butpanel bp) { ld = this; // reference to this instance of this class this.ls = ls; // load switch this.Lang = Lang; // language switch this.cb = cb; // URL (less file name) from where this applet came this.mm = mm; // context reference of calling applet this.mp = mp; // message panel reference this.bp = bp; // reference to the button panel object this.rs = rs; // reference to route selector menu this.ws = ws; // reference to waypoint selector menu this.ac = ac; // reference to the aircraft object // get the reference of the distance & bearing object db = gs.getDandB(); this.am = am; // reference to airmap object this.np = np; // reference to navigation data panel object lp = 1; // start the route names loading process } void loadRoute(int sr) { // LOAD THE FEATURES OF A SELECTED ROUTE featuresLoaded = false; // selected route's waypoints not yet loaded loadingFeatures = true; // state type of file being loaded // form string version of the selected route number loadFile = "route" + pad(sr) + ".txt"; waypnt.killAll(); // remove all currently installed waypoints nf = NF[sr]; // number of geographic features within route fn = 0; // index of a geographic feature within route int x = NW[sr]; // number of waypoints in the selected route r = new route(x,ac,bp); // create new route of required no. of waypoints // clear the previous route's features from the Choice Menu ws.clearWaypoints(x); lp = 1; // start the features loading process } private void fileConnect() { // CONNECT TO APPROPRIATE DATA FILE ON SERVER // true = normal message, false = not image load mp.showMsg(MSG[Lang][0],true,false); try { // set to capture any exceptions locally switch(ls) { case 0: // Create an input stream to load ile local file L = (int)(new File(loadFile)).length(); I = new FileInputStream(loadFile); break; case 1: // Create an input stream to load ile from jar file L = 4096; //default maximum content length I = getClass().getResourceAsStream(loadFile); break; case 2: // Create an input stream to load ile from server URLConnection u = new URL(cb + loadFile).openConnection(); I = u.getInputStream(); L = (int)u.getContentLength(); } B = new byte[L]; // create the gigantic buffer for the data l = 0; // number of bytes so far successfully downloaded lp = 2; // advance to the index loading phase } /* If any exception at all occurs, note what kind of exception it was and where it occurred. */ catch(Exception e) { System.out.println("fileConnect() " + e); if(loadingFeatures) mp.showMsg(MSG[Lang][1],false,false); else mp.showMsg(MSG[Lang][2],false,false); lp = 4; // reset loader to its idle state } } private void fileLoad() { // DOWNLOAD THE APPROPRIATE DATA FILE if(loadingFeatures) mp.showMsg(MSG[Lang][3],true,false); else mp.showMsg(MSG[Lang][4],true,false); int k; // current byte being read() try { if(ls == 1) { // L is unknown for jar files /* While the entire file has not yet been downloaded, add each new byte to the big byte array. */ while((k = I.read()) != -1) B[l++] = (byte)k; L = l; } else // L is known for local files and server connections while(l < L && (k = I.read()) != -1) B[l++] = (byte)k; I.close(); // close URL Connection's [or local file's] input stream lp = 3; // go to post-loading phase } // catch any data transmission or file loading error conditions catch(Exception e) { /* note the kind of exception and where it occurred then display it in the terminal. */ System.out.println("fileLoad() " + e + L + " " + l); lp = 5; // reset loader to its idle state if(loadingFeatures) mp.showMsg(MSG[Lang][5],false,false); else mp.showMsg(MSG[Lang][6],false,false); } } private void postLoad() { // TRANSFER LOADED DATA FROM BYTE ARRAY try { // allow the WHILE loop to be interrupted by external events R = new BufferedReader( new InputStreamReader( new ByteArrayInputStream(B) ) ); String s; // string to accommodate next line of text /* While the end of the input stream has not yet been reached, read the next line of text, and extract its contents. */ while((s = R.readLine()) != null) extract(s); R.close(); // then close the byte array reader } catch(Exception e) { } // ignore any errors. it's local B = null; // garbage the byte array to release memory if(loadingFeatures) { // if loading a route's list of waypoints r.init(); // initialize the newly-loaded route r.reset(); // reset aircraft to start of the current route ws.installWaypoints(); // install all the waypoints in the Choice menu featuresLoaded = true; // signal that this route's waypoints are loaded am.atualizar(); // display the initial air map content np.atualizar(); // display the initial navigation data bp.setBlack(); mp.showMsg(MSG[Lang][7],true,false); lp = 0; // reset loader to its idle state } else { // else, the list of route names is being loaded rs.installRoutes(); // install all the routes in the Choice menu namesLoaded = true; // following call uses the state of this flag loadRoute(0); // pre-select the default route name } } boolean routeDataLoaded() { // RUN THE AUXILIARY THREAD switch(lp) { // THE 2 DOWNLOADING PHASES case 1: fileConnect(); break; // connect to index resource on server case 2: fileLoad(); break; // manage the downloading of its content case 3: postLoad(); // post-loading phase } return (namesLoaded && featuresLoaded); } /* Interpret an incoming waypoint entry back into humanly readable form Format of input line *+000000+000000Name 0123456789012345 Format of output line *000:00:00N 000:00:00W Name */ private String interpret(String s) { return format(Integer.parseInt((s.substring(1,8)).trim()), true) + format(Integer.parseInt((s.substring(8,15)).trim()), false) + s.substring(15,s.length()); } // Reformats a latitude or longitude private String format(int x, boolean lat) { String S; // NSEW indicator string if(x < 0) { // if the latitude or longitude be negative if(lat) // if it's a latitude S = "S"; // then it is South else // else, it must be a longitude S = "W"; // so it must be West x = -x; // reverse its sign to make it positive } else // else, the latitude or longitude is positive if(lat) // so, if it's a latitude S = "N"; // it must be North else // else it must be a longitude S = "E"; // so it must be East int d = x / 3600, // whole degrees s = x % 3600, // seconds left after extracting whole degrees m = s / 60; // whole minutes s %= 60; // remaining seconds after extracting minutes String D = "" + d; // integral number of degrees as a string int l = 3; // length of degrees string is 3 digits if(lat) // but if its a latitude l = 2; // it must be reduced to 2-digits while(D.length() < l) D = "0" + D; // pad out degrees with leading zeros as necessary String M = "" + m; // whole number of minues as a string while(M.length() < 2) M = "0" + M; // pad out with leading zero if necessary String X = "" + s; // remaining seconds as a string while(X.length() < 2) X = "0" + X; // pad out with leading zero if necessary // Return the full latitude or longitude in readable format. return D + ":" + M + ":" + X + S + " "; } // This is executed for each line of the route names file or waypoints file. private void extract(String s) { if(loadingFeatures) { // if loading a route's list of waypoints w = new waypnt( // create a new waypoint object secRad(s,1, 8), // feature's latitude in radians secRad(s,8,15), // feature's longitude in radians s.substring(15), // name of this geographic feature ac, db // reference to aircraft and dist/brg objects ); /* If this feature is an en-route waypoint, add it to the current route and the reformatted waypoint to the current route's waypoints list. */ if(s.substring(0,1).equals("*")) { r.AddWayPnt(w); ws.addWaypoint(interpret(s)); } fn++; // increment the feature number } /* else, if loading the first line of the route names file, store the number of available routes NR, create new arrays NF & NW for the number of geographic features + the number of waypoints respectively in this route, clear waypoint names from the Route Selector menu, set current route number to zero and unset the loading 'first time through' flag. */ else if(loadingNamesFTT) { NR = Integer.parseInt(s.trim()); NF = new int[NR]; NW = new int[NR]; rs.clearRoutes(NR); rn = 0; loadingNamesFTT = false; } /* else, must be loading an ordinary route name, so save total number of geographic features in this route and the number of these that are waypoints; then add the route names to the route's choice menu array. */ else { NF[rn] = si(s,0,3); NW[rn++] = si(s,4,7); rs.addRoute(s.substring(8)); } } // Converts a 3-digit string to an interger value. private int si(String s, int x, int y) { return Integer.parseInt((s.substring(x,y)).trim()); } // Converts seconds of arc to radians. private double secRad(String s,int x,int y){ int z = Integer.parseInt((s.substring(x,y)).trim()); return (double)z * Math.PI / 648000; } /* Returns an int as a 3-digit string with leading zeros added where necessary. */ private static String pad(int x) { if(x > 998) return "999"; String s = "" + x; while(s.length() < 3) s = "0" + s; return s; } boolean getNamesLoaded(){ return namesLoaded; // return the status of the namesLoaded flag } int getlp(){return lp;} void setlp(int x){lp = x;} boolean DefaultsLoaded(){return (namesLoaded && featuresLoaded);} }