import java.applet.*; import java.net.*; import java.io.*; import java.awt.*; import java.lang.*; import java.util.*; import java.text.*; public class WormWatchApplet extends Applet { static int W = 375; static int H = 520; int nquakes = 0; int realcounteq; static int MAX_QUAKES = 1000; String MapImageFile; Image im; Image mapImage; Graphics offscreen; QuakeChecker quakechecker; Quake quakes[] = new Quake[MAX_QUAKES]; int CurrentQuake = -1; int ClickQuake = -1; public void init() { MapImageFile = getParameter("MapImageFile"); if( MapImageFile == null ) { MapImageFile = "topo3.gif"; } mapImage = getImage(getCodeBase(), MapImageFile); resize(W, H); try { im = createImage(W, H); offscreen = im.getGraphics(); } catch (Exception e) { offscreen = null; } } /* Start the threads for each part of the applet */ public void start() { nquakes = 0; try { quakechecker = new QuakeChecker(this); } catch (Exception e) { System.out.println("Reloading Quakes"); } } public void addquake(Quake q) { quakes[nquakes++] = q; } /* when applet is stopped, stop individual threads */ public void stop() { // Clean Up? } /* override the update method to reduce flashing */ public void update(Graphics g) { paint(g); } /* Use double buffering to reduce flashing */ public void paint(Graphics g) { if (offscreen != null) { paintApplet(offscreen); g.drawImage(im,0,0,this); } else { paintApplet(g); } } /* call each thing's paint method */ public void paintApplet(Graphics g) { boolean changed = false; g.setColor(Color.white); g.fillRect(0, 0, W, H); g.drawImage(mapImage,0,0,this); if ( nquakes != 0 ) { for(int i=0; i < nquakes; i++) { if( quakes[i].IsThisMonth() ) quakes[i].PlotCircle(g); if( quakes[i].IsThisWeek() ) quakes[i].PlotSquare(g); if( quakes[i].IsToday() ) quakes[i].PlotSolidSquare(g); } } if( CurrentQuake >= 0 && ClickQuake == -1 ) { g.setFont(new Font("Helvetica",Font.BOLD,14)); g.setColor(Color.black); realcounteq = CurrentQuake + 1; g.drawString("Event #" + realcounteq,2,455); quakes[CurrentQuake].describe(g); } else if ( CurrentQuake == -1 && ClickQuake == -1 ) { g.setFont(new Font("Helvetica",Font.BOLD,14)); g.setColor(Color.black); g.drawString("Move or Click mouse", 2, 455); } else if ( CurrentQuake == -1 && ClickQuake > -1 ) { g.setFont(new Font("Helvetica",Font.BOLD,14)); g.setColor(Color.black); realcounteq = ClickQuake + 1; g.drawString("Event #" + realcounteq,2,455); quakes[ClickQuake].describe(g); quakes[ClickQuake].highlight(g); } } /* click to shift overlapping events */ public boolean mouseDown(Event e, int x, int y) { Graphics g = getGraphics(); CurrentQuake = -1; ClickQuake = ClickQuake + 1; if(ClickQuake == nquakes ) ClickQuake = 0; paint(g); return(true); } /* when mouse focus is lost reset print message */ public boolean mouseExit(Event e) { CurrentQuake = -1; ClickQuake = -1; return(true); } public boolean mouseEnter(Event e) { CurrentQuake = -1; ClickQuake = -1; return(true); } /* get the event at the cursor position */ public boolean mouseMove(Event e, int x, int y) { Graphics g = getGraphics(); ClickQuake = -1; for( int i=0; i < nquakes; i++) { if( quakes[i].inside(x,y) ) { CurrentQuake = i; } } paint(g); return(true); } } class Quake { int orid, nph; Location loc; double mag, depth; String comment; Date time; int splatrad; static int MagScale = 4; Quake(int a_orid, double a_lat, double a_lon, double a_depth, double a_mag, int a_ph, String a_comment, Date a_time ) { orid = a_orid; this.loc = new Location(a_lat,a_lon); depth = a_depth; mag = a_mag; nph = a_ph; comment = a_comment; time = a_time; } public void plotpoints(int x0, int y0, int x, int y, Graphics g) { g.drawLine(x0+x,y0+y,x0+x,y0+y); g.drawLine(x0+y,y0+x,x0+y,y0+x); g.drawLine(x0+y,y0-x,x0+y,y0-x); g.drawLine(x0+x,y0-y,x0+x,y0-y); g.drawLine(x0-x,y0-y,x0-x,y0-y); g.drawLine(x0-y,y0-x,x0-y,y0-x); g.drawLine(x0-y,y0+x,x0-y,y0+x); g.drawLine(x0-x,y0+y,x0-x,y0+y); } public void circle(int x0, int y0, int r, Graphics g) { int x,y; float d; x=0; y=r; d=5/4-r; plotpoints(x0,y0,x,y,g); while (y>x){ if (d<0) { d=d+2*x+3; x++; } else { d=d+2*(x-y)+5; x++; y--; } plotpoints(x0,y0,x,y,g); } } public void box(int x0, int y0, int r, Graphics g) { int x1,x2,y1,y2; x1 = x0 - r; x2 = x0 + r; y1 = y0 - r; y2 = y0 + r; g.drawLine(x1, y1, x2, y1); g.drawLine(x2, y1, x2, y2); g.drawLine(x2, y2, x1, y2); g.drawLine(x1, y2, x1, y1); } public void highlight(Graphics g) { PixelCoord pc; pc = this.loc.pix(); g.setColor(Color.white); splatrad = MagScale*2*(int)mag; box(pc.x, pc.y, splatrad, g); splatrad = (MagScale*2*(int)mag)-1; box(pc.x, pc.y, splatrad, g); } public void describe(Graphics g) { Date epoch = new Date(); epoch = this.time ; SimpleDateFormat utcformatter = new SimpleDateFormat("EE MMMM dd (DD), yyyy HH:mm:ss zzz"); SimpleDateFormat pdtformatter = new SimpleDateFormat("EE MMMM dd, yyyy hh:mm:ss a zzz"); SimpleTimeZone utc = new SimpleTimeZone(0 * 60 * 60 * 1000, "UTC"); utcformatter.setTimeZone(utc); String pdtstring = pdtformatter.format(time); String gmtstring = utcformatter.format(time); g.setFont(new Font("Helvetica",Font.PLAIN,10)); g.setColor(Color.black); g.drawString("Pacific Time=" + pdtstring, 5,470); g.drawString("UTC Time=" + gmtstring, 5, 485); g.drawString("Location= " + this.loc.lat + "N " + -this.loc.lon + "W Depth=" + this.depth + "km Ml=" + this.mag + " Number Phases=" + nph, 5,500); g.drawString(comment, 5, 515); } public boolean IsToday() { Date Otime = this.time; int yr, mo, da, hr, mn; SimpleTimeZone pdt = new SimpleTimeZone(-8 * 60 * 60 * 1000, "PST"); pdt.setStartRule(3, 1, 1, 2*60*60*1000); pdt.setEndRule(9, -1, 1, 2*60*60*1000); GregorianCalendar current = new GregorianCalendar(pdt); yr = current.get(Calendar.YEAR)-1900; mo = current.get(Calendar.MONTH); da = current.get(Calendar.DAY_OF_MONTH); hr = current.get(Calendar.HOUR_OF_DAY); mn = current.get(Calendar.MINUTE); Date Time = new Date(yr, mo, da-1, hr, mn, 0); if( Otime.after(Time) ) return(true); else return(false); } public boolean IsThisMonth() { Date Otime = this.time; int yr, mo, da, hr, mn; SimpleTimeZone pdt = new SimpleTimeZone(-8 * 60 * 60 * 1000, "PST"); pdt.setStartRule(3, 1, 1, 2*60*60*1000); pdt.setEndRule(9, -1, 1, 2*60*60*1000); GregorianCalendar current = new GregorianCalendar(pdt); yr = current.get(Calendar.YEAR)-1900; mo = current.get(Calendar.MONTH); da = current.get(Calendar.DAY_OF_MONTH); hr = current.get(Calendar.HOUR_OF_DAY); mn = current.get(Calendar.MINUTE); Date Time1 = new Date(yr, mo-1, da, hr, mn, 0); Date Time2 = new Date(yr, mo, da-7, hr, mn, 0); if( Otime.before(Time2) && Otime.after(Time1) ) return(true); else return(false); } public boolean IsThisWeek() { Date Otime = this.time; int yr, mo, da, hr, mn; SimpleTimeZone pdt = new SimpleTimeZone(-8 * 60 * 60 * 1000, "PST"); pdt.setStartRule(3, 1, 1, 2*60*60*1000); pdt.setEndRule(9, -1, 1, 2*60*60*1000); GregorianCalendar current = new GregorianCalendar(pdt); yr = current.get(Calendar.YEAR)-1900; mo = current.get(Calendar.MONTH); da = current.get(Calendar.DAY_OF_MONTH); hr = current.get(Calendar.HOUR_OF_DAY); mn = current.get(Calendar.MINUTE); Date Time1 = new Date(yr, mo, da-7, hr, mn, 0); Date Time2 = new Date(yr, mo, da, hr-24, mn, 0); if( Otime.before(Time2) && Otime.after(Time1) ) return(true); else return(false); } public void PlotSquare(Graphics g) { PixelCoord pc; pc = this.loc.pix(); g.setColor(Color.yellow); splatrad = MagScale * (int)mag - 4; if( splatrad == 0 ) splatrad = 1; box(pc.x, pc.y, splatrad, g); } public void PlotSolidSquare(Graphics g) { PixelCoord pc; pc = this.loc.pix(); g.setColor(Color.black); splatrad = MagScale * (int)mag - 3; if( splatrad == 0 ) splatrad = 1; box(pc.x, pc.y, splatrad, g); for( int i=splatrad-1; i>=0; i-=1) { g.setColor(Color.red); box(pc.x, pc.y, i, g); } } public boolean inside(int x, int y) { double xdist,ydist,dist; PixelCoord pc; pc = this.loc.pix(); xdist = (double) (x - pc.x); ydist = (double) (y - pc.y); dist = Math.sqrt(xdist*xdist+ydist*ydist); if((int) dist <= splatrad) { return(true); } else { return(false); } } public void PlotCircle(Graphics g) { PixelCoord pc; pc = this.loc.pix(); g.setColor(Color.cyan); if( mag < 0 ) mag=1.0; splatrad = MagScale * (int)mag; circle(pc.x,pc.y,splatrad,g); } } class PixelCoord { int x, y; PixelCoord(int a_x,int a_y) { x = a_x; y = a_y; } } class Location { double lat, lon; static int MapXCtr = 203; static int MapYCtr = 217; static int PixPerLatDeg = 68; static int PixPerLonDeg = 54; static double MapLatCtr = 38.0; static double MapLonCtr = -119.0; Location(double a_lat, double a_lon) { lat = a_lat; lon = a_lon; }; public PixelCoord latlon2pix(Location loc) { int x,y; double lat,lon,xdist,ydist,dist,az,pixdist; lat = loc.lat; lon = loc.lon; ydist = PixPerLatDeg * ( lat - MapLatCtr ); xdist = PixPerLonDeg * ( lon - MapLonCtr ); az = Math.atan(ydist/xdist); if(xdist<0.) az += Math.PI; dist = (xdist*xdist) + (ydist*ydist); pixdist = Math.sqrt(dist); x = (int) (MapXCtr + pixdist*Math.cos(az)); y = (int) (MapYCtr - pixdist*Math.sin(az)); return new PixelCoord(x,y); } PixelCoord pix() { return latlon2pix(this); } } class QuakeChecker { double lat = 0, lon = 0, depth = 0, ml = 0; int orid = 0, ndef = 0; int year = 0, month = 0, day = 0, hourmin = 0; int hour=0, min=0, second = 0; Date time; String comment; Quake q; URL u; InputStream is; StreamTokenizer st; WormWatchApplet applet; public QuakeChecker(WormWatchApplet applet) throws java.io.IOException { this.applet = applet; u = new URL("http://www.seismo.unr.edu/ftp/pub/ichinose/JavaWorm/nbe.html"); is = u.openStream(); st = new StreamTokenizer(is); st.eolIsSignificant(true); st.commentChar('#'); st.quoteChar('"'); st.nextToken(); while(st.ttype != StreamTokenizer.TT_EOF) { orid++; year = (int) st.nval ; st.nextToken(); month = (int) st.nval - 1; st.nextToken(); day = (int) st.nval; st.nextToken(); hourmin = (int) st.nval; hour = (int) Math.floor((double)hourmin/100.0) ; min = hourmin - hour*100; st.nextToken(); second = (int) st.nval; st.nextToken(); lat = st.nval; st.nextToken(); lon = st.nval; st.nextToken(); depth = st.nval; st.nextToken(); ml = st.nval; st.nextToken(); ndef = (int) st.nval; st.nextToken(); comment = st.sval; st.nextToken(); st.nextToken(); // get a calendar with current Pacific Standard zone and daylight savings hour offset // SimpleTimeZone pdt = new SimpleTimeZone(-8 * 60 * 60 * 1000, "PST"); pdt.setStartRule(3, 1, 1, 2*60*60*1000); pdt.setEndRule(9, -1, 1, 2*60*60*1000); GregorianCalendar current = new GregorianCalendar(pdt); int zone = current.get(Calendar.ZONE_OFFSET)/(60*60*1000); int dst = current.get(Calendar.DST_OFFSET)/(60*60*1000); // now convert GMT time to PDT time // Date time = new Date( year - 1900, month, day, hour+zone+dst, min, second ); // TEST PRINT // SimpleDateFormat utcformatter = new SimpleDateFormat ("(DD) dd MMMM yyyy HH:mm:ss zzz"); // SimpleDateFormat pdtformatter = new SimpleDateFormat ("EE MMMM dd, yyyy KK:mm:ss a zzz"); // SimpleTimeZone utc = new SimpleTimeZone(0 * 60 * 60 * 1000, "UTC"); // utcformatter.setTimeZone(utc); // System.out.println( "origin=" + orid + // " " + pdtformatter.format(time) + " " + utcformatter.format(time) ); q = new Quake(orid,lat,lon,depth,ml,ndef,comment,time); applet.addquake(q); } } }