import java.applet.Applet; import java.awt.Canvas; import java.awt.Color; import java.awt.Component; import java.awt.Event; import java.awt.FlowLayout; import java.awt.Frame; import java.awt.Graphics; import java.awt.Image; import java.awt.MediaTracker; import java.awt.TextField; import java.io.DataInputStream; import java.io.IOException; import java.net.URL; import java.net.MalformedURLException; /** * * EKBrowse is an applet used to view a reference image which is divided * up into a grid of equal sized sections. The number of grid sections is * determined by a scale factor specified as a parameter in the html file. * Clicking on one of the grids in the reference image will load a zoom * image below the reference image which is a high resolution view into * that grid box. Clicking in the zoom image will update a display of the * latitude and longitude at that point. * * @author Tommy Jasmin * * @see RefCanvas * @see ZoomCanvas * */ public class EKBrowse extends Applet { private Image refImg; private Image zoomImg; private RefCanvas refCanvas; private ZoomCanvas zoomCanvas; private int winWidth; private int winHeight; private int enh; private boolean enhFlag = false; private URL url; private DataInputStream din; private TextField imgInfo; private String prefix; private static final int PANELBORDER = 4; private static final int TEXTBOXSIZE = 50; private static final int IMGMAXWIDTH = 8192; private static final int IMGMAXHEIGHT = 4096; /** * * Reads html parameters for the initial reference image file name, zoom * image file name, scale factor, and an enhancement flag. The enhancement * flag is used to tell the applet to use enhanced versions of the images, * which are distinguished by the text "e_" prepended to each filename. *
* The date and time of the current reference image are obtained by
* opening a URL and DataInputStream to the file COMP_TIME (which stands
* for composite image time) on the server. The information is read as
* a single line of text from this file and displayed in a TextField
* object below the zoom image.
*
* @see RefCanvas
* @see ZoomCanvas
*
*/
public void init() {
// get file names for reference image and initial (default) zoom image
String refFileName = getParameter("refFileName");
String zoomFileName = getParameter("zoomFileName");
int scale = (Integer.valueOf(getParameter("scale"))).intValue();
int enh = (Integer.valueOf(getParameter("enhancement"))).intValue();
if (enh == 1) {
enhFlag = true;
}
// load the initial reference and zoom images
refImg = WaitForImage(refFileName);
zoomImg = WaitForImage(zoomFileName);
// time of the current data is stored in a one line text file
// first step in reading this is creating a URL object
try {
url = new URL(getDocumentBase(), "COMP_TIME");
} catch (MalformedURLException e) {
System.out.println(e);
}
// from the URL, open a data stream
try {
din = new DataInputStream(url.openStream());
} catch (IOException e) {
System.out.println(e);
}
// then read the single line with image time info
try {
prefix = din.readLine();
} catch (IOException e) {
System.out.println(e);
}
// tack on the UTC label to the time text
prefix = prefix + " UTC";
// create the text box that will hold time and lat/lon info
imgInfo = new TextField(prefix, TEXTBOXSIZE);
imgInfo.setEditable(false);
imgInfo.setBackground(Color.yellow);
imgInfo.setForeground(Color.black);
// add one to width to make it easier to overlay grid boxes
winWidth = refImg.getWidth(null) + 1;
winHeight = refImg.getHeight(null) + zoomImg.getHeight(null) +
imgInfo.getColumns() + PANELBORDER + PANELBORDER;
// set background color
setBackground(Color.black);
resize(winWidth, winHeight);
zoomCanvas = new ZoomCanvas (
zoomImg,
scale,
IMGMAXWIDTH,
IMGMAXHEIGHT,
imgInfo
);
refCanvas = new RefCanvas (
refImg,
scale,
zoomCanvas,
enhFlag,
this
);
// set layout manager and place the frame components
setLayout(new FlowLayout());
add(refCanvas);
add(zoomCanvas);
add(imgInfo);
}
/**
*
* Uses MediaTracker to load an image and suspend execution until the
* load has finished.
*
* @param fileName file name of image to load
* @return reference to image object loaded;
* null if load fails for any reason
*
*/
private Image WaitForImage(String fileName) {
Image image = null;
MediaTracker mt = new MediaTracker(this);
try {
image = getImage(getDocumentBase(), fileName);
}
catch (Exception e1) {
System.out.println(e1);
}
mt.addImage(image, 0);
try {
showStatus("loading image " + fileName);
mt.checkID(0, true);
mt.waitForID(0);
}
catch (InterruptedException e2) {
System.out.println(e2);
}
return image;
}
/**
*
* override update to avoid blanking the frame and causing flickering.
*
* @param g graphics object
*
*/
public void update(Graphics g) {
paint(g);
}
}
/**
*
* provides a context for the reference image and all associated functions.
*
* @author Tommy Jasmin
*
* @see EKBrowse
* @see ZoomCanvas
*
*/
class RefCanvas extends Canvas {
Image image;
Graphics g;
int imgWidth;
int imgHeight;
private int scaleFactor;
private ZoomCanvas zoomCanvas;
private Applet applet;
private int prevId = -1;
private int prvHiLite;
private boolean enhFlag;
/**
*
* puts the canvas in an initial state.
*
* @param refImg reference image
* @param scale scale factor
* @param zc zoom canvas object reference; needed for
* accessor calls like setting a new zoom image.
* @param ef enhancement flag
* @param a reference to parent applet
*
*/
RefCanvas(Image refImg, int scale, ZoomCanvas zc, boolean ef, Applet a) {
image = refImg;
scaleFactor = scale;
zoomCanvas = zc;
enhFlag = ef;
applet = a;
imgWidth = image.getWidth(null);
imgHeight = scaleFactor * scaleFactor;
resize((imgWidth + 1), (imgHeight + 1));
}
/**
*
* update is just a paint call to avoid flickering when loading images
*
* @param g graphics object
*
*/
public void update(Graphics g) {
paint(g);
}
/**
*
* draw the reference image and the zoom image selection grid.
*
* @param g graphics object
*
*/
// to paint the reference canvas, first we draw the current reference
// image, then we draw the zoom grid by drawing a number of vertical
// and horizontal lines corresponding to the scale factor.
public void paint(Graphics g) {
g.drawImage(image, 0, 0, this);
g.setColor(Color.yellow);
for (int i = 0; i < scaleFactor + 1; i++) {
g.drawLine(i * scaleFactor * 2, 0, i * scaleFactor * 2, imgHeight);
g.drawLine(0, i * scaleFactor, imgHeight * 2, i * scaleFactor);
}
}
/**
*
* highlights the current grid box.
*
* @param evt mouse movement event
* @param x current x coordinate of the cursor
* @param y current y coordinate of the cursor
* @return true
, event always handled
*
*/
public boolean mouseMove(Event evt, int x, int y) {
// the current grid box id number
int id;
// X start, Y start, X increment
int xS, yS, xI;
Graphics g = getGraphics();
id = (((x / 2) / scaleFactor) * scaleFactor) + (y / scaleFactor);
// highlight new box only if changed
if (id != prvHiLite) {
xI = scaleFactor * 2;
// restore previously highlighted box to default color
if (prvHiLite >= 0) {
g.setColor(Color.yellow);
xS = (prvHiLite / scaleFactor) * xI;
yS = (prvHiLite % scaleFactor) * scaleFactor;
g.drawRect(xS, yS, xI, scaleFactor);
}
g.setColor(Color.red);
xS = (id / scaleFactor) * xI;
yS = (id % scaleFactor) * scaleFactor;
g.drawRect(xS, yS, xI, scaleFactor);
// set grid id for next mouse move
prvHiLite = id;
}
g.dispose();
return true;
}
/**
*
* loads a new zoom image if there is a mouse click in a new grid box.
*
* @param evt current event, only mouse down handled here
* @return true
, mouse down handled;
* else event passed up to parent.
*
*/
public boolean handleEvent(Event evt) {
int id;
String fName;
// only handle mouse clicks, pass all other events up the chain
if (evt.id == Event.MOUSE_DOWN) {
// convert x/y coord of the event to a grid box id number
id = (((evt.x / 2) / scaleFactor) * scaleFactor) + (evt.y / scaleFactor);
System.out.println(" ref window clicked, maps to grid box id: " + id);
// skip load if this is the same box that was last clicked
if (id == prevId) {
System.out.println(" image already loaded - ignoring mouse click");
return true;
}
// the only version specific code - load an enhanced image if told to
if (enhFlag) {
fName = "e_irc" + id + ".gif";
} else {
fName = "irc" + id + ".gif";
}
// switch to the new image, and store current state
Image image = SwitchImage(fName);
zoomCanvas.setImage(image);
zoomCanvas.setGrid(id);
prevId = id;
return true;
} else {
return super.handleEvent(evt);
}
}
/**
*
* changes the current zoom image. uses MediaTracker to suspend
* execution till image is completely loaded.
*
* @param fileName file name of image to load
* @return reference to new image object loaded;
* null if load fails for any reason
*
*/
private Image SwitchImage(String fileName) {
// get reference to parent Frame object, needed to change cursor
Component c = (Component) this;
while (c != null) {
c = c.getParent();
if (c instanceof Frame) {
break;
}
}
// change to wait cursor while image loads
if (c != null) {
((Frame) c).setCursor(Frame.WAIT_CURSOR);
c.repaint();
}
// use MediaTracker to load image and wait until loading finishes
Image image = null;
MediaTracker mt = new MediaTracker(this);
try {
image = applet.getImage(applet.getDocumentBase(), fileName);
}
catch (Exception e1) {
System.out.println(e1);
}
mt.addImage(image, 0);
try {
applet.showStatus("loading image " + fileName);
mt.checkID(0, true);
mt.waitForID(0);
}
catch (InterruptedException e2) {
System.out.println(e2);
}
applet.showStatus("");
// switch back to default cursor before leaving
if (c != null) {
((Frame) c).setCursor(Frame.DEFAULT_CURSOR);
c.repaint();
}
return image;
}
}
/**
*
* provides a context for the zoom image and all associated functions.
*
* @author Tommy Jasmin
*
* @see EKBrowse
* @see RefCanvas
*
*/
class ZoomCanvas extends Canvas {
// current zoom image being displayed
private Image image;
// how many grids to divide the reference image into
private int scaleFactor;
// current grid in reference image which determines the zoom image
private int gridNum;
// image width and height used for panel sizing
private int imgWidth;
private int imgHeight;
private TextField imgInfo;
// max line and element needed for Mercator navigation
private int maxLine;
private int maxElem;
// set an interesting initial grid number, determines initial zoom image
private static final int INITGRIDNUM = 68;
// size of the image time, lat/lon text field that doesn't change
private static final int ITTEXTSIZE = 22;
/**
*
* loads constants, sizes display panel
*
* @param zoomImg zoom image
* @param scale scale factor
* @param maxW maximum width/element of a zoom image
* @param maxH maximum height/line of a zoom image
* @param ii text field which holds zoom image information
*
*/
// we resize 1 greater than width to be consistent with the reference
// image panel, which did this to make gridding easier.
ZoomCanvas(Image zoomImg, int scale, int maxW, int maxH, TextField ii) {
image = zoomImg;
scaleFactor = scale;
maxLine = maxH;
maxElem = maxW;
gridNum = INITGRIDNUM;
imgInfo = ii;
imgWidth = scaleFactor * scaleFactor * 2;
imgHeight = scaleFactor * scaleFactor;
resize((imgWidth + 1), imgHeight);
}
/**
*
* changes cursor to crosshair while within zoom panel
*
* @param evt mouse enter event
* @param x current x coordinate of the cursor
* @param y current y coordinate of the cursor
* @return true
, event always handled
*
*/
public boolean mouseEnter(Event evt, int x, int y) {
// get reference to parent Frame object
Component c = (Component) this;
while (c != null) {
c = c.getParent();
if (c instanceof Frame) {
break;
}
}
// change to crosshair cursor
if (c != null) {
((Frame) c).setCursor(Frame.CROSSHAIR_CURSOR);
c.repaint();
}
return true;
}
/**
*
* changes cursor to default when leaving zoom panel
*
* @param evt mouse enter event
* @param x current x coordinate of the cursor
* @param y current y coordinate of the cursor
* @return true
, event always handled
*
*/
public boolean mouseExit(Event evt, int x, int y) {
// get reference to parent Frame object
Component c = (Component) this;
while (c != null) {
c = c.getParent();
if (c instanceof Frame) {
break;
}
}
// change to default cursor
if (c != null) {
((Frame) c).setCursor(Frame.DEFAULT_CURSOR);
c.repaint();
}
return true;
}
/**
*
* updates lat/lon values when user clicks in the zoom window.
*
* @param evt current event, only mouse down handled here
* @return true
, mouse down handled;
* else event passed up to parent.
*
*/
public boolean handleEvent(Event evt) {
int x, y;
float fLat, fLon;
// only looking for mouse clicks for now
if (evt.id == Event.MOUSE_DOWN) {
// first, translate x/y to absolute x/y in the reference image
x = evt.x + ((gridNum / scaleFactor) * imgWidth);
y = evt.y + ((gridNum % scaleFactor) * imgHeight);
// next, compute a lat/lon pair from the given x/y pair
fLat = 0.0f - ((float) y - ((float) maxLine / 2.0f)) * 0.0439f;
fLon = 0.0f - ((float) x - ((float) maxElem / 2.0f)) * 0.0439f;
if (fLon > 180) fLon = fLon - 360;
System.out.println(" lat, lon : " + fLat + ", " + fLon);
System.out.println(" x , y : " + x + ", " + y);
// convert to a string, with at most two digits after decimal point
String latStr = Float.toString(fLat);
int dotIdx = latStr.lastIndexOf(".");
int endIdx = latStr.length();
dotIdx = dotIdx + 1;
if (dotIdx > 0) {
if ((endIdx - dotIdx) >= 2) {
latStr = latStr.substring(0, (endIdx - (endIdx - dotIdx - 2)));
}
}
// convert to a string, with at most two digits after decimal point
String lonStr = Float.toString(fLon);
dotIdx = lonStr.lastIndexOf(".");
endIdx = lonStr.length();
dotIdx = dotIdx + 1;
if (dotIdx > 0) {
if ((endIdx - dotIdx) >= 2) {
lonStr = lonStr.substring(0, (endIdx - (endIdx - dotIdx - 2)));
// negative longitude gets converted to positive West longitude
// if we see a minus sign, strip it and add a W, otherwise add an E
int dshIdx = lonStr.lastIndexOf("-");
if (dshIdx >= 0) {
lonStr = lonStr.substring(1) + " E";
} else {
lonStr = lonStr + " W";
}
}
}
// now, get the current text, keep the date part, and update lat/lon
String tmpStr = imgInfo.getText();
tmpStr = tmpStr.substring(0, ITTEXTSIZE);
imgInfo.setText (
tmpStr +
" latitude: " +
latStr +
", longitude: " +
lonStr
);
return true;
// all other events get passed up
} else {
return super.handleEvent(evt);
}
}
/**
*
* changes current zoom image being displayed
*
* @param img reference to new image
*
*/
public void setImage(Image img) {
image = img;
// clear out lat/lon data when loading a new image
String tmpStr = imgInfo.getText();
tmpStr = tmpStr.substring(0, ITTEXTSIZE);
imgInfo.setText(tmpStr);
repaint();
}
/**
*
* changes reference grid the current zoom image corresponds to
*
* @param grid grid number
*
*/
public void setGrid(int grid) {
gridNum = grid;
}
/**
*
* update is just a paint call to avoid flickering when loading images
*
* @param g graphics object
*
*/
public void update(Graphics g) {
paint(g);
}
/**
*
* paint simply draws the image
*
* @param g graphics object
*
*/
public void paint(Graphics g) {
g.drawImage(image, 0, 0, this);
}
}