// **********************************************************************
//
// <copyright>
//
// BBN Technologies
// 10 Moulton Street
// Cambridge, MA 02138
// (617) 873-8000
//
// Copyright (C) BBNT Solutions LLC. All rights reserved.
//
// </copyright>
// **********************************************************************
//
// $Source: /cvs/distapps/openmap/src/openmap/com/bbn/openmap/layer/rpf/MakeToc.java,v $
// $RCSfile: MakeToc.java,v $
// $Revision: 1.6.2.8 $
// $Date: 2006/08/17 15:18:17 $
// $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.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.util.Vector;
import com.bbn.openmap.event.ProgressEvent;
import com.bbn.openmap.event.ProgressListener;
import com.bbn.openmap.event.ProgressSupport;
import com.bbn.openmap.io.BinaryBufferedFile;
import com.bbn.openmap.io.BinaryFile;
import com.bbn.openmap.util.Debug;
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.
* <P>
*
* 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.
* <P>
*
* 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.
* <P>
*
* 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.
* <P>
*
* 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.
* <P>
*
* <pre>
*
*
* Usage: java com.bbn.openmap.layer.rpf.MakeToc (RPF dir path) (RPF dir path) ...
*
*
* </pre>
*
* This will create an A.TOC file in the current directory for the RPF
* files in the RPF directory paths. Use:
*
* <pre>
*
*
* java com.bbn.openmap.layer.rpf.MakeToc -help
*
*
* </pre>
*
* for other options.
*
* <P>
* NOTE: Make sure that the RPF directories and their contents are in
* upper case. It's 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.
* Also, if there is more than one RPF directory in the path to the
* image frames, use the absolute path option. Otherwise, the code
* will focus on making the top-most RPF directory the one to key the
* internal relative paths off of, and that might not be what you
* want.
* </P>
*
* @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;
/* 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.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);
// Frame file names are 8.3 notation, might want to
// check
// that here, to blow off dummy files.
String fn = binFile.getName();
if (fn.length() != 12) {
// Not a RPF Frame file
if (Debug.debugging("maketoc")) {
Debug.error("MakeToc: " + framePath
+ " is not a RPF image file - ignoring");
}
continue;
}
RpfFileSections rfs = new RpfFileSections();
binFile.seek(0);
if (!head.read(binFile)) {
// Not a RPF Frame file
if (Debug.debugging("maketoc")) {
Debug.error("MakeToc: " + framePath
+ " is not a RPF image 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 12, was 15 for some reason.
int scaleStringLength = 12;
if (scaleString.length() < scaleStringLength) {
padding = createPadding(scaleStringLength - scaleString.length(), false);
scaleString = scaleString + padding;
} else if (scaleString.length() > scaleStringLength) {
scaleString = scaleString.substring(0, scaleStringLength);
}
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 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 < nFrames; i++) { /* for each frame file */
/*
* set tail to ptr to last occurrence of '/' in
* filename
*/
/* frames[i].filename is full pathname */
frame = (Frame) frames.elementAt(i);
tail = frame.filename.lastIndexOf(File.separatorChar);
if (tail == -1) {
direct[i] = frame.filename;
} else {
// Java-cise the name, so it meets spec.
direct[i] = frame.filename.substring(0, ++tail)
.replace('\\', '/');
}
if (Debug.debugging("maketocdetail"))
Debug.output("MakeToc: Matching directory: " + direct[i]);
/* Match direct. names with list of uniq direct. names */
/* flag for found name in list */
boolean uniq_dir_match = false;
String tmpDir = null;
if (relativeFramePaths) {
int rpfIndex = direct[i].lastIndexOf("RPF");
if (rpfIndex == -1) {
rpfIndex = direct[i].lastIndexOf("rpf");
}
if (rpfIndex != -1) {
rpfIndex += 3;
if (direct[i].length() > rpfIndex
&& direct[i].charAt(rpfIndex) == '/') {
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 < uniq_dir_cnt; j++) {
if (tmpDir.equals(uniq_dir[j])) {
uniq_dir_ptr[i] = j;
uniq_dir_match = true;
if (Debug.debugging("maketocdetail"))
Debug.output("Found match with: " + uniq_dir[j]);
break;
}
}
if (!uniq_dir_match) {
uniq_dir[uniq_dir_cnt] = tmpDir;
uniq_dir_ptr[i] = uniq_dir_cnt;
if (Debug.debugging("maketoc"))
Debug.output("Adding Unique directory: "
+ uniq_dir[uniq_dir_cnt]);
uniq_dir_cnt++;
} /* if */
} /* for i */
if (Debug.debugging("maketoc"))
Debug.output("Uniq_dir_cnt: " + uniq_dir_cnt);
/* compute uniq dir pathname table length */
int path_table_len = 0;
for (j = 0; j < uniq_dir_cnt; j++) {
/* 2 for path length var. in hdr */
path_table_len += 2 + uniq_dir[j].length();
} /* for j */
/* compute directory name positions in file */
uniq_dir_pos[0] = 0;
for (j = 1; j < uniq_dir_cnt; j++) {
uniq_dir_pos[j] = uniq_dir_pos[j - 1] + 2
+ uniq_dir[j - 1].length();
} /* for j */
for (j = 0; j < uniq_dir_cnt; j++) {
if (Debug.debugging("maketocdetail"))
Debug.output("j: " + j + ", uniq_dir_pos[j]: "
+ uniq_dir_pos[j]);
} /* for j */
/* compute direct. positions for each input file pathname */
for (i = 0; i < nFrames; i++) {
pathname_pos[i] = uniq_dir_pos[uniq_dir_ptr[i]];
if (Debug.debugging("maketocdetail"))
Debug.output("i: " + i + ", pathname_pos[i]:"
+ pathname_pos[i]);
} /* for i */
/*
* frame file index record length: 9 + nFrames * 33 +
* path_table_len
*/
Frame_sec_len = Frame_hdr_len + nFrames * Frame_index_rec_len
+ path_table_len;
/* START LOCATION RECORD 1 */
/* ID #: */
fout.writeShort((short) RpfFileSections.LOC_BOUNDARY_SECTION_SUBHEADER);
// The boundary section subheader is the first part of the
// bounfary rectangle section. The boundary section comes
// right after the header (in this program - the spec will
// allow it to be anywhere).
/* Boundary section subheader length */
fout.writeInt(Bound_hdr_len);
/* DKS. Physical location */
/* 0 + 48 + 54 */
fout.writeInt(TOC_Nitf_hdr_size + RpfHeader.HEADER_SECTION_LENGTH
+ Loc_sec_len);
/* START LOCATION RECORD 2 */
/* ID #: */
fout.writeShort((short) RpfFileSections.LOC_BOUNDARY_RECTANGLE_TABLE);
/* Boundary rectangle table length */
Bound_tbl_len = groupCount * Bound_rec_len;
fout.writeInt(Bound_tbl_len);
/* DKS. Physical location */
/* 0 + 48 + 54 + 8 */
fout.writeInt(TOC_Nitf_hdr_size + RpfHeader.HEADER_SECTION_LENGTH
+ Loc_sec_len + Bound_hdr_len);
Bound_sec_len = Bound_hdr_len + Bound_tbl_len;
/* START LOCATION RECORD 3 */
/* ID #: */
fout.writeShort((short) RpfFileSections.LOC_FRAME_FILE_INDEX_SUBHEADER);
/* length */
fout.writeInt(Frame_hdr_len);
/* physical index (offset) */
fout.writeInt(TOC_Nitf_hdr_size + RpfHeader.HEADER_SECTION_LENGTH
+ Loc_sec_len + Bound_sec_len);
/* START LOCATION RECORD 4 */
/* ID #: */
fout.writeShort((short) RpfFileSections.LOC_FRAME_FILE_INDEX_SUBSECTION);
/* length */
/* Frame_sec_len computed above */
fout.writeInt(Frame_sec_len - Frame_hdr_len);
/* physical index (offset) */
fout.writeInt(TOC_Nitf_hdr_size + RpfHeader.HEADER_SECTION_LENGTH
+ Loc_sec_len + Bound_sec_len + Frame_hdr_len);
if (Debug.debugging("maketoc")) {
Debug.output("MakeToc: boundary section at : "
+ fout.getFilePointer());
}
/* START BOUNDARY RECTANGLE SECTION */
if (Debug.debugging("maketoc")) {
Debug.output("MakeToc: *** writing boundary rectangles ***");
}
/* Subheader */
/* boundary rectangle table offset */
fout.writeInt(0);
/* # of boundary rectangle records */
fout.writeShort((short) groupCount);
/* boundary rectangle record length */
fout.writeShort((short) Bound_rec_len);
/* For each boundary rectangle record */
for (i = 0; i < groupCount; i++) {
group = (Group) groups.elementAt(i);
/*
* Key off flag to write proper data to A.TOC for
* browse menu later
*/
if (group.cib) {
fout.writeBytes("CIB ");
fout.writeBytes("8:1 "); /* Compr. ratio */
} else if (group.cdted) {
fout.writeBytes("CDTED");
fout.writeBytes("6.5:1"); /*
* Compr. ratio:
* VARIABLE
*/
} else {
fout.writeBytes("CADRG");
fout.writeBytes("55:1 ");
} /* else */
// Should be 12 padded chars, check just in case...
if (group.scale.length() < 12) {
fout.writeBytes(group.scale);
fout.writeBytes(createPadding(12 - group.scale.length(),
false));
} else {
fout.writeBytes(group.scale.substring(0, 12)); // Already
// 12
// padded
// chars
}
// All this trouble just for a silly character.
charArray[0] = group.zone;
charString = new String(charArray);
fout.writeBytes(charString);
/* DKS changed from AFESC to DMAAC 8/2/94 */
/* Producer: */
// Should be OpenMap BBN, I guess.
fout.writeBytes(producer);
/*
* PBF - If group is polar, change boundaries from
* rect coordinates to lat-lon -- 6-19-94
*/
if (group.zone == '9' || group.zone == 'J') { /*
* polar
* zone
*/
/*
* DKS: switched x,y to match spec: x increases
* right, y up.
*/
ytop = group.horiz_pos[group.top];
ybottom = group.horiz_pos[group.bottom];
xleft = group.vert_pos[group.left];
xright = group.vert_pos[group.right];
if (Debug.debugging("maketoc")) {
Debug.output("POLAR ZONE. ytop: " + ytop
+ ", ybottom: " + ybottom + ", xleft: " + xleft
+ ", xright:" + xright);
}
/* see CADRG SPEC 89038, p. 50 */
/*
* FIND LATITUDES from x,y. x increases right, y
* up.
*/
/* DKS new 1/95 to handle South pole separately. */
/* h_interval converts from pix to deg. */
if (group.zone == '9') { /* "9": NORTH POLE */
top = 90 - (Math.sqrt((ytop * ytop) + (xleft * xleft)) * (group.h_interval));
bottom = 90 - (Math.sqrt((ybottom * ybottom)
+ (xright * xright)) * (group.h_interval));
} /* North pole */
else { /* "J": South Pole */
top = -90
+ (Math.sqrt((ytop * ytop) + (xleft * xleft)) * (group.h_interval));
bottom = -90
+ (Math.sqrt((ybottom * ybottom)
+ (xright * xright)) * (group.h_interval));
} /* South pole */
if (Debug.debugging("maketoc"))
Debug.output("LATS. top: " + top + ", bottom: "
+ bottom);
/*
* Cvt from x,y to LONGITUDE; from radians to
* degrees
*/
/* DKS added South pole case 1/95 */
if (group.zone == '9') { /* "9": NORTH POLE */
left_t = 180.0
/ Math.PI
* Math.acos(-ytop
/ Math.sqrt((ytop * ytop)
+ (xleft * xleft)));
/* DKS fixed bug 1/95: from ytop to ybottom */
left_b = 180.0
/ Math.PI
* Math.acos(-ybottom
/ Math.sqrt((ybottom * ybottom)
+ (xleft * xleft)));
right_t = 180.0
/ Math.PI
* Math.acos(-ytop
/ Math.sqrt((ytop * ytop)
+ (xright * xright)));
/* DKS fixed bug 1/95: from ytop to ybottom */
right_b = 180.0
/ Math.PI
* Math.acos(-ybottom
/ Math.sqrt((ybottom * ybottom)
+ (xright * xright)));
} /* if North pole */
else { /* South Pole */
left_t = 180.0
/ Math.PI
* Math.acos(ytop
/ Math.sqrt((ytop * ytop)
+ (xleft * xleft)));
/* DKS fixed bug 1/95: from ytop to ybottom */
left_b = 180.0
/ Math.PI
* Math.acos(ybottom
/ Math.sqrt((ybottom * ybottom)
+ (xleft * xleft)));
right_t = 180.0
/ Math.PI
* Math.acos(ytop
/ Math.sqrt((ytop * ytop)
+ (xright * xright)));
/* DKS fixed bug 1/95: from ytop to ybottom */
right_b = 180.0
/ Math.PI
* Math.acos(ybottom
/ Math.sqrt((ybottom * ybottom)
+ (xright * xright)));
} /* if South pole */
/* For both poles: */
if (xleft < 0) { /*
* left half of earth has
* negative longits
*/
left_t = -left_t;
left_b = -left_b;
}
/* This will hardly ever happen: */
if (xright < 0) { /*
* left half of earth has
* negative longs
*/
right_t = -right_t;
right_b = -right_b;
}
if (Debug.debugging("maketoc"))
Debug.output("LONGS. left_t: " + left_t + ", right_t: "
+ right_t);
if (Debug.debugging("maketoc"))
Debug.output("LONGS. left_b: " + left_b + ", right_b: "
+ right_b);
// #if 0
// /* !!!!!!!!!!!!!!!!!!! Fix to getlat [80,90],
// longit. [-180,180] */
// bottom = 80.0 ;
// top = 90.0 ;
// left = -180.0 ;
// right = 180.0 ;
// #endif
} /* if polar zone */
/* end DKS portion */
/* end PBF cvt from xy to lat-long */
else { /* non-polar zone */
left_t = group.vert_pos[group.left];
left_b = left_t;
right_t = group.vert_pos[group.right];
right_b = right_t;
top = group.horiz_pos[group.top];
bottom = group.horiz_pos[group.bottom];
} /* else */
// Debug.output("For RpfTocEntry, writing: \n top = "
// + top +
// "\n bottom = " + bottom + "\n left = " + left_t +
// "\n right = " + right_t + "\n---------");
// Writing all doubles
fout.writeDouble(top);
fout.writeDouble(left_t);
fout.writeDouble(bottom);
fout.writeDouble(left_b);
fout.writeDouble(top);
fout.writeDouble(right_t);
fout.writeDouble(bottom);
fout.writeDouble(right_b);
fout.writeDouble(group.v_resolution);
fout.writeDouble(group.h_resolution);
fout.writeDouble(group.v_interval);
fout.writeDouble(group.h_interval);
/* # frames */
fout.writeInt((int) (group.bottom - group.top));
fout.writeInt((int) (group.right - group.left));
}
if (Debug.debugging("maketoc")) {
Debug.output("MakeToc: *** writing frame section ***");
Debug.output("MakeToc: started with a 'U'");
}
/* START FRAME SECTION */
/* Now write frames */
/* security classif */
charArray[0] = 'U';
charString = new String(charArray);
fout.writeBytes(charString);
/* frame file index tbl offset */
fout.writeInt(0);
/* # of frame file index records */
fout.writeInt(nFrames);
/* # of pathname (directory) records */
/* DKS NEW: was nFrames: */
fout.writeShort(uniq_dir_cnt);
/* frame file index record length : 33 */
fout.writeShort(Frame_index_rec_len);
/* Frame file index subsection */
for (i = 0; i < nFrames; i++) { /* for each frame file */
frame = (Frame) frames.elementAt(i);
group = (Group) groups.elementAt(frame.group);
if (!frame.marked) {
Debug.error(frame.filename + ": not in a boundary rect??");
// continue;
}
/* NEW, DKS: +1 removed so range is [0,n]: */
fout.writeShort(frame.group); /* Boundary rect. rec. # */
/* Frame location ROW number */
/*
* DKS. Changed from top to bottom to fix bug in
* Theron's frame numbering
*/
/*
* Should start numbering at BOTTOM (southern-most
* part) of group
*/
/* !!! Changed back so row num is never <= 0 */
/* Alternative is bottom-y, not y-bottom. Try later */
/*
* us = frames[i].y - groups[frames[i].group].bottom +
* 1;
*/
/* NEW, DKS: START AT 0, NOT 1: REMOVE "+ 1": */
/* us = frames[i].y - groups[frames[i].group].top; */
/*
* SMN The frames number are from the bottom left not
* top left
*/
us = (short) (group.bottom - frame.y - 1);
if (Debug.debugging("maketocframedetail")) {
Debug.output("iframe: " + i + ", frame.y: " + frame.y);
Debug.output("frame.group: " + frame.group);
Debug.output("group.bottom:" + group.bottom);
Debug.output("group.top:" + group.top);
Debug.output("frame row #:" + us);
}
fout.writeShort(us);
/* Frame location Column number */
/* NEW, DKS: START AT 0, NOT 1: REMOVE "+ 1": */
fout.writeShort((short) (frame.x - group.left));
/* pathname record offset: */
/*
* DKS 11/10: Now w.r.t. frame file index table
* subsection
*/
/*
* ui = head.HEADER_SECTION_LENGTH + Loc_sec_len +
* Bound_sec_len + Frame_hdr_len +
* nFrames*Frame_index_rec_len + pathname_pos[i] ;
*/
fout.writeInt((int) (nFrames * Frame_index_rec_len + pathname_pos[i]));
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("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;
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';
// 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);
}
}