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); } }