// ********************************************************************** // // // // BBN Technologies, a Verizon Company // 10 Moulton Street // Cambridge, MA 02138 // (617) 873-8000 // // Copyright (C) BBNT Solutions LLC. All rights reserved. // // // ********************************************************************** // // $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/layer/rpf/MakeToc.java,v $ // $RCSfile: MakeToc.java,v $ // $Revision: 1.7 $ // $Date: 2004/03/23 05:35:26 $ // $Author: dietrick $ // // ********************************************************************** /* * The meat of this code is based on source code provided by The MITRE * Corporation, through the browse application source code. Many * thanks to Nancy Markuson who provided BBN with the software, and to * Theron Tock, who wrote the software, and Daniel Scholten, who * revised it - (c) 1994 The MITRE Corporation for those parts, and * used/distributed with permission. */ package com.bbn.openmap.layer.rpf; import java.io.*; import java.util.*; import com.bbn.openmap.event.ProgressEvent; import com.bbn.openmap.event.ProgressListener; import com.bbn.openmap.event.ProgressSupport; import com.bbn.openmap.util.Debug; import com.bbn.openmap.io.*; import com.bbn.openmap.util.ArgParser; import com.bbn.openmap.LatLonPoint; import com.bbn.openmap.proj.coords.DMSLatLonPoint; /** * This is a class that will generate A.TOC files that the RpfLayer * requires. A.TOC files provide the RpfLayer with an idea of what * data is available to it, its geographic coverage, and chart type. * With the A.TOC contents, the RpfLayer is able to find which frames * are appropriate for a given projection location. It is very * important to have a valid A.TOC directory.

* * The RPF specification, MIL-STD-2411, has definitions for how frames * are to be laid out and found within a RPF directory. All RPF data * is supposed to lie under one RPF directory, and an A.TOC file, * describing all the files and their groupings, should be directly * within the RPF directory. That's why the RpfLayer needs a path to * a RPF directory - it's really looking for the A.TOC file, and * knows where to find it. It also needs a path to the RPF directory * because it needs to prepend that path to the paths to the files * that the A.TOC file knows about.

* * The A.TOC files that can be created with this MakeToc class can be * created to contain absolute frame paths. The MakeToc class can * take the paths to several RPF directories, and create a single * A.TOC file that preserves all of their current file paths. You * have to use alot of caution with this capability, however. These * A.TOCs containing absolute file paths will not work if the data is * moved to another machine, or if referenced by a machine with a * different type file system (i.e. Windows). They may not work for * other implementations of code that display RPF data - the code in * this package has been modified to test for absolute file names.

* * That said, absolute file names should be used instead of giving the * RpfLayer several RPF directories. The RpfTocHandler does much less * work when it is allowed to group coverages together to make bigger * areas.

* * This code was ported from C code provided in the original Mitre RPF * package that had limits to the number of frames that could make up * the areas. I'll be working to eliminate those limits, but I wanted * to get a working version of the code out there. I'm also planning * on modifying this class so that it can load the RpfTocHandler * directly, therefore eliminating the need for A.TOCs altogether when * there is more than one RPF directory.

*

 * Usage:  java com.bbn.openmap.layer.rpf.MakeToc (RPF dir path) (RPF dir path) ...
 * 
* This will create an A.TOC file in the current directory for the RPF * files in the RPF directory paths. Use: *
 * java com.bbn.openmap.layer.rpf.MakeToc -help
 * 
* * for other options. * *

NOTE: Make sure that the RPF directories and their contents are in * upper case. Its a spec requirement, although with CD copies and * FTP downloads, the file name cases sometimes get switched. Use * com.bbn.openmap.layer.rpf.ChangeCase to modify the file name * cases.

* @see com.bbn.openmap.layer.rpf.ChangeCase */ public class MakeToc { /** * According to Dan Scholten's original code, this was 2 times the * max - changed from 30 on 6/17/94 to 200 for 81 JNC's in zone 1. * This might not be enough for world-wide coverage of larger * scale maps that are now available. This number may have to be * increased depending on how much data you need. */ public final static int DEFAULT_MAX_SIDE = 200; public final static double EPS = 0.01; public final static double EPS2 = 0.0001; /** Output file name of the A.TOC file. */ public final static String ATOC_FILENAME = "A.TOC"; /** The boundary edge frame length for groups. */ protected int maxSide = DEFAULT_MAX_SIDE; /** Flag to use relative frames paths - default is true. */ protected boolean relativeFramePaths = true; /** The producer name for the frame files. Default is DMAAC. */ protected String producer = "DMAAC"; protected ProgressSupport progressSupport; /** An internal representation of a Frame file. */ public class Frame { double left; double right; double top; double bottom; /* New DKS: for computing GEOREF #'s over polar region */ double swlat; double swlon; double h_interval; double v_interval; double h_resolution; double v_resolution; String scale; // length 12 char zone; boolean marked; int group; int x; int y; String filename; boolean cib; boolean cdted; public double EPS () { return (Math.abs(right - left) * MakeToc.EPS); }; public String toString() { StringBuffer s = new StringBuffer(); s.append("Frame - " + filename + "\n"); s.append(" zone = " + zone + "\n"); s.append(" marked = " + marked + "\n"); s.append(" scale = " + scale + "\n"); s.append(" group = " + group + "\n"); if (Debug.debugging("maketocframe")) { s.append(" top = " + top + "\n"); s.append(" bottom = " + bottom + "\n"); s.append(" left = " + left + "\n"); s.append(" right = " + right + "\n"); s.append(" h_interval = " + h_interval + "\n"); s.append(" v_interval = " + v_interval + "\n"); s.append(" h_resolution = " + h_resolution + "\n"); s.append(" v_resolution = " + v_resolution + "\n"); } return s.toString(); } } /** An internal representation of a boundary rectangle for frames. */ public class Group { double[] horiz_pos; double[] vert_pos; int left; int right; int top; int bottom; String scale; char zone; double h_interval; double v_interval; double h_resolution; double v_resolution; boolean cib; boolean cdted; public Group() { horiz_pos = new double[maxSide]; vert_pos = new double[maxSide]; } public String toString() { StringBuffer s = new StringBuffer(); s.append("Group - \n"); s.append(" zone = " + zone + "\n"); s.append(" scale = " + scale + "\n"); s.append(" left = " + left + "\n"); s.append(" right = " + right + "\n"); s.append(" top = " + top + "\n"); s.append(" bottom = " + bottom + "\n"); s.append(" is cdted = " + cdted + "\n"); s.append(" is cib = " + cib + "\n"); return s.toString(); } } public MakeToc() { progressSupport = new ProgressSupport(this); } /** * Create an A.TOC file. * @param argv The arguments should at least include a path to a * RPF file root directory. Other options can be found by using a * -help option. */ public static void main(String[] argv) { Debug.init(); boolean Dchum = false; ArgParser ap = new ArgParser("MakeToc"); ap.add("absolute", "Use absolute paths in A.TOC - Use for multiple RPF Directories"); ap.add("boundary", "Maximum frames on a boundary edge (Default 200)", 1); ap.add("dchum", "DCHUM files are included."); ap.add("log", "Pathname of log file to list A.TOC creation output.", 1); ap.add("output", "Path to directory to place A.TOC file. (Default is current directory)", 1); ap.add("producer", "The producer of the frames (Default DMAAC). Five letter code.", 1); ap.add("verbose", "Print out progress"); ap.add("extraverbose", "Print out ALL progress"); ap.add("nw", "Don't put up swing progress window (Use this if you are getting weird exceptions)"); ap.add("paths", "Space separated paths to RPF directory or directories. Should be last. If more than one directory is listed, then absolute paths are used in the A.TOC file.", ArgParser.TO_END); if (!ap.parse(argv)) { ap.printUsage(); System.exit(0); } String outputFile = "." + File.separator + RpfTocHandler.RPF_TOC_FILE_NAME; String arg[]; arg = ap.getArgValues("output"); if (arg != null) { outputFile = arg[0] + File.separator + RpfTocHandler.RPF_TOC_FILE_NAME; } arg = ap.getArgValues("log"); if (arg != null) { String logfile = arg[0]; Debug.directOutput(logfile, false, true); Debug.output("MakeToc: Creating log at " + logfile + " at " + java.util.Calendar.getInstance().getTime()); } arg = ap.getArgValues("dchum"); if (arg != null) { Dchum = true; } arg = ap.getArgValues("verbose"); if (arg != null) { Debug.put("maketoc"); } arg = ap.getArgValues("extraverbose"); if (arg != null) { Debug.put("maketoc"); Debug.put("maketocdetail"); } String[] paths = null; arg = ap.getArgValues("paths"); if (arg != null) { paths = arg; } else { paths = ap.getRest(); } if (paths == null || paths.length == 0) { Debug.output("MakeToc: need a path to start searching for RPF frames."); System.exit(0); } MakeToc mt = new MakeToc(); // If the -nw argument was not used, add a progress gauge. arg = ap.getArgValues("nw"); if (arg == null) { try { mt.addProgressListener(new com.bbn.openmap.gui.ProgressListenerGauge("RPF A.TOC File Creation")); } catch (RuntimeException re) { } } boolean argFlagged = false; arg = ap.getArgValues("absolute"); if (arg != null) { argFlagged = true; } arg = ap.getArgValues("producer"); if (arg != null) { mt.setProducer(arg[0]); } if (paths.length > 1 || argFlagged) { Debug.output("MakeToc: creating A.TOC with absolute path names."); mt.setRelativeFramePaths(false); } arg = ap.getArgValues("boundary"); int max_side = DEFAULT_MAX_SIDE; if (arg != null) { try { max_side = Integer.parseInt(arg[0]); if (max_side <= DEFAULT_MAX_SIDE) { Debug.output("MakeToc: Boundary number specified (" + max_side + ") is too small. Using default of 200."); max_side = DEFAULT_MAX_SIDE; } } catch (NumberFormatException nfe) { Debug.output("MakeToc: Tried to pass a bogus integer (" + arg[0] + ") as a boundary limit. Using default of 200."); max_side = DEFAULT_MAX_SIDE; } } mt.setMaxSide(max_side); mt.fireProgressUpdate(ProgressEvent.START, "Searching for RPF frames", 0, 100); paths = mt.searchForRpfFiles(paths); try { mt.create(paths, outputFile, Dchum); } catch (MakeTocException mte) { Debug.error("Problem creating A.TOC file: \n" + mte.getMessage()); } System.exit(0); } /** * Create a A.TOC file specificed by the frame file list, at the * location specified. * @param rpfFilePaths An array of all RPF Frame file paths. If * these paths are relative, the MakeToc class should be set for * that. * @param outputFile the complete pathname to an A.TOC file to be * written. * @exception MakeTocException if anything goes wrong. */ public void create(String[] rpfFilePaths, String outputFile) throws MakeTocException { create(rpfFilePaths, outputFile, false); } /** * Create a A.TOC file specificed by the frame file list, at the * location specified. * @param rpfFilePaths An array of all RPF Frame file paths. If * these paths are relative, the MakeToc class should be set for * that. * @param outputFile the complete pathname to an A.TOC file to be * written. * @param dchum If dchum is present, all frames get placed in * their own group. False is default. Dchum are replacement subframes. * @exception MakeTocException if anything goes wrong. */ public void create(String[] rpfFilePaths, String outputFile, boolean dchum) throws MakeTocException { RpfHeader head = new RpfHeader(); Vector frames = new Vector(rpfFilePaths.length); Vector groups = new Vector(); fireProgressUpdate(ProgressEvent.UPDATE, "Organizing frames", 0, 100); organizeFrames(rpfFilePaths, head, frames); if (head.standardNumber == null) { throw new MakeTocException("MakeToc: No RPF frames found."); } groupFrames(frames, groups, dchum); fireProgressUpdate(ProgressEvent.UPDATE, "Writing A.TOC file", 100, 100); writeTOCFile(outputFile, head, frames, groups); fireProgressUpdate(ProgressEvent.DONE, "A.TOC file complete", 100, 100); } /** * Look for RPF frame files, given a bunch of places to start * looking. The output of this can be passed to the create method. * @param startDirs Directory paths. * @return an array of strings representing path names to RPF * frame files. */ public String[] searchForRpfFiles(String[] startDirs) { RpfFileSearch search = new RpfFileSearch(); for (int i = 0; i < startDirs.length; i++) { search.handleEntry(startDirs[i]); } return search.getFiles(); } /** * Set whether to use relative frame paths in the A.TOC file. */ public void setRelativeFramePaths(boolean setting) { relativeFramePaths = setting; } public boolean getRelativeFramePaths() { return relativeFramePaths; } /** * Set the 5 letter producer code for the frames. If you didn't * make the frames, they DMA probably did, so the default is * applicable - DMAAC. There are a bunch of accepted codes in the * MIL-STD-2411 for producers. */ public void setProducer(String setting) { if (setting.length() != 5) { if (setting.length() >= 5) { producer = setting.substring(0, 5); } else { producer = setting + createPadding(5 - setting.length(), false); } } else { producer = setting; } } /** Get the producer code currently set.*/ public String getProducer() { return producer; } /** * Set the Maximum number of frames along a group boundary edge. Don't * change this after starting to group the frames. */ protected void setMaxSide(int set) { maxSide = set; } /** * Get the Maximum number of frames along a group boundary edge. */ protected int getMaxSide() { return maxSide; } /** A little function to tell of one edge is near another. */ protected boolean near(double a, double b, double eps) { return (Math.abs(a-b) < eps); /* EPS was 0.0001 */ } /** * Get all the frame paths, and sort through them. This method * sets up the frames vector and loads each Frame with it's * attributes, so it can be grouped with its neighbors. * @param framePaths the array of RPF file paths. * @param head an RpfHeader object to load with production * information, that will be put into the A.TOC file. * @param frames the frame vector to load. */ public void organizeFrames(String[] framePaths, RpfHeader head, Vector frames) { int tail; int i, j; boolean done; /* New, DKS */ boolean Cib = false; /* CIB data flag: 1:I1(10M); 2:I2(5M) */ boolean Cdted = false; /* CDTED data flag: 1: DT1(100M) */ boolean isoverview = false; boolean islegend = false; Frame frame; RpfFileSections.RpfLocationSection loc; RpfFileSections.RpfCoverageSection coverage; Debug.message("maketoc", "MakeToc.organizeFrames: *** initial look at frames ***"); /* # of frames = # of pathname records = #files */ int nFrames = framePaths.length; if (Debug.debugging("maketoc")) { Debug.output("Number of frames: " + nFrames); } /* for each frame file */ for (i = 0; i < nFrames; i++) { isoverview = false; islegend = false; String framePath = framePaths[i]; if (Debug.debugging("maketoc")) { Debug.output("MakeToc: frame number " + i + ", " + framePath); } try { BinaryFile binFile = new BinaryBufferedFile(framePath); // binFile = new BinaryFile(framePath); RpfFileSections rfs = new RpfFileSections(); binFile.seek(0); if (!head.read(binFile)) { // Not a RPF Frame file Debug.error("MakeToc: " + framePath + " is not a RPF file - ignoring"); continue; } binFile.seek(head.locationSectionLocation); rfs.parse(binFile); coverage = rfs.parseCoverageSection(binFile); if (coverage == null) { Debug.error("MakeToc: error reading coverage section for " + framePath + ", (file " + i + ") skipping"); binFile.close(); continue; } if (Debug.debugging("maketocframedetail")) { Debug.output("MakeToc.organizeFrames: coverage section for " + framePath + ", " + coverage); } binFile.close(); binFile = null; } catch (FileNotFoundException e) { Debug.error("MakeToc: " + framePath + " not found, being ignored."); continue; } catch (IOException ioe) { Debug.error("MakeToc: File IO Error during read of: " + framePath + "! Being ignored. \n" + ioe); continue; } frame = new Frame(); frames.add(frame); frame.filename = framePath; // This will be the actual file name, without parental path. String framename; tail = frame.filename.lastIndexOf(File.separatorChar); if (tail == -1) { framename = frame.filename ; } else { framename = frame.filename.substring(++tail); } if (framename.length() != 12) { Debug.error( "filename must be 12 chars long - " + framename); return; } // 9 is the character after the period. isoverview = (framename.charAt(9) == 'O'); if (!isoverview) { islegend = framename.regionMatches(true, 9, "LG", 0, 2); } // Check and see of the file thinks it's name is the same // as it acutally is. If they differ, rule in favor of // what the frame thinks it is. // Let's just be passive here, and name it to whatever it // is. If we found the frame, then we'll find it later, // too. -DFD // if (!framename.equals(head.filename)) { /* DKS */ // File file = new File(frame.filename); // File newFile = new File(frame.filename.substring(0, tail), // head.filename); // file.renameTo(newFile); // framename = head.filename; // Debug.output("WARNING: File \"" + framename + // "\" doesn't match internal name \"" + head.filename + // "\" - Fixed."); // } isoverview = false; islegend = false; String padding = null; String seriesCode = head.filename.substring(9, 11); RpfProductInfo rpi = RpfProductInfo.get(seriesCode); String scaleString = rpi.scaleString; if (rpi == RpfConstants.UK) { Debug.output("MakeToc: " + frame.filename + " unknown map type " + seriesCode + " - ignoring."); continue; } else if (rpi.scale == RpfConstants.Various) { // need to figure out how to consult the frame for what it is. // RpfAttributes.chartSeriesCode might have something to base it off. // GNC = GN, JNC = JN, ONC = ON, TPC = TP, JOG = 15, TLM50 = V7, // But I'm not sure about the others. For now, prompt for scale. scaleString = promptForScale("What is the scale for " + frame.filename + "? (Answer should look like: 1:XXX,XXX)"); if (scaleString == null || scaleString.length() == 0) { Debug.error("Bad input for scale for " + frame.filename + ", skipping."); continue; } } if (rpi.dataType.equalsIgnoreCase(RpfConstants.CIB)) { frame.cib = true; } else if (rpi.dataType.equalsIgnoreCase(RpfConstants.CDTED)) { frame.cdted = true; } // else do nothing for CADRG // Set the string to length 15 if (scaleString.length() < 15) { padding = createPadding(15 - scaleString.length(), false); scaleString = scaleString + padding; } else if (scaleString.length() > 15) { scaleString = scaleString.substring(0, 15); } frame.scale = scaleString; frame.zone = head.filename.charAt(11); if (isoverview) { coverage.nwlat = coverage.nelat = coverage.nwlon = coverage.swlon = coverage.swlat = coverage.selat = coverage.nelon = coverage.selon = 0; coverage.latInterval = coverage.lonInterval = coverage.nsVertRes = coverage.ewHorRes = 0; } if (islegend) { coverage.nwlat = coverage.nelat = coverage.nwlon = coverage.swlon = coverage.swlat = coverage.selat = coverage.nelon = coverage.selon = 0; coverage.latInterval = coverage.lonInterval = coverage.nsVertRes = coverage.ewHorRes = 0; } /* PBF 6-18-94 check for rectangular coverage or polar frame */ if (frame.zone == '9' || frame.zone == 'J') { /* Polar. Convert boundary from lat-long degrees to pixels */ /* DKS 1/95: North pole: "9" code */ if (frame.zone == '9') { if (Debug.debugging("maketoc")) Debug.output("Processing NORTH pole") ; frame.left = (90.0 - coverage.nwlat) * Math.sin(coverage.nwlon * Math.PI / 180.0) / coverage.latInterval; frame.right = (90.0 - coverage.selat) * Math.sin(coverage.selon * Math.PI / 180.0) / coverage.latInterval; frame.top = -1 * (90.0 - coverage.nwlat) * Math.cos(coverage.nwlon * Math.PI / 180.0) / coverage.latInterval; frame.bottom = -1 * (90.0 - coverage.selat) * Math.cos(coverage.selon * Math.PI / 180.0) / coverage.latInterval; } else { /* DKS 1/95: South pole: "J" code */ if (Debug.debugging("maketoc")) Debug.output("Processing SOUTH pole"); frame.left = (90.0 + coverage.nwlat) * Math.sin(coverage.nwlon * Math.PI / 180.0) / coverage.latInterval; frame.right = (90.0 + coverage.selat) * Math.sin(coverage.selon * Math.PI / 180.0) / coverage.latInterval; frame.top = (90.0 + coverage.nwlat) * Math.cos(coverage.nwlon * Math.PI / 180.0) / coverage.latInterval; frame.bottom = (90.0 + coverage.selat) * Math.cos(coverage.selon * Math.PI / 180.0) / coverage.latInterval; } /* if South pole */ /* DKS 8/1/94: Added for GEOREF calc later */ frame.swlat = coverage.swlat; frame.swlon = coverage.swlon; if (Debug.debugging("maketoc")) { Debug.output("MakeToc: " + frame.filename + " is a Polar frame"); } /* if Debug.debugging("maketoc") */ } else { frame.left = coverage.nwlon; frame.right = coverage.selon; /* NEW, DKS 6/94. Correct for frame straddling 180 deg. */ if (coverage.selon < coverage.nwlon) { frame.right = 180.0; } frame.top = coverage.nwlat; frame.bottom = coverage.selat; } frame.h_interval = coverage.lonInterval; frame.v_interval = coverage.latInterval; frame.h_resolution = coverage.ewHorRes; frame.v_resolution = coverage.nsVertRes; frame.marked = false; if (Debug.debugging("maketocframedetail")) { Debug.output("MakeToc: nw_lon = " + coverage.nwlon + ", se_lon = " + coverage.selon + "\n nwlat = " + coverage.nwlat + ", selat = " + coverage.selat + "\n NEW: swlat = " + coverage.swlat + ", swlon = " + coverage.swlon + "\n vert_interval = " + coverage.latInterval + ", horiz_interval = " + coverage.lonInterval + "\n vertical resolution = " + coverage.nsVertRes + ", horizontal resolution = " + coverage.ewHorRes + "\n left = " + frame.left + ", right = " + frame.right + "\n top = " + frame.top + ", bottom = " + frame.bottom + "\n"); } } /* for i: each input frame file */ } /** * Prompt for input. */ public String promptForScale(String query) { try { String answer = null; System.out.println(query); InputStreamReader isr = new InputStreamReader(System.in); BufferedReader bufr = new BufferedReader(isr); answer = bufr.readLine(); return answer; } catch (IOException ioe) { Debug.error("MakeToc: IOException trying to get an answer from you. Dang."); return null; } } /** * Create and write out an A.TOC file. * @param filename the output filename. * @param head the RpfHeader containing header information. * @param frames the frame Vector. * @param groups the file groups Vector. */ public void writeTOCFile(String filename, RpfHeader head, Vector frames, Vector groups) throws MakeTocException { short us; int ui; int i, j, tail; /* DKS changed from left, right for polar zone: new left_bottom longit. */ double left_b, left_t, right_b, right_t, top, bottom; double xleft, xright, ytop, ybottom; /* !! To be filled in later */ int TOC_Nitf_hdr_size = 0; /* ?? Nitf header size for output TOC */ int Loc_sec_len; /* Location section length */ int Bound_tbl_len; /* Boundary rectangle table length */ int Frame_hdr_len = 13; /* Frame index header length */ int Frame_index_rec_len = 33; /* Frame index record length (was 37) */ int Frame_sec_len ; /* Frame section length */ RandomAccessFile fout = null; int groupCount = groups.size(); int nFrames = frames.size(); /* cumulative pathname positions */ int[] pathname_pos = new int[nFrames]; /* List of pathnames: directories */ String[] direct = new String[nFrames]; /* Allocations for uniq directories */ int[] uniq_dir_ptr = new int[nFrames]; /* index from filename to uniq direct. */ int[] uniq_dir_pos = new int[nFrames]; /* position of direct. name in file */ /* list of direct. names */ String[] uniq_dir = new String[nFrames]; String georef = "AAAAAA" ; /* GEOREF # */ Frame frame; Group group; // Right now, just write the new file locally. try { fout = new RandomAccessFile(filename, "rw"); /* WRITE TOC : */ if (Debug.debugging("maketoc")) { Debug.output("MakeToc: *** writing TOC ***\n at: " + filename) ; } /* HEADER SECTION */ if (Debug.debugging("maketoc")) { Debug.output("MakeToc: *** writing header section ***"); } String charString; char[] nt = new char[1]; nt[0] = '\0'; /* DKS. Can't write structure because of pad bytes */ /* fwrite(&head, sizeof(head), 1, fout); */ fout.writeBoolean(head.endian); // Big Endian - should match head.endian fout.writeShort(RpfHeader.HEADER_SECTION_LENGTH); fout.writeBytes(" A.TOC"); // has to be padded. fout.writeByte(head.neww); fout.writeBytes(head.standardNumber); if (head.standardNumber.length() < 15) { fout.writeBytes(createPadding(15 - head.standardNumber.length(), false)); } fout.writeBytes(head.standardDate); if (head.standardDate.length() < 8) { fout.writeBytes(createPadding(8 - head.standardDate.length(), false)); } // All this trouble just for a silly character. char[] charArray = new char[1]; charArray[0] = head.classification; charString = new String(charArray); fout.writeBytes(charString); Debug.message("maketoc", "MakeToc: writing country(" + head.country + ") and release(" + head.release + ")"); fout.writeBytes(head.country); fout.writeBytes(head.release); /* New, DKS. no longer head.loc_sec_phys_loc. Always write 48. */ /* DFD - This isn't true, but since we don't care about NITF formatting, it may be. Just write out where we are. */ int location_section_location = (int)fout.getFilePointer() + 4; fout.writeInt(location_section_location); if (Debug.debugging("maketoc")) { Debug.output("MakeToc: location section location is : " + location_section_location); } if (Debug.debugging("maketoc")) { Debug.output("MakeToc: *** writing location section ***"); } /* LOCATION SECTION */ int Loc_hdr_len = 14; /* Location section header length */ int Loc_sec_comp_len = 10; /* Location section component length */ /* 14 + 4 * 10 = 54 */ Loc_sec_len = Loc_hdr_len + (RpfFileSections.TOC_LOCATION_KEY * Loc_sec_comp_len); fout.writeShort(Loc_sec_len); /* compon. loc tbl offset: location section hdr length */ fout.writeInt(Loc_hdr_len); /* # records in location section: 4 */ fout.writeShort(RpfFileSections.TOC_LOCATION_KEY); /* component location record length: 10 */ fout.writeShort(Loc_sec_comp_len); if (Debug.debugging("maketoc")) { Debug.output("MakeToc:\n location section length: " + Loc_sec_len + "\n location header length: " + Loc_hdr_len + "\n number of location records: " + RpfFileSections.TOC_LOCATION_KEY + "\n location section comp length: " + Loc_sec_comp_len); } /* compon. aggregate len: unknown here. Fill in after doing all else.*/ /* location component aggregate length file location */ long agg_loc = fout.getFilePointer(); /* save for later */ fout.writeInt(0); // place holder. /* Begin: location section, component location table */ int Bound_hdr_len = 8; /* Boundary section header length */ int Bound_rec_len = 132; /* Boundary record length */ /* Boundary section length */ int Bound_sec_len = Bound_hdr_len + (groupCount * Bound_rec_len); /* Compute frame section length, for later */ pathname_pos[0] = 0; /* cum. offset */ int uniq_dir_cnt = 0; /* # of unique directory paths. */ // Looking for the directory name for each file. for (i=0; i rpfIndex && direct[i].charAt(rpfIndex) == File.separatorChar) { rpfIndex++; } tmpDir = "./" + direct[i].substring(rpfIndex); } else { if (Debug.debugging("maketoc")) { Debug.output("RPF directory not found in directory path " + direct[i] + ", using absolute path"); } tmpDir = direct[i]; } } else { tmpDir = direct[i]; } for (j=0; j 12) { Debug.error("MakeToc: encountered a frame name that's too long!\n" + framename); framename = framename.substring(0, 12); } /* frame file name */ fout.writeBytes(framename); String seriesCode = framename.substring(9, 11); /* Check for Overview image: affects GEOREF */ if ( !seriesCode.equalsIgnoreCase("OV") && !seriesCode.equalsIgnoreCase("LG") && !seriesCode.equalsIgnoreCase("OI")) { /* Not Overview or Lengend img */ /* DKS 8/1/94: handle polar zone separately */ if (frame.zone != '9' || frame.zone != 'J') { /* polar zone */ georef = latlong2GEOREF(frame.swlat, frame.swlon); } else { /* not polar */ georef = latlong2GEOREF(frame.bottom, frame.left); } /* else */ } else { /* Overview image has no GEOREF */ if (Debug.debugging("maketoc")) Debug.output("Overview image has no GEOREF") ; georef = "000000"; } /* else */ fout.writeBytes(georef); /* classification */ // HACK - assumes unclassified data. fout.writeBytes(charString); fout.writeBytes(head.country); fout.writeBytes(head.release); } /* for i (each frame file) */ Debug.message("maketoc", "MakeToc: *** writing directory section ***"); /* Pathname table */ /* Write UNIQUE pathnames: really Directory name, e.g. "./CENTRAL.USA/" */ for (j = 0; j < uniq_dir_cnt; j++) { /* DKS new */ /* write pathname length. !!?? may be padded in front to align on word bndary!!?? */ fout.writeShort((short)(uniq_dir[j].length())); /* pathname */ fout.writeBytes(uniq_dir[j]); } /* for j (each uniq directory) */ /* No color table index section */ /* Go back and fill in component aggregate length in location section */ fout.seek(agg_loc); fout.writeInt((int)(Bound_sec_len + Frame_sec_len)); fout.close(); Debug.message("maketoc", "MakeToc: *** Normal end of make-toc ***"); } catch (IOException ioe) { throw new MakeTocException(ioe.getMessage()); } } /* main */ /** * Take the Vector of frames, and group them into boundary * rectangles, represented by groups. If Dchum is present, all * frames get placed in their own group. * @param frames the frame Vector. * @param groups the group Vector. * @param isDchum flag to note if Dchum frames are present. */ public void groupFrames(Vector frames, Vector groups, boolean isDchum) throws MakeTocException { Frame frame; Group group; int groupCount; int nFrames = frames.size(); Debug.message("maketoc", "MakeToc: *** grouping frames ***"); /* For each frame file */ for (int i = 0; i < nFrames; i++) { Debug.message("maketocdetail", "MakeToc: group addition, starting outer loop"); // Assuming that the vector objects are in the same order // as initally loaded. frame = (Frame)frames.elementAt(i); if (!frame.marked) { groupCount = groups.size(); group = new Group(); group.left = maxSide/2; group.right = group.left + 1; group.top = maxSide/2; group.bottom = group.top + 1; group.horiz_pos[group.top] = frame.top; group.horiz_pos[group.bottom] = frame.bottom; group.vert_pos[group.left] = frame.left; group.vert_pos[group.right] = frame.right; group.h_interval = frame.h_interval; group.v_interval = frame.v_interval; group.h_resolution = frame.h_resolution; group.v_resolution = frame.v_resolution; group.scale = frame.scale; group.zone = frame.zone; group.cib = frame.cib; group.cdted = frame.cdted; group.cib = false; group.cdted = false; frame.x = group.left; /* DKS. Changed from top to bottom to fix bug in Theron's frame numbering */ /* Should start numbering at BOTTOM (southern-most part) of group */ /* DKS. Switched back to fix row #<=0 bug */ frame.y = group.top; frame.group = groupCount; frame.marked = true; Debug.message("maketocdetail", "Maketoc.groupFrames: created group " + groupCount + " for frame " + i + ", - " + frame.filename + " checking other frames for neighbors"); /* If Dchum, create 1 group for each file. No need for call to "add". */ if (!isDchum) { for (int j = 0; j < nFrames; j++) { if (i == j) { Debug.message("maketocdetail", "Maketoc.groupFrames: inner loop, i = j = " + i + ", frame that created group added to group, expecting false return"); continue; } Frame f = (Frame)frames.elementAt(j); if (addFrameToGroup(group, f, groupCount)) { Debug.message("maketocdetail", "Maketoc.groupFrames: added frame " + j + " to group " + groupCount); continue; } } } Debug.message("maketocdetail", "Maketoc.groupFrames: adding another group - " + groupCount + " *******************\n\n"); groups.add(group); } /* if !frame.marked */ fireProgressUpdate(ProgressEvent.UPDATE, "Organizing frames", i, nFrames); }/* for (i = 0; i < nFrames; i++) */ if (Debug.debugging("maketoc")) { Debug.output("MakeToc: Number of boundary rectangles (groups): " + groups.size()); } } /** * Does the actual checking to see if the frame gets added to the * group, by checking the frame's location with the group's * current boundaries, and resizing the group boundary if the * frame is touching it. Assumes everything has been allocated in * the group and frame. Not prepared for either being null. * @param grp the group * @param frm the frame. * @param index the group index, referring to it's position in the * Group Vector. */ protected boolean addFrameToGroup(Group grp, Frame frm, int index) throws MakeTocException { int i; int x; int y; if (frm.marked || !frm.scale.equalsIgnoreCase(grp.scale) || frm.zone != grp.zone) { Debug.message("maketocframedetail", "\nMakeToc.addFrameToGroup: no action needed for frame, returning.\n frm.marked = " + frm.marked + "\n frm.zone(" + frm.zone + ") = grp.zone(" + grp.zone + ")\n frm.scale(" + frm.scale + ") = grp.scale(" + grp.scale + ")\n"); return false; } Debug.message("maketocframedetail", "MakeToc.addFrameToGroup: adding unmarked frame"); double eps = frm.EPS(); /* DKS. EPS TOLERANCE ADDED throughout */ if (frm.left >= grp.vert_pos[grp.left] - eps && frm.right <= grp.vert_pos[grp.right] + eps && frm.bottom >= grp.horiz_pos[grp.bottom] - eps && frm.top <= grp.horiz_pos[grp.top] + eps) { if (Debug.debugging("maketocdetail")) { Debug.output(frm.filename + " is in group " + index); } } else if (near(frm.right, grp.vert_pos[grp.left], eps) && frm.top <= grp.horiz_pos[grp.top] + eps && frm.bottom >= grp.horiz_pos[grp.bottom] - eps) { if (Debug.debugging("maketocdetail")) { Debug.output(frm.filename + " add frame to group " + index + ": left side"); } if (grp.left == 0) { throw new MakeTocException("Boundary rectangle too small - Increase the boudary size to be larger than " + maxSide); } grp.left--; /* add to left side */ grp.vert_pos[grp.left] = frm.left; } else if (near(frm.left, grp.vert_pos[grp.right], eps) && frm.top <= grp.horiz_pos[grp.top] + eps && frm.bottom >= grp.horiz_pos[grp.bottom] - eps) { if (Debug.debugging("maketocdetail")) { Debug.output(frm.filename + ":add frame to group " + index + ": right side"); } if (grp.right == maxSide) { throw new MakeTocException("Boundary rectangle too small - Increase the boudary size to be larger than " + maxSide); } grp.vert_pos[grp.right] = frm.left; grp.right++; /* add to right */ grp.vert_pos[grp.right] = frm.right; } else if (near(frm.bottom, grp.horiz_pos[grp.top], eps) && frm.right <= grp.vert_pos[grp.right] + eps && frm.left >= grp.vert_pos[grp.left] - eps) { if (Debug.debugging("maketocdetail")) { Debug.output(frm.filename + ":add frame to group " + index + ": top"); } if (grp.top == 0) { throw new MakeTocException("Boundary rectangle too small - Increase the boudary size to be larger than " + maxSide); } grp.top--; /* add to top */ grp.horiz_pos[grp.top] = frm.top; } else if (near(frm.top, grp.horiz_pos[grp.bottom], eps) && frm.right <= grp.vert_pos[grp.right] + eps && frm.left >= grp.vert_pos[grp.left] - eps) { if (Debug.debugging("maketocdetail")) { Debug.output(frm.filename + ":add frame to group " + index + ": bottom"); } if (grp.bottom == maxSide) { throw new MakeTocException("Boundary rectangle too small - Increase the boudary size to be larger than " + maxSide); } grp.horiz_pos[grp.bottom] = frm.top; grp.bottom++; /* add to bottom */ grp.horiz_pos[grp.bottom] = frm.bottom; } else { Debug.message("maketocframedetail", "MakeToc.add: frame not close enough to anything else, not adding to group."); return false; } x = y = -1; for (i = grp.left; i < grp.right; i++) { /* PBF - Change from (==) to near function for polar 6-19-94 */ if (near(frm.left, grp.vert_pos[i], eps)) { x = i; break; } } for (i = grp.top; i < grp.bottom; i++) { /* PBF - Change from (==) to near function for polar 6-19-94*/ if (near(frm.top , grp.horiz_pos[i], eps)) { y = i; break; } } if (x < 0 || y < 0) { Debug.output("MakeToc: " + frm.filename + ": in rect but can't find boundary (horizontal" + (x<0?" bad":" OK") + ", vertical" + (y<0?" bad":" OK")); if (Debug.debugging("maketocframedetail")) { Debug.output(" - For frame: \n " + frm.toString()); Debug.output(" - Group horizontal left: " + grp.left + " vs. right: " + grp.right); for (i = grp.left; i < grp.right; i++) { /* PBF - Change from (==) to near function for polar 6-19-94 */ Debug.output(" - Checking horizontal: " + frm.left + " <-> " + grp.vert_pos[i]); if (near(frm.left, grp.vert_pos[i], eps)) { Debug.output(" Last one should have hit."); } } Debug.output(" - Group vertical top: " + grp.horiz_pos[grp.top] + " vs. bottom: " + grp.horiz_pos[grp.bottom] + ", frame top = " + frm.top + " and frame bottom = " + frm.bottom); for (i = grp.top; i < grp.bottom; i++) { /* PBF - Change from (==) to near function for polar 6-19-94*/ Debug.output(" - Checking vertical: " + frm.top + " <-> " + grp.horiz_pos[i]); if (near(frm.top , grp.horiz_pos[i], eps)) { Debug.output(" Last one should have hit."); } } } throw new MakeTocException(frm.filename + " in rect but can't find boundary (horizontal" + (x<0?" bad":" OK") + ", vertical" + (y<0?" bad":" OK")); } /* DKS ABS, frm.EPS2 added */ /* DKS 8/16/94: h_resolution (meters/pix) will vary from frame to frame NS */ /* Therefore don't check for a match here */ if (Math.abs (frm.h_interval - grp.h_interval) > EPS2 || Math.abs (frm.v_interval - grp.v_interval) > EPS2 ) /* deg/pix */ /* Math.abs (frm.h_resolution - grp.h_resolution) > EPS2 || Math.abs (frm.v_resolution - grp.v_resolution) > EPS2) */ { Debug.error(frm.filename + ": interval mismatch\n frm.h_interval: " + frm.h_interval + ", grp.h_interval:" + grp.h_interval + "\n frm.v_interval: " + frm.v_interval + ", grp.v_interval: " + grp.v_interval + "\n frm.h_resolution: " + frm.h_resolution + ", grp.h_resolution: " + grp.h_resolution + "\n frm.h_resolution: " + frm.h_resolution + ", grp.h_resolution: " + grp.h_resolution) ; throw new MakeTocException(frm.filename + " has mismatched frame resolution"); } frm.marked = true; frm.group = index; frm.x = x; frm.y = y; grp.cib = frm.cib; grp.cdted = frm.cdted; return true; } /* add */ /** * This program attempts to convert latitudes and longitudes given * in a decimal format into a GEOREF alphanumeric designation * code. The first letter of the code denotes the longitudinal 15 * degree grid that contains the area of interest. The second * letter denotes the latitudinal 15 degree grid. The third letter * denotes the one degree longitudinal grid within the 15 degree * longitudinal grid. The fourth letter denotes the one degree * latitudinal grid within the 15 degree latitudinal grid. The * fifth character is a number denoting the minutes longitudinally * to the nearest 10. The sixth number denotes the minutes * latitudinally to the nearest 10. Wouldn't it just have been * easier to use the decimal latitudes and longitudes? */ protected String latlong2GEOREF(double latitude, double longitude) { int i; char tmp = 'A'; // no reason for 'A' char tmp1 = 'A'; char tmp2 = 'A'; int deg, minute, sec; // These serve as tmps in integer form. int tmpi, tmpi1, tmpi2; /* this portion of the code calculates the longitudinal part of the */ /* GEOREF number. I can't explain the logic -- I don't understand */ /* how it works. All that I know is that it seems to. */ LatLonPoint llp = new LatLonPoint((float) latitude, (float) longitude); DMSLatLonPoint dmsp = new DMSLatLonPoint(llp); char[] GEOSTRING = new char[6]; if (longitude == 0.0000) { tmp = 'N'; tmp1 = 'A'; tmp2 = '0'; } else if (longitude == -180.0000) { tmp = 'A'; tmp1 = 'A'; tmp2 = '0'; } else if (longitude == 180.0000) { tmp = 'Z'; tmp1 = 'Q'; tmp2 = '9'; } else if (longitude > 0.0000) { tmpi = dmsp.lon_degrees/15; tmpi += 78; if (tmpi >= 79) { tmpi += 1; } if (tmpi > 90) { tmpi = 90; } tmp = (char) tmpi; // Setting i to a certain value, based on longitude. for(i=0;i*15 < (int)(longitude+0.9999); i++) ; tmpi1 = 15*i - (int)(longitude); if ((tmpi1 >= 3) && (tmpi1 < 8)) { tmpi1 += 1; } else if (tmpi1 >= 8) { tmpi1 += 2; } if (tmpi1 != 0) { tmpi1 = 82 - tmpi1; tmp1 = (char) tmpi1; } else { tmp1 = 'A'; } if (tmp1 == 'R') { tmp1 = 'A'; } tmpi2 = (int)('0') + (dmsp.lon_minutes/10); tmp2 = (char) tmpi2; } else if (longitude <= 0.0000) { tmpi = (int)(((double)dmsp.lon_degrees)/15.0 - 0.999); tmpi = 77 - Math.abs(tmpi); if (tmpi >= 73) { tmpi += 1; } if (tmpi > 77) { tmpi = 77; } tmp = (char)tmpi; /* DKS changed from abs to fabs */ for(i=0; i*15 < (int)(Math.abs((longitude - 0.9999)));i++) ; /* DKS changed from abs to fabs */ tmpi1 = i*15 - (int)(Math.abs((longitude - 0.9999))); if ((tmpi1 >= 8) && (tmpi1 < 13)) { tmpi1 += 1; } else if (tmpi1 >= 13) { tmpi1 += 2; } if (tmpi1 > 16) { tmpi1 = 16; } tmpi1 += 65; tmp1 = (char)tmpi1; if ((int)(dmsp.lon_minutes/10) != 0) { tmpi2 = ((int)'0')+(6-(int)(dmsp.lon_minutes/10)); tmp2 = (char) tmpi2; } else { tmp2 = '0'; } } GEOSTRING[0] = tmp; GEOSTRING[2] = tmp1; GEOSTRING[4] = tmp2; /* this portion of the code calculates the latitudinal part of the */ /* GEOREF number. I can't explain the logic -- I don't understand */ /* how it works. All that I know is that it seems to. */ if (latitude == 0.0000) { tmp = 'G'; tmp1 = 'A'; tmp2 = '0'; } else if (latitude == 90.0000) { tmp = 'M'; tmp1 = 'Q'; tmp2 = '9'; } else if (latitude == -90.0000) { tmp = 'A'; tmp1 = 'A'; tmp2 = '0'; } else if (latitude > 0.0000) { tmpi = dmsp.lat_degrees/15; tmpi += 71; if (tmpi >= 73) { tmpi += 1; } if (tmpi > 77) { tmpi = 77; } tmp = (char) tmpi; for(i=0;i*15 < (int)(latitude+0.9999); i++) ; tmpi1 = 15*i - (int)(latitude); if ((tmpi1 >= 3) && (tmpi1 < 8)) { tmpi1 += 1; } else if (tmpi1 >= 8) { tmpi1 += 2; } tmpi1 = 82 - tmpi1; tmp1 = (char) tmpi1; if (tmp1 == 'R') { tmp1 = 'A'; } if ((dmsp.lat_minutes/10) != 0) { tmpi2 = ((int)'0') + (int)(dmsp.lat_minutes/10); tmp2 = (char) tmpi2; } else { tmp2 = '0'; } } else if (latitude < 0.0000) { tmpi = (int)((double)dmsp.lat_degrees/15.0 - 0.999); tmpi = 71 - Math.abs(tmpi); if (tmpi < 65) { tmpi = 65; } /* DKS changed from abs to fabs */ for(i=0; i*15 < (int)(Math.abs((latitude - 0.9999)));i++) ; /* DKS changed from abs to fabs */ tmpi1 = i*15 - (int)(Math.abs((latitude - 0.9999))); if ((tmpi1 >= 8) && (tmpi1 < 13)) { tmpi1 += 1; } else if (tmpi1 >= 13) { tmpi1 += 2; } if (tmpi1 > 16) { tmpi1 = 16; } tmpi1 = 65 + tmpi1; tmp1 = (char)tmpi1; tmpi2 = ((int)'0') + (6-(int)(dmsp.lat_minutes/10)); tmp2 = (char) tmpi2; } GEOSTRING[1] = tmp; GEOSTRING[3] = tmp1; GEOSTRING[5] = tmp2; String ret = new String(GEOSTRING); if (Debug.debugging("maketocdetail")) { Debug.output("latlon2GEOREF: lat = " + latitude + ", lon = " + longitude + ", GEOREF = " + ret); } return ret; } /* latlong2GEOREF() */ public String createPadding(int length, boolean nullTerminated) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < length; i++) { if (i == length - 1 && nullTerminated) { sb.append("/0"); } else { sb.append(" "); } } return sb.toString(); } /** * Add a ProgressListener that will display build progress. */ public void addProgressListener(ProgressListener list) { progressSupport.addProgressListener(list); } /** * Remove a ProgressListener that displayed build progress. */ public void removeProgressListener(ProgressListener list) { progressSupport.removeProgressListener(list); } /** * Clear all progress listeners. */ public void clearProgressListeners() { progressSupport.removeAll(); } /** * Fire an build update to progress listeners. * @param frameNumber the current frame count * @param totalFrames the total number of frames. */ protected void fireProgressUpdate(int type, String task, int frameNumber, int totalFrames) { progressSupport.fireUpdate(type, task, totalFrames, frameNumber); } }