diff --git a/.gitignore b/.gitignore index 982446bd..6d40ef6b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ /target/ /classes/ src/main/java/gov/usgs/volcanoes/swarm/Version.java +/.DS_Store +/dependency-reduced-pom.xml +/Swarm.config +/Swarm.config.bak diff --git a/CHANGES.md b/CHANGES.md index 52863203..373efeb2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,11 @@ +## Version 2.6.0 + * Add option to plot hypocenters from NEIC summary files on map + * Add event inspector dialog + * Expand maximum time span of inset wave window + * Quote command line args before passing them to java + * Fix wave close buttons + * Fix kisok enter/exit keys + ## Version 2.5.2 * Remove obsoleted DefaultMetadata Class. diff --git a/RsamDefaults.config b/RsamDefaults.config new file mode 100644 index 00000000..af0afee7 --- /dev/null +++ b/RsamDefaults.config @@ -0,0 +1,30 @@ +default.autoScale=true + +default.binSize=Hour + +default.countsPeriod=10 + +default.detrend=false + +default.eventMaxLength=300.0 + +default.eventRatio=1.3 + +default.eventThreshold=50.0 + +default.runningMean=false + +default.runningMeanPeriod=300.0 + +default.runningMedian=false + +default.runningMedianPeriod=300.0 + +default.scaleMax=100.0 + +default.scaleMin=0.0 + +default.valuesPeriod=600 + +default.viewType=VALUES + diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml deleted file mode 100644 index 1fa6b1da..00000000 --- a/dependency-reduced-pom.xml +++ /dev/null @@ -1,383 +0,0 @@ - - - 4.0.0 - gov.usgs.volcanoes - swarm - Swarm - 2.5.3 - Swarm is a Java application designed to display and analyze seismic waveforms in real-time. Swarm can connect to and read from a variety of different static and dynamics data sources, including Earthworm waveservers, IRIS DMCs, SEED and SAC files, and simple ASCII. Swarm has both time- and frequency-domain analysis tools, along with a simple but powerful mapping platfrom. A full-screen kiosk mode can act as a low-cost, low-maintenance replacement for paper drum recorders. Swarm was written by and for scientists and provides fine control over many different program settings and variables. - http://volcanoes.usgs.gov/software/swarm - - GitHub - https://github.com/usgs/swarm/issues - - - Travis - https://travis-ci.org/usgs/swarm/ - - - - tparker - Tom Parker - tparker@usgs.gov - U.S. Geological Survey, Alaska Volcano Observatory - - true - - - - cervelli - Peter Cervelli - pcervelli@usgs.gov - U.S. Geological Survey, Volcano Science Center - - true - - - - lantolik - Loren Antolik - lantolik@usgs.gov - U.S. Geological Survey, Hawaiian Volcano Observatory - - true - - - - dcervelli - Dan Cervelli - - false - - - - - - Kevin Frechette (ISTI) - - - Anthony Lomax - - - Dave Ketchum - - - Chirag Patel - - - Ivan Henson - - - - - CC0 1.0 Universal - http://creativecommons.org/publicdomain/zero/1.0 /> - repo - - - - scm:git:git@github.com:usgs/swarm.git - scm:git:git@github.com:usgs/swarm.git - git@github.com:usgs/swarm.git - - - - - org.apache.maven.wagon - wagon-ssh-external - 1.0-beta-6 - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - com.google.code.maven-replacer-plugin - maven-replacer-plugin - 1.4.0 - - replace - - - - - - source: - - - - - - - - - - maven-shade-plugin - 2.4.1 - - - package - - shade - - - true - - - org.apache:log4j - - org/apache/log4j/** - - - - org.freemarker:freemarker - - freemarker/** - - - - com.jgoodies:jgoodies-common - - ** - - - - com.jgoodies:jgoodies-forms - - ** - - - - com.jgoodies:jgoodies-looks - - ** - - - - - - gov.usgs.volcanoes:usgs - gov.usgs.volcanoes:volcano-core - gov.usgs.volcanoes:winston - gov.usgs.volcanoes:swarm - org.freemarker:freemarker - com.jgoodies:* - com.martiansoftware:jsap - net.sourceforge.jtransforms:jtransforms - org.slf4j:* - org.apache:log4j - net.alomax:* - edu.sc.seis:* - colt:colt - edu.iris:JavaSeedLite - - - - - - - - maven-jar-plugin - 2.6 - - - - true - gov.usgs.volcanoes.swarm.Swarm - - - - - - com.google.code.maven-replacer-plugin - maven-replacer-plugin - 1.4.0 - - - process-sources - - replace - - - - - ${version.template.file} - ${version.file} - - - @buildnumber@ - ${svn.revision} - - - @buildtime@ - ${maven.build.timestamp} - - - @pomversion@ - ${project.version} - - - - - - org.codehaus.mojo - cobertura-maven-plugin - 2.7 - - xml - 256m - true - - - - org.eluder.coveralls - coveralls-maven-plugin - 3.2.0 - - ${env.coveralls_repo_token} - - - - maven-site-plugin - 3.4 - - - org.apache.maven.wagon - wagon-scm - 2.9 - - - org.apache.maven.scm - maven-scm-manager-plexus - 1.9.4 - - - org.apache.maven.scm - maven-scm-provider-gitexe - 1.9.4 - - - org.apache.maven.scm - maven-scm-api - 1.9.4 - - - - - maven-assembly-plugin - 2.6 - - - make-assembly - package - - single - - - - - - src/assembly/swarmBin.xml - - - - - - - - - true - - volcanoes - http://volcanoes.usgs.gov/software/maven2/ - - - - - edu.iris - seedCodec - 1.0.10 - compile - - - - - - maven-pmd-plugin - 3.5 - - - /rulesets/java/braces.xml - /rulesets/java/naming.xml - .ruleset - - - - - org.codehaus.mojo - findbugs-maven-plugin - 3.0.0 - - Max - Low - true - - - - maven-jxr-plugin - 2.5 - - - maven-javadoc-plugin - 2.10.3 - - false - - - - org.codehaus.mojo - cobertura-maven-plugin - 2.7 - - - html - xml - - - - - org.codehaus.mojo - javancss-maven-plugin - 2.1 - - utf-8 - false - - - - - - - volcanoes.web - Volcanoes website - scpexe://vulcan4.wr.usgs.gov/webdata/volcanoes.usgs.gov/htdocs/software/maven2 - - - github.pages - scm:git:https://git@github.com/usgs/swarm.git - - - - src/main/resources/Version.java.template - src/main/java/gov/usgs/volcanoes/swarm/Version.java - UTF-8 - - - diff --git a/pom.xml b/pom.xml index 9416a11a..b70cdcd0 100644 --- a/pom.xml +++ b/pom.xml @@ -4,11 +4,11 @@ gov.usgs.volcanoes swarm - 2.5.9 + 2.6.0-SNAPSHOT jar Swarm - http://volcanoes.usgs.gov/software/swarm + https://volcanoes.usgs.gov/software/swarm Swarm is a Java application designed to display and analyze seismic waveforms in real-time. Swarm can connect to and read from a variety of different static and dynamics data sources, including Earthworm waveservers, IRIS DMCs, SEED and SAC files, and simple ASCII. Swarm has both time- and frequency-domain analysis tools, along with a simple but powerful mapping platfrom. A full-screen kiosk mode can act as a low-cost, low-maintenance replacement for paper drum recorders. Swarm was written by and for scientists and provides fine control over many different program settings and variables. UTF-8 @@ -177,6 +177,37 @@ + + org.apache.maven.plugins maven-shade-plugin @@ -191,7 +222,7 @@ true - org.apache:log4j + log4j:log4j org/apache/log4j/** @@ -226,6 +257,18 @@ ** + + com.sun.xml.bind:jaxb-impl + + ** + + + + com.sun.xml.bind:jaxb-api + + ** + + @@ -238,12 +281,15 @@ com.martiansoftware:jsap net.sourceforge.jtransforms:jtransforms org.slf4j:* - org.apache:log4j + log4j:log4j net.alomax:* edu.sc.seis:* colt:colt edu.iris:JavaSeedLite opensymphony:oscache + gov.usgs.earthquake:messageUtils + com.sun.xml.bind:jaxb-impl + com.sun.xml.bind:jaxb-api @@ -388,6 +434,42 @@ + + sh.tak.appbundler + appbundle-maven-plugin + 1.2.0 + + gov.usgs.volcanoes.swarm.Swarm + /Users/tparker/jre/jre1.8.0_121.jre + true + + + ${project.basedir} + + README.md + LICENSE.md + CHANGES.md + IRIS_networks.txt + NTP.config + WaveDefaults.config + clip.wav + swarm.sh + swarm_console.bat + layouts + mapdata + + + + + + + package + + bundle + + + + @@ -404,9 +486,8 @@ volcanoes - true - http://volcanoes.usgs.gov/software/maven2/ + https://volcanoes.usgs.gov/software/maven2/ @@ -449,7 +530,7 @@ edu.sc.seis seisFile - 1.6.3 + 1.7.3 net.alomax @@ -472,35 +553,64 @@ 1.7.1 - org.apache + log4j log4j 1.2.13 gov.usgs.volcanoes usgs - 1.2 + 1.3.4 gov.usgs.volcanoes volcano-core - [1.3.2,) + [1.3.7,) gov.usgs.volcanoes winston 1.3.0-SNAPSHOT + + io.netty + netty-all + 4.0.31.Final + opensymphony oscache 2.4.1 - - - javax.jms - jms - - + + + javax.jms + jms + + + + + org.pegdown + pegdown + 1.6.0 + + + + gov.usgs.earthquake + messageUtils + 1.0 + + + com.sun.xml.bind + jaxb-impl + 2.1.2 + + + + javax.xml.bind + jaxb-api + 2.1 + + diff --git a/release.properties b/release.properties new file mode 100644 index 00000000..a47bae19 --- /dev/null +++ b/release.properties @@ -0,0 +1,17 @@ +#release configuration +#Tue Oct 13 16:26:44 AKDT 2015 +project.scm.gov.usgs.volcanoes\:swarm.tag=swarm-2.5 +scm.tagNameFormat=@{project.artifactId}-@{project.version} +project.scm.gov.usgs.volcanoes\:swarm.url=git@github.com\:usgs/swarm.git +scm.tag=swarm-2.5 +project.rel.gov.usgs.volcanoes\:swarm=2.5 +pushChanges=true +scm.url=scm\:git\:git@github.com\:usgs/swarm.git +preparationGoals=clean verify +project.scm.gov.usgs.volcanoes\:swarm.connection=scm\:git\:git@github.com\:usgs/swarm.git +remoteTagging=true +scm.commentPrefix=[maven-release-plugin] +project.dev.gov.usgs.volcanoes\:swarm=2.5.1-SNAPSHOT +project.scm.gov.usgs.volcanoes\:swarm.developerConnection=scm\:git\:git@github.com\:usgs/swarm.git +exec.snapshotReleasePluginAllowed=false +completedPhase=run-preparation-goals diff --git a/src/main/java/gov/usgs/volcanoes/swarm/AboutDialog.java b/src/main/java/gov/usgs/volcanoes/swarm/AboutDialog.java index 4180809d..96cba3b0 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/AboutDialog.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/AboutDialog.java @@ -35,54 +35,51 @@ * * @author Dan Cervelli */ -public class AboutDialog extends JDialog implements Runnable -{ +public class AboutDialog extends JDialog implements Runnable { private static final long serialVersionUID = -1; private static final JFrame applicationFrame = Swarm.getApplicationFrame(); + + private static final int PANE_WIDTH = 344; + private static final int PANEL_PADDING = 9; + private final static int TITLE_LOC_Y = 5; - private final static int TITLE_HEIGHT = 190; - private final static int MAIN_PAD_HEIGHT = 9; - private final static int MAIN_LOC_Y = TITLE_LOC_Y + TITLE_HEIGHT + MAIN_PAD_HEIGHT; - private final static int MAIN_HEIGHT = 100; + private final static int TITLE_HEIGHT = 80; + + private final static int ATTRIBUTION_LOC_Y = TITLE_LOC_Y + TITLE_HEIGHT + PANEL_PADDING; + private final static int ATTRIBUTION_HEIGHT = 175; + + private final static int MEMORY_LOC_Y = ATTRIBUTION_LOC_Y + ATTRIBUTION_HEIGHT + PANEL_PADDING; + private final static int MEMORY_HEIGHT = 100; + private final static int BUTTON_PAD_HEIGHT = 11; - private final static int BUTTON_LOC_Y = MAIN_LOC_Y + MAIN_HEIGHT + BUTTON_PAD_HEIGHT; + private final static int BUTTON_LOC_Y = MEMORY_LOC_Y + MEMORY_HEIGHT + BUTTON_PAD_HEIGHT; private final static int BUTTON_HEIGHT = 30; - private final static int BG_WIDTH = 237; + private final static int BG_WIDTH = 357; private final static int BG_HEIGHT = BUTTON_LOC_Y + BUTTON_HEIGHT + 10; - private static final int WIDTH = BG_WIDTH + 3; - private static final int HEIGHT = BG_HEIGHT + 26; - private final JPanel mainPanel; + private static final int WIDTH = BG_WIDTH; + private static final int HEIGHT = BG_HEIGHT + 20; + private final MemoryPanel memoryPanel; - private final JLabel freeMemory; - private final JLabel totalMemory; - private final JLabel usedMemory; - private final JLabel maxMemory; - private final JLabel cacheMemory; - private final JButton gcButton; - private final ImageIcon background; - private final JButton okButton; - + private Thread updateThread; - + private boolean kill = false; - + /** * Construct an about dialog. */ - public AboutDialog() - { + public AboutDialog() { super(applicationFrame, "About", true); this.setSize(WIDTH, HEIGHT); Dimension parentSize = applicationFrame.getSize(); Point parentLoc = applicationFrame.getLocation(); this.setLocation(parentLoc.x + (parentSize.width / 2 - WIDTH / 2), parentLoc.y + (parentSize.height / 2 - HEIGHT / 2)); - + this.setResizable(false); - - background = Icons.honeycomb; - if (background.getIconWidth() != BG_WIDTH || background.getIconHeight() != BG_HEIGHT) - { + + ImageIcon background = Icons.honeycomb; + if (background.getIconWidth() != BG_WIDTH || background.getIconHeight() != BG_HEIGHT) { // resize the background image Image image = background.getImage(); image = image.getScaledInstance(BG_WIDTH, BG_HEIGHT, Image.SCALE_FAST); @@ -91,113 +88,107 @@ public AboutDialog() JPanel bp = new JPanel(new BorderLayout()); bp.add(new JLabel(background), BorderLayout.CENTER); bp.setSize(BG_WIDTH, BG_HEIGHT); - bp.setLocation(0,0); + bp.setLocation(0, 0); + this.getLayeredPane().add(bp); this.getLayeredPane().setLayer(bp, JLayeredPane.DEFAULT_LAYER.intValue()); - - mainPanel = new JPanel(new BorderLayout()); - mainPanel.setBorder(LineBorder.createBlackLineBorder()); - memoryPanel = new MemoryPanel(new GridLayout(5, 2)); - memoryPanel.setBorder(new EmptyBorder(3, 6, 3, 6)); - memoryPanel.add(new JLabel("Free memory: ")); - freeMemory = new JLabel(); - memoryPanel.add(freeMemory); - memoryPanel.add(new JLabel("Total memory: ")); - totalMemory = new JLabel(); - memoryPanel.add(totalMemory); - memoryPanel.add(new JLabel("Used memory: ")); - usedMemory = new JLabel(); - memoryPanel.add(usedMemory); - memoryPanel.add(new JLabel("Max memory: ")); - maxMemory = new JLabel(); - memoryPanel.add(maxMemory); - memoryPanel.add(new JLabel("Cache size: ")); - cacheMemory = new JLabel(); - memoryPanel.add(cacheMemory); - - mainPanel.add(memoryPanel, BorderLayout.CENTER); - mainPanel.setSize(224, MAIN_HEIGHT); - mainPanel.setLocation(5, MAIN_LOC_Y); - memoryPanel.setBackground(new Color(255, 255, 0)); - mainPanel.setBackground(new Color(0, 0, 0, 0)); - this.getLayeredPane().add(mainPanel); - this.getLayeredPane().setLayer(mainPanel, JLayeredPane.PALETTE_LAYER.intValue()); - - JTextPane title = new JTextPane(); - title.setFont(freeMemory.getFont()); // use label font for title - title.setEditable(false); //make title not modifiable - title.setBackground(new Color(255, 255, 0, 210)); - title.setSize(224, TITLE_HEIGHT); - title.setLocation(5, TITLE_LOC_Y); - title.setBorder(LineBorder.createBlackLineBorder()); - title.setContentType("text/html"); - title.setText("
" - + "SWARM:
" - + "Seismic Wave Analysis/
" - + "Real-time Monitoring/
" - + "Version: " + Version.POM_VERSION + "
" - + "
" - + "Funded by:
" - + "USGS http://www.usgs.gov
" - + "IRIS http://www.iris.edu
" - + "ISTI http://www.isti.com
" - + "
" - + "Library versions:
" - + BuildVersion.getName() + " " + BuildVersion.getVersion() + "
" - + SLClient.PROGRAM_NAME + "
" - + "
"); + + JTextPane title = getTitlePane(); this.getLayeredPane().add(title); this.getLayeredPane().setLayer(title, JLayeredPane.PALETTE_LAYER.intValue()); + + JTextPane attribution = getAttributionPane(); + this.getLayeredPane().add(attribution); + this.getLayeredPane().setLayer(attribution, JLayeredPane.PALETTE_LAYER.intValue()); + + + memoryPanel = new MemoryPanel(new GridLayout(5, 2)); + + JPanel memoryContainer = new JPanel(new BorderLayout()); + memoryContainer.setBorder(LineBorder.createBlackLineBorder()); + memoryContainer.add(memoryPanel, BorderLayout.CENTER); + memoryContainer.setSize(PANE_WIDTH, MEMORY_HEIGHT); + memoryContainer.setLocation(5, MEMORY_LOC_Y); + memoryContainer.setBackground(new Color(0, 0, 0, 0)); - okButton = new JButton("OK"); + this.getLayeredPane().add(memoryContainer); + this.getLayeredPane().setLayer(memoryContainer, JLayeredPane.PALETTE_LAYER.intValue()); + + JButton okButton = new JButton("OK"); okButton.setSize(60, BUTTON_HEIGHT); okButton.setLocation(170, BUTTON_LOC_Y); - okButton.addActionListener(new ActionListener() - { - public void actionPerformed(ActionEvent e) - { - dispose(); - kill = true; - } - }); - - gcButton = new JButton("Run GC"); + okButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + dispose(); + kill = true; + } + }); + + JButton gcButton = new JButton("Run GC"); gcButton.setSize(90, BUTTON_HEIGHT); gcButton.setLocation(70, BUTTON_LOC_Y); - gcButton.addActionListener(new ActionListener() - { - public void actionPerformed(ActionEvent e) - { - System.gc(); - // update(); - // no need to update since it will be done by thread - } - }); - + gcButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + System.gc(); + // update(); + // no need to update since it will be done by thread + } + }); + this.getLayeredPane().add(okButton); - this.getLayeredPane().add(gcButton); - + this.getLayeredPane().add(gcButton); + this.getLayeredPane().setLayer(okButton, JLayeredPane.PALETTE_LAYER.intValue()); this.getLayeredPane().setLayer(gcButton, JLayeredPane.PALETTE_LAYER.intValue()); } - - public void setVisible(boolean v) - { + + private JTextPane getTitlePane() { + JTextPane pane = new JTextPane(); + pane.setBorder(new EmptyBorder(3, 6, 3, 6)); + + pane.setEditable(false); // make title not modifiable + pane.setBackground(new Color(255, 255, 0, 210)); + pane.setSize(PANE_WIDTH, TITLE_HEIGHT); + pane.setLocation(5, TITLE_LOC_Y); + pane.setBorder(LineBorder.createBlackLineBorder()); + pane.setContentType("text/html"); + pane.setText("
" + "SWARM:
" + + "Seismic Wave Analysis / " + + "Real-time Monitoring
https://volcanoes.usgs.gov/software/swarm/
" + "Version: " + Version.POM_VERSION + + "
" + + ""); + return pane; + } + + private JTextPane getAttributionPane() { + JTextPane pane = new JTextPane(); + pane.setBorder(new EmptyBorder(3, 6, 3, 6)); + pane.setEditable(false); // make title not modifiable + pane.setBackground(new Color(255, 255, 0, 210)); + pane.setSize(PANE_WIDTH, ATTRIBUTION_HEIGHT); + pane.setLocation(5, ATTRIBUTION_LOC_Y); + pane.setBorder(LineBorder.createBlackLineBorder()); + pane.setContentType("text/html"); + pane.setText("" + "
Developed by:
U.S. Geological Survey" + + "

With contributions from:" + "
Instrumental Software Technologies, Inc. (ISTI)" + + "
The Incorporated Research Institutions for Seismology (IRIS)" + + "

Primary developers:
Dan Cervelli
Tom Parker" + "

"); + return pane; + } + + public void setVisible(boolean v) { updateThread = new Thread(this, "About"); updateThread.start(); super.setVisible(v); } - - public void run() - { - while (!kill) - { - try - { + + public void run() { + while (!kill) { + try { update(); Thread.sleep(500); + } catch (Exception e) { } - catch (Exception e) {} } kill = false; } @@ -205,53 +196,76 @@ public void run() /** * The memory panel overrides the paint method to update the values. */ - private class MemoryPanel extends JPanel - { + private class MemoryPanel extends JPanel { /** serial version UID */ private static final long serialVersionUID = 1L; /** re-use the number format safely on the Event Dispatch Thread. */ private final NumberFormat nf = new DecimalFormat("#.##"); - - public MemoryPanel(LayoutManager layout) - { + + private final JLabel freeMemory; + private final JLabel totalMemory; + private final JLabel usedMemory; + private final JLabel maxMemory; + private final JLabel cacheMemory; + + public MemoryPanel(LayoutManager layout) { super(layout); + setBorder(new EmptyBorder(3, 6, 3, 6)); + add(new JLabel("Free memory: ")); + freeMemory = new JLabel(); + add(freeMemory); + add(new JLabel("Total memory: ")); + totalMemory = new JLabel(); + add(totalMemory); + add(new JLabel("Used memory: ")); + usedMemory = new JLabel(); + add(usedMemory); + add(new JLabel("Max memory: ")); + maxMemory = new JLabel(); + add(maxMemory); + add(new JLabel("Cache size: ")); + cacheMemory = new JLabel(); + add(cacheMemory); + setSize(PANE_WIDTH, MEMORY_HEIGHT); + setLocation(5, MEMORY_LOC_Y); + setBackground(new Color(255, 255, 0)); } /** * Invoked by Swing to draw components. This method should not be called * directly, use the repaint method. * - * @param g the graphics. + * @param g + * the graphics. */ - public void paint(Graphics g) - { + public void paint(Graphics g) { update(); super.paint(g); } - - /** + + /** * Formats a long as a number of bytes. - * @param bytes the number of bytes + * + * @param bytes + * the number of bytes * @return a formatted string */ - private String toByteString(long bytes) - { + private String toByteString(long bytes) { return nf.format(bytes / 1000000.0) + " MB"; } - + /** - * Updates the memory information displayed in the dialog. - * This should only be called on the Event Dispatch Thread. + * Updates the memory information displayed in the dialog. This should + * only be called on the Event Dispatch Thread. */ - private void update() - { + private void update() { Runtime r = Runtime.getRuntime(); freeMemory.setText(toByteString(r.freeMemory())); totalMemory.setText(toByteString(r.totalMemory())); usedMemory.setText(toByteString(r.totalMemory() - r.freeMemory())); maxMemory.setText(toByteString(r.maxMemory())); - + CachedDataSource cache = CachedDataSource.getInstance(); cacheMemory.setText(toByteString(cache.getSize())); } @@ -260,9 +274,8 @@ private void update() /** * Updates the memory information displayed in the dialog. */ - public void update() - { - //this will cause paint to be called on the Event Dispatch Thread + public void update() { + // this will cause paint to be called on the Event Dispatch Thread memoryPanel.repaint(); } } \ No newline at end of file diff --git a/src/main/java/gov/usgs/volcanoes/swarm/ConfigListener.java b/src/main/java/gov/usgs/volcanoes/swarm/ConfigListener.java new file mode 100644 index 00000000..deceaaf1 --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/ConfigListener.java @@ -0,0 +1,5 @@ +package gov.usgs.volcanoes.swarm; + +public interface ConfigListener { + public void settingsChanged(); +} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/FileTypeDialog.java b/src/main/java/gov/usgs/volcanoes/swarm/FileTypeDialog.java index 62e28209..c244b4b2 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/FileTypeDialog.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/FileTypeDialog.java @@ -20,7 +20,7 @@ /* * @author Tom Parker */ -public class FileTypeDialog extends SwarmDialog { +public class FileTypeDialog extends SwarmModalDialog { private static final long serialVersionUID = 1L; private static final JFrame applicationFrame = Swarm.getApplicationFrame(); private JLabel filename; @@ -30,7 +30,7 @@ public class FileTypeDialog extends SwarmDialog { private boolean opened = false; public FileTypeDialog() { - super(applicationFrame, "Select File Type", true); + super(applicationFrame, "Select File Type"); setSizeAndLocation(); } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/HelpDialog.java b/src/main/java/gov/usgs/volcanoes/swarm/HelpDialog.java new file mode 100644 index 00000000..82d701d4 --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/HelpDialog.java @@ -0,0 +1,41 @@ +package gov.usgs.volcanoes.swarm; + +import org.pegdown.PegDownProcessor; + +import java.awt.Component; +import java.awt.Dimension; +import java.util.Scanner; + +import javax.swing.BoxLayout; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextPane; + +public final class HelpDialog { + + private static final PegDownProcessor pegdownProcessor = new PegDownProcessor(); + + public static void displayHelp(Component parent, String helpFile) { + final JPanel helpPanel = new JPanel(); + helpPanel.setLayout(new BoxLayout(helpPanel, BoxLayout.PAGE_AXIS)); + JTextPane htmlPane = new JTextPane(); + + Scanner scanner = new Scanner(HelpDialog.class.getResourceAsStream("/help/" + helpFile), "UTF-8"); + String text = scanner.useDelimiter("\\A").next(); + scanner.close(); + + htmlPane.setContentType("text/html"); + htmlPane.setText(pegdownProcessor.markdownToHtml(text)); + htmlPane.setPreferredSize(new Dimension(500, 600)); + + JScrollPane paneScrollPane = new JScrollPane(htmlPane); + helpPanel.add(paneScrollPane); + + JOptionPane.showMessageDialog(parent, + helpPanel, + "A plain message", + JOptionPane.PLAIN_MESSAGE); + + } +} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/Icons.java b/src/main/java/gov/usgs/volcanoes/swarm/Icons.java index 796d25bc..b29ba831 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/Icons.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/Icons.java @@ -87,6 +87,7 @@ public class Icons { public static final ImageIcon rsam_values = getIcon("images/rsam_values.png"); public static final ImageIcon rsam_counts = getIcon("images/rsam_counts.png"); public static final ImageIcon particle_motion = getIcon("images/bee.png"); + public static final ImageIcon locate = getIcon("images/bee.png"); private static ImageIcon getIcon(String key) { return new ImageIcon(ClassLoader.getSystemResource(key)); diff --git a/src/main/java/gov/usgs/volcanoes/swarm/OptionsDialog.java b/src/main/java/gov/usgs/volcanoes/swarm/OptionsDialog.java index be636781..a09e975c 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/OptionsDialog.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/OptionsDialog.java @@ -1,6 +1,8 @@ package gov.usgs.volcanoes.swarm; import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.Arrays; @@ -19,6 +21,7 @@ import com.jgoodies.forms.builder.DefaultFormBuilder; import com.jgoodies.forms.layout.FormLayout; +import gov.usgs.volcanoes.swarm.map.NationalMapLayer; import gov.usgs.volcanoes.swarm.options.SwarmOptions; /** @@ -26,189 +29,204 @@ * @author Dan Cervelli * @version $Id: OptionsDialog.java,v 1.7 2007-05-21 02:38:41 dcervelli Exp $ */ -public class OptionsDialog extends SwarmDialog { - private static final long serialVersionUID = 1L; - private static final JFrame applicationFrame = Swarm.getApplicationFrame(); - - private JPanel dialogPanel; - - private JCheckBox durationEnabled; - private JTextField durationA; - private JTextField durationB; - private JCheckBox useLargeCursor; - - private JCheckBox tzInstrument; - private JRadioButton tzLocal; - private JRadioButton tzSpecific; - private JComboBox timeZones; - - private JRadioButton useMapPacks; - private JRadioButton useWMS; - private JTextField wmsServer; - private JTextField wmsLayer; - private JTextField wmsStyles; - private JLabel wmsServerLabel; - private JLabel wmsLayerLabel; - private JLabel wmsStylesLabel; - - public OptionsDialog() { - super(applicationFrame, "Options", true); - createUI(); - setCurrentValues(); - setSizeAndLocation(); - } - - private void createFields() { - durationEnabled = new JCheckBox("Enabled"); - durationA = new JTextField(); - durationB = new JTextField(); - useLargeCursor = new JCheckBox("Large Helicorder Cursor"); - tzInstrument = new JCheckBox("Use instrument time zone if available"); - tzLocal = new JRadioButton("Use local machine time zone:"); - tzSpecific = new JRadioButton("Use specific time zone:"); - ButtonGroup tzGroup = new ButtonGroup(); - tzGroup.add(tzLocal); - tzGroup.add(tzSpecific); - String[] tzs = TimeZone.getAvailableIDs(); - Arrays.sort(tzs); - timeZones = new JComboBox(tzs); - - useMapPacks = new JRadioButton("Use local MapPacks"); - useWMS = new JRadioButton("Use WMS"); - ButtonGroup mapGroup = new ButtonGroup(); - mapGroup.add(useMapPacks); - mapGroup.add(useWMS); - wmsLayer = new JTextField(); - wmsServer = new JTextField(); - wmsStyles = new JTextField(); - } - - protected void createUI() { - super.createUI(); - createFields(); - - FormLayout layout = new FormLayout( - "right:max(30dlu;pref), 3dlu, 40dlu, 3dlu, right:max(40dlu;pref), 3dlu, 40dlu", ""); - - DefaultFormBuilder builder = new DefaultFormBuilder(layout); - builder.setDefaultDialogBorder(); - - builder.appendSeparator("Time Zone"); - - builder.append(tzInstrument, 7); - builder.nextLine(); - TimeZone local = TimeZone.getDefault(); - - builder.append(tzLocal, 7); - builder.append(" "); - builder.append(new JLabel(local.getID()), 5); - builder.nextLine(); - - builder.append(tzSpecific, 7); - builder.nextLine(); - - builder.append(" "); - builder.append(timeZones, 5); - builder.nextLine(); - - builder.appendSeparator("Duration Magnitude"); - builder.append(durationEnabled, 7); - builder.nextLine(); - builder.append("Md=", durationA); - builder.append("* Log(t) +", durationB); - - builder.appendSeparator("Maps"); - builder.append(useMapPacks, 7); - builder.nextLine(); - builder.append(useWMS, 7); - builder.nextLine(); - wmsServerLabel = new JLabel("Server:"); - wmsServerLabel.setLabelFor(wmsServer); - builder.append(wmsServerLabel); - builder.append(wmsServer, 5); - builder.nextLine(); - wmsLayerLabel = new JLabel("Layer:"); - wmsLayerLabel.setLabelFor(wmsLayer); - builder.append(wmsLayerLabel); - builder.append(wmsLayer, 5); - builder.nextLine(); - wmsStylesLabel = new JLabel("Styles:"); - wmsStylesLabel.setLabelFor(wmsStyles); - builder.append(wmsStylesLabel); - builder.append(wmsStyles, 5); - builder.nextLine(); - - useMapPacks.addItemListener(new ItemListener() { - public void itemStateChanged(ItemEvent e) { - doEnables(); - } - }); - - builder.appendSeparator("Other"); - builder.append(useLargeCursor, 7); - builder.nextLine(); - - dialogPanel = builder.getPanel(); - mainPanel.add(dialogPanel, BorderLayout.CENTER); - } - - public void doEnables() { - boolean state = useMapPacks.isSelected(); - wmsServer.setEnabled(!state); - wmsLayer.setEnabled(!state); - wmsStyles.setEnabled(!state); - wmsServerLabel.setEnabled(!state); - wmsLayerLabel.setEnabled(!state); - wmsStylesLabel.setEnabled(!state); - } - - public void setCurrentValues() { - useLargeCursor.setSelected(swarmConfig.useLargeCursor); - durationA.setText(Double.toString(swarmConfig.durationA)); - durationB.setText(Double.toString(swarmConfig.durationB)); - durationEnabled.setSelected(swarmConfig.durationEnabled); - tzInstrument.setSelected(swarmConfig.useInstrumentTimeZone); - if (swarmConfig.useLocalTimeZone) - tzLocal.setSelected(true); - else - tzSpecific.setSelected(true); - timeZones.setSelectedItem(swarmConfig.specificTimeZone.getID()); - - useMapPacks.setSelected(!swarmConfig.useWMS); - useWMS.setSelected(swarmConfig.useWMS); - wmsServer.setText(swarmConfig.wmsServer); - wmsLayer.setText(swarmConfig.wmsLayer); - wmsStyles.setText(swarmConfig.wmsStyles); - doEnables(); - } - - public boolean allowOK() { - String message = null; - try { - message = "The duration magnitude constants must be numbers."; - Double.parseDouble(durationA.getText().trim()); - Double.parseDouble(durationB.getText().trim()); - - return true; - } catch (Exception e) { - JOptionPane.showMessageDialog(this, message, "Options Error", JOptionPane.ERROR_MESSAGE); - } - return false; - } - - public void wasOK() { - swarmConfig.useLargeCursor = useLargeCursor.isSelected(); - swarmConfig.durationEnabled = durationEnabled.isSelected(); - swarmConfig.durationA = Double.parseDouble(durationA.getText().trim()); - swarmConfig.durationB = Double.parseDouble(durationB.getText().trim()); - swarmConfig.useInstrumentTimeZone = tzInstrument.isSelected(); - swarmConfig.useLocalTimeZone = tzLocal.isSelected(); - swarmConfig.specificTimeZone = TimeZone.getTimeZone((String) timeZones.getSelectedItem()); - swarmConfig.useWMS = useWMS.isSelected(); - swarmConfig.wmsServer = wmsServer.getText(); - swarmConfig.wmsLayer = wmsLayer.getText(); - swarmConfig.wmsStyles = wmsStyles.getText(); - - SwarmOptions.optionsChanged(); - } +public class OptionsDialog extends SwarmModalDialog { + private static final long serialVersionUID = 1L; + private static final JFrame applicationFrame = Swarm.getApplicationFrame(); + + private JPanel dialogPanel; + + private JCheckBox durationEnabled; + private JTextField durationA; + private JTextField durationB; + private JCheckBox useLargeCursor; + + private JCheckBox tzInstrument; + private JRadioButton tzLocal; + private JRadioButton tzSpecific; + private JComboBox timeZones; + private JComboBox natMapList; + private JRadioButton useMapPacks; + private JRadioButton useWMS; + private JTextField wmsServer; + private JTextField wmsLayer; + private JTextField wmsStyles; + private JLabel wmsServerLabel; + private JLabel wmsLayerLabel; + private JLabel wmsStylesLabel; + + public OptionsDialog() { + super(applicationFrame, "Options"); + createUI(); + setCurrentValues(); + setSizeAndLocation(); + } + + private void createFields() { + durationEnabled = new JCheckBox("Enabled"); + durationA = new JTextField(); + durationB = new JTextField(); + useLargeCursor = new JCheckBox("Large Helicorder Cursor"); + tzInstrument = new JCheckBox("Use instrument time zone if available"); + tzLocal = new JRadioButton("Use local machine time zone:"); + tzSpecific = new JRadioButton("Use specific time zone:"); + ButtonGroup tzGroup = new ButtonGroup(); + tzGroup.add(tzLocal); + tzGroup.add(tzSpecific); + String[] tzs = TimeZone.getAvailableIDs(); + Arrays.sort(tzs); + timeZones = new JComboBox(tzs); + + useMapPacks = new JRadioButton("Use local MapPacks"); + useWMS = new JRadioButton("Use WMS"); + ButtonGroup mapGroup = new ButtonGroup(); + mapGroup.add(useMapPacks); + mapGroup.add(useWMS); + + + natMapList = new JComboBox(NationalMapLayer.values()); + wmsLayer = new JTextField(); + wmsServer = new JTextField(); + wmsStyles = new JTextField(); + } + + protected void createUI() { + super.createUI(); + createFields(); + + FormLayout layout = new FormLayout( + "right:max(30dlu;pref), 3dlu, 40dlu, 3dlu, right:max(40dlu;pref), 3dlu, 40dlu", ""); + + DefaultFormBuilder builder = new DefaultFormBuilder(layout); + builder.setDefaultDialogBorder(); + + builder.appendSeparator("Time Zone"); + + builder.append(tzInstrument, 7); + builder.nextLine(); + TimeZone local = TimeZone.getDefault(); + + builder.append(tzLocal, 7); + builder.append(" "); + builder.append(new JLabel(local.getID()), 5); + builder.nextLine(); + + builder.append(tzSpecific, 7); + builder.nextLine(); + + builder.append(" "); + builder.append(timeZones, 5); + builder.nextLine(); + + builder.appendSeparator("Duration Magnitude"); + builder.append(durationEnabled, 7); + builder.nextLine(); + builder.append("Md=", durationA); + builder.append("* Log(t) +", durationB); + + builder.appendSeparator("Maps"); + builder.append(useMapPacks, 7); + builder.nextLine(); + builder.append(useWMS, 7); + builder.nextLine(); + builder.append("USGS National Map:"); + builder.append(natMapList, 5); + builder.nextLine(); + wmsServerLabel = new JLabel("Server:"); + wmsServerLabel.setLabelFor(wmsServer); + builder.append(wmsServerLabel); + builder.append(wmsServer, 5); + builder.nextLine(); + wmsLayerLabel = new JLabel("Layer:"); + wmsLayerLabel.setLabelFor(wmsLayer); + builder.append(wmsLayerLabel); + builder.append(wmsLayer, 5); + builder.nextLine(); + wmsStylesLabel = new JLabel("Styles:"); + wmsStylesLabel.setLabelFor(wmsStyles); + builder.append(wmsStylesLabel); + builder.append(wmsStyles, 5); + builder.nextLine(); + + useMapPacks.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + doEnables(); + } + }); + + natMapList.addActionListener (new ActionListener () { + public void actionPerformed(ActionEvent e) { + NationalMapLayer layer = (NationalMapLayer) ((JComboBox) e.getSource()).getSelectedItem(); + wmsServer.setText(layer.server); + wmsLayer.setText(layer.layer); + wmsStyles.setText(layer.sytels); + } + }); + builder.appendSeparator("Other"); + builder.append(useLargeCursor, 7); + builder.nextLine(); + + dialogPanel = builder.getPanel(); + mainPanel.add(dialogPanel, BorderLayout.CENTER); + } + + public void doEnables() { + boolean state = useMapPacks.isSelected(); + wmsServer.setEnabled(!state); + wmsLayer.setEnabled(!state); + wmsStyles.setEnabled(!state); + wmsServerLabel.setEnabled(!state); + wmsLayerLabel.setEnabled(!state); + wmsStylesLabel.setEnabled(!state); + natMapList.setEnabled(!state); + } + + public void setCurrentValues() { + useLargeCursor.setSelected(swarmConfig.useLargeCursor); + durationA.setText(Double.toString(swarmConfig.durationA)); + durationB.setText(Double.toString(swarmConfig.durationB)); + durationEnabled.setSelected(swarmConfig.durationEnabled); + tzInstrument.setSelected(swarmConfig.useInstrumentTimeZone); + if (swarmConfig.useLocalTimeZone) + tzLocal.setSelected(true); + else + tzSpecific.setSelected(true); + timeZones.setSelectedItem(swarmConfig.specificTimeZone.getID()); + + useMapPacks.setSelected(!swarmConfig.useWMS); + useWMS.setSelected(swarmConfig.useWMS); + wmsServer.setText(swarmConfig.wmsServer); + wmsLayer.setText(swarmConfig.wmsLayer); + wmsStyles.setText(swarmConfig.wmsStyles); + doEnables(); + } + + public boolean allowOK() { + String message = null; + try { + message = "The duration magnitude constants must be numbers."; + Double.parseDouble(durationA.getText().trim()); + Double.parseDouble(durationB.getText().trim()); + + return true; + } catch (Exception e) { + JOptionPane.showMessageDialog(this, message, "Options Error", JOptionPane.ERROR_MESSAGE); + } + return false; + } + + public void wasOK() { + swarmConfig.useLargeCursor = useLargeCursor.isSelected(); + swarmConfig.durationEnabled = durationEnabled.isSelected(); + swarmConfig.durationA = Double.parseDouble(durationA.getText().trim()); + swarmConfig.durationB = Double.parseDouble(durationB.getText().trim()); + swarmConfig.useInstrumentTimeZone = tzInstrument.isSelected(); + swarmConfig.useLocalTimeZone = tzLocal.isSelected(); + swarmConfig.specificTimeZone = TimeZone.getTimeZone((String) timeZones.getSelectedItem()); + swarmConfig.useWMS = useWMS.isSelected(); + swarmConfig.wmsServer = wmsServer.getText(); + swarmConfig.wmsLayer = wmsLayer.getText(); + swarmConfig.wmsStyles = wmsStyles.getText(); + + SwarmOptions.optionsChanged(); + } } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/Swarm.java b/src/main/java/gov/usgs/volcanoes/swarm/Swarm.java index c3a18bdf..706f3e22 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/Swarm.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/Swarm.java @@ -1,5 +1,10 @@ package gov.usgs.volcanoes.swarm; +import com.jgoodies.looks.plastic.Plastic3DLookAndFeel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.awt.Dimension; import java.awt.Frame; import java.awt.KeyboardFocusManager; @@ -24,13 +29,9 @@ import javax.swing.SwingUtilities; import javax.swing.UIManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.jgoodies.looks.plastic.Plastic3DLookAndFeel; - import gov.usgs.plot.data.Wave; import gov.usgs.volcanoes.core.configfile.ConfigFile; +import gov.usgs.volcanoes.core.quakeml.Event; import gov.usgs.volcanoes.core.time.CurrentTime; import gov.usgs.volcanoes.core.time.J2kSec; import gov.usgs.volcanoes.core.ui.GlobalKeyManager; @@ -38,12 +39,11 @@ import gov.usgs.volcanoes.swarm.chooser.DataChooser; import gov.usgs.volcanoes.swarm.data.CachedDataSource; import gov.usgs.volcanoes.swarm.data.SeismicDataSource; +import gov.usgs.volcanoes.swarm.event.EventFrame; import gov.usgs.volcanoes.swarm.heli.HelicorderViewerFrame; import gov.usgs.volcanoes.swarm.internalFrame.InternalFrameListener; import gov.usgs.volcanoes.swarm.internalFrame.SwarmInternalFrames; import gov.usgs.volcanoes.swarm.map.MapFrame; -import gov.usgs.volcanoes.swarm.picker.PickerFrame; -import gov.usgs.volcanoes.swarm.picker.PickerWavePanel; import gov.usgs.volcanoes.swarm.rsam.RsamViewerFrame; import gov.usgs.volcanoes.swarm.wave.MultiMonitor; import gov.usgs.volcanoes.swarm.wave.WaveClipboardFrame; @@ -167,8 +167,8 @@ public void actionPerformed(final ActionEvent e) { } }); - m.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0), "fullScreenToggle"); - m.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F12, InputEvent.CTRL_DOWN_MASK), + m.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0), "fullScreenToggle"); + m.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_F11, InputEvent.CTRL_DOWN_MASK), "fullScreenToggle"); m.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SLASH, InputEvent.CTRL_DOWN_MASK), "fullScreenToggle"); @@ -435,7 +435,7 @@ public void setFullScreenMode(final boolean full) { for (final JInternalFrame frame : SwarmInternalFrames.getFrames()) { // Why did this check isVisible()? -// if (frame.isVisible() && frame instanceof Kioskable) { + // if (frame.isVisible() && frame instanceof Kioskable) { if (frame instanceof Kioskable) { final Kioskable f = (Kioskable) frame; f.setKioskMode(full); @@ -576,16 +576,25 @@ public static RsamViewerFrame openRsam(final SeismicDataSource source, final Str return frame; } - public static PickerFrame openPicker(final WaveViewPanel insetWavePanel) { - PickerWavePanel p = new PickerWavePanel(insetWavePanel); - p.setDataSource(insetWavePanel.getDataSource().getCopy()); - PickerFrame pickerFrame = new PickerFrame(); - pickerFrame.setVisible(true); - pickerFrame.requestFocus(); - SwarmInternalFrames.add(pickerFrame); - pickerFrame.setBaseWave(p); - return pickerFrame; - } +// public static PickerFrame openPicker(final WaveViewPanel insetWavePanel) { +// PickerWavePanel p = new PickerWavePanel(insetWavePanel); +// p.setDataSource(insetWavePanel.getDataSource().getCopy()); +// PickerFrame pickerFrame = new PickerFrame(); +// pickerFrame.setVisible(true); +// pickerFrame.requestFocus(); +// SwarmInternalFrames.add(pickerFrame); +// pickerFrame.setBaseWave(p); +// return pickerFrame; +// } + +// public static PickerFrame openPicker(Event event) { +// PickerFrame pickerFrame = new PickerFrame(event); +// pickerFrame.setVisible(true); +// pickerFrame.requestFocus(); +// SwarmInternalFrames.add(pickerFrame); +// return pickerFrame; +// } + public void saveLayout(String name) { final boolean fixedName = (name != null); @@ -595,8 +604,9 @@ public void saveLayout(String name) { if (name == null) { name = (String) JOptionPane.showInputDialog(this, "Enter a name for this layout:", "Save Layout", JOptionPane.INFORMATION_MESSAGE, null, null, lastLayout); + name = name.trim(); } - if (name != null) { + if (name != null && "".equals(name)) { if (Swarm.config.layouts.containsKey(name)) { boolean overwrite = false; if (!fixedName) { @@ -956,4 +966,14 @@ public static void main(final String[] args) { swarm.parseKiosk(); } + public static EventFrame openEvent(final Event event) { + final EventFrame eventFrame = new EventFrame(event); + eventFrame.setVisible(true); + eventFrame.requestFocus(); + SwarmInternalFrames.add(eventFrame); + + return eventFrame; + } + + } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/SwarmConfig.java b/src/main/java/gov/usgs/volcanoes/swarm/SwarmConfig.java index ee376339..4cc0ecfa 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/SwarmConfig.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/SwarmConfig.java @@ -1,8 +1,5 @@ package gov.usgs.volcanoes.swarm; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.awt.Color; import java.io.File; import java.util.ArrayList; @@ -16,21 +13,30 @@ import java.util.TimeZone; import java.util.TreeMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import gov.usgs.plot.map.WMSGeoImageSet; import gov.usgs.volcanoes.core.configfile.ConfigFile; import gov.usgs.volcanoes.core.util.StringUtils; import gov.usgs.volcanoes.swarm.data.DataSourceType; import gov.usgs.volcanoes.swarm.data.SeismicDataSource; +import gov.usgs.volcanoes.swarm.map.hypocenters.HypocenterLayer; +import gov.usgs.volcanoes.swarm.map.hypocenters.HypocenterSource; /** - * Swarm configuration class. - * + * Swarm configuration class. + * + * TODO: This is getting our of hand. Extract configs for individual components. e.g. map + * * @author Dan Cervelli */ public class SwarmConfig { private static final Logger LOGGER = LoggerFactory.getLogger(SwarmConfig.class); + private final List listeners; + private static String[] DEFAULT_SERVERS = new String[] {"AVO Winston;wws:pubavo1.wr.usgs.gov:16022:10000:1" // "IRIS DMC - New @@ -86,6 +92,7 @@ public class SwarmConfig { public int mapWidth; public int mapHeight; public boolean mapMaximized; + private HypocenterSource hypocenterSource; public double mapScale; public double mapLongitude; @@ -111,15 +118,34 @@ public class SwarmConfig { public String wmsLayer; public String wmsStyles; - public String labelSource; public String fdsnDataselectURL; public String fdsnStationURL; - private SwarmConfig() {} + private SwarmConfig() { + listeners = new ArrayList(); + } + public void addListener(ConfigListener configListener) { + listeners.add(configListener); + } + + private void notifyListeners() { + for (ConfigListener listener : listeners) { + listener.settingsChanged(); + } + } public static SwarmConfig getInstance() { return SwarmConfigHolder.swarmConfig; } + + public void setHypocenterSource(HypocenterSource hypocenterSource) { + this.hypocenterSource = hypocenterSource; + notifyListeners(); + } + + public HypocenterSource getHypocenterSource() { + return hypocenterSource; + } public void createConfig(final String[] args) { LOGGER.info("current directory: " + System.getProperty("user.dir")); @@ -261,11 +287,10 @@ public void assignMetadataSource(final Collection channels, } /** - * Sets Swarm configuration variables based on the contents of a ConfigFile; - * sets default values if missing. + * Sets Swarm configuration variables based on the contents of a ConfigFile; sets default values + * if missing. * - * @param config - * the configuration information + * @param config the configuration information */ public void parseConfig(final ConfigFile config) { configFilename = config.getString("configFile"); @@ -341,8 +366,8 @@ public void parseConfig(final ConfigFile config) { StringUtils.stringToString(config.getString("wmsLayer"), WMSGeoImageSet.DEFAULT_LAYER); wmsStyles = StringUtils.stringToString(config.getString("wmsStyles"), WMSGeoImageSet.DEFAULT_STYLE); - - labelSource = StringUtils.stringToString(config.getString("labelSource"), ""); + + hypocenterSource = HypocenterSource.valueOf(StringUtils.stringToString(config.getString("hypocenterSource"), "NONE")); fdsnDataselectURL = StringUtils.stringToString(config.getString("fdsnDataselectURL"), "http://service.iris.edu/fdsnws/dataselect/1/query"); @@ -497,7 +522,7 @@ public ConfigFile toConfigFile() { config.put("wmsLayer", wmsLayer); config.put("wmsStyles", wmsStyles); - config.put("labelSource", labelSource); + config.put("hypocenterSource", hypocenterSource.name()); config.put("fdsnDataselectURL", fdsnDataselectURL); config.put("fdsnStationURL", fdsnStationURL); diff --git a/src/main/java/gov/usgs/volcanoes/swarm/SwarmMenu.java b/src/main/java/gov/usgs/volcanoes/swarm/SwarmMenu.java index 48b7f92e..74bcfd64 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/SwarmMenu.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/SwarmMenu.java @@ -398,13 +398,13 @@ public void actionPerformed(ActionEvent e) { } } - private class RemoveLayoutDialog extends SwarmDialog { + private class RemoveLayoutDialog extends SwarmModalDialog { private static final long serialVersionUID = 1L; private JList layoutList; private DefaultListModel model; protected RemoveLayoutDialog() { - super(Swarm.getApplicationFrame(), "Remove Layouts", true); + super(Swarm.getApplicationFrame(), "Remove Layouts"); setSizeAndLocation(); } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/SwarmDialog.java b/src/main/java/gov/usgs/volcanoes/swarm/SwarmModalDialog.java similarity index 75% rename from src/main/java/gov/usgs/volcanoes/swarm/SwarmDialog.java rename to src/main/java/gov/usgs/volcanoes/swarm/SwarmModalDialog.java index e7bfb860..6cad8952 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/SwarmDialog.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/SwarmModalDialog.java @@ -1,6 +1,8 @@ package gov.usgs.volcanoes.swarm; +import com.jgoodies.forms.builder.ButtonBarBuilder; + import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Point; @@ -10,25 +12,32 @@ import java.awt.event.WindowEvent; import javax.swing.BorderFactory; +import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRootPane; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextPane; import javax.swing.SwingUtilities; +import javax.swing.text.BadLocationException; +import javax.swing.text.StyledDocument; import gov.usgs.volcanoes.core.util.UiUtils; -import com.jgoodies.forms.builder.ButtonBarBuilder; - /** * @author Dan Cervelli */ -public class SwarmDialog extends JDialog { +public class SwarmModalDialog extends JDialog { private static final long serialVersionUID = -1; protected JButton okButton; protected JButton cancelButton; + protected JButton helpButton; + protected JPanel buttonPanel; protected JPanel mainPanel; @@ -39,14 +48,20 @@ public class SwarmDialog extends JDialog { protected static SwarmConfig swarmConfig; protected static final JFrame applicationFrame = Swarm.getApplicationFrame(); + protected String helpFile; - protected SwarmDialog(JFrame parent, String title, boolean modal) { - super(parent, title, modal); + protected SwarmModalDialog(JFrame parent, String title, String helpFile) { + super(parent, title, true); + this.helpFile = helpFile; swarmConfig = SwarmConfig.getInstance(); setResizable(false); this.parent = parent; createUI(); } + + protected SwarmModalDialog(JFrame parent, String title) { + this(parent, title, null); + } protected void setSizeAndLocation() { Dimension d = mainPanel.getPreferredSize(); @@ -70,6 +85,7 @@ public void actionPerformed(ActionEvent e) { } } }); + cancelButton = new JButton("Cancel"); cancelButton.setMnemonic('C'); cancelButton.addActionListener(new ActionListener() { @@ -95,17 +111,30 @@ public void windowClosing(WindowEvent e) { } }); - // buttonPanel = ButtonBarFactory.buildOKCancelBar(okButton, - // cancelButton); ButtonBarBuilder builder = new ButtonBarBuilder(); builder.addGlue(); - builder.addButton(okButton, cancelButton); + + if (helpFile != null) { + helpButton = new JButton("Help"); + helpButton.setMnemonic('H'); + + helpButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + HelpDialog.displayHelp(parent, helpFile); + } + }); + builder.addButton(okButton, cancelButton, helpButton); + } else { + builder.addButton(okButton, cancelButton); + } buttonPanel = builder.getPanel(); buttonPanel.setBorder(BorderFactory.createEmptyBorder(10, 5, 10, 10)); mainPanel.add(buttonPanel, BorderLayout.SOUTH); this.setContentPane(mainPanel); } - + + + protected boolean allowOK() { return true; } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/chooser/EditDataSourceDialog.java b/src/main/java/gov/usgs/volcanoes/swarm/chooser/EditDataSourceDialog.java index 98fb9ccf..471dbedd 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/chooser/EditDataSourceDialog.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/chooser/EditDataSourceDialog.java @@ -17,129 +17,131 @@ import gov.usgs.volcanoes.swarm.Swarm; import gov.usgs.volcanoes.swarm.SwarmConfig; -import gov.usgs.volcanoes.swarm.SwarmDialog; +import gov.usgs.volcanoes.swarm.SwarmModalDialog; /** * * @author Dan Cervelli */ -public class EditDataSourceDialog extends SwarmDialog { - private static final long serialVersionUID = 1L; - private static final JFrame applicationFrame = Swarm.getApplicationFrame(); - - private String source; - private boolean edit; - - private JTextField name; - - private JTabbedPane tabPane; - - private List panels; - - private String result; - - public EditDataSourceDialog(String s) { - super(applicationFrame, "", true); - createPanels(); - source = s; - if (source == null) { - this.setTitle("New Data Source"); - edit = false; - } else { - this.setTitle("Edit Data Source"); - edit = true; - } - createDataSourceUI(); - setSizeAndLocation(); - } - - private void createPanels() { - panels = new ArrayList(); - panels.add(new WWSPanel()); - panels.add(new WaveServerPanel()); - // panels.add(new DHIPanel()); - panels.add(new WebServicesPanel()); - panels.add(new SeedLinkPanel()); - } - - protected void createDataSourceUI() { - JPanel dsPanel = new JPanel(new BorderLayout()); - - String src = null; - if (source != null) - src = source.substring(source.indexOf(';') + 1, source.indexOf(':')); - - tabPane = new JTabbedPane(); - for (DataSourcePanel dsp : panels) { - dsp.setSource(source); - JPanel p = dsp.getPanel(); - tabPane.add(dsp.getName(), p); - if (src != null && src.equals(dsp.getCode())) - tabPane.setSelectedComponent(p); - } - - dsPanel.add(tabPane, BorderLayout.CENTER); - - Box namePanel = new Box(BoxLayout.X_AXIS); - namePanel.add(new JLabel("Data Source Name:")); - namePanel.add(Box.createHorizontalStrut(10)); - String n = ""; - if (source != null) - n = source.substring(0, source.indexOf(';')); - name = new JTextField(30); - namePanel.add(name); - name.setText(n); - dsPanel.add(namePanel, BorderLayout.NORTH); - dsPanel.setBorder(new EmptyBorder(new Insets(10, 10, 10, 10))); - mainPanel.add(dsPanel, BorderLayout.CENTER); - } - - public void resetSource(String src) { - if (src != null) { - source = src; - String s = source.substring(source.indexOf(';') + 1, source.indexOf(':')); - for (DataSourcePanel dsp : panels) { - dsp.resetSource(source); - JPanel p = dsp.getPanel(); - if (s.equals(dsp.getCode())) - tabPane.setSelectedComponent(p); - } - name.setText(source.substring(0, source.indexOf(';'))); - } - } - - protected boolean allowOK() { - String n = name.getText(); - String message = null; - if (n == null || n.length() <= 0) - message = "You must specify a name for this data source."; - else if (!edit && SwarmConfig.getInstance().sourceExists(n)) - message = "A data source by that name already exists."; - - if (message != null) { - JOptionPane.showMessageDialog(applicationFrame, message, "Error", JOptionPane.ERROR_MESSAGE); - return false; - } - - DataSourcePanel p = panels.get(tabPane.getSelectedIndex()); - return p.allowOK(edit); - } - - public void setVisible(boolean b) { - if (b) { - if (source == null) - name.setText(""); - result = null; - } - super.setVisible(b); - } - - protected void wasOK() { - DataSourcePanel p = panels.get(tabPane.getSelectedIndex()); - result = name.getText() + ";" + p.wasOK(); - } - - public String getResult() { - return result; - } +public class EditDataSourceDialog extends SwarmModalDialog { + private static final long serialVersionUID = 1L; + private static final JFrame applicationFrame = Swarm.getApplicationFrame(); + + private String source; + private boolean edit; + + private JTextField name; + + private JTabbedPane tabPane; + + private List panels; + + private String result; + + public EditDataSourceDialog(String s) { + super(applicationFrame, ""); + createPanels(); + source = s; + if (source == null) { + this.setTitle("New Data Source"); + edit = false; + } else { + this.setTitle("Edit Data Source"); + edit = true; + } + createDataSourceUI(); + setSizeAndLocation(); + } + + private void createPanels() { + panels = new ArrayList(); + panels.add(new WWSPanel()); + panels.add(new WaveServerPanel()); + // panels.add(new DHIPanel()); + panels.add(new WebServicesPanel()); + panels.add(new SeedLinkPanel()); + } + + protected void createDataSourceUI() { + JPanel dsPanel = new JPanel(new BorderLayout()); + + String src = null; + if (source != null) + src = source.substring(source.indexOf(';') + 1, source.indexOf(':')); + + tabPane = new JTabbedPane(); + for (DataSourcePanel dsp : panels) { + dsp.setSource(source); + JPanel p = dsp.getPanel(); + tabPane.add(dsp.getName(), p); + if (src != null && src.equals(dsp.getCode())) + tabPane.setSelectedComponent(p); + } + + dsPanel.add(tabPane, BorderLayout.CENTER); + + Box namePanel = new Box(BoxLayout.X_AXIS); + namePanel.add(new JLabel("Data Source Name:")); + namePanel.add(Box.createHorizontalStrut(10)); + String n = ""; + if (source != null) + n = source.substring(0, source.indexOf(';')); + name = new JTextField(30); + namePanel.add(name); + name.setText(n); + dsPanel.add(namePanel, BorderLayout.NORTH); + dsPanel.setBorder(new EmptyBorder(new Insets(10, 10, 10, 10))); + mainPanel.add(dsPanel, BorderLayout.CENTER); + } + + public void resetSource(String src) { + if (src != null) { + source = src; + String s = source.substring(source.indexOf(';') + 1, source.indexOf(':')); + for (DataSourcePanel dsp : panels) { + dsp.resetSource(source); + JPanel p = dsp.getPanel(); + if (s.equals(dsp.getCode())) + tabPane.setSelectedComponent(p); + } + name.setText(source.substring(0, source.indexOf(';'))); + } + } + + protected boolean allowOK() { + String n = name.getText(); + String message = null; + if (n == null || n.length() <= 0) + message = "You must specify a name for this data source."; + else if (!edit && SwarmConfig.getInstance().sourceExists(n)) + message = "A data source by that name already exists."; + else if (n.contains(";")) + message = "Data source name cannot contain ';'"; + + if (message != null) { + JOptionPane.showMessageDialog(applicationFrame, message, "Error", JOptionPane.ERROR_MESSAGE); + return false; + } + + DataSourcePanel p = panels.get(tabPane.getSelectedIndex()); + return p.allowOK(edit); + } + + public void setVisible(boolean b) { + if (b) { + if (source == null) + name.setText(""); + result = null; + } + super.setVisible(b); + } + + protected void wasOK() { + DataSourcePanel p = panels.get(tabPane.getSelectedIndex()); + result = name.getText() + ";" + p.wasOK(); + } + + public String getResult() { + return result; + } } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/data/fdsnWs/AbstractDataRecordClient.java b/src/main/java/gov/usgs/volcanoes/swarm/data/fdsnWs/AbstractDataRecordClient.java index f94e6459..f92ec0ec 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/data/fdsnWs/AbstractDataRecordClient.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/data/fdsnWs/AbstractDataRecordClient.java @@ -43,7 +43,7 @@ public AbstractDataRecordClient(SeismicDataSource source) { * @throws UnsupportedCompressionType * @throws CodecException */ - public List addWaves(final List waves, final DataRecord dr) + public static List addWaves(final List waves, final DataRecord dr) throws UnsupportedCompressionType, CodecException { for (Blockette blockette : dr.getBlockettes(1000)) { if (blockette instanceof Blockette1000) { @@ -67,7 +67,7 @@ public void assignChannels(List channels) { * * @return the list of waves. */ - public List createWaves() { + public static List createWaves() { return new ArrayList(); } @@ -84,7 +84,7 @@ public List createWaves() { * @param t the time. * @return the date. */ - protected Date getDate(double t) { + protected static Date getDate(double t) { return J2kSec.asDate(t); } @@ -94,7 +94,7 @@ protected Date getDate(double t) { * @param date the date. * @return the text. */ - protected String getDateText(Date date) { + protected static String getDateText(Date date) { return WebServiceUtils.getDateText(date); } @@ -104,7 +104,7 @@ protected String getDateText(Date date) { * @param date the date. * @return the text. */ - protected String getDateText(double t) { + protected static String getDateText(double t) { return getDateText(getDate(t)); } @@ -133,7 +133,7 @@ public SeismicDataSource getSource() { * @param waves the list of Wave s * @return the new joined wave */ - public Wave join(List waves) { + public static Wave join(List waves) { // TODO ensure index is good and no gaps? return Wave.join(waves); } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/data/DataSelectReader.java b/src/main/java/gov/usgs/volcanoes/swarm/data/fdsnWs/DataSelectReader.java similarity index 98% rename from src/main/java/gov/usgs/volcanoes/swarm/data/DataSelectReader.java rename to src/main/java/gov/usgs/volcanoes/swarm/data/fdsnWs/DataSelectReader.java index a84e7196..4dc32006 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/data/DataSelectReader.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/data/fdsnWs/DataSelectReader.java @@ -1,4 +1,4 @@ -package gov.usgs.volcanoes.swarm.data; +package gov.usgs.volcanoes.swarm.data.fdsnWs; import edu.sc.seis.seisFile.SeisFileException; import edu.sc.seis.seisFile.StringMSeedQueryReader; @@ -23,7 +23,6 @@ import gov.usgs.volcanoes.swarm.Swarm; import gov.usgs.volcanoes.swarm.Version; -import gov.usgs.volcanoes.swarm.data.fdsnWs.WebServiceUtils; /** * The data select reader reads data from the web services. This class a based diff --git a/src/main/java/gov/usgs/volcanoes/swarm/data/fdsnWs/WebServicesClient.java b/src/main/java/gov/usgs/volcanoes/swarm/data/fdsnWs/WebServicesClient.java index fdeae728..a0cdf8f5 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/data/fdsnWs/WebServicesClient.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/data/fdsnWs/WebServicesClient.java @@ -1,16 +1,16 @@ package gov.usgs.volcanoes.swarm.data.fdsnWs; -import edu.sc.seis.seisFile.mseed.DataRecord; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.Date; -import java.util.List; - +import edu.sc.seis.seisFile.mseed.DataRecord; import gov.usgs.plot.data.Wave; import gov.usgs.volcanoes.swarm.ChannelInfo; -import gov.usgs.volcanoes.swarm.data.DataSelectReader; +import gov.usgs.volcanoes.swarm.SwarmConfig; import gov.usgs.volcanoes.swarm.data.SeismicDataSource; public class WebServicesClient extends AbstractDataRecordClient { @@ -223,4 +223,52 @@ public boolean processRecord(DataRecord dr) { } return wave; } + + /** + * Retrieve a single waveform without providing full SDS functions. + * + * TODO: integrate with getRawData(). should all SDSs have a static wave retrieval method? + * + * @param channelInfo the channel information. + * @param t1 the start time. + * @param t2 the end time. + * @return the raw data. + */ + public static Wave getWave(final String code, final double t1, final double t2) { + final Date begin = getDate(t1); + final Date end = getDate(t2); + final List waves = new ArrayList(); + final DataSelectReader reader = + new DataSelectReader(SwarmConfig.getInstance().fdsnDataselectURL) { + /** + * Process a data record. + * + * @param dr the data record. + * @return true if data record should be added to the list, false + * otherwise. + */ + public boolean processRecord(DataRecord dr) { + try { + addWaves(waves, dr); + } catch (Exception ex) { + LOGGER.warn("could not get web service raw data ({}): {}", code, ex.getMessage()); + } + return true; + } + }; + try { + String[] comps = code.split("\\$"); + final String query = reader.createQuery(comps[2], comps[0], (comps.length > 3 ? comps[3] : "--"), comps[1], begin, end); + reader.read(query, (List) null); + } catch (Exception ex) { + LOGGER.warn("could not get web service raw data ({}): {}", code, ex.getMessage()); + } + Wave wave = join(waves); + if (wave != null && WebServiceUtils.isDebug()) { + LOGGER.debug("web service raw data ({}, {})", getDateText(wave.getStartTime()), + getDateText(wave.getEndTime()) + ")"); + } + return wave; + } + } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/data/fdsnWs/WebServicesSource.java b/src/main/java/gov/usgs/volcanoes/swarm/data/fdsnWs/WebServicesSource.java index 6083a660..eae0a0f3 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/data/fdsnWs/WebServicesSource.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/data/fdsnWs/WebServicesSource.java @@ -32,28 +32,30 @@ public class WebServicesSource extends SeismicDataSource { public static final String PARAM_FMT_TEXT = "%s|%s|%s|%s|%d|%d|%s|%s"; /** instance counter */ private static int counter = 0; - /** The channel. */ - private String chan; - /** Web Services Client. */ - private WebServicesClient client; /** instance count */ private final int count = ++counter; + /** Web Services Client. */ + private WebServicesClient client; + /** The colon separated parameters. */ + private String params; /** The gulp delay. */ private int gulpDelay; /** The gulp size. */ private int gulpSize; - /** The location. */ - private String loc; - /** The network. */ - private String net; - /** The colon separated parameters. */ - private String params; - /** The station. */ - private String sta; - /** Web Services dataselect URL. */ - private String wsDataSelectUrl; - /** Web Services station URL. */ - private String wsStationUrl; + // /** The channel. */ + // private String chan; + // /** The location. */ + // private String loc; + // /** The network. */ + // private String net; + // /** The station. */ + // private String sta; + // /** Web Services dataselect URL. */ + // private String wsDataSelectUrl; + // /** Web Services station URL. */ + // private String wsStationUrl; + + private String configString; static { typeString = DataSourceType.getShortName(WebServicesSource.class); @@ -65,14 +67,16 @@ public void parse(String params) { this.params = params; String[] ss = params.split(PARAM_SPLIT_TEXT); int ssIndex = 0; - net = ss[ssIndex++]; - sta = ss[ssIndex++]; - loc = ss[ssIndex++]; - chan = ss[ssIndex++]; + String net = ss[ssIndex++]; + String sta = ss[ssIndex++]; + String loc = ss[ssIndex++]; + String chan = ss[ssIndex++]; gulpSize = Integer.parseInt(ss[ssIndex++]); gulpDelay = Integer.parseInt(ss[ssIndex++]); - wsDataSelectUrl = ss[ssIndex++]; - wsStationUrl = ss[ssIndex++]; + String wsDataSelectUrl = ss[ssIndex++]; + String wsStationUrl = ss[ssIndex++]; + configString = String.format("%s;%s:" + PARAM_FMT_TEXT, name, typeString, net, sta, loc, chan, + gulpSize, gulpDelay, wsDataSelectUrl, wsStationUrl); client = new WebServicesClient(this, net, sta, loc, chan, wsDataSelectUrl, wsStationUrl); LOGGER.debug("web service started {}", count); } @@ -209,7 +213,6 @@ public synchronized void notifyDataNotNeeded(String station, double t1, double t * @return the configuration string. */ public String toConfigString() { - return String.format("%s;%s:" + PARAM_FMT_TEXT, name, typeString, net, sta, loc, chan, gulpSize, - gulpDelay, wsDataSelectUrl, wsStationUrl); + return configString; } } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/data/seedLink/SeedLinkClient.java b/src/main/java/gov/usgs/volcanoes/swarm/data/seedLink/SeedLinkClient.java index 6d69e5c8..c891b269 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/data/seedLink/SeedLinkClient.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/data/seedLink/SeedLinkClient.java @@ -561,7 +561,7 @@ protected void runAndWait() { try { - threadSyncObj.wait(); + threadSyncObj.wait(); } catch (InterruptedException ex) { diff --git a/src/main/java/gov/usgs/volcanoes/swarm/event/EventFrame.java b/src/main/java/gov/usgs/volcanoes/swarm/event/EventFrame.java new file mode 100644 index 00000000..5b7dae59 --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/event/EventFrame.java @@ -0,0 +1,250 @@ +/** + * I waive copyright and related rights in the this work worldwide + * through the CC0 1.0 Universal public domain dedication. + * https://creativecommons.org/publicdomain/zero/1.0/legalcode + */ + +package gov.usgs.volcanoes.swarm.event; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.io.IOException; +import java.util.TreeSet; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JViewport; +import javax.swing.ScrollPaneConstants; +import javax.swing.WindowConstants; +import javax.swing.event.InternalFrameAdapter; +import javax.swing.event.InternalFrameEvent; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import gov.usgs.volcanoes.core.CodeTimer; +import gov.usgs.volcanoes.core.quakeml.Arrival; +import gov.usgs.volcanoes.core.quakeml.Event; +import gov.usgs.volcanoes.core.quakeml.EventObserver; +import gov.usgs.volcanoes.core.quakeml.Origin; +import gov.usgs.volcanoes.core.quakeml.Pick; +import gov.usgs.volcanoes.core.time.J2kSec; +import gov.usgs.volcanoes.swarm.Icons; +import gov.usgs.volcanoes.swarm.SwarmFrame; +import gov.usgs.volcanoes.swarm.SwingWorker; +import gov.usgs.volcanoes.swarm.internalFrame.SwarmInternalFrames; + +/** + * The picker internal frame. Adapted from the WaveClipboardFrame. + * + * @author Tom Parker + */ +public class EventFrame extends SwarmFrame implements EventObserver { + private static final Logger LOGGER = LoggerFactory.getLogger(EventFrame.class); + private static final String EVENT_URL = "http://earthquake.usgs.gov/fdsnws/event/1/query?eventid="; + + /** default */ + public static final long serialVersionUID = -1; + + + private final PickBox pickBox; + private final JLabel statusLabel; + private final Event event; + private final JSplitPane mainPanel; + private final PickToolBar toolbar; + + private boolean closing = false; + + /** + * Constructor. + * + * @param event Event to display + */ + public EventFrame(Event event) { + super("Event - " + event.getEventSource() + event.getEvid(), true, true, true, false); + this.event = event; + event.addObserver(this); + + statusLabel = new JLabel(" "); + pickBox = new PickBox(statusLabel); + pickBox.setLayout(new BoxLayout(pickBox, BoxLayout.PAGE_AXIS)); + + toolbar = new PickToolBar(pickBox); + + mainPanel = + new JSplitPane(JSplitPane.VERTICAL_SPLIT, ParameterPanel.create(event), createPickPanel()); + mainPanel.setOneTouchExpandable(true); + mainPanel.setDividerLocation(0.25); + + setContentPane(mainPanel); + setFrameIcon(Icons.ruler); + setSize(swarmConfig.clipboardWidth, swarmConfig.clipboardHeight); + setLocation(swarmConfig.clipboardX, swarmConfig.clipboardY); + setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + + createListeners(); + + this.setVisible(true); + this.setFocusable(true); + updateEvent(); + } + + private JPanel createPickPanel() { + JPanel pickPanel = new JPanel(); + pickPanel.setLayout(new BoxLayout(pickPanel, BoxLayout.PAGE_AXIS)); + + pickPanel.add(toolbar); + pickBox.addListener(toolbar); + + final JScrollPane scrollPane = new JScrollPane(pickBox); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + scrollPane.getVerticalScrollBar().setUnitIncrement(40); + + JViewport viewPort = scrollPane.getViewport(); + viewPort.setScrollMode(JViewport.BLIT_SCROLL_MODE); + + pickPanel.add(scrollPane); + + JPanel statusPanel = new JPanel(); + statusPanel.setLayout(new BorderLayout()); + statusLabel.setBorder(BorderFactory.createEtchedBorder()); + statusPanel.add(statusLabel); + statusPanel + .setMaximumSize(new Dimension(Integer.MAX_VALUE, statusPanel.getPreferredSize().height)); + pickPanel.add(statusPanel); + + return pickPanel; + } + + private void createListeners() { + this.addInternalFrameListener(new InternalFrameAdapter() { + @Override + public void internalFrameActivated(final InternalFrameEvent e) {} + + @Override + public void internalFrameDeiconified(final InternalFrameEvent e) {} + + @Override + public void internalFrameClosing(final InternalFrameEvent e) { + closing = true; + dispose(); + SwarmInternalFrames.remove(EventFrame.this); + } + + @Override + public void internalFrameClosed(final InternalFrameEvent e) {} + }); + } + + private void updateEvent() { + new SwingWorker() { + @Override + public Object construct() { + toolbar.incrementThrobber(); + CodeTimer timer = new CodeTimer("Detailed event"); + fetchDetailedEvent(); + timer.stopAndReport(); + populatePicks(); + return null; + } + + @Override + public void finished() { + toolbar.decrementThrobber(); + repaint(); + } + }.start(); + } + + + public void fetchDetailedEvent() { + String neicEvid = event.getEventSource() + event.getEvid(); + Event workingEvent = event; + + String url = EVENT_URL + neicEvid; + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = null; + Document doc = null; + + try { + dBuilder = dbFactory.newDocumentBuilder(); + doc = dBuilder.parse(url); + doc.getDocumentElement().normalize(); + + NodeList eventElements = doc.getElementsByTagName("event"); + Element eventElement = (Element) eventElements.item(0); + + // NEIC has better descriptions + NodeList descriptionNodes = eventElement.getElementsByTagName("description"); + for (int idx = 0; idx < descriptionNodes.getLength(); idx++) { + eventElement.removeChild(descriptionNodes.item(idx)); + } + + workingEvent.updateEvent(eventElement); + } catch (SAXException e) { + LOGGER.warn("Unable to redtieve detailed event description. ({})", e.getLocalizedMessage()); + } catch (IOException e) { + LOGGER.warn("Unable to redtieve detailed event description. ({})", e.getLocalizedMessage()); + } catch (ParserConfigurationException e) { + LOGGER.warn("Unable to redtieve detailed event description. ({})", e.getLocalizedMessage()); + } + } + + private void populatePicks() { + toolbar.incrementThrobber(); + + Origin origin = event.getPreferredOrigin(); + + long firstPick = Long.MAX_VALUE; + long lastPick = Long.MIN_VALUE; + for (Arrival arrival : origin.getArrivals()) { + Pick pick = arrival.getPick(); + firstPick = Math.min(pick.getTime(), firstPick); + lastPick = Math.max(pick.getTime(), lastPick); + } + + double waveStart = J2kSec.fromEpoch(firstPick) - 1; + double waveEnd = J2kSec.fromEpoch(lastPick) + 1; + + pickBox.setStart(waveStart); + pickBox.setEnd(waveEnd); + + TreeSet arrivals = new TreeSet(Arrival.distanceComparator()); + arrivals.addAll(origin.getArrivals()); + for (Arrival arrival : arrivals) { + if (closing) { + break; + } + pickBox.addPick(arrival); + mainPanel.validate(); + } + + toolbar.decrementThrobber(); + } + + @Override + public void setVisible(final boolean isVisible) { + super.setVisible(isVisible); + if (isVisible) + toFront(); + } + + public void eventUpdated() { + int loc = mainPanel.getDividerLocation(); + mainPanel.setTopComponent(ParameterPanel.create(event)); + mainPanel.setBottomComponent(createPickPanel()); + mainPanel.setDividerLocation(loc); + } +} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/event/Networks.java b/src/main/java/gov/usgs/volcanoes/swarm/event/Networks.java new file mode 100644 index 00000000..a32d6405 --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/event/Networks.java @@ -0,0 +1,127 @@ +/** + * I waive copyright and related rights in the this work worldwide + * through the CC0 1.0 Universal public domain dedication. + * https://creativecommons.org/publicdomain/zero/1.0/legalcode + */ + +package gov.usgs.volcanoes.swarm.event; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +/** + * Translate FDSN network codes into organizational names. + * + * @author Tom Parker + * + */ +public class Networks { + private final static Logger LOGGER = LoggerFactory.getLogger(Networks.class); + + private final static String NETWORKS_FILE = "networks.csv"; + private final Map networks; + + public String getName(String code) { + return networks.get(code); + } + + private Networks() { + networks = new HashMap(); + Reader reader = null; + try { + reader = new InputStreamReader(ClassLoader.getSystemResource(NETWORKS_FILE).openStream()); + + try { + List fields = Networks.parseLine(reader); + while (fields != null) { + networks.put(fields.get(0), fields.get(2)); + fields = Networks.parseLine(reader); + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } catch (IOException e) { + LOGGER.info("Unable to read networks", e); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException ignored) { + } + } + } + + } + + public static Networks getInstance() { + return NetworksHolder.networks; + } + + private static class NetworksHolder { + public static Networks networks = new Networks(); + } + + /** + * Adapted from https://agiletribe.wordpress.com/2012/11/23/the-only-class-you-need-for-csv-files/ + * + * @param r + * @return + * @throws Exception + */ + public static List parseLine(Reader r) throws Exception { + int ch = r.read(); + while (ch == '\r') { + // ignore linefeed chars wherever, particularly just before end of file + ch = r.read(); + } + if (ch < 0) { + return null; + } + Vector store = new Vector(); + StringBuffer curVal = new StringBuffer(); + boolean inquotes = false; + boolean started = false; + while (ch >= 0) { + if (inquotes) { + started = true; + if (ch == '\"') { + inquotes = false; + } else { + curVal.append((char) ch); + } + } else { + if (ch == '\"') { + inquotes = true; + if (started) { + // if this is the second quote in a value, add a quote + // this is for the double quote in the middle of a value + curVal.append('\"'); + } + } else if (ch == ',') { + store.add(curVal.toString()); + curVal = new StringBuffer(); + started = false; + } else if (ch == '\r') { + // ignore LF characters + } else if (ch == '\n') { + // end of a line, break out + break; + } else { + curVal.append((char) ch); + } + } + ch = r.read(); + } + store.add(curVal.toString()); + return store; + } +} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/event/ParameterPanel.java b/src/main/java/gov/usgs/volcanoes/swarm/event/ParameterPanel.java new file mode 100644 index 00000000..8f5269d1 --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/event/ParameterPanel.java @@ -0,0 +1,193 @@ +/** + * I waive copyright and related rights in the this work worldwide + * through the CC0 1.0 Universal public domain dedication. + * https://creativecommons.org/publicdomain/zero/1.0/legalcode + */ + +package gov.usgs.volcanoes.swarm.event; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.Component; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.text.SimpleDateFormat; +import java.util.TimeZone; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingConstants; + +import gov.usgs.volcanoes.core.quakeml.Event; +import gov.usgs.volcanoes.core.quakeml.Magnitude; +import gov.usgs.volcanoes.core.quakeml.Origin; + +public class ParameterPanel { + private static final Logger LOGGER = LoggerFactory.getLogger(ParameterPanel.class); + private static final Font KEY_FONT = Font.decode("dialog-BOLD-12"); + private static final Font VALUE_FONT = Font.decode("dialog-12"); + + private ParameterPanel() {} + + public static Component create(Event event) { + Origin origin = event.getPreferredOrigin(); + Magnitude magnitude = event.getPerferredMagnitude(); + + JPanel parameterPanel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + if (origin == null) { + LOGGER.error("Cannot find perferred origin."); + return parameterPanel; + } + + + c.fill = GridBagConstraints.NONE; + c.anchor = GridBagConstraints.NORTHWEST; + c.gridy = 0; + c.gridx = GridBagConstraints.RELATIVE; + c.ipadx = 3; + c.ipady = 2; + + addKey("Event source: ", parameterPanel, c); + + String source = Networks.getInstance().getName(event.getEventSource().toUpperCase()); + if (source == null) { + source = event.getEventSource(); + } + addValue(source, parameterPanel, c); + + c.gridy++; + addKey("Description: ", parameterPanel, c); + + // wrap description to support multi-line descriptions + String description = event.getDescription(); + description = description.replace("\n", "
"); + description = "" + description + ""; + addValue(description, parameterPanel, c); + + c.gridy++; + addKey("Origin date: ", parameterPanel, c); + + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z"); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + String date = dateFormat.format(event.getPreferredOrigin().getTime()); + addValue(date, parameterPanel, c); + + c.gridy++; + addKey("Type: ", parameterPanel, c); + + if (origin != null) { + addValue(event.getTypeDescription(), parameterPanel, c); + } + + c.gridy++; + addKey("Hypocenter: ", parameterPanel, c); + + String loc = origin.getLatitude() + ", " + origin.getLongitude(); + loc += " at " + (origin.getDepth() / 1000) + " km depth"; + addValue(loc, parameterPanel, c); + + c.gridy++; + addKey("Error (RMS): ", parameterPanel, c); + + double error = origin.getStandardError(); + if (!Double.isNaN(error)) { + addValue(Double.toString(error), parameterPanel, c); + } + + c.gridy++; + addKey("Azimuthal gap: ", parameterPanel, c); + + double gap = origin.getAzimuthalGap(); + if (!Double.isNaN(gap)) { + addValue(gap + "\u00B0", parameterPanel, c); + } + + c.gridy++; + addKey("Nearest station: ", parameterPanel, c); + + double distance = origin.getMinimumDistance(); + if (!Double.isNaN(distance)) { + String tag = distance + "\u00B0" + " \u2248" + String.format("%.2f", Math.toRadians(distance * 6371)) + " km"; + addValue(tag, parameterPanel, c); + } + + c.gridy++; + addKey("Phase count: ", parameterPanel, c); + + int phaseCount = origin.getPhaseCount(); + if (phaseCount > 0) { + addValue(Integer.toString(phaseCount), parameterPanel, c); + } + + c.gridy++; + addKey("Magnitude: ", parameterPanel, c); + + if (magnitude != null) { + String mag = String.format("%s %s", magnitude.getMag(), magnitude.getType()); + String uncertaintly = magnitude.getUncertainty(); + if (uncertaintly != null) { + mag += " (" + uncertaintly + ")"; + } + addValue(mag, parameterPanel, c); + } + + c.gridy++; + addKey("Evalutation: ", parameterPanel, c); + + String evaluationTag = ""; + Origin.EvaluationMode evaluationMode = origin.getEvaluationMode(); + if (evaluationMode != null) { + evaluationTag += evaluationMode.toString().toLowerCase(); + } + + Origin.EvaluationStatus evaluationStatus = origin.getEvaluationStatus(); + if (evaluationStatus != null) { + if (evaluationTag.length() > 0) { + evaluationTag += " / "; + } + evaluationTag += evaluationStatus.toString().toLowerCase(); + } + addValue(evaluationTag, parameterPanel, c); + + + c.gridy++; + addKey("Event id: ", parameterPanel, c); + addValue(event.getEvid(), parameterPanel, c); + + c.weighty = 1; + c.weightx = 1; + c.gridy++; + c.gridx = 10; + JPanel filler = new JPanel(); + parameterPanel.add(filler, c); + + final JScrollPane scrollPane = new JScrollPane(parameterPanel); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + scrollPane.getVerticalScrollBar().setUnitIncrement(40); + + return scrollPane; + } + + private static void addKey(String labelS, JPanel parameterPanel, GridBagConstraints c) { + addLabel(labelS, KEY_FONT, parameterPanel, c); + } + + private static void addValue(String labelS, JPanel parameterPanel, GridBagConstraints c) { + addLabel(labelS, VALUE_FONT, parameterPanel, c); + } + + private static void addLabel(String labelS, Font font, JPanel parameterPanel, + GridBagConstraints c) { + JLabel label = new JLabel(labelS, SwingConstants.LEFT); + label.setFont(font); + parameterPanel.add(label, c); + + } + +} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/event/PickBox.java b/src/main/java/gov/usgs/volcanoes/swarm/event/PickBox.java new file mode 100644 index 00000000..bf209e5b --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/event/PickBox.java @@ -0,0 +1,561 @@ +/** + * I waive copyright and related rights in the this work worldwide + * through the CC0 1.0 Universal public domain dedication. + * https://creativecommons.org/publicdomain/zero/1.0/legalcode + */ + +package gov.usgs.volcanoes.swarm.event; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.MouseEvent; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileOutputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; +import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.CopyOnWriteArrayList; + +import javax.swing.AbstractButton; +import javax.swing.BoxLayout; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.Scrollable; + +import gov.usgs.plot.data.Wave; +import gov.usgs.volcanoes.core.CodeTimer; +import gov.usgs.volcanoes.core.contrib.PngEncoder; +import gov.usgs.volcanoes.core.contrib.PngEncoderB; +import gov.usgs.volcanoes.core.quakeml.Arrival; +import gov.usgs.volcanoes.core.quakeml.Pick; +import gov.usgs.volcanoes.core.time.J2kSec; +import gov.usgs.volcanoes.core.util.UiUtils; +import gov.usgs.volcanoes.swarm.FileChooser; +import gov.usgs.volcanoes.swarm.Swarm; +import gov.usgs.volcanoes.swarm.SwarmConfig; +import gov.usgs.volcanoes.swarm.SwingWorker; +import gov.usgs.volcanoes.swarm.data.CachedDataSource; +import gov.usgs.volcanoes.swarm.data.SeismicDataSource; +import gov.usgs.volcanoes.swarm.data.fdsnWs.WebServicesSource; +import gov.usgs.volcanoes.swarm.wave.AbstractWavePanel; +import gov.usgs.volcanoes.swarm.wave.WaveViewPanelAdapter; +import gov.usgs.volcanoes.swarm.wave.WaveViewPanelListener; +import gov.usgs.volcanoes.swarm.wave.WaveViewSettings; +import gov.usgs.volcanoes.swarm.wave.WaveViewSettings.ViewType; +import gov.usgs.volcanoes.swarm.wave.WaveViewSettingsDialog; + +/** + * A panel to display picks for a single event. + * + * @author Tom Parker + * + */ +public class PickBox extends JPanel implements Scrollable, PickToolBarListener { + private static final long serialVersionUID = 1L; + private static final Logger LOGGER = LoggerFactory.getLogger(PickBox.class); + private static final int DEFAULT_WAVE_PANEL_HEIGHT = 150; + private static final Color SELECT_COLOR = new Color(200, 220, 241); + private static final Color BACKGROUND_COLOR = new Color(0xf7, 0xf7, 0xf7); + private static final String DATASELECT_URL = "http://service.iris.edu/fdsnws/dataselect/1/query"; + private static final String STATION_URL = "http://service.iris.edu/fdsnws/station/1/query"; + private static final JFrame APPLICATION_FRAME = Swarm.getApplicationFrame(); + private static final SwarmConfig SWARM_CONFIG = SwarmConfig.getInstance(); + + private final Map> histories; + private final Set selectedSet; + private final List panels; + + private BufferedImage image; + private double startJ2k; + private double endJ2k; + private final JLabel statusLabel; + private WaveViewPanelListener selectListener; + private final Map seismicSources; + private int wavePanelHeight; + private int lastClickedIndex; + private final Set listeners; + private JLabel emptyArrivalLabel; + + public PickBox(JLabel statusLabel) { + this.statusLabel = statusLabel; + panels = new CopyOnWriteArrayList(); + + seismicSources = new HashMap(); + histories = new HashMap>(); + listeners = new HashSet(); + selectedSet = new ConcurrentSkipListSet(); + wavePanelHeight = DEFAULT_WAVE_PANEL_HEIGHT; + setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); + emptyArrivalLabel = new JLabel("No arrivals available."); + add(emptyArrivalLabel); + addComponentListener(new ComponentAdapter() { + @Override + public void componentMoved(final ComponentEvent e) { + resizeWaves(); + } + + @Override + public void componentResized(final ComponentEvent e) { + createImage(); + resizeWaves(); + } + }); + + selectListener = new WaveViewPanelAdapter() { + public void mousePressed(final AbstractWavePanel src, final MouseEvent e, + final boolean dragging) { + PickWavePanel panel = (PickWavePanel) src; + requestFocusInWindow(); + final int thisIndex = getWaveIndex(src); + + if (!e.isControlDown() && !e.isShiftDown() && !e.isAltDown()) { + deselectAll(); + select(panel); + lastClickedIndex = findPanelIndex(panel); + } else if (e.isControlDown()) { + if (selectedSet.contains(src)) + deselect(src); + else + select(panel); + } else if (e.isShiftDown()) { + if (lastClickedIndex == -1) { + select(panel); + } else { + deselectAll(); + final int min = Math.min(lastClickedIndex, thisIndex); + final int max = Math.max(lastClickedIndex, thisIndex); + for (int i = min; i <= max; i++) { + select(panels.get(i)); + } + } + } + lastClickedIndex = thisIndex; + // event.notifyObservers(); + validate(); + repaint(); + } + + @Override + public void waveZoomed(final AbstractWavePanel src, final double st, final double et, + final double nst, final double net) { + final double[] t = new double[] {st, et}; + addHistory(src, t); + for (final AbstractWavePanel wvp : selectedSet) { + if (wvp != src) { + addHistory(wvp, t); + wvp.zoom(nst, net); + wvp.createImage(); + } + } + } + + @Override + public void waveClosed(final AbstractWavePanel src) { + LOGGER.debug("Removing wave: {}", src.getChannel()); + remove(src); + } + }; + } + + public void addListener(PickBoxListener listener) { + listeners.add(listener); + } + + protected int getWaveIndex(AbstractWavePanel src) { + + int panelIdx = -1; + int searchIdx = 0; + while (panelIdx < 0 && searchIdx < panels.size()) { + + if (src == panels.get(searchIdx)) { + panelIdx = searchIdx; + } else { + searchIdx++; + } + } + + return panelIdx; + } + + + public void setStart(Double startJ2k) { + this.startJ2k = startJ2k; + } + + public void setEnd(Double startJ2k) { + this.endJ2k = startJ2k; + } + + private synchronized void createImage() { + if (getWidth() > 0 && getHeight() > 0) + image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_4BYTE_ABGR); + } + + @Override + public void paint(final Graphics g) { + if (image == null) { + createImage(); + } + super.paint(image.getGraphics()); + g.drawImage(image, 0, 0, null); + g.setColor(Color.WHITE); + } + + public void addPick(Arrival arrival) { + CodeTimer timer = new CodeTimer("arrival"); + this.remove(emptyArrivalLabel); + Pick pick = arrival.getPick(); + String channel = pick.getChannel(); + + PickWavePanel wavePanel = findPanel(channel); + if (wavePanel == null) { + wavePanel = new PickWavePanel(); + wavePanel.setStatusLabel(statusLabel); + wavePanel.setChannel(channel); + SeismicDataSource source = seismicSources.get(channel); + if (source == null) { + source = new WebServicesSource(); + source.parse(buildParams(channel)); + } + wavePanel.setDataSource(source); + Wave wave = source.getWave(channel, startJ2k, endJ2k); + if (wave != null) { + panels.add(wavePanel); + wavePanel.setWave(wave, startJ2k, endJ2k); + add(wavePanel); + wavePanel.setBottomBorderColor(Color.GRAY); + wavePanel.setSize(getWidth(), wavePanelHeight); + wavePanel.setDisplayTitle(true); + wavePanel.setOffsets(54, 8, 21, 19); + wavePanel.createImage(); + wavePanel.addListener(selectListener); + } + } + + wavePanel.addArrival(arrival); + timer.stopAndReport(); + } + + private String buildParams(String channel) { + String[] comps = channel.split("\\$"); + LOGGER.debug("SPLIT {}", channel); + StringBuilder sb = new StringBuilder(); + sb.append(comps[2]).append("|"); + sb.append(comps[0]).append("|"); + + if (comps.length > 3) { + sb.append(comps[3]).append("|"); + } else { + sb.append("--|"); + } + sb.append(comps[1]).append("|"); + sb.append(3600).append("|"); + sb.append(1000).append("|"); + sb.append(DATASELECT_URL).append("|"); + sb.append(STATION_URL); + + return sb.toString(); + } + + private void resizeWaves() { + for (PickWavePanel panel : panels) { + panel.setSize(getWidth(), wavePanelHeight); + panel.createImage(); + } + } + + public Dimension getPreferredScrollableViewportSize() { + return null; + } + + public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { + return 0; + } + + public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { + return 0; + } + + public boolean getScrollableTracksViewportWidth() { + return true; + } + + public boolean getScrollableTracksViewportHeight() { + // TODO Auto-generated method stub + return false; + } + + public void setWaveHeight(int height) { + wavePanelHeight = height; + resizeWaves(); + } + + + public void writeImage() { + SwarmConfig swarmConfig = SwarmConfig.getInstance(); + if (panels.size() == 0) + return; + + final JFileChooser chooser = FileChooser.getFileChooser(); + final File lastPath = new File(swarmConfig.lastPath); + chooser.setCurrentDirectory(lastPath); + chooser.setSelectedFile(new File("clipboard.png")); + chooser.setDialogTitle("Save Clipboard Screen Capture"); + final int result = chooser.showSaveDialog(APPLICATION_FRAME); + File f = null; + if (result == JFileChooser.APPROVE_OPTION) { + f = chooser.getSelectedFile(); + + if (f.exists()) { + final int choice = JOptionPane.showConfirmDialog(APPLICATION_FRAME, + "File exists, overwrite?", "Confirm", JOptionPane.YES_NO_OPTION); + if (choice != JOptionPane.YES_OPTION) + return; + } + swarmConfig.lastPath = f.getParent(); + } + if (f == null) + return; + + int height = 0; + + final int width = panels.get(0).getWidth(); + for (final AbstractWavePanel panel : panels) { + height += panel.getHeight(); + } + + final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR); + final Graphics g = image.getGraphics(); + for (final AbstractWavePanel panel : panels) { + panel.paint(g); + g.translate(0, panel.getHeight()); + } + try { + final PngEncoderB png = new PngEncoderB(image, false, PngEncoder.FILTER_NONE, 7); + final FileOutputStream out = new FileOutputStream(f); + final byte[] bytes = png.pngEncode(); + out.write(bytes); + out.close(); + } catch (final Exception ex) { + ex.printStackTrace(); + } + } + + + private void deselect(final AbstractWavePanel p) { + selectedSet.remove(p); + p.setBackgroundColor(BACKGROUND_COLOR); + p.createImage(); + notifyListeners(); + } + + private void deselectAll() { + final AbstractWavePanel[] panels = selectedSet.toArray(new AbstractWavePanel[0]); + for (final AbstractWavePanel p : panels) { + deselect(p); + } + notifyListeners(); + } + + private void select(final PickWavePanel p) { + if (p == null || selectedSet.contains(p)) + return; + + selectedSet.add(p); + p.setBackgroundColor(SELECT_COLOR); + p.createImage(); + notifyListeners(); + } + + private void notifyListeners() { + for (PickBoxListener listener : listeners) { + listener.selectCountChanged(selectedSet.size()); + } + } + + private PickWavePanel findPanel(String channel) { + for (PickWavePanel panel : panels) { + if (panel.getChannel().equals(channel)) { + return panel; + } + } + return null; + } + + private int findPanelIndex(PickWavePanel panel) { + int panelIdx = -1; + int searchIdx = 0; + while (panelIdx < 0 && searchIdx < panels.size()) { + if (panels.get(searchIdx) == panel) { + panelIdx = searchIdx; + } else { + searchIdx++; + } + } + return panelIdx; + } + + private void addHistory(final AbstractWavePanel wvp, final double[] t) { + Stack history = histories.get(wvp); + if (history == null) { + history = new Stack(); + histories.put(wvp, history); + } + history.push(t); + } + + public void scaleTime(final AbstractWavePanel wvp, final double pct) { + final double st = wvp.getStartTime(); + final double et = wvp.getEndTime(); + final double[] t = new double[] {st, et}; + addHistory(wvp, t); + final double dt = (et - st) * (1 - pct); + final double mt = (et - st) / 2 + st; + final double nst = mt - dt / 2; + final double net = mt + dt / 2; + fetchNewWave(wvp, nst, net); + } + + public void scaleTime(final double pct) { + for (final AbstractWavePanel p : selectedSet) + scaleTime(p, pct); + } + + public void back(final AbstractWavePanel wvp) { + final Stack history = histories.get(wvp); + if (history == null || history.empty()) + return; + + final double[] t = history.pop(); + fetchNewWave(wvp, t[0], t[1]); + } + + public void back() { + for (final AbstractWavePanel p : selectedSet) + back(p); + } + + private void shiftTime(final AbstractWavePanel wvp, final double pct) { + LOGGER.debug("shifting time {}", pct); + final double st = wvp.getStartTime(); + final double et = wvp.getEndTime(); + final double[] t = new double[] {st, et}; + addHistory(wvp, t); + final double dt = (et - st) * pct; + final double nst = st + dt; + final double net = et + dt; + fetchNewWave(wvp, nst, net); + } + + public void shiftTime(final double pct) { + for (final AbstractWavePanel p : selectedSet) + shiftTime(p, pct); + } + + // TODO: This isn't right, this should be a method of waveviewpanel + private void fetchNewWave(final AbstractWavePanel wvp, final double nst, final double net) { + final SwingWorker worker = new SwingWorker() { + @Override + public Object construct() { + final SeismicDataSource sds = wvp.getDataSource(); + // Hacky fix for bug #84 + Wave sw = null; + if (sds instanceof CachedDataSource) + sw = ((CachedDataSource) sds).getBestWave(wvp.getChannel(), nst, net); + else + sw = sds.getWave(wvp.getChannel(), nst, net); + wvp.setWave(sw, nst, net); + return null; + } + + @Override + public void finished() { + repaint(); + } + }; + worker.start(); + } + + public void displaySettingsDialog() { + if (panels.size() == 0) + return; + + WaveViewSettings s = panels.get(0).getSettings(); + WaveViewSettingsDialog wvsd = WaveViewSettingsDialog.getInstance(s, selectedSet.size()); + wvsd.setVisible(true); + for (PickWavePanel panel : selectedSet) { + WaveViewSettings settings = panel.getSettings(); + settings.copy(s); + } + } + + public void mapKeyStroke(String keyStroke, String name, AbstractButton button) { + UiUtils.mapKeyStrokeToButton(this, keyStroke, name, button); + } + + public void setType(ViewType viewType) { + for (AbstractWavePanel panel : selectedSet) { + panel.getSettings().setType(viewType); + panel.createImage(); + } + } + + + public void gotoTime(final AbstractWavePanel wvp, String t) { + double j2k = Double.NaN; + try { + if (t.length() == 12) + t = t + "30"; + + j2k = J2kSec.parse("yyyyMMddHHmmss", t); + } catch (final Exception e) { + JOptionPane.showMessageDialog(APPLICATION_FRAME, "Illegal time value.", "Error", + JOptionPane.ERROR_MESSAGE); + } + + if (!Double.isNaN(j2k)) { + double dt = 60; + if (wvp.getWave() != null) { + final double st = wvp.getStartTime(); + final double et = wvp.getEndTime(); + final double[] ts = new double[] {st, et}; + addHistory(wvp, ts); + dt = (et - st); + } + + final double tzo = + SWARM_CONFIG.getTimeZone(wvp.getChannel()).getOffset(System.currentTimeMillis()) / 1000; + + final double nst = j2k - tzo - dt / 2; + final double net = nst + dt; + + fetchNewWave(wvp, nst, net); + } + wvp.createImage(); + } + + public void gotoTime(final String t) { + for (final AbstractWavePanel p : selectedSet) + gotoTime(p, t); + } + + public void sortChannelsByNearest() { + // TODO Auto-generated method stub + + } +} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/event/PickBoxListener.java b/src/main/java/gov/usgs/volcanoes/swarm/event/PickBoxListener.java new file mode 100644 index 00000000..198a0072 --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/event/PickBoxListener.java @@ -0,0 +1,17 @@ +/** + * I waive copyright and related rights in the this work worldwide + * through the CC0 1.0 Universal public domain dedication. + * https://creativecommons.org/publicdomain/zero/1.0/legalcode + */ + +package gov.usgs.volcanoes.swarm.event; + +/** + * Listener interface for pickBox objects. + * + * @author Tom Parker + * + */ +public interface PickBoxListener { + public void selectCountChanged(int count); +} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/event/PickToolBar.java b/src/main/java/gov/usgs/volcanoes/swarm/event/PickToolBar.java new file mode 100644 index 00000000..f25d0e0d --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/event/PickToolBar.java @@ -0,0 +1,250 @@ +/** + * I waive copyright and related rights in the this work worldwide + * through the CC0 1.0 Universal public domain dedication. + * https://creativecommons.org/publicdomain/zero/1.0/legalcode + */ + +package gov.usgs.volcanoes.swarm.event; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JPopupMenu; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.JToolBar; + +import gov.usgs.volcanoes.swarm.Icons; +import gov.usgs.volcanoes.swarm.SwarmUtil; +import gov.usgs.volcanoes.swarm.Throbber; +import gov.usgs.volcanoes.swarm.wave.WaveViewToolBar; + +/** + * Toolbar used in event frame. + * + * @author Tom Parker + * + */ +public class PickToolBar extends JToolBar implements PickBoxListener { + private static final long serialVersionUID = 1L; + private final static Logger LOGGER = LoggerFactory.getLogger(PickToolBar.class); + + private final JButton sizeButton; + private final JButton sortButton; + private final JButton captureButton; + private final JButton histButton; + + private final JButton compXButton; + private final JButton expXButton; + private final JButton forwardButton; + private final JButton backButton; + private final Throbber throbber; + private final WaveViewToolBar waveViewToolBar; + private JPopupMenu popup; + private int waveHeight; + + private final PickToolBarListener listener; + + public PickToolBar(PickToolBarListener listener) { + this.listener = listener; + setFloatable(false); + setRollover(true); + setBorder(BorderFactory.createEmptyBorder(1, 0, 0, 0)); + + sizeButton = createSizeButton(); + sortButton = createSortButton(); + captureButton = createCaptureButton(); + backButton = createBackButton(); + compXButton = createCompXButton(); + expXButton = createExpXButton(); + forwardButton = createForwardButton(); + histButton = createHistButton(); + placeButtons(); + + waveViewToolBar = new WaveViewToolBar(null, this, listener); + selectCountChanged(0); + add(Box.createHorizontalGlue()); + throbber = new Throbber(); + add(throbber); + } + + public void placeButtons() { + add(sizeButton); + add(sortButton); + addSeparator(); + add(captureButton); + addSeparator(); + add(backButton); + add(forwardButton); + add(compXButton); + add(expXButton); + add(histButton); + addSeparator(); + } + + public void selectCountChanged(int count) { + LOGGER.debug("New select count {}", count); + boolean enable = count > 0; + histButton.setEnabled(enable); + compXButton.setEnabled(enable); + expXButton.setEnabled(enable); + forwardButton.setEnabled(enable); + backButton.setEnabled(enable); + waveViewToolBar.setEnabled(enable); + + sortButton.setEnabled(count == 1); + } + + private JButton createSizeButton() { + JButton sizeButton = + SwarmUtil.createToolBarButton(Icons.resize, "Set wave height", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + doSizePopup(); + } + }); + + return sizeButton; + } + + + private JButton createCaptureButton() { + JButton captureButton = SwarmUtil.createToolBarButton(Icons.camera, "Save pick image (P)", + new CaptureActionListener()); + listener.mapKeyStroke("P", "capture", captureButton); + + return captureButton; + } + + + private JButton createSortButton() { + JButton sortButton = SwarmUtil.createToolBarButton(Icons.geosort, + "Sort waves by nearest to selected wave", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + listener.sortChannelsByNearest(); + } + }); + + return sortButton; + } + + private JButton createBackButton() { + JButton backButton = SwarmUtil.createToolBarButton(Icons.left, + "Scroll back time 20% (Left arrow)", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + shiftTime(-0.20); + } + }); + listener.mapKeyStroke("LEFT", "backward1", backButton); + + return backButton; + } + + private JButton createForwardButton() { + JButton forwardButton = SwarmUtil.createToolBarButton(Icons.right, + "Scroll forward time 20% (Right arrow)", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + shiftTime(0.20); + } + }); + listener.mapKeyStroke("RIGHT", "forward1", forwardButton); + + return forwardButton; + } + + private JButton createCompXButton() { + JButton compXButton = SwarmUtil.createToolBarButton(Icons.xminus, + "Shrink sample time 20% (Alt-left arrow, +)", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + scaleTime(0.20); + } + }); + listener.mapKeyStroke("alt LEFT", "compx", compXButton); + listener.mapKeyStroke("EQUALS", "compx2", compXButton); + listener.mapKeyStroke("shift EQUALS", "compx2", compXButton); + + return compXButton; + } + + private JButton createExpXButton() { + JButton expXButton = SwarmUtil.createToolBarButton(Icons.xplus, + "Expand sample time 20% (Alt-right arrow, -)", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + scaleTime(-0.20); + } + }); + listener.mapKeyStroke("alt RIGHT", "expx", expXButton); + listener.mapKeyStroke("MINUS", "expx", expXButton); + + return expXButton; + } + + private JButton createHistButton() { + JButton histButton = SwarmUtil.createToolBarButton(Icons.timeback, + "Last time settings (Backspace)", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + back(); + } + }); + listener.mapKeyStroke("BACK_SPACE", "back", histButton); + + return histButton; + } + + public void scaleTime(final double pct) { + listener.scaleTime(pct); + } + + public void back() { + listener.back(); + } + + public void shiftTime(final double pct) { + listener.shiftTime(pct); + } + + class CaptureActionListener implements ActionListener { + public void actionPerformed(final ActionEvent e) { + listener.writeImage(); + } + } + + private void doSizePopup() { + if (popup == null) { + final String[] labels = new String[] {"Auto", null, "Tiny", "Small", "Medium", "Large"}; + final int[] sizes = new int[] {-1, -1, 50, 100, 160, 230}; + popup = new JPopupMenu(); + final ButtonGroup group = new ButtonGroup(); + for (int i = 0; i < labels.length; i++) { + if (labels[i] != null) { + final int size = sizes[i]; + final JRadioButtonMenuItem mi = new JRadioButtonMenuItem(labels[i]); + mi.addActionListener(new ActionListener() { + public void actionPerformed(final ActionEvent e) { + listener.setWaveHeight(size); + } + }); + if (waveHeight == size) + mi.setSelected(true); + group.add(mi); + popup.add(mi); + } else + popup.addSeparator(); + } + } + popup.show(sizeButton.getParent(), sizeButton.getX(), sizeButton.getY()); + } + + public void incrementThrobber() { + throbber.increment(); + } + + public void decrementThrobber() { + throbber.decrement(); + } +} \ No newline at end of file diff --git a/src/main/java/gov/usgs/volcanoes/swarm/event/PickToolBarListener.java b/src/main/java/gov/usgs/volcanoes/swarm/event/PickToolBarListener.java new file mode 100644 index 00000000..4f358748 --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/event/PickToolBarListener.java @@ -0,0 +1,28 @@ +/** + * I waive copyright and related rights in the this work worldwide + * through the CC0 1.0 Universal public domain dedication. + * https://creativecommons.org/publicdomain/zero/1.0/legalcode + */ +package gov.usgs.volcanoes.swarm.event; + +import gov.usgs.volcanoes.swarm.wave.WaveViewToolBarListener; + +/** + * Listener interface for PickToolBar objects. + * + * @author Tom Parker + * + */ +public interface PickToolBarListener extends WaveViewToolBarListener { + public void setWaveHeight(int height); + + public void scaleTime(final double pct); + + public void back(); + + public void shiftTime(final double pct); + + public void writeImage(); + + public void sortChannelsByNearest(); +} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/event/PickWavePanel.java b/src/main/java/gov/usgs/volcanoes/swarm/event/PickWavePanel.java new file mode 100644 index 00000000..0e28410d --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/event/PickWavePanel.java @@ -0,0 +1,162 @@ +/** + * I waive copyright and related rights in the this work worldwide + * through the CC0 1.0 Universal public domain dedication. + * https://creativecommons.org/publicdomain/zero/1.0/legalcode + */ +package gov.usgs.volcanoes.swarm.event; + +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Stack; + +import gov.usgs.volcanoes.core.quakeml.Arrival; +import gov.usgs.volcanoes.core.quakeml.EventObserver; +import gov.usgs.volcanoes.core.quakeml.Pick; +import gov.usgs.volcanoes.core.time.J2kSec; +import gov.usgs.volcanoes.swarm.time.TimeListener; +import gov.usgs.volcanoes.swarm.time.WaveViewTime; +import gov.usgs.volcanoes.swarm.wave.AbstractWavePanel; +import gov.usgs.volcanoes.swarm.wave.WaveViewPanelAdapter; + +/** + * A wave panel which adds pick annotations. + * + * @author Tom Parker + * + */ +public class PickWavePanel extends AbstractWavePanel + implements EventObserver, Comparable { + + private static final long serialVersionUID = 1L; + private static final Font ANNOTATION_FONT = new Font("Monospaced", Font.BOLD, 12); + private static final Color P_BACKGROUND = new Color(128, 255, 128, 192); + private static final Color S_BACKGROUND = new Color(128, 128, 255, 192); + private static final Color RESIDUAL_COLOR = new Color(128, 128, 128, 32); + + private final List arrivals; + private final Stack zoomHistory; + + public PickWavePanel() { + super(); + allowDragging = true; + arrivals = new ArrayList(); + zoomHistory = new Stack(); + createListeners(); + } + + public void addArrival(Arrival arrival) { + arrivals.add(arrival); + } + + + private void createListeners() { + WaveViewTime.addTimeListener(new TimeListener() { + public void timeChanged(final double j2k) { + setCursorMark(j2k); + } + }); + + this.addListener(new WaveViewPanelAdapter() { + @Override + public void waveZoomed(final AbstractWavePanel src, final double st, final double et, + final double nst, final double net) { + final double[] t = new double[] {st, et}; + zoomHistory.push(t); + zoom(nst, net); + } + }); + } + + @Override + protected void annotateImage(Graphics2D g2) { + if (getVisibleRect().isEmpty()) { + return; + } + + for (Arrival arrival : arrivals) { + Pick pick = arrival.getPick(); + + String tag = arrival.getTag(); + double j2k = J2kSec.fromEpoch(pick.getTime()); + double[] t = getTranslation(); + if (t == null) + continue; + + double x = 2 + (j2k - t[1]) / t[0]; + g2.setColor(DARK_GREEN); + g2.draw(new Line2D.Double(x, yOffset, x, getHeight() - bottomHeight - 1)); + + double residual = arrival.getTimeResidual(); + if (residual != 0) { + double residualMark = 2 + (j2k + residual - t[1]) / t[0]; + g2.setColor(RESIDUAL_COLOR); + g2.fill(new Rectangle2D.Double(Math.min(x, residualMark), yOffset, + Math.abs(x - residualMark), getHeight() - bottomHeight - 1)); + } + + g2.setFont(ANNOTATION_FONT); + + FontMetrics fm = g2.getFontMetrics(); + int width = fm.stringWidth(tag); + int height = fm.getAscent(); + + int offset = 2; + int lw = width + 2 * offset; + + if (tag.indexOf('P') != -1) { + g2.setColor(P_BACKGROUND); + } else if (tag.indexOf('S') != -1) { + g2.setColor(S_BACKGROUND); + } + + g2.fillRect((int) x, 3, lw, height + 2 * offset); + g2.setColor(Color.black); + g2.drawRect((int) x, 3, lw, height + 2 * offset); + + g2.drawString(tag, (int) x + offset, 3 + (fm.getAscent() + offset)); + } + } + + @Override + protected void processRightMouseRelease(MouseEvent e) { + pauseCursorMark = false; + } + + public void eventUpdated() { + repaint(); + } + + @Override + protected void processRightMousePress(MouseEvent e) { + settings.cycleType(); + } + + public int compareTo(PickWavePanel o) { + return Arrival.distanceComparator().compare(arrivals.get(0), o.arrivals.get(0)); + } + + public static Comparator distanceComparator() { + return new Comparator() { + public int compare(final PickWavePanel e1, final PickWavePanel e2) { + return Arrival.distanceComparator().compare(e1.arrivals.get(0), e2.arrivals.get(0)); + } + }; + } + + public static Comparator stationDistanceComparator(final PickWavePanel panel) { + return new Comparator() { + public int compare(final PickWavePanel e1, final PickWavePanel e2) { + return Arrival.distanceComparator().compare(e1.arrivals.get(0), e2.arrivals.get(0)); + } + }; + } + +} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/event/QuakeMlUtils.java b/src/main/java/gov/usgs/volcanoes/swarm/event/QuakeMlUtils.java new file mode 100644 index 00000000..c89be2dd --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/event/QuakeMlUtils.java @@ -0,0 +1,40 @@ +/** + * I waive copyright and related rights in the this work worldwide + * through the CC0 1.0 Universal public domain dedication. + * https://creativecommons.org/publicdomain/zero/1.0/legalcode + */ +package gov.usgs.volcanoes.swarm.event; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.ParseException; +import java.text.SimpleDateFormat; + +/** + * A utility class for working with QuakeML files + * + * @author Tom Parker + * + */ +public class QuakeMlUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(QuakeMlUtils.class); + private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSX"; + + public static long parseTime(String timeString) { + String inString = timeString; + timeString = timeString.replaceFirst("\\.(\\d)Z?", ".$100Z"); + timeString = timeString.replaceFirst("\\.(\\d{2})Z?$", ".$10Z"); + timeString = timeString.replaceFirst(":(\\d{2})Z?$", ":$1.000Z"); + + long time = Long.MIN_VALUE; + SimpleDateFormat dateF = new SimpleDateFormat(DATE_FORMAT); + try { + time = dateF.parse(timeString).getTime(); + } catch (ParseException e) { + LOGGER.error("Cannot parse time String {}", inString); + throw new RuntimeException("Cannot parse time string " + inString); + } + return time; + } +} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/heli/HelicorderViewPanel.java b/src/main/java/gov/usgs/volcanoes/swarm/heli/HelicorderViewPanel.java index 8979ba58..685c0c9d 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/heli/HelicorderViewPanel.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/heli/HelicorderViewPanel.java @@ -44,12 +44,10 @@ import gov.usgs.volcanoes.core.time.J2kSec; import gov.usgs.volcanoes.swarm.Icons; import gov.usgs.volcanoes.swarm.Metadata; -import gov.usgs.volcanoes.swarm.Swarm; import gov.usgs.volcanoes.swarm.SwarmConfig; import gov.usgs.volcanoes.swarm.SwingWorker; import gov.usgs.volcanoes.swarm.options.SwarmOptions; import gov.usgs.volcanoes.swarm.options.SwarmOptionsListener; -import gov.usgs.volcanoes.swarm.picker.PickerFrame; import gov.usgs.volcanoes.swarm.time.UiTime; import gov.usgs.volcanoes.swarm.wave.AbstractWavePanel; import gov.usgs.volcanoes.swarm.wave.WaveClipboardFrame; @@ -62,814 +60,843 @@ * @author Dan Cervelli */ public class HelicorderViewPanel extends JComponent implements SwarmOptionsListener { - private static final Logger LOGGER = LoggerFactory.getLogger(HelicorderViewPanel.class); - - private static final Color BACKGROUND_COLOR = new Color(0xf7, 0xf7, 0xf7); - public static final long serialVersionUID = -1; - - public static final int X_OFFSET = 70; - public static final int Y_OFFSET = 10; - public static final int RIGHT_WIDTH = 70; - public static final int BOTTOM_HEIGHT = 35; - private int insetHeight = 200; - - private Plot plot; - private HelicorderRenderer heliRenderer; - private HelicorderViewerSettings settings; - private HelicorderData heliData; - private double startTime; - private double endTime; - private double mean; - private double bias; - private HelicorderViewerFrame parent; - private double[] translation; - - private WaveViewPanel insetWavePanel; - - private BufferedImage bufferImage, displayImage; - private DateFormat dateFormat; - - private boolean working; - private boolean resized; - - private int insetY; - - private boolean fullScreen; - - private boolean minimal; - - private double startMark = Double.NaN; - private double endMark = Double.NaN; - private double lastMark = Double.NaN; - - private EventListenerList listeners = new EventListenerList(); - private static SwarmConfig swarmConfig; - - public HelicorderViewPanel(HelicorderViewerFrame hvf) { - swarmConfig = SwarmConfig.getInstance(); - - // setBackground(new Color(255, 255, 255, 128)); - parent = hvf; - dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); - plot = new Plot(); - plot.setBackgroundColor(BACKGROUND_COLOR); - // plot.setBackgroundColor(null); - settings = hvf.getHelicorderViewerSettings(); - heliRenderer = new HelicorderRenderer(); - if (swarmConfig.heliColors != null) - heliRenderer.setDefaultColors(swarmConfig.heliColors);// DCK: add - // configured - // colors - heliRenderer.setExtents(0, 1, Double.MAX_VALUE, -Double.MAX_VALUE); - plot.addRenderer(heliRenderer); - - this.setRequestFocusEnabled(true); - this.addMouseListener(new HelicorderMouseListener()); - this.addMouseMotionListener(new HelicorderMouseMotionListener()); - this.addMouseWheelListener(new HelicorderMouseWheelListener()); - - cursorChanged(); - - SwarmOptions.addOptionsListener(this); - } - - public void addListener(HelicorderViewPanelListener listener) { - listeners.add(HelicorderViewPanelListener.class, listener); - } - - public void removeListener(HelicorderViewPanelListener listener) { - listeners.remove(HelicorderViewPanelListener.class, listener); - } - - public void fireInsetCreated(double st, double et) { - Object[] ls = listeners.getListenerList(); - for (int i = ls.length - 2; i >= 0; i -= 2) - if (ls[i] == HelicorderViewPanelListener.class) - ((HelicorderViewPanelListener) ls[i + 1]).insetCreated(st, et); - } - - public void cursorChanged() { - Cursor crosshair = new Cursor(Cursor.CROSSHAIR_CURSOR); - if (swarmConfig.useLargeCursor) { - Image cursorImg = Icons.crosshair.getImage(); - crosshair = Toolkit.getDefaultToolkit().createCustomCursor(cursorImg, new Point(16, 16), - "Large crosshair"); - } - - this.setCursor(crosshair); - } - - public void settingsChanged() { - if (insetWavePanel != null) { - double zoomOffset = parent.getHelicorderViewerSettings().waveZoomOffset; - double j2k = insetWavePanel.getStartTime() - + (insetWavePanel.getEndTime() - insetWavePanel.getStartTime()) / 2; - loadInsetWave(j2k - zoomOffset, j2k + zoomOffset); - } - - repaint(); - } - - public void setStartMark(double t) { - startMark = t; - } - - public void setEndMark(double t) { - endMark = t; - } - - public void clearMarks() { - startMark = endMark = Double.NaN; - } - - public void setCursorMark(double t) { - if (insetWavePanel != null) - insetWavePanel.setCursorMark(t); - } - - public void markTime(double t) { - if (Double.isNaN(startMark) && Double.isNaN(endMark)) { - startMark = t; - } else if (!Double.isNaN(startMark) && Double.isNaN(endMark)) { - endMark = t; - if (endMark < startMark) { - double tm = startMark; - startMark = endMark; - endMark = tm; - } - } else { - startMark = Math.min(lastMark, t); - endMark = Math.max(lastMark, t); - } - lastMark = t; - if (insetWavePanel != null) { - insetWavePanel.setMarks(startMark, endMark); - } - repaint(); - } - - class HelicorderMouseMotionListener implements MouseMotionListener { - public void mouseDragged(MouseEvent e) { - UiTime.touchTime(); - HelicorderViewPanel.this.requestFocus(); - int mx = e.getX(); - int my = e.getY(); - - if (mx < heliRenderer.getGraphX() || my < heliRenderer.getGraphY() - || mx > heliRenderer.getGraphX() + heliRenderer.getGraphWidth() - 1 - || my > heliRenderer.getGraphY() + heliRenderer.getGraphHeight() - 1) { - /* - * // removed because it wasn't helpful! if (insetWavePanel != - * null) { removeWaveInset(); repaint(); } return; - */ - } else { - double j2k = getMouseJ2K(mx, my); - processMousePosition(mx, my); - - if (SwingUtilities.isLeftMouseButton(e)) - createWaveInset(j2k, mx, my); - } - } - - public void mouseMoved(MouseEvent e) { - UiTime.touchTime(); - processMousePosition(e.getX(), e.getY()); - } - } - - class HelicorderMouseWheelListener implements MouseWheelListener { - int totalScroll = 0; - Delay delay; - - public void mouseWheelMoved(MouseWheelEvent e) { - UiTime.touchTime(); - totalScroll += e.getWheelRotation(); - if (delay == null) - delay = new Delay(250); - else - delay.restart(); - } - - public void delayOver() { - removeWaveInset(); - SwingUtilities.invokeLater(new Runnable() { - public void run() { - parent.scroll(totalScroll); - delay = null; - totalScroll = 0; - } - }); - } - - class Delay extends Thread { - long delayLeft; - - public Delay(long ms) { - delayLeft = ms; - start(); - } - - public void restart() { - interrupt(); - } - - public void run() { - boolean done = false; - while (!done) { - try { - Thread.sleep(delayLeft); - done = true; - } catch (Exception e) { - } - } - delayOver(); - } - } - } - - class HelicorderMouseListener implements MouseListener { - public void mouseClicked(MouseEvent e) { - UiTime.touchTime(); - HelicorderViewPanel.this.requestFocus(); - } - - public void mouseEntered(MouseEvent e) {} - - public void mouseExited(MouseEvent e) {} - - public void mousePressed(MouseEvent e) { - UiTime.touchTime(); - if (e.getButton() == MouseEvent.BUTTON1) { - int mx = e.getX(); - int my = e.getY(); - if (mx < heliRenderer.getGraphX() || my < heliRenderer.getGraphY() - || mx > heliRenderer.getGraphX() + heliRenderer.getGraphWidth() - 1 - || my > heliRenderer.getGraphY() + heliRenderer.getGraphHeight() - 1) - return; - - double j2k = getMouseJ2K(mx, my); - if (j2k != -1E300) { - if (insetWavePanel != null) - insetWavePanel.setWave(null, 0, 1); - createWaveInset(j2k, mx, my); - } - } - /* - * else if (e.getButton() == MouseEvent.BUTTON3) { if - * (insetWavePanel != null) { removeWaveInset(); repaint(); } } - */ - } - - public void mouseReleased(MouseEvent e) {} - } - - public boolean hasInset() { - return insetWavePanel != null; - } - - public HelicorderData getData() { - return heliData; - } - - public double getStartTime() { - return startTime; - } - - public double getEndTime() { - return endTime; - } - - public void insetToClipboard() { - if (insetWavePanel != null) { - WaveViewPanel p = new WaveViewPanel(insetWavePanel); - p.setDataSource(insetWavePanel.getDataSource().getCopy()); - WaveClipboardFrame cb = WaveClipboardFrame.getInstance(); - cb.setVisible(true); - cb.addWave(p); - requestFocus(); - } - } - - private void processMousePosition(int x, int y) { - if (heliData == null) - return; - - String status = null; - - boolean wp = false; - if (insetWavePanel != null) { - Point loc = insetWavePanel.getLocation(); - wp = insetWavePanel.processMousePosition(x - loc.x, y - loc.y); - } - - if (!wp) { - if (status == null) { - if (!(x < heliRenderer.getGraphX() || y < heliRenderer.getGraphY() - || x > heliRenderer.getGraphX() + heliRenderer.getGraphWidth() - 1 - || y > heliRenderer.getGraphY() + heliRenderer.getGraphHeight() - 1)) { - double j2k = getMouseJ2K(x, y); - status = dateFormat.format(J2kSec.asDate(j2k)); - TimeZone tz = swarmConfig.getTimeZone(settings.channel); - double tzo = tz.getOffset(J2kSec.asEpoch(j2k)); - if (tzo != 0) { - String tza = tz.getDisplayName(tz.inDaylightTime(J2kSec.asDate(j2k)), TimeZone.SHORT); - status = dateFormat.format(J2kSec.asDate(j2k + tzo / 1000)) + " (" + tza + "), " + status - + " (UTC)"; - } - } - } - - if (status == null) - status = " "; - - if (!Double.isNaN(startMark) && !Double.isNaN(endMark)) { - double dur = Math.abs(startMark - endMark); - String pre = - String.format("Duration: %.2fs (Md: %.2f)", dur, swarmConfig.getDurationMagnitude(dur)); - if (status.length() > 2) - status = pre + ", " + status; - else - status = pre; - } - - if (status != null) - parent.setStatus(status); - } - } - - public double getMouseJ2K(int mx, int my) { - double j2k = 0; - if (translation != null) { - j2k = translation[4]; - j2k += (mx - translation[0]) * translation[7]; - j2k += getHelicorderRow(my) * translation[6]; - } - return j2k; - } - - public int getHelicorderRow(int my) { - return (int) Math.floor((my - translation[3]) / translation[2]); - } - - public void removeWaveInset() { - if (insetWavePanel != null) { - parent.setInsetButtonsEnabled(false); - this.remove(insetWavePanel); - insetWavePanel = null; - repaint(); - } - } - - public void moveInset(int offset) { - if (insetWavePanel != null) { - double st = insetWavePanel.getStartTime(); - double et = insetWavePanel.getEndTime(); - double dt = et - st; - double nst = st + dt * offset; - double net = et + dt * offset; - - int row = heliRenderer.getRow(st + dt * offset + (dt / 2)); - if (row < 0 || row >= heliRenderer.getNumRows()) - return; - - loadInsetWave(nst, net); - int height = insetHeight; - - if (row * translation[2] + translation[3] > height + translation[3]) { - int y = (int) Math.ceil((row - 1) * translation[2] + translation[3]); - insetWavePanel.setLocation(0, y - height); - } else { - int y = (int) Math.ceil((row + 2) * translation[2] + translation[3]); - insetWavePanel.setLocation(0, y); - } - } - } - - public void createWaveInset(final double j2k, final int mx, final int my) { - if (working) - return; - - insetY = my; - - if (insetWavePanel == null) { - insetWavePanel = new WaveViewPanel(parent.getWaveViewSettings()); - insetWavePanel.addListener(new WaveViewPanelAdapter() { - public void waveClosed(AbstractWavePanel src) { - removeWaveInset(); - } - - public void waveTimePressed(AbstractWavePanel src, MouseEvent e, double j2k) { - if (swarmConfig.durationEnabled && SwingUtilities.isLeftMouseButton(e)) - markTime(j2k); - insetWavePanel.processMousePosition(e.getX(), e.getY()); - } - }); - } - - // insetWavePanel.setHelicorderPanel(this); - insetWavePanel.setMarks(startMark, endMark); - insetWavePanel.setChannel(settings.channel); - insetWavePanel.setDataSource(parent.getDataSource()); - insetWavePanel.setStatusLabel(parent.getStatusLabel()); - - Dimension d = getSize(); - insetHeight = getHeight() / 4; - int height = insetHeight; - int row = heliRenderer.getRow(j2k); - - insetWavePanel.setSize(d.width + 2, height); - if (insetY - heliRenderer.getRowHeight() > height + translation[3]) { - int y = (int) Math.ceil((row - 1) * translation[2] + translation[3]); - insetWavePanel.setLocation(-1, y - height); - } else { - int y = (int) Math.ceil((row + 2) * translation[2] + translation[3]); - insetWavePanel.setLocation(-1, y); - } - - insetWavePanel.setAllowClose(true); - insetWavePanel.setWorking(true); - - double zoomOffset = parent.getHelicorderViewerSettings().waveZoomOffset; - loadInsetWave(j2k - zoomOffset, j2k + zoomOffset); - parent.setInsetButtonsEnabled(true); - this.add(insetWavePanel); - repaint(); - } - - private void loadInsetWave(final double st, final double et) { - fireInsetCreated(st, et); - final SwingWorker worker = new SwingWorker() { - private Wave sw; - - public Object construct() { - parent.getThrobber().increment(); - working = true; - sw = parent.getWave(st, et); - return null; - } - - public void finished() { - parent.getThrobber().decrement(); - working = false; - if (insetWavePanel != null) { - insetWavePanel.setWave(sw, st, et); - insetWavePanel.setWorking(false); - } - repaint(); - } - }; - worker.start(); - } - - public void setHelicorder(HelicorderData d, double time1, double time2) { - heliData = d; - if (heliData != null) { - startTime = time1; - endTime = time2; - heliRenderer.setData(heliData); - heliRenderer.setTimeChunk(settings.timeChunk); - heliRenderer.setTimeZone(swarmConfig.getTimeZone(settings.channel)); - heliRenderer.setForceCenter(settings.forceCenter); - heliRenderer.setClipBars(settings.clipBars); - heliRenderer.setShowClip(settings.showClip); - heliRenderer.setAlertClip(settings.alertClip); - heliRenderer.setClipWav("clip.wav"); - heliRenderer.setClipAlertTimeout(settings.alertClipTimeout); - mean = heliData.getMeanMax(); - bias = heliData.getBias(); - if (bias != mean) - mean = Math.abs(bias - mean); - heliRenderer.setClipValue(settings.clipValue); - - } - } - - public void invalidateImage() { - final SwingWorker worker = new SwingWorker() { - public Object construct() { - createImage(); - return null; - } - - public void finished() { - displayImage = bufferImage; - repaint(); - } - }; - worker.start(); - } - - public void setResized(boolean b) { - resized = b; - } - - private void createImage() { - if (heliData == null) - return; - - Dimension d = this.getSize(); - if (d.width <= 0 || d.height <= 0) - return; - - bufferImage = new BufferedImage(d.width, d.height, BufferedImage.TYPE_4BYTE_ABGR); - - Graphics2D ig = (Graphics2D) bufferImage.getGraphics(); - plot.setSize(d); - - double offset = 0; - double multiplier = 1; - Metadata md = swarmConfig.getMetadata(settings.channel); - if (md != null) { - offset = md.getOffset(); - multiplier = md.getMultiplier(); - } - - if (minimal) - heliRenderer.setLocation(X_OFFSET / 2, Y_OFFSET, d.width - X_OFFSET - 5, - d.height - Y_OFFSET - BOTTOM_HEIGHT / 2); - else - heliRenderer.setLocation(X_OFFSET, Y_OFFSET, d.width - X_OFFSET - RIGHT_WIDTH, - d.height - Y_OFFSET - BOTTOM_HEIGHT); - - if (settings.autoScale) { - settings.barRange = (int) (mean * settings.barMult); - settings.clipValue = (int) (mean * settings.clipBars); - heliRenderer.setHelicorderExtents(startTime, endTime, -1 * Math.abs(settings.barRange), - Math.abs(settings.barRange)); - - } else { - heliRenderer.setHelicorderExtents(startTime, endTime, - -1 * Math.abs((settings.barRange - offset) / multiplier), - Math.abs((settings.barRange - offset) / multiplier)); - } - - heliRenderer.setTimeZone(swarmConfig.getTimeZone(settings.channel)); - heliRenderer.setClipValue(settings.clipValue); - if (minimal) { - // System.out.println("minimal"); - // heliRenderer.createMinimumAxis(); - heliRenderer.setFrameDecorator(new SmallDecorator()); - } else - heliRenderer.createDefaultAxis(); - - if (md == null || md.getAlias() == null) - heliRenderer.setChannel(settings.channel); - else - heliRenderer.setChannel(md.getAlias()); - - translation = heliRenderer.getTranslationInfo(false); - heliRenderer.setLargeChannelDisplay(fullScreen); - - try { - plot.render(ig); - } catch (PlotException e) { - e.printStackTrace(); - } - } - - class SmallDecorator extends FrameDecorator { - public void decorate(FrameRenderer fr) { - HelicorderRenderer hr = (HelicorderRenderer) fr; - AxisRenderer axis = new AxisRenderer(fr); - axis.createDefault(); - fr.setAxis(axis); - - int minutes = (int) Math.round(settings.timeChunk / 60.0); - int majorTicks = minutes; - if (minutes > 30 && minutes < 180) - majorTicks = minutes / 5; - else if (minutes >= 180 && minutes < 360) - majorTicks = minutes / 10; - else if (minutes >= 360) - majorTicks = minutes / 20; - double[] mjt = SmartTick.intervalTick(0, settings.timeChunk, majorTicks); - - axis.createBottomTicks(null, mjt); - axis.createTopTicks(null, mjt); - axis.createVerticalGridLines(mjt); - - int bc = (settings.timeChunk / 5) + 2; - String[] btl = new String[bc]; - double[] btlv = new double[bc]; - btl[0] = "+"; - btlv[0] = 30; - for (int i = 0, j = 1; i < mjt.length; i++) { - long m = Math.round(mjt[i] / 60.0); - if (m % 5 == 0) { - btl[j] = Long.toString(m); - btlv[j++] = mjt[i]; - } - } - axis.createBottomTickLabels(btlv, btl); - - int numRows = hr.getNumRows(); - double[] labelPosLR = new double[numRows]; - String[] leftLabelText = new String[numRows]; - String[] rightLabelText = new String[numRows]; - TimeZone timeZone = swarmConfig.getTimeZone(settings.channel); - - DateFormat localTimeFormat = new SimpleDateFormat("HH:mm"); - localTimeFormat.setTimeZone(timeZone); - - DateFormat localDayFormat = new SimpleDateFormat("MM-dd"); - localDayFormat.setTimeZone(timeZone); - - double pixelsPast = 0; - double pixelsPerRow = fr.getGraphHeight() / numRows; - String lastLocalDay = ""; - for (int i = numRows - 1; i >= 0; i--) { - pixelsPast += pixelsPerRow; - labelPosLR[i] = i + 0.5; - - String localTime = localTimeFormat - .format(J2kSec.asDate(hr.getHelicorderMaxX() - (i + 1) * settings.timeChunk)); - leftLabelText[i] = null; - if (pixelsPast > 20) { - leftLabelText[i] = localTime; - pixelsPast = 0; - } - - Date dtz = J2kSec.asDate(hr.getHelicorderMaxX() - (i + 1) * settings.timeChunk); - String localDay = - localDayFormat.format(new Date(dtz.getTime() + settings.timeChunk * 1000)); - if (!localDay.equals(lastLocalDay)) { - rightLabelText[i] = localDay; - lastLocalDay = localDay; - } - } - - axis.createLeftTickLabels(labelPosLR, leftLabelText); - axis.createRightTickLabels(labelPosLR, rightLabelText); - - boolean dst = timeZone.inDaylightTime(J2kSec.asDate(hr.getViewEndTime())); - String timeZoneName = timeZone.getDisplayName(dst, TimeZone.SHORT); - TextRenderer tr = - new TextRenderer(3, fr.getGraphY() + fr.getGraphHeight() + 14, timeZoneName); - tr.font = Font.decode("dialog-PLAIN-9"); - tr.antiAlias = false; - axis.addPostRenderer(tr); - - double[] hg = new double[numRows - 1]; - for (int i = 0; i < numRows - 1; i++) - hg[i] = i + 1.0; - - axis.createHorizontalGridLines(hg); - - axis.setBackgroundColor(Color.white); - } - } - - public void setFullScreen(boolean b) { - fullScreen = b; - } - - public void setMinimal(boolean b) { - minimal = b; - } - - private void drawMark(Graphics2D g2, double t, Color color) { - if (Double.isNaN(t)) - return; - - int x = (int) (heliRenderer.helicorderGetXPixel(t)); - int row = heliRenderer.getRow(t); - int y = (int) Math.ceil(row * translation[2] + translation[3]); - g2.setColor(color); - g2.draw(new Line2D.Double(x, y, x, y + translation[2])); - - GeneralPath gp = new GeneralPath(); - gp.moveTo(x, y); - gp.lineTo((float) x - 4, (float) y - 6); - gp.lineTo((float) x + 4, (float) y - 6); - gp.closePath(); - g2.setColor(Color.GREEN); - g2.fill(gp); - g2.setColor(DARK_GREEN); - g2.draw(gp); - - gp.reset(); - gp.moveTo(x, (float) (y + translation[2])); - gp.lineTo((float) x - 4, (float) (y + 6 + translation[2])); - gp.lineTo((float) x + 4, (float) (y + 6 + translation[2])); - gp.closePath(); - gp.closePath(); - g2.setColor(Color.GREEN); - g2.fill(gp); - g2.setColor(DARK_GREEN); - g2.draw(gp); - } - - private static final Color DARK_GREEN = new Color(0, 168, 0); - - public void paint(Graphics g) { - Graphics2D g2 = (Graphics2D) g; - Dimension d = this.getSize(); - if (heliData == null) { - // if (parent.isWorking()) - // g2.drawString("Attempting to retrieve data...", d.width / 2 - 75, - // d.height / 2); - // else - if (!parent.isWorking()) { - // g2.drawString("The server returned no helicorder data.", - // d.width / 2 - 150, d.height / 2); - parent.setStatus("The server returned no helicorder data."); - // double start = settings.getBottomTime() - settings.span * 60; - // double end = settings.getBottomTime(); - // g2.drawString(String.format("Time range: %s to %s", - // Time.format(Time.STANDARD_TIME_FORMAT, start), - // Time.format(Time.STANDARD_TIME_FORMAT, end)), - // d.width / 2 - 100, d.height / 2 + 16); - } - } else if (displayImage != null) - g2.drawImage(displayImage, 0, 0, null); - - drawMark(g2, startMark, DARK_GREEN); - drawMark(g2, endMark, DARK_GREEN); - - if (insetWavePanel != null) { - // find out where time highlight will be, possibly reposition the - // insetWavePanel - double t1 = insetWavePanel.getStartTime(); - double t2 = insetWavePanel.getEndTime(); - double t = (t2 - t1) / 2 + t1; - int row = heliRenderer.getRow(t); - if (resized) { - insetWavePanel.setSize(d.width, insetHeight); - insetWavePanel.createImage(); - if (row * translation[2] > insetHeight + translation[3]) { - int y = (int) Math.ceil((row - 1) * translation[2] + translation[3]); - insetWavePanel.setLocation(0, y - insetHeight); - } else { - int y = (int) Math.ceil((row + 2) * translation[2] + translation[3]); - insetWavePanel.setLocation(0, y); - } - } - - // now it's safe to draw the waveInsetPanel - Point p = insetWavePanel.getLocation(); - g2.translate(p.x, p.y); - insetWavePanel.paint(g2); - Dimension wvd = insetWavePanel.getSize(); - g.setColor(Color.gray); - g.drawRect(0, 0, wvd.width - 1, wvd.height); - g2.translate(-p.x, -p.y); - - // fixes bug where highlight was being drawn before wave loaded - if (row < 0) - return; - - // finally, draw the highlight. support for spanning one row - // above and one row below the center point. - // TODO: support multi-row span - Paint pnt = g2.getPaint(); - g2.setPaint(new Color(255, 255, 0, 128)); - Rectangle2D.Double rect = new Rectangle2D.Double(); - int zoomOffset = parent.getHelicorderViewerSettings().waveZoomOffset; - double xo = 1.0 / translation[7] * zoomOffset; - int bx = (int) (heliRenderer.helicorderGetXPixel(t) - xo); - int width = (int) (xo * 2); - int right = heliRenderer.getGraphX() + heliRenderer.getGraphWidth(); - if (bx < heliRenderer.getGraphX()) { - int width2 = heliRenderer.getGraphX() - bx; - rect.setRect(right - width2, (int) Math.ceil((row - 1) * translation[2] + translation[3]), - width2, (int) Math.ceil(translation[2])); - if (row - 1 >= 0) - g2.fill(rect); - bx = heliRenderer.getGraphX(); - width = width - width2; - } - if (bx + width > right) { - int width2 = bx + width - right; - rect.setRect(heliRenderer.getGraphX() + 1, - (int) Math.ceil((row + 1) * translation[2] + translation[3]), width2, - (int) Math.ceil(translation[2])); - if (row + 1 < heliRenderer.getNumRows()) - g2.fill(rect); - width = width - width2; - } - rect.setRect(bx, (int) Math.ceil(row * translation[2] + translation[3]), width, - (int) Math.floor(translation[2])); - - g2.fill(rect); - g2.setPaint(pnt); - } - - resized = false; - } - - public void optionsChanged() { - cursorChanged(); - invalidateImage(); - if (!SwarmConfig.getInstance().durationEnabled) - clearMarks(); - } - - public void insetToPicker() { - if (insetWavePanel != null) { - LOGGER.debug("Sending wave to picker"); - Swarm.openPicker(insetWavePanel); - } - } + private static final Logger LOGGER = LoggerFactory.getLogger(HelicorderViewPanel.class); + + private static final Color BACKGROUND_COLOR = new Color(0xf7, 0xf7, 0xf7); + public static final long serialVersionUID = -1; + + public static final int X_OFFSET = 70; + public static final int Y_OFFSET = 10; + public static final int RIGHT_WIDTH = 70; + public static final int BOTTOM_HEIGHT = 35; + private int insetHeight = 200; + + // TODO: extract translation into an object + private static final int GRAPH_LEFT = 0; + private static final int GRAPH_RIGHT = 1; + private static final int ROW_HEIGHT = 2; + private static final int GRAPH_Y = 3; + private static final int MIN_TIME_LOCAL = 4; + private static final int MAX_TIME_LOCAL = 5; + private static final int TIME_CHUNK = 6; + private static final int PIXEL_TIME_SPAN = 7; + + private Plot plot; + private HelicorderRenderer heliRenderer; + private HelicorderViewerSettings settings; + private HelicorderData heliData; + private double startTime; + private double endTime; + private double mean; + private double bias; + private HelicorderViewerFrame parent; + + private double[] translation; + + private WaveViewPanel insetWavePanel; + + private BufferedImage bufferImage, displayImage; + private DateFormat dateFormat; + + private boolean working; + private boolean resized; + + private int insetY; + + private boolean fullScreen; + + private boolean minimal; + + private double startMark = Double.NaN; + private double endMark = Double.NaN; + private double lastMark = Double.NaN; + + private EventListenerList listeners = new EventListenerList(); + private static SwarmConfig swarmConfig; + + public HelicorderViewPanel(HelicorderViewerFrame hvf) { + swarmConfig = SwarmConfig.getInstance(); + + // setBackground(new Color(255, 255, 255, 128)); + parent = hvf; + dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); + plot = new Plot(); + plot.setBackgroundColor(BACKGROUND_COLOR); + // plot.setBackgroundColor(null); + settings = hvf.getHelicorderViewerSettings(); + heliRenderer = new HelicorderRenderer(); + if (swarmConfig.heliColors != null) + heliRenderer.setDefaultColors(swarmConfig.heliColors);// DCK: add + // configured + // colors + heliRenderer.setExtents(0, 1, Double.MAX_VALUE, -Double.MAX_VALUE); + plot.addRenderer(heliRenderer); + + this.setRequestFocusEnabled(true); + this.addMouseListener(new HelicorderMouseListener()); + this.addMouseMotionListener(new HelicorderMouseMotionListener()); + this.addMouseWheelListener(new HelicorderMouseWheelListener()); + + cursorChanged(); + + SwarmOptions.addOptionsListener(this); + } + + public void addListener(HelicorderViewPanelListener listener) { + listeners.add(HelicorderViewPanelListener.class, listener); + } + + public void removeListener(HelicorderViewPanelListener listener) { + listeners.remove(HelicorderViewPanelListener.class, listener); + } + + public void fireInsetCreated(double st, double et) { + Object[] ls = listeners.getListenerList(); + for (int i = ls.length - 2; i >= 0; i -= 2) + if (ls[i] == HelicorderViewPanelListener.class) + ((HelicorderViewPanelListener) ls[i + 1]).insetCreated(st, et); + } + + public void cursorChanged() { + Cursor crosshair = new Cursor(Cursor.CROSSHAIR_CURSOR); + if (swarmConfig.useLargeCursor) { + Image cursorImg = Icons.crosshair.getImage(); + crosshair = Toolkit.getDefaultToolkit().createCustomCursor(cursorImg, new Point(16, 16), "Large crosshair"); + } + + this.setCursor(crosshair); + } + + public void settingsChanged() { + if (insetWavePanel != null) { + double zoomOffset = parent.getHelicorderViewerSettings().waveZoomOffset; + double j2k = insetWavePanel.getStartTime() + + (insetWavePanel.getEndTime() - insetWavePanel.getStartTime()) / 2; + loadInsetWave(j2k - zoomOffset, j2k + zoomOffset); + } + + repaint(); + } + + public void setStartMark(double t) { + startMark = t; + } + + public void setEndMark(double t) { + endMark = t; + } + + public void clearMarks() { + startMark = endMark = Double.NaN; + } + + public void setCursorMark(double t) { + if (insetWavePanel != null) + insetWavePanel.setCursorMark(t); + } + + public void markTime(double t) { + if (Double.isNaN(startMark) && Double.isNaN(endMark)) { + startMark = t; + } else if (!Double.isNaN(startMark) && Double.isNaN(endMark)) { + endMark = t; + if (endMark < startMark) { + double tm = startMark; + startMark = endMark; + endMark = tm; + } + } else { + startMark = Math.min(lastMark, t); + endMark = Math.max(lastMark, t); + } + lastMark = t; + if (insetWavePanel != null) { + insetWavePanel.setMarks(startMark, endMark); + } + repaint(); + } + + class HelicorderMouseMotionListener implements MouseMotionListener { + public void mouseDragged(MouseEvent e) { + UiTime.touchTime(); + HelicorderViewPanel.this.requestFocus(); + int mx = e.getX(); + int my = e.getY(); + + if (mx < heliRenderer.getGraphX() || my < heliRenderer.getGraphY() + || mx > heliRenderer.getGraphX() + heliRenderer.getGraphWidth() - 1 + || my > heliRenderer.getGraphY() + heliRenderer.getGraphHeight() - 1) { + /* + * // removed because it wasn't helpful! if (insetWavePanel != + * null) { removeWaveInset(); repaint(); } return; + */ + } else { + double j2k = getMouseJ2K(mx, my); + processMousePosition(mx, my); + + if (SwingUtilities.isLeftMouseButton(e)) + createWaveInset(j2k, mx, my); + } + } + + public void mouseMoved(MouseEvent e) { + UiTime.touchTime(); + processMousePosition(e.getX(), e.getY()); + } + } + + class HelicorderMouseWheelListener implements MouseWheelListener { + int totalScroll = 0; + Delay delay; + + public void mouseWheelMoved(MouseWheelEvent e) { + UiTime.touchTime(); + totalScroll += e.getWheelRotation(); + if (delay == null) + delay = new Delay(250); + else + delay.restart(); + } + + public void delayOver() { + removeWaveInset(); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + parent.scroll(totalScroll); + delay = null; + totalScroll = 0; + } + }); + } + + class Delay extends Thread { + long delayLeft; + + public Delay(long ms) { + delayLeft = ms; + start(); + } + + public void restart() { + interrupt(); + } + + public void run() { + boolean done = false; + while (!done) { + try { + Thread.sleep(delayLeft); + done = true; + } catch (Exception e) { + } + } + delayOver(); + } + } + } + + class HelicorderMouseListener implements MouseListener { + public void mouseClicked(MouseEvent e) { + UiTime.touchTime(); + HelicorderViewPanel.this.requestFocus(); + } + + public void mouseEntered(MouseEvent e) { + } + + public void mouseExited(MouseEvent e) { + } + + public void mousePressed(MouseEvent e) { + UiTime.touchTime(); + if (e.getButton() == MouseEvent.BUTTON1) { + int mx = e.getX(); + int my = e.getY(); + if (mx < heliRenderer.getGraphX() || my < heliRenderer.getGraphY() + || mx > heliRenderer.getGraphX() + heliRenderer.getGraphWidth() - 1 + || my > heliRenderer.getGraphY() + heliRenderer.getGraphHeight() - 1) + return; + + double j2k = getMouseJ2K(mx, my); + if (j2k != -1E300) { + if (insetWavePanel != null) + insetWavePanel.setWave(null, 0, 1); + createWaveInset(j2k, mx, my); + } + } + /* + * else if (e.getButton() == MouseEvent.BUTTON3) { if + * (insetWavePanel != null) { removeWaveInset(); repaint(); } } + */ + } + + public void mouseReleased(MouseEvent e) { + } + } + + public boolean hasInset() { + return insetWavePanel != null; + } + + public HelicorderData getData() { + return heliData; + } + + public double getStartTime() { + return startTime; + } + + public double getEndTime() { + return endTime; + } + + public void insetToClipboard() { + if (insetWavePanel != null) { + WaveViewPanel p = new WaveViewPanel(insetWavePanel); + p.setDataSource(insetWavePanel.getDataSource().getCopy()); + WaveClipboardFrame cb = WaveClipboardFrame.getInstance(); + cb.setVisible(true); + cb.addWave(p); + requestFocus(); + } + } + + private void processMousePosition(int x, int y) { + if (heliData == null) + return; + + String status = null; + + boolean wp = false; + if (insetWavePanel != null) { + Point loc = insetWavePanel.getLocation(); + wp = insetWavePanel.processMousePosition(x - loc.x, y - loc.y); + } + + if (!wp) { + if (status == null) { + if (!(x < heliRenderer.getGraphX() || y < heliRenderer.getGraphY() + || x > heliRenderer.getGraphX() + heliRenderer.getGraphWidth() - 1 + || y > heliRenderer.getGraphY() + heliRenderer.getGraphHeight() - 1)) { + double j2k = getMouseJ2K(x, y); + status = dateFormat.format(J2kSec.asDate(j2k)); + TimeZone tz = swarmConfig.getTimeZone(settings.channel); + double tzo = tz.getOffset(J2kSec.asEpoch(j2k)); + if (tzo != 0) { + String tza = tz.getDisplayName(tz.inDaylightTime(J2kSec.asDate(j2k)), TimeZone.SHORT); + status = dateFormat.format(J2kSec.asDate(j2k + tzo / 1000)) + " (" + tza + "), " + status + + " (UTC)"; + } + } + } + + if (status == null) + status = " "; + + if (!Double.isNaN(startMark) && !Double.isNaN(endMark)) { + double dur = Math.abs(startMark - endMark); + String pre = String.format("Duration: %.2fs (Md: %.2f)", dur, swarmConfig.getDurationMagnitude(dur)); + if (status.length() > 2) + status = pre + ", " + status; + else + status = pre; + } + + if (status != null) + parent.setStatus(status); + } + } + + public double getMouseJ2K(int mx, int my) { + double j2k = 0; + if (translation != null) { + j2k = translation[4]; + j2k += (mx - translation[0]) * translation[7]; + j2k += getHelicorderRow(my) * translation[6]; + } + return j2k; + } + + public int getHelicorderRow(int my) { + return (int) Math.floor((my - translation[3]) / translation[2]); + } + + public void removeWaveInset() { + if (insetWavePanel != null) { + parent.setInsetButtonsEnabled(false); + this.remove(insetWavePanel); + insetWavePanel = null; + repaint(); + } + } + + /** + * Move selected wave window. + * + * @param offset + * window lengths to move + */ + public void moveInset(int offset) { + if (insetWavePanel == null) { + return; + } + + double st = insetWavePanel.getStartTime(); + double et = insetWavePanel.getEndTime(); + double dt = et - st; + double newStartTime = st + dt * offset; + double newEndTime = et + dt * offset; + + int firstRow = heliRenderer.getRow(newStartTime); + int lastRow = heliRenderer.getRow(newEndTime); + if (lastRow < 0 || firstRow > heliRenderer.getNumRows() - 1) { + return; + } + + loadInsetWave(newStartTime, newEndTime); + } + + public void createWaveInset(final double j2k, final int mx, final int my) { + if (working) + return; + + insetY = my; + + if (insetWavePanel == null) { + insetWavePanel = new WaveViewPanel(parent.getWaveViewSettings()); + insetWavePanel.addListener(new WaveViewPanelAdapter() { + public void waveClosed(AbstractWavePanel src) { + removeWaveInset(); + } + + public void waveTimePressed(AbstractWavePanel src, MouseEvent e, double j2k) { + if (swarmConfig.durationEnabled && SwingUtilities.isLeftMouseButton(e)) + markTime(j2k); + insetWavePanel.processMousePosition(e.getX(), e.getY()); + } + }); + } + + // insetWavePanel.setHelicorderPanel(this); + insetWavePanel.setMarks(startMark, endMark); + insetWavePanel.setChannel(settings.channel); + insetWavePanel.setDataSource(parent.getDataSource()); + insetWavePanel.setStatusLabel(parent.getStatusLabel()); + + Dimension d = getSize(); + insetHeight = getHeight() / 4; + int height = insetHeight; + int row = heliRenderer.getRow(j2k); + + insetWavePanel.setSize(d.width + 2, height); + double zoomOffset = parent.getHelicorderViewerSettings().waveZoomOffset; + int rowSpan = (int) Math.ceil(2 * zoomOffset / heliRenderer.getTimeChunk()); + if (insetY - heliRenderer.getRowHeight() > height + translation[GRAPH_Y]) { + int y = (int) Math.ceil((row - rowSpan) * translation[ROW_HEIGHT] + translation[GRAPH_Y]); + insetWavePanel.setLocation(-1, y - height); + } else { + int y = (int) Math.ceil((row + 1 + rowSpan) * translation[ROW_HEIGHT] + translation[GRAPH_Y]); + insetWavePanel.setLocation(-1, y); + } + + insetWavePanel.setAllowClose(true); + insetWavePanel.setWorking(true); + + loadInsetWave(j2k - zoomOffset, j2k + zoomOffset); + parent.setInsetButtonsEnabled(true); + this.add(insetWavePanel); + repaint(); + } + + private void loadInsetWave(final double st, final double et) { + fireInsetCreated(st, et); + final SwingWorker worker = new SwingWorker() { + private Wave sw; + + public Object construct() { + parent.getThrobber().increment(); + working = true; + sw = parent.getWave(st, et); + return null; + } + + public void finished() { + parent.getThrobber().decrement(); + working = false; + if (insetWavePanel != null) { + insetWavePanel.setWave(sw, st, et); + insetWavePanel.setWorking(false); + } + repaint(); + } + }; + worker.start(); + } + + public void setHelicorder(HelicorderData d, double time1, double time2) { + heliData = d; + if (heliData != null) { + startTime = time1; + endTime = time2; + heliRenderer.setData(heliData); + heliRenderer.setTimeChunk(settings.timeChunk); + heliRenderer.setTimeZone(swarmConfig.getTimeZone(settings.channel)); + heliRenderer.setForceCenter(settings.forceCenter); + heliRenderer.setClipBars(settings.clipBars); + heliRenderer.setShowClip(settings.showClip); + heliRenderer.setAlertClip(settings.alertClip); + heliRenderer.setClipWav("clip.wav"); + heliRenderer.setClipAlertTimeout(settings.alertClipTimeout); + mean = heliData.getMeanMax(); + bias = heliData.getBias(); + if (bias != mean) + mean = Math.abs(bias - mean); + heliRenderer.setClipValue(settings.clipValue); + + } + } + + public void invalidateImage() { + final SwingWorker worker = new SwingWorker() { + public Object construct() { + createImage(); + return null; + } + + public void finished() { + displayImage = bufferImage; + repaint(); + } + }; + worker.start(); + } + + protected void setResized(boolean b) { + resized = b; + } + + private void createImage() { + if (heliData == null) + return; + + Dimension d = this.getSize(); + if (d.width <= 0 || d.height <= 0) + return; + + bufferImage = new BufferedImage(d.width, d.height, BufferedImage.TYPE_4BYTE_ABGR); + + Graphics2D ig = (Graphics2D) bufferImage.getGraphics(); + plot.setSize(d); + + double offset = 0; + double multiplier = 1; + Metadata md = swarmConfig.getMetadata(settings.channel); + if (md != null) { + offset = md.getOffset(); + multiplier = md.getMultiplier(); + } + + if (minimal) + heliRenderer.setLocation(X_OFFSET / 2, Y_OFFSET, d.width - X_OFFSET - 5, + d.height - Y_OFFSET - BOTTOM_HEIGHT / 2); + else + heliRenderer.setLocation(X_OFFSET, Y_OFFSET, d.width - X_OFFSET - RIGHT_WIDTH, + d.height - Y_OFFSET - BOTTOM_HEIGHT); + + if (settings.autoScale) { + settings.barRange = (int) (mean * settings.barMult); + settings.clipValue = (int) (mean * settings.clipBars); + heliRenderer.setHelicorderExtents(startTime, endTime, -1 * Math.abs(settings.barRange), + Math.abs(settings.barRange)); + + } else { + heliRenderer.setHelicorderExtents(startTime, endTime, + -1 * Math.abs((settings.barRange - offset) / multiplier), + Math.abs((settings.barRange - offset) / multiplier)); + } + + heliRenderer.setTimeZone(swarmConfig.getTimeZone(settings.channel)); + heliRenderer.setClipValue(settings.clipValue); + if (minimal) { + // System.out.println("minimal"); + // heliRenderer.createMinimumAxis(); + heliRenderer.setFrameDecorator(new SmallDecorator()); + } else + heliRenderer.createDefaultAxis(); + + if (md == null || md.getAlias() == null) + heliRenderer.setChannel(settings.channel); + else + heliRenderer.setChannel(md.getAlias()); + + translation = heliRenderer.getTranslationInfo(false); + heliRenderer.setLargeChannelDisplay(fullScreen); + + try { + plot.render(ig); + } catch (PlotException e) { + e.printStackTrace(); + } + } + + class SmallDecorator extends FrameDecorator { + public void decorate(FrameRenderer fr) { + HelicorderRenderer hr = (HelicorderRenderer) fr; + AxisRenderer axis = new AxisRenderer(fr); + axis.createDefault(); + fr.setAxis(axis); + + int minutes = (int) Math.round(settings.timeChunk / 60.0); + int majorTicks = minutes; + if (minutes > 30 && minutes < 180) + majorTicks = minutes / 5; + else if (minutes >= 180 && minutes < 360) + majorTicks = minutes / 10; + else if (minutes >= 360) + majorTicks = minutes / 20; + double[] mjt = SmartTick.intervalTick(0, settings.timeChunk, majorTicks); + + axis.createBottomTicks(null, mjt); + axis.createTopTicks(null, mjt); + axis.createVerticalGridLines(mjt); + + int bc = (settings.timeChunk / 5) + 2; + String[] btl = new String[bc]; + double[] btlv = new double[bc]; + btl[0] = "+"; + btlv[0] = 30; + for (int i = 0, j = 1; i < mjt.length; i++) { + long m = Math.round(mjt[i] / 60.0); + if (m % 5 == 0) { + btl[j] = Long.toString(m); + btlv[j++] = mjt[i]; + } + } + axis.createBottomTickLabels(btlv, btl); + + int numRows = hr.getNumRows(); + double[] labelPosLR = new double[numRows]; + String[] leftLabelText = new String[numRows]; + String[] rightLabelText = new String[numRows]; + TimeZone timeZone = swarmConfig.getTimeZone(settings.channel); + + DateFormat localTimeFormat = new SimpleDateFormat("HH:mm"); + localTimeFormat.setTimeZone(timeZone); + + DateFormat localDayFormat = new SimpleDateFormat("MM-dd"); + localDayFormat.setTimeZone(timeZone); + + double pixelsPast = 0; + double pixelsPerRow = fr.getGraphHeight() / numRows; + String lastLocalDay = ""; + for (int i = numRows - 1; i >= 0; i--) { + pixelsPast += pixelsPerRow; + labelPosLR[i] = i + 0.5; + + String localTime = localTimeFormat + .format(J2kSec.asDate(hr.getHelicorderMaxX() - (i + 1) * settings.timeChunk)); + leftLabelText[i] = null; + if (pixelsPast > 20) { + leftLabelText[i] = localTime; + pixelsPast = 0; + } + + Date dtz = J2kSec.asDate(hr.getHelicorderMaxX() - (i + 1) * settings.timeChunk); + String localDay = localDayFormat.format(new Date(dtz.getTime() + settings.timeChunk * 1000)); + if (!localDay.equals(lastLocalDay)) { + rightLabelText[i] = localDay; + lastLocalDay = localDay; + } + } + + axis.createLeftTickLabels(labelPosLR, leftLabelText); + axis.createRightTickLabels(labelPosLR, rightLabelText); + + boolean dst = timeZone.inDaylightTime(J2kSec.asDate(hr.getViewEndTime())); + String timeZoneName = timeZone.getDisplayName(dst, TimeZone.SHORT); + TextRenderer tr = new TextRenderer(3, fr.getGraphY() + fr.getGraphHeight() + 14, timeZoneName); + tr.font = Font.decode("dialog-PLAIN-9"); + tr.antiAlias = false; + axis.addPostRenderer(tr); + + double[] hg = new double[numRows - 1]; + for (int i = 0; i < numRows - 1; i++) + hg[i] = i + 1.0; + + axis.createHorizontalGridLines(hg); + + axis.setBackgroundColor(Color.white); + } + } + + public void setFullScreen(boolean b) { + fullScreen = b; + } + + public void setMinimal(boolean b) { + minimal = b; + } + + private void drawMark(Graphics2D g2, double t, Color color) { + if (Double.isNaN(t)) + return; + + int x = (int) (heliRenderer.helicorderGetXPixel(t)); + int row = heliRenderer.getRow(t); + int y = (int) Math.ceil(row * translation[2] + translation[3]); + g2.setColor(color); + g2.draw(new Line2D.Double(x, y, x, y + translation[2])); + + GeneralPath gp = new GeneralPath(); + gp.moveTo(x, y); + gp.lineTo((float) x - 4, (float) y - 6); + gp.lineTo((float) x + 4, (float) y - 6); + gp.closePath(); + g2.setColor(Color.GREEN); + g2.fill(gp); + g2.setColor(DARK_GREEN); + g2.draw(gp); + + gp.reset(); + gp.moveTo(x, (float) (y + translation[2])); + gp.lineTo((float) x - 4, (float) (y + 6 + translation[2])); + gp.lineTo((float) x + 4, (float) (y + 6 + translation[2])); + gp.closePath(); + gp.closePath(); + g2.setColor(Color.GREEN); + g2.fill(gp); + g2.setColor(DARK_GREEN); + g2.draw(gp); + } + + private static final Color DARK_GREEN = new Color(0, 168, 0); + + public void paint(Graphics g) { + Graphics2D g2 = (Graphics2D) g; + Dimension d = this.getSize(); + if (heliData == null) { + if (!parent.isWorking()) { + parent.setStatus("The server returned no helicorder data."); + } + } else if (displayImage != null) + g2.drawImage(displayImage, 0, 0, null); + + drawMark(g2, startMark, DARK_GREEN); + drawMark(g2, endMark, DARK_GREEN); + + if (insetWavePanel != null) { + // find out where time highlight will be, possibly reposition the + // insetWavePanel + double t1 = insetWavePanel.getStartTime(); + double t2 = insetWavePanel.getEndTime(); + double dt = t2 - t1; + double spanCenter = (t2 - t1) / 2 + t1; + int row = heliRenderer.getRow(spanCenter); + + int rowSpan = (int) Math.ceil(dt / heliRenderer.getMaxX()) + 1; + double rowHeight = translation[ROW_HEIGHT]; + double graphY = translation[GRAPH_Y]; + + if (resized) { + insetWavePanel.setSize(d.width, insetHeight); + insetWavePanel.createImage(); + } + + int panelY; + if ((row - rowSpan) * rowHeight > insetHeight + graphY) { + panelY = (int) Math.ceil((row - rowSpan) * rowHeight + graphY - insetHeight); + } else { + panelY = (int) Math.ceil((row + rowSpan) * rowHeight + graphY); + } + + if (panelY != insetWavePanel.getLocation().y) { + insetWavePanel.setLocation(0, panelY); + } + + // now it's safe to draw the waveInsetPanel + Point p = insetWavePanel.getLocation(); + g2.translate(p.x, p.y); + insetWavePanel.paint(g2); + Dimension wvd = insetWavePanel.getSize(); + g.setColor(Color.gray); + g.drawRect(0, 0, wvd.width - 1, wvd.height); + g2.translate(-p.x, -p.y); + + /* + * Not sure what below is meant to address. I removed the check + * because negative row offsets were necessary to handle multi-row + * highlights. If there's a problem with wave loading, find a more + * direct fix. --tjp + */ + // fixes bug where highlight was being drawn before wave loaded + // if (row < 0) + // return; + + // finally, draw the highlight. + Paint pnt = g2.getPaint(); + g2.setPaint(new Color(255, 255, 0, 128)); + Rectangle2D.Double rect = new Rectangle2D.Double(); + + int zoomTimeSpan = parent.getHelicorderViewerSettings().waveZoomOffset * 2; + double zoomPixelSpan = 1.0 / translation[PIXEL_TIME_SPAN] * zoomTimeSpan; + + // highlight left + int highlightStart = (int) heliRenderer.helicorderGetXPixel(spanCenter); + int highlightSpan = (int) (zoomPixelSpan / 2); + int rowOffset = 0; + while (highlightSpan > 0 && row - rowOffset >= 0) { + int width = Math.min(highlightSpan, (int) (highlightStart - translation[GRAPH_LEFT])); + + if (row - rowOffset < heliRenderer.getNumRows()) { + rect.setRect(highlightStart - width, + (int) Math.ceil((row - rowOffset) * translation[ROW_HEIGHT] + translation[GRAPH_Y]), width, + (int) Math.ceil(translation[ROW_HEIGHT])); + g2.fill(rect); + } + highlightSpan -= width; + highlightStart = (int) translation[GRAPH_RIGHT]; + rowOffset++; + + } + + // highlight right + highlightStart = (int) heliRenderer.helicorderGetXPixel(spanCenter); + highlightSpan = (int) (zoomPixelSpan / 2); + rowOffset = 0; + while (highlightSpan > 0 && row + rowOffset < heliRenderer.getNumRows()) { + int width = Math.min(highlightSpan, (int) (translation[GRAPH_RIGHT] - highlightStart)); + + if (row + rowOffset >= 0) { + rect.setRect(highlightStart, + (int) Math.ceil((row + rowOffset) * translation[ROW_HEIGHT] + translation[GRAPH_Y]), width, + (int) Math.ceil(translation[ROW_HEIGHT])); + g2.fill(rect); + } + + highlightSpan -= width; + highlightStart = (int) translation[GRAPH_LEFT]; + rowOffset++; + } + + g2.setPaint(pnt); + } + + resized = false; + } + + public void optionsChanged() { + cursorChanged(); + invalidateImage(); + if (!SwarmConfig.getInstance().durationEnabled) + clearMarks(); + } + + // public void insetToPicker() { + // if (insetWavePanel != null) { + // LOGGER.debug("Sending wave to picker"); + // Swarm.openPicker(insetWavePanel); + // } + // } } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/heli/HelicorderViewerFrame.java b/src/main/java/gov/usgs/volcanoes/swarm/heli/HelicorderViewerFrame.java index ee6c2018..01bb4303 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/heli/HelicorderViewerFrame.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/heli/HelicorderViewerFrame.java @@ -75,9 +75,12 @@ public class HelicorderViewerFrame extends SwarmFrame implements Kioskable { public static final long serialVersionUID = -1; + private static final int MINUTE = 60; + private static final int HOUR = MINUTE * 60; + // minutes * 60 = seconds public static final int[] chunkValues = - new int[] {10 * 60, 15 * 60, 20 * 60, 30 * 60, 60 * 60, 120 * 60, 180 * 60, 360 * 60}; + new int[] {10 * MINUTE, 15 * MINUTE, 20 * MINUTE, 30 * MINUTE, 1 * HOUR, 2 * HOUR, 180 * MINUTE, 6 * HOUR}; // hours * 60 = minutes public static final int[] spanValues = @@ -85,7 +88,7 @@ public class HelicorderViewerFrame extends SwarmFrame implements Kioskable { 144 * 60, 168 * 60, 192 * 60, 216 * 60, 240 * 60, 264 * 60, 288 * 60, 312 * 60, 336 * 60}; // seconds - public static final int[] zoomValues = new int[] {1, 2, 5, 10, 20, 30, 60, 120, 300, 600}; + public static final int[] zoomValues = new int[] {1, 2, 5, 10, 20, 30, MINUTE, 2 * MINUTE, 5 * MINUTE, 10 * MINUTE, 20 * MINUTE, 40 * MINUTE, 1 * HOUR, 90 * MINUTE}; private final RefreshThread refreshThread; private final SeismicDataSource dataSource; diff --git a/src/main/java/gov/usgs/volcanoes/swarm/heli/HelicorderViewerSettingsDialog.java b/src/main/java/gov/usgs/volcanoes/swarm/heli/HelicorderViewerSettingsDialog.java index e9c3c8ad..ef3231b5 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/heli/HelicorderViewerSettingsDialog.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/heli/HelicorderViewerSettingsDialog.java @@ -29,7 +29,7 @@ import gov.usgs.volcanoes.core.time.J2kSec; import gov.usgs.volcanoes.swarm.Icons; import gov.usgs.volcanoes.swarm.Swarm; -import gov.usgs.volcanoes.swarm.SwarmDialog; +import gov.usgs.volcanoes.swarm.SwarmModalDialog; import gov.usgs.volcanoes.swarm.SwingWorker; import gov.usgs.volcanoes.swarm.wave.WaveViewSettings; import gov.usgs.volcanoes.swarm.wave.WaveViewSettingsDialog; @@ -38,7 +38,7 @@ * * @author Dan Cervelli */ -public class HelicorderViewerSettingsDialog extends SwarmDialog { +public class HelicorderViewerSettingsDialog extends SwarmModalDialog { public static final long serialVersionUID = -1; private HelicorderViewerSettings settings; @@ -68,7 +68,7 @@ public class HelicorderViewerSettingsDialog extends SwarmDialog { private HelicorderViewerSettingsDialog() { // super(Swarm.getApplication(), "Helicorder View Settings", true, // WIDTH, HEIGHT); - super(Swarm.getApplicationFrame(), "Helicorder View Settings", true); + super(Swarm.getApplicationFrame(), "Helicorder View Settings"); utcDateFormat = new SimpleDateFormat("yyyyMMddHHmm"); utcDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); diff --git a/src/main/java/gov/usgs/volcanoes/swarm/map/ClickableGeoLabel.java b/src/main/java/gov/usgs/volcanoes/swarm/map/ClickableGeoLabel.java index 0c89a0d6..23404587 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/map/ClickableGeoLabel.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/map/ClickableGeoLabel.java @@ -15,7 +15,7 @@ */ abstract public class ClickableGeoLabel extends GeoLabel { - abstract public void mouseClicked(MouseEvent e); + abstract public boolean mouseClicked(MouseEvent e); abstract public Rectangle getClickBox(); public ClickableGeoLabel() diff --git a/src/main/java/gov/usgs/volcanoes/swarm/map/FdsnLabelSource.java b/src/main/java/gov/usgs/volcanoes/swarm/map/FdsnLabelSource.java deleted file mode 100644 index ad57839d..00000000 --- a/src/main/java/gov/usgs/volcanoes/swarm/map/FdsnLabelSource.java +++ /dev/null @@ -1,115 +0,0 @@ -package gov.usgs.volcanoes.swarm.map; - -import java.awt.geom.Point2D; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.TimeZone; - -import javax.xml.stream.XMLStreamException; - -import edu.sc.seis.seisFile.SeisFileException; -import edu.sc.seis.seisFile.fdsnws.FDSNEventQuerier; -import edu.sc.seis.seisFile.fdsnws.FDSNEventQueryParams; -import edu.sc.seis.seisFile.fdsnws.FDSNWSException; -import edu.sc.seis.seisFile.fdsnws.quakeml.Event; -import edu.sc.seis.seisFile.fdsnws.quakeml.EventIterator; -import edu.sc.seis.seisFile.fdsnws.quakeml.Magnitude; -import edu.sc.seis.seisFile.fdsnws.quakeml.Origin; -import edu.sc.seis.seisFile.fdsnws.quakeml.QuakeMLTagNames; -import edu.sc.seis.seisFile.fdsnws.quakeml.Quakeml; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import gov.usgs.proj.GeoRange; -import gov.usgs.volcanoes.core.time.J2kSec; -import gov.usgs.volcanoes.core.time.Time; - -/* - * A class to retrieve events from IRIS for plotting on the map. - * - * @author: Tom Parker - */ -public class FdsnLabelSource implements LabelSource { - - private static final Logger LOGGER = LoggerFactory.getLogger(FdsnLabelSource.class); - - - public List getLabels() { - - FDSNEventQueryParams queryParams = new FDSNEventQueryParams(); - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); - sdf.setTimeZone(TimeZone.getTimeZone("GMT")); - - MapFrame mapFrame = MapFrame.getInstance(); - if (mapFrame == null) - return null; - GeoRange range = mapFrame.getRange(); - - float minLat = (float) range.getSouth(); - float maxLat = (float) range.getNorth(); - float minLon = (float) range.getWest(); - float maxLon = (float) range.getEast(); - - Date endTime = new Date(System.currentTimeMillis()); - Date startTime = new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000L); - - queryParams.area(minLat, maxLat, minLon, maxLon).setStartTime(startTime).setEndTime(endTime) - .setMaxDepth(100).setMinMagnitude(1).setOrderBy(FDSNEventQueryParams.ORDER_TIME_ASC); - - FDSNEventQuerier querier = new FDSNEventQuerier(queryParams); - // System.err.println("::" + querier.getConnectionUri()); - Quakeml quakeml = null; - try { - quakeml = querier.getQuakeML(); - } catch (FDSNWSException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - return null; - } - if (!quakeml.checkSchemaVersion()) { - System.out.println(""); - System.out.println( - "WARNING: XmlSchema of this document does not match this code, results may be incorrect."); - System.out.println("XmlSchema (code): " + QuakeMLTagNames.CODE_MAIN_SCHEMA_VERSION); - System.out.println("XmlSchema (doc): " + quakeml.getSchemaVersion()); - } - - List hypos = new ArrayList(); - EventIterator eIt = quakeml.getEventParameters().getEvents(); - try { - while (eIt.hasNext()) { - Event e = eIt.next(); - Origin o = e.getOriginList().get(0); - Magnitude m = e.getMagnitudeList().get(0); - System.out.println(o.getLatitude() + "/" + o.getLongitude() + " " + m.getMag().getValue() - + " " + m.getType() + " " + o.getTime().getValue()); - Hypocenter h = new Hypocenter(); - double lat = o.getLatitude().getValue(); - double lon = o.getLongitude().getValue(); - h.location = new Point2D.Double(lon, lat); - h.text = "M" + m.getMag().getValue(); - h.depth = o.getDepth().getValue(); - try { - h.time = J2kSec.parse(Time.FDSN_TIME_FORMAT, o.getTime().getValue()); - } catch (ParseException e1) { - LOGGER.info("Can't parse label date: {}", o.getTime().getValue()); - continue; - } - hypos.add(h); - - } - } catch (XMLStreamException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (SeisFileException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - return hypos; - } -} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/map/Hypocenter.java b/src/main/java/gov/usgs/volcanoes/swarm/map/Hypocenter.java deleted file mode 100644 index b70d686d..00000000 --- a/src/main/java/gov/usgs/volcanoes/swarm/map/Hypocenter.java +++ /dev/null @@ -1,80 +0,0 @@ -package gov.usgs.volcanoes.swarm.map; - -import gov.usgs.math.Geometry; -import gov.usgs.plot.render.DataPointRenderer; -import gov.usgs.util.Pair; -import gov.usgs.volcanoes.swarm.Metadata; -import gov.usgs.volcanoes.swarm.SwarmConfig; -import gov.usgs.volcanoes.swarm.wave.MultiMonitor; -import gov.usgs.volcanoes.swarm.wave.SwarmMultiMonitors; - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Rectangle; -import java.awt.event.MouseEvent; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * - * @author Dan Cervelli - */ -public class Hypocenter extends ClickableGeoLabel -{ - public double time; - public double depth; - public double magnitude; - - private static SwarmConfig swarmConfig; - - public Hypocenter() - { - swarmConfig = SwarmConfig.getInstance(); - - DataPointRenderer r = new DataPointRenderer(); - r.antiAlias = true; - r.stroke = new BasicStroke(1.2f); - r.filled = true; - r.paint = Color.RED; - r.color = Color.yellow; - r.shape = Geometry.STAR_10; - marker = r; - } - - @Override - public Rectangle getClickBox() - { - return new Rectangle(-7, -7, 17, 17); - } - - @Override - public void mouseClicked(MouseEvent e) - { - Map metadata = swarmConfig.getMetadata(); - List> nrst = Metadata.findNearest(swarmConfig.getMetadata(), location, true); - Set cleared = new HashSet(); - if (nrst != null) - { - for (int i = 0, total = 0; i < nrst.size() && total < 10; i++) - { - String ch = nrst.get(i).item2; - if (ch.matches(".* ..Z .*")) - { - Metadata md = metadata.get(ch); - MultiMonitor mm = SwarmMultiMonitors.getMonitor(md.source); - if (!cleared.contains(mm)) - { - mm.removeAllWaves(); - cleared.add(mm); - } - mm.addChannel(ch); - mm.setVisible(true); - mm.setPauseStartTime(time - 4); - total++; - } - } - } - } -} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/map/MapFrame.java b/src/main/java/gov/usgs/volcanoes/swarm/map/MapFrame.java index fe6dd421..bcf46181 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/map/MapFrame.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/map/MapFrame.java @@ -1,5 +1,8 @@ package gov.usgs.volcanoes.swarm.map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.awt.BorderLayout; import java.awt.Graphics; import java.awt.Insets; @@ -12,6 +15,7 @@ import java.beans.PropertyVetoException; import java.io.File; import java.io.FileOutputStream; +import java.net.MalformedURLException; import java.text.ParseException; import javax.swing.BorderFactory; @@ -35,7 +39,6 @@ import gov.usgs.volcanoes.core.configfile.ConfigFile; import gov.usgs.volcanoes.core.contrib.PngEncoder; import gov.usgs.volcanoes.core.contrib.PngEncoderB; -import gov.usgs.volcanoes.core.time.CurrentTime; import gov.usgs.volcanoes.core.time.J2kSec; import gov.usgs.volcanoes.core.util.UiUtils; import gov.usgs.volcanoes.swarm.FileChooser; @@ -47,6 +50,7 @@ import gov.usgs.volcanoes.swarm.heli.HelicorderViewPanelListener; import gov.usgs.volcanoes.swarm.map.MapPanel.DragMode; import gov.usgs.volcanoes.swarm.map.MapPanel.LabelSetting; +import gov.usgs.volcanoes.swarm.map.hypocenters.HypocenterLayer; import gov.usgs.volcanoes.swarm.options.SwarmOptions; import gov.usgs.volcanoes.swarm.options.SwarmOptionsListener; import gov.usgs.volcanoes.swarm.time.UiTime; @@ -59,519 +63,535 @@ * @author Dan Cervelli */ public class MapFrame extends SwarmFrame implements Runnable, Kioskable, SwarmOptionsListener { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; + private static final Logger LOGGER = LoggerFactory.getLogger(MapFrame.class); - private JToolBar toolbar; - private JPanel mainPanel; - private JButton optionsButton; - private JButton labelButton; - private JLabel statusLabel; - private JToggleButton linkButton; - private JToggleButton realtimeButton; - private JButton compXButton; - private JButton expXButton; - private JButton forwardTimeButton; - private JButton backTimeButton; - private JButton gotoButton; - private JButton timeHistoryButton; - private JButton captureButton; - private JButton clipboardButton; - - private JToggleButton dragButton; - private JToggleButton dragZoomButton; - private JToggleButton rulerButton; - - private WaveViewSettingsToolbar waveToolbar; - private WaveViewPanel selected; - - private final Thread updateThread; - - private MapPanel mapPanel; - - private Throbber throbber; - - private int spanIndex = 3; - private boolean realtime = true; - - private long refreshInterval = 1000; - - private HelicorderViewPanelListener linkListener; - - private boolean heliLinked = true; - - private Border border; - - private MapFrame() { - super("Map", true, true, true, false); - this.setFocusable(true); - UiTime.touchTime(); - - createUI(); - - updateThread = new Thread(this, "Map Update"); - updateThread.start(); - SwarmOptions.addOptionsListener(this); - } - - public static MapFrame getInstance() { - return MapFrameHolder.mapFrame; - } - - @Override - public void saveLayout(final ConfigFile cf, final String prefix) { - super.saveLayout(cf, prefix); - mapPanel.saveLayout(cf, prefix + ".panel"); - } - - public void processLayout(final ConfigFile cf) { - processStandardLayout(cf); - mapPanel.processLayout(cf.getSubConfig("panel")); - final LabelSetting ls = mapPanel.getLabelSetting(); - labelButton.setIcon(ls.getIcon()); - } - - private void createUI() { - setFrameIcon(Icons.earth); - setSize(swarmConfig.mapWidth, swarmConfig.mapHeight); - setLocation(swarmConfig.mapX, swarmConfig.mapY); - setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); - - mainPanel = new JPanel(new BorderLayout()); - - createToolbar(); - - mainPanel.add(toolbar, BorderLayout.NORTH); - - mapPanel = new MapPanel(); - - border = BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 2, 0, 3), - LineBorder.createGrayLineBorder()); - mapPanel.setBorder(border); - mainPanel.add(mapPanel, BorderLayout.CENTER); - statusLabel = new JLabel(" "); - statusLabel.setBorder(BorderFactory.createEmptyBorder(1, 5, 1, 1)); - mainPanel.add(statusLabel, BorderLayout.SOUTH); - setContentPane(mainPanel); - - this.addComponentListener(new ComponentAdapter() { - @Override - public void componentShown(final ComponentEvent e) { - if (!mapPanel.imageValid()) - mapPanel.resetImage(); - } - }); - - this.addInternalFrameListener(new InternalFrameAdapter() { - @Override - public void internalFrameClosing(final InternalFrameEvent e) { - setVisible(false); - } - }); - - linkListener = new HelicorderViewPanelListener() { - public void insetCreated(final double st, final double et) { - if (heliLinked) { - if (!realtime) - mapPanel.timePush(); - - setRealtime(false); - mapPanel.setTimes(st, et, true); - } - } - }; - - setVisible(true); - } - - @Override - public void setVisible(final boolean isVisible) { - super.setVisible(isVisible); - if (isVisible) - toFront(); - } - - @Override - public void setMaximum(final boolean max) throws PropertyVetoException { - if (max) { - swarmConfig.mapX = getX(); - swarmConfig.mapY = getY(); - } - super.setMaximum(max); - } - - public GeoRange getRange() { - return mapPanel.getRange(); - } - - private void createToolbar() { - toolbar = SwarmUtil.createToolBar(); - optionsButton = - SwarmUtil.createToolBarButton(Icons.settings, "Map options", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - final MapSettingsDialog msd = MapSettingsDialog.getInstance(MapFrame.this); - msd.setVisible(true); - } - }); - toolbar.add(optionsButton); - - labelButton = SwarmUtil.createToolBarButton(Icons.label_some, "Change label settings", - new ActionListener() { - public void actionPerformed(final ActionEvent e) { - final LabelSetting ls = mapPanel.getLabelSetting().next(); - labelButton.setIcon(ls.getIcon()); - mapPanel.setLabelSetting(ls); - } - }); - toolbar.add(labelButton); - - toolbar.addSeparator(); - - realtimeButton = - SwarmUtil.createToolBarToggleButton(Icons.clock, "Realtime mode (N)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - setRealtime(realtimeButton.isSelected()); - } - }); - UiUtils.mapKeyStrokeToButton(this, "N", "realtime", realtimeButton); - realtimeButton.setSelected(realtime); - toolbar.add(realtimeButton); - - linkButton = SwarmUtil.createToolBarToggleButton(Icons.helilink, - "Synchronize times with helicorder wave (H)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - heliLinked = linkButton.isSelected(); - } - }); - UiUtils.mapKeyStrokeToButton(this, "H", "helilink", linkButton); - linkButton.setSelected(heliLinked); - toolbar.add(linkButton); - - toolbar.addSeparator(); - - final JButton earthButton = SwarmUtil.createToolBarButton(Icons.earth, - "Zoom out to full scale (Home)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - final Point2D.Double c = new Point2D.Double(mapPanel.getCenter().x, 0); - mapPanel.setCenterAndScale(c, 100000); - } - }); - UiUtils.mapKeyStrokeToButton(this, "HOME", "home", earthButton); - toolbar.add(earthButton); - - dragButton = - SwarmUtil.createToolBarToggleButton(Icons.drag, "Drag map (D)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - mapPanel.setDragMode(DragMode.DRAG_MAP); - } - }); - UiUtils.mapKeyStrokeToButton(this, "D", "drag", dragButton); - dragButton.setSelected(true); - toolbar.add(dragButton); - - dragZoomButton = SwarmUtil.createToolBarToggleButton(Icons.dragbox, "Zoom into box (B)", - new ActionListener() { - public void actionPerformed(final ActionEvent e) { - mapPanel.setDragMode(DragMode.BOX); - } - }); - UiUtils.mapKeyStrokeToButton(this, "B", "box", dragZoomButton); - dragZoomButton.setSelected(false); - toolbar.add(dragZoomButton); - - rulerButton = SwarmUtil.createToolBarToggleButton(Icons.ruler, "Measure distances (M)", - new ActionListener() { - public void actionPerformed(final ActionEvent e) { - mapPanel.setDragMode(DragMode.RULER); - } - }); - UiUtils.mapKeyStrokeToButton(this, "M", "measure", rulerButton); - toolbar.add(rulerButton); - toolbar.addSeparator(); - - final ButtonGroup group = new ButtonGroup(); - group.add(dragButton); - group.add(dragZoomButton); - group.add(rulerButton); - - final JButton zoomIn = - SwarmUtil.createToolBarButton(Icons.zoomplus, "Zoom in (+)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - mapPanel.zoom(0.5); - } - }); - UiUtils.mapKeyStrokeToButton(this, "EQUALS", "zoomin1", zoomIn); - UiUtils.mapKeyStrokeToButton(this, "shift EQUALS", "zoomin2", zoomIn); - toolbar.add(zoomIn); - - final JButton zoomOut = - SwarmUtil.createToolBarButton(Icons.zoomminus, "Zoom out (-)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - mapPanel.zoom(2); - } - }); - UiUtils.mapKeyStrokeToButton(this, "MINUS", "zoomout1", zoomOut); - toolbar.add(zoomOut); - - final JButton backButton = SwarmUtil.createToolBarButton(Icons.geoback, - "Last map view (Ctrl-backspace)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - mapPanel.mapPop(); - } - }); - UiUtils.mapKeyStrokeToButton(this, "ctrl BACK_SPACE", "mapback1", backButton); - toolbar.add(backButton); - - toolbar.addSeparator(); - - backTimeButton = SwarmUtil.createToolBarButton(Icons.left, "Scroll back time 20% (Left arrow)", - new ActionListener() { - public void actionPerformed(final ActionEvent e) { - setRealtime(false); - mapPanel.shiftTime(-0.20); - } - }); - UiUtils.mapKeyStrokeToButton(this, "LEFT", "backward1", backTimeButton); - toolbar.add(backTimeButton); - - forwardTimeButton = SwarmUtil.createToolBarButton(Icons.right, - "Scroll forward time 20% (Right arrow)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - setRealtime(false); - mapPanel.shiftTime(0.20); - } - }); - toolbar.add(forwardTimeButton); - UiUtils.mapKeyStrokeToButton(this, "RIGHT", "forward1", forwardTimeButton); - - gotoButton = - SwarmUtil.createToolBarButton(Icons.gototime, "Go to time (Ctrl-G)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - String t = JOptionPane.showInputDialog(applicationFrame, - "Input time in 'YYYYMMDDhhmm[ss]' format:", "Go to Time", - JOptionPane.PLAIN_MESSAGE); - if (t != null) { - if (t.length() == 12) - t = t + "30"; - - double j2k; - - try { - j2k = J2kSec.parse("yyyyMMddHHmmss", t); - setRealtime(false); - mapPanel.gotoTime(j2k); - } catch (final ParseException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - } - } - }); - toolbar.add(gotoButton); - UiUtils.mapKeyStrokeToButton(this, "ctrl G", "goto", gotoButton); - - compXButton = SwarmUtil.createToolBarButton(Icons.xminus, "Shrink time axis (Alt-left arrow", - new ActionListener() { - public void actionPerformed(final ActionEvent e) { - if (realtime) { - if (spanIndex != 0) - spanIndex--; - } else { - mapPanel.scaleTime(0.20); - } - } - }); - toolbar.add(compXButton); - UiUtils.mapKeyStrokeToButton(this, "alt LEFT", "compx", compXButton); - - expXButton = SwarmUtil.createToolBarButton(Icons.xplus, "Expand time axis (Alt-right arrow)", - new ActionListener() { - public void actionPerformed(final ActionEvent e) { - if (realtime) { - if (spanIndex < MultiMonitor.SPANS.length - 1) - spanIndex++; - } else { - mapPanel.scaleTime(-0.20); - } - } - }); - toolbar.add(expXButton); - UiUtils.mapKeyStrokeToButton(this, "alt RIGHT", "expx", expXButton); - - timeHistoryButton = SwarmUtil.createToolBarButton(Icons.timeback, - "Last time settings (Backspace)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - if (mapPanel.timePop()) - setRealtime(false); - } - }); - UiUtils.mapKeyStrokeToButton(this, "BACK_SPACE", "back", timeHistoryButton); - toolbar.add(timeHistoryButton); - toolbar.addSeparator(); - - waveToolbar = new WaveViewSettingsToolbar(null, toolbar, this); - - clipboardButton = SwarmUtil.createToolBarButton(Icons.clipboard, - "Copy inset to clipboard (C or Ctrl-C)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - mapPanel.wavesToClipboard(); - } - }); - UiUtils.mapKeyStrokeToButton(this, "control C", "clipboard1", clipboardButton); - UiUtils.mapKeyStrokeToButton(this, "C", "clipboard2", clipboardButton); - toolbar.add(clipboardButton); - - toolbar.addSeparator(); - - captureButton = SwarmUtil.createToolBarButton(Icons.camera, "Save map image (P)", - new CaptureActionListener()); - UiUtils.mapKeyStrokeToButton(this, "P", "capture", captureButton); - toolbar.add(captureButton); - - toolbar.addSeparator(); - - toolbar.add(Box.createHorizontalGlue()); - throbber = new Throbber(); - toolbar.add(throbber); - } - - class CaptureActionListener implements ActionListener { - public void actionPerformed(final ActionEvent e) { - final JFileChooser chooser = FileChooser.getFileChooser(); - final File lastPath = new File(swarmConfig.lastPath); - chooser.setCurrentDirectory(lastPath); - chooser.setSelectedFile(new File("map.png")); - chooser.setDialogTitle("Save Map Screen Capture"); - final int result = chooser.showSaveDialog(applicationFrame); - File f = null; - if (result == JFileChooser.APPROVE_OPTION) { - f = chooser.getSelectedFile(); - - if (f.exists()) { - final int choice = JOptionPane.showConfirmDialog(applicationFrame, - "File exists, overwrite?", "Confirm", JOptionPane.YES_NO_OPTION); - if (choice != JOptionPane.YES_OPTION) - return; - } - swarmConfig.lastPath = f.getParent(); - } - if (f == null) - return; - - final Insets i = mapPanel.getInsets(); - final BufferedImage image = new BufferedImage(mapPanel.getWidth() - i.left - i.right, - mapPanel.getHeight() - i.top - i.bottom, BufferedImage.TYPE_4BYTE_ABGR); - final Graphics g = image.getGraphics(); - g.translate(-i.left, -i.top); - mapPanel.paint(g); - try { - final PngEncoderB png = new PngEncoderB(image, false, PngEncoder.FILTER_NONE, 7); - final FileOutputStream out = new FileOutputStream(f); - final byte[] bytes = png.pngEncode(); - out.write(bytes); - out.close(); - } catch (final Exception ex) { - ex.printStackTrace(); - } - } - } - - public void setRealtime(final boolean b) { - realtime = b; - realtimeButton.setSelected(realtime); - } - - public Throbber getThrobber() { - return throbber; - } - - public void setRefreshInterval(final long r) { - refreshInterval = r; - } - - public long getRefreshInterval() { - return refreshInterval; - } - - public MapPanel getMapPanel() { - return mapPanel; - } - - public void setSelectedWave(final WaveViewPanel wvp) { - selected = wvp; - waveToolbar.setSettings(selected.getSettings()); - } - - public void setView(final GeoRange gr) { - final double lr1 = gr.getLonRange(); - final double lr2 = GeoRange.getLonRange(gr.getEast(), gr.getWest()); - if (lr2 < lr1) - gr.flipEastWest(); - - mapPanel.setCenterAndScale(gr); - } - - public void setStatusText(final String t) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - String missing = ""; - if (mapPanel.getMissing() == 1) - missing = "(" + mapPanel.getMissing() + " channel hidden) "; - else if (mapPanel.getMissing() > 1) - missing = "(" + mapPanel.getMissing() + " channels hidden) "; - statusLabel.setText(missing + t); - statusLabel.repaint(); - } - }); - } - - public void reloadImages() { - mapPanel.loadMaps(true); - } - - public void reset(final boolean doMap) { - mapPanel.resetImage(doMap); - } - - public void mapSettingsChanged() { - mapPanel.loadMaps(true); - } - - public HelicorderViewPanelListener getLinkListener() { - return linkListener; - } - - public void setKioskMode(final boolean b) { - setDefaultKioskMode(b); - if (fullScreen) { - mainPanel.remove(toolbar); - mapPanel.setBorder(null); - } else { - mainPanel.add(toolbar, BorderLayout.NORTH); - mapPanel.setBorder(border); - } - } - - public void run() { - while (true) { - try { - if (this.isVisible() && realtime) { - final double end = J2kSec.now(); - final double start = end - MultiMonitor.SPANS[spanIndex]; - mapPanel.setTimes(start, end, false); - } - - Thread.sleep(refreshInterval); - } catch (final Throwable e) { - e.printStackTrace(); - } - } - } - - public void optionsChanged() { - reloadImages(); - } - - private static class MapFrameHolder { - public static MapFrame mapFrame = new MapFrame(); - } + private JToolBar toolbar; + private JPanel mainPanel; + private JButton optionsButton; + private JButton labelButton; + private JLabel statusLabel; + private JToggleButton linkButton; + private JToggleButton realtimeButton; + private JButton compXButton; + private JButton expXButton; + private JButton forwardTimeButton; + private JButton backTimeButton; + private JButton gotoButton; + private JButton timeHistoryButton; + private JButton captureButton; + private JButton clipboardButton; + + private JToggleButton dragButton; + private JToggleButton dragZoomButton; + private JToggleButton rulerButton; + + private WaveViewSettingsToolbar waveToolbar; + private WaveViewPanel selected; + + private final Thread updateThread; + + private MapPanel mapPanel; + + private Throbber throbber; + + private int spanIndex = 3; + private boolean realtime = true; + + private long refreshInterval = 1000; + + private HelicorderViewPanelListener linkListener; + + private boolean heliLinked = true; + + private Border border; + + private MapSettingsDialog mapSettingsDialog; + + private MapFrame() { + super("Map", true, true, true, false); + this.setFocusable(true); + UiTime.touchTime(); + + mapSettingsDialog = new MapSettingsDialog(this); + + createUI(); + addLayers(); + + updateThread = new Thread(this, "Map Update"); + updateThread.start(); + SwarmOptions.addOptionsListener(this); + } + + private void addLayers() { + MapLayer mapLayer; + try { + mapLayer = new HypocenterLayer(); + mapLayer.setMapPanel(mapPanel); + addLayer(mapLayer); + } catch (MalformedURLException ex) { + LOGGER.error("Unable to load layer. {}", ex); + } + } + + public static MapFrame getInstance() { + return MapFrameHolder.mapFrame; + } + + @Override + public void saveLayout(final ConfigFile cf, final String prefix) { + super.saveLayout(cf, prefix); + mapPanel.saveLayout(cf, prefix + ".panel"); + } + + public void processLayout(final ConfigFile cf) { + processStandardLayout(cf); + mapPanel.processLayout(cf.getSubConfig("panel")); + final LabelSetting ls = mapPanel.getLabelSetting(); + labelButton.setIcon(ls.getIcon()); + } + + private void createUI() { + setFrameIcon(Icons.earth); + setSize(swarmConfig.mapWidth, swarmConfig.mapHeight); + setLocation(swarmConfig.mapX, swarmConfig.mapY); + setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + + mainPanel = new JPanel(new BorderLayout()); + + createToolbar(); + + mainPanel.add(toolbar, BorderLayout.NORTH); + + mapPanel = new MapPanel(); + + border = BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(0, 2, 0, 3), + LineBorder.createGrayLineBorder()); + mapPanel.setBorder(border); + mainPanel.add(mapPanel, BorderLayout.CENTER); + statusLabel = new JLabel(" "); + statusLabel.setBorder(BorderFactory.createEmptyBorder(1, 5, 1, 1)); + mainPanel.add(statusLabel, BorderLayout.SOUTH); + setContentPane(mainPanel); + + this.addComponentListener(new ComponentAdapter() { + @Override + public void componentShown(final ComponentEvent e) { + if (!mapPanel.imageValid()) + mapPanel.resetImage(); + } + }); + + this.addInternalFrameListener(new InternalFrameAdapter() { + @Override + public void internalFrameClosing(final InternalFrameEvent e) { + setVisible(false); + } + }); + + linkListener = new HelicorderViewPanelListener() { + public void insetCreated(final double st, final double et) { + if (heliLinked) { + if (!realtime) + mapPanel.timePush(); + + setRealtime(false); + mapPanel.setTimes(st, et, true); + } + } + }; + + setVisible(true); + } + + @Override + public void setVisible(final boolean isVisible) { + LOGGER.debug("Frame closing"); + super.setVisible(isVisible); + if (mapPanel != null) { + mapPanel.setVisible(isVisible); + } + if (isVisible) + toFront(); + } + + @Override + public void setMaximum(final boolean max) throws PropertyVetoException { + if (max) { + swarmConfig.mapX = getX(); + swarmConfig.mapY = getY(); + } + super.setMaximum(max); + } + + public GeoRange getRange() { + return mapPanel.getRange(); + } + + private void createToolbar() { + toolbar = SwarmUtil.createToolBar(); + optionsButton = SwarmUtil.createToolBarButton(Icons.settings, "Map options", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + mapSettingsDialog.setToCurrent(); + mapSettingsDialog.setVisible(true); + } + }); + toolbar.add(optionsButton); + + labelButton = SwarmUtil.createToolBarButton(Icons.label_some, "Change label settings", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + final LabelSetting ls = mapPanel.getLabelSetting().next(); + labelButton.setIcon(ls.getIcon()); + mapPanel.setLabelSetting(ls); + } + }); + toolbar.add(labelButton); + + toolbar.addSeparator(); + + realtimeButton = SwarmUtil.createToolBarToggleButton(Icons.clock, "Realtime mode (N)", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + setRealtime(realtimeButton.isSelected()); + } + }); + UiUtils.mapKeyStrokeToButton(this, "N", "realtime", realtimeButton); + realtimeButton.setSelected(realtime); + toolbar.add(realtimeButton); + + linkButton = SwarmUtil.createToolBarToggleButton(Icons.helilink, "Synchronize times with helicorder wave (H)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + heliLinked = linkButton.isSelected(); + } + }); + UiUtils.mapKeyStrokeToButton(this, "H", "helilink", linkButton); + linkButton.setSelected(heliLinked); + toolbar.add(linkButton); + + toolbar.addSeparator(); + + final JButton earthButton = SwarmUtil.createToolBarButton(Icons.earth, "Zoom out to full scale (Home)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + final Point2D.Double c = new Point2D.Double(mapPanel.getCenter().x, 0); + mapPanel.setCenterAndScale(c, 100000); + } + }); + UiUtils.mapKeyStrokeToButton(this, "HOME", "home", earthButton); + toolbar.add(earthButton); + + dragButton = SwarmUtil.createToolBarToggleButton(Icons.drag, "Drag map (D)", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + mapPanel.setDragMode(DragMode.DRAG_MAP); + } + }); + UiUtils.mapKeyStrokeToButton(this, "D", "drag", dragButton); + dragButton.setSelected(true); + toolbar.add(dragButton); + + dragZoomButton = SwarmUtil.createToolBarToggleButton(Icons.dragbox, "Zoom into box (B)", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + mapPanel.setDragMode(DragMode.BOX); + } + }); + UiUtils.mapKeyStrokeToButton(this, "B", "box", dragZoomButton); + dragZoomButton.setSelected(false); + toolbar.add(dragZoomButton); + + rulerButton = SwarmUtil.createToolBarToggleButton(Icons.ruler, "Measure distances (M)", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + mapPanel.setDragMode(DragMode.RULER); + } + }); + UiUtils.mapKeyStrokeToButton(this, "M", "measure", rulerButton); + toolbar.add(rulerButton); + toolbar.addSeparator(); + + final ButtonGroup group = new ButtonGroup(); + group.add(dragButton); + group.add(dragZoomButton); + group.add(rulerButton); + + final JButton zoomIn = SwarmUtil.createToolBarButton(Icons.zoomplus, "Zoom in (+)", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + mapPanel.zoom(0.5); + } + }); + UiUtils.mapKeyStrokeToButton(this, "EQUALS", "zoomin1", zoomIn); + UiUtils.mapKeyStrokeToButton(this, "shift EQUALS", "zoomin2", zoomIn); + toolbar.add(zoomIn); + + final JButton zoomOut = SwarmUtil.createToolBarButton(Icons.zoomminus, "Zoom out (-)", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + mapPanel.zoom(2); + } + }); + UiUtils.mapKeyStrokeToButton(this, "MINUS", "zoomout1", zoomOut); + toolbar.add(zoomOut); + + final JButton backButton = SwarmUtil.createToolBarButton(Icons.geoback, "Last map view (Ctrl-backspace)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + mapPanel.mapPop(); + } + }); + UiUtils.mapKeyStrokeToButton(this, "ctrl BACK_SPACE", "mapback1", backButton); + toolbar.add(backButton); + + toolbar.addSeparator(); + + backTimeButton = SwarmUtil.createToolBarButton(Icons.left, "Scroll back time 20% (Left arrow)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + setRealtime(false); + mapPanel.shiftTime(-0.20); + } + }); + UiUtils.mapKeyStrokeToButton(this, "LEFT", "backward1", backTimeButton); + toolbar.add(backTimeButton); + + forwardTimeButton = SwarmUtil.createToolBarButton(Icons.right, "Scroll forward time 20% (Right arrow)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + setRealtime(false); + mapPanel.shiftTime(0.20); + } + }); + toolbar.add(forwardTimeButton); + UiUtils.mapKeyStrokeToButton(this, "RIGHT", "forward1", forwardTimeButton); + + gotoButton = SwarmUtil.createToolBarButton(Icons.gototime, "Go to time (Ctrl-G)", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + String t = JOptionPane.showInputDialog(applicationFrame, "Input time in 'YYYYMMDDhhmm[ss]' format:", + "Go to Time", JOptionPane.PLAIN_MESSAGE); + if (t != null) { + if (t.length() == 12) + t = t + "30"; + + double j2k; + + try { + j2k = J2kSec.parse("yyyyMMddHHmmss", t); + setRealtime(false); + mapPanel.gotoTime(j2k); + } catch (final ParseException e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + } + } + }); + toolbar.add(gotoButton); + UiUtils.mapKeyStrokeToButton(this, "ctrl G", "goto", gotoButton); + + compXButton = SwarmUtil.createToolBarButton(Icons.xminus, "Shrink time axis (Alt-left arrow", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + if (realtime) { + if (spanIndex != 0) + spanIndex--; + } else { + mapPanel.scaleTime(0.20); + } + } + }); + toolbar.add(compXButton); + UiUtils.mapKeyStrokeToButton(this, "alt LEFT", "compx", compXButton); + + expXButton = SwarmUtil.createToolBarButton(Icons.xplus, "Expand time axis (Alt-right arrow)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + if (realtime) { + if (spanIndex < MultiMonitor.SPANS.length - 1) + spanIndex++; + } else { + mapPanel.scaleTime(-0.20); + } + } + }); + toolbar.add(expXButton); + UiUtils.mapKeyStrokeToButton(this, "alt RIGHT", "expx", expXButton); + + timeHistoryButton = SwarmUtil.createToolBarButton(Icons.timeback, "Last time settings (Backspace)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + if (mapPanel.timePop()) + setRealtime(false); + } + }); + UiUtils.mapKeyStrokeToButton(this, "BACK_SPACE", "back", timeHistoryButton); + toolbar.add(timeHistoryButton); + toolbar.addSeparator(); + + waveToolbar = new WaveViewSettingsToolbar(null, toolbar, this); + + clipboardButton = SwarmUtil.createToolBarButton(Icons.clipboard, "Copy inset to clipboard (C or Ctrl-C)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + mapPanel.wavesToClipboard(); + } + }); + UiUtils.mapKeyStrokeToButton(this, "control C", "clipboard1", clipboardButton); + UiUtils.mapKeyStrokeToButton(this, "C", "clipboard2", clipboardButton); + toolbar.add(clipboardButton); + + toolbar.addSeparator(); + + captureButton = SwarmUtil.createToolBarButton(Icons.camera, "Save map image (P)", new CaptureActionListener()); + UiUtils.mapKeyStrokeToButton(this, "P", "capture", captureButton); + toolbar.add(captureButton); + + toolbar.addSeparator(); + + toolbar.add(Box.createHorizontalGlue()); + throbber = new Throbber(); + toolbar.add(throbber); + } + + class CaptureActionListener implements ActionListener { + public void actionPerformed(final ActionEvent e) { + final JFileChooser chooser = FileChooser.getFileChooser(); + final File lastPath = new File(swarmConfig.lastPath); + chooser.setCurrentDirectory(lastPath); + chooser.setSelectedFile(new File("map.png")); + chooser.setDialogTitle("Save Map Screen Capture"); + final int result = chooser.showSaveDialog(applicationFrame); + File f = null; + if (result == JFileChooser.APPROVE_OPTION) { + f = chooser.getSelectedFile(); + + if (f.exists()) { + final int choice = JOptionPane.showConfirmDialog(applicationFrame, "File exists, overwrite?", + "Confirm", JOptionPane.YES_NO_OPTION); + if (choice != JOptionPane.YES_OPTION) + return; + } + swarmConfig.lastPath = f.getParent(); + } + if (f == null) + return; + + final Insets i = mapPanel.getInsets(); + final BufferedImage image = new BufferedImage(mapPanel.getWidth() - i.left - i.right, + mapPanel.getHeight() - i.top - i.bottom, BufferedImage.TYPE_4BYTE_ABGR); + final Graphics g = image.getGraphics(); + g.translate(-i.left, -i.top); + mapPanel.paint(g); + try { + final PngEncoderB png = new PngEncoderB(image, false, PngEncoder.FILTER_NONE, 7); + final FileOutputStream out = new FileOutputStream(f); + final byte[] bytes = png.pngEncode(); + out.write(bytes); + out.close(); + } catch (final Exception ex) { + ex.printStackTrace(); + } + } + } + + public void setRealtime(final boolean b) { + realtime = b; + realtimeButton.setSelected(realtime); + } + + public Throbber getThrobber() { + return throbber; + } + + public void setRefreshInterval(final long r) { + refreshInterval = r; + } + + public long getRefreshInterval() { + return refreshInterval; + } + + public MapPanel getMapPanel() { + return mapPanel; + } + + public void setSelectedWave(final WaveViewPanel wvp) { + selected = wvp; + waveToolbar.setSettings(selected.getSettings()); + } + + public void setView(final GeoRange gr) { + final double lr1 = gr.getLonRange(); + final double lr2 = GeoRange.getLonRange(gr.getEast(), gr.getWest()); + if (lr2 < lr1) + gr.flipEastWest(); + + mapPanel.setCenterAndScale(gr); + } + + public void setStatusText(final String t) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + String missing = ""; + if (mapPanel.getMissing() == 1) + missing = "(" + mapPanel.getMissing() + " channel hidden) "; + else if (mapPanel.getMissing() > 1) + missing = "(" + mapPanel.getMissing() + " channels hidden) "; + statusLabel.setText(missing + t); + statusLabel.repaint(); + } + }); + } + + public void reloadImages() { + mapPanel.loadMaps(true); + } + + public void reset(final boolean doMap) { + mapPanel.resetImage(doMap); + } + + public void mapSettingsChanged() { + mapPanel.loadMaps(true); + } + + public HelicorderViewPanelListener getLinkListener() { + return linkListener; + } + + public void setKioskMode(final boolean b) { + setDefaultKioskMode(b); + if (fullScreen) { + mainPanel.remove(toolbar); + mapPanel.setBorder(null); + } else { + mainPanel.add(toolbar, BorderLayout.NORTH); + mapPanel.setBorder(border); + } + } + + public MapPanel addLayer(MapLayer layer) { + mapPanel.addLayer(layer); + + return mapPanel; + } + + public void run() { + while (true) { + try { + if (this.isVisible() && realtime) { + final double end = J2kSec.now(); + final double start = end - MultiMonitor.SPANS[spanIndex]; + mapPanel.setTimes(start, end, false); + } + + Thread.sleep(refreshInterval); + } catch (final Throwable e) { + e.printStackTrace(); + } + } + } + + public void optionsChanged() { + reloadImages(); + } + + private static class MapFrameHolder { + public static MapFrame mapFrame = new MapFrame(); + } } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/map/MapLayer.java b/src/main/java/gov/usgs/volcanoes/swarm/map/MapLayer.java new file mode 100644 index 00000000..8bd5bdcf --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/map/MapLayer.java @@ -0,0 +1,18 @@ +package gov.usgs.volcanoes.swarm.map; + +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.event.MouseEvent; + +import gov.usgs.proj.GeoRange; +import gov.usgs.proj.Projection; + +public interface MapLayer { + + public void draw(Graphics2D g2); + public boolean mouseClicked(MouseEvent e); + public void setMapPanel(MapPanel mapPanel); + public void setVisible(boolean isVisible); + public boolean mouseMoved(MouseEvent e); + +} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/map/MapLineDialog.java b/src/main/java/gov/usgs/volcanoes/swarm/map/MapLineDialog.java index 5e0815f1..53a7b3d1 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/map/MapLineDialog.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/map/MapLineDialog.java @@ -2,12 +2,12 @@ import javax.swing.JFrame; -import gov.usgs.volcanoes.swarm.SwarmDialog; +import gov.usgs.volcanoes.swarm.SwarmModalDialog; -public class MapLineDialog extends SwarmDialog { +public class MapLineDialog extends SwarmModalDialog { - protected MapLineDialog(JFrame parent, String title, boolean modal) { - super(parent, title, modal); + protected MapLineDialog(JFrame parent, String title) { + super(parent, title); } } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/map/MapLinePreview.java b/src/main/java/gov/usgs/volcanoes/swarm/map/MapLinePreview.java index 7d95a655..c97c8686 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/map/MapLinePreview.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/map/MapLinePreview.java @@ -88,7 +88,6 @@ public void changedUpdate(DocumentEvent e) { } private void updateLineWidth(String w) { - System.out.println("-- " + w); try { lineWidth = Integer.parseInt(w); widthBox.setBackground(Color.white); diff --git a/src/main/java/gov/usgs/volcanoes/swarm/map/MapPanel.java b/src/main/java/gov/usgs/volcanoes/swarm/map/MapPanel.java index 3da848f3..570e9a3e 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/map/MapPanel.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/map/MapPanel.java @@ -6,6 +6,7 @@ import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; +import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; @@ -36,6 +37,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -178,7 +180,7 @@ else if (s.equals("S")) private LabelSetting labelSetting = LabelSetting.SOME; - private List clickableLabels; + private List layers; public MapPanel() { swarmConfig = SwarmConfig.getInstance(); @@ -189,12 +191,20 @@ public MapPanel() { layouts = Collections.synchronizedMap(new HashMap()); visiblePanels = Collections.synchronizedList(new ArrayList()); selectedPanels = new HashSet(); + layers = new ArrayList(); final Cursor crosshair = new Cursor(Cursor.CROSSHAIR_CURSOR); this.setCursor(crosshair); createUI(); + + } + public void addLayer(MapLayer layer) { + LOGGER.debug("Layer added"); + layers.add(layer); + } + public void saveLayout(final ConfigFile cf, final String prefix) { cf.put(prefix + ".longitude", Double.toString(center.x)); cf.put(prefix + ".latitude", Double.toString(center.y)); @@ -325,23 +335,6 @@ public void keyTyped(final KeyEvent e) {} pane.add(mapImagePanel, new Integer(10)); add(pane, BorderLayout.CENTER); - loadLabels(); - - } - - public void loadLabels() { - if (swarmConfig.labelSource.isEmpty()) - return; - - try { - final Class cl = Class.forName(swarmConfig.labelSource); - final LabelSource src = (LabelSource) cl.newInstance(); - clickableLabels = src.getLabels(); - repaint(); - } catch (final Exception e) { - LOGGER.warn("Can't load labelSource {}", swarmConfig.labelSource); - e.printStackTrace(); - } } public void wavesToClipboard() { @@ -641,7 +634,6 @@ private void checkLayouts() { private BufferedImage updateMapRenderer() { BufferedImage mi = null; - final CodeTimer ct = new CodeTimer("whole map"); try { swarmConfig.mapScale = scale; swarmConfig.mapLongitude = center.x; @@ -656,9 +648,7 @@ private BufferedImage updateMapRenderer() { LOGGER.debug("map scale: " + scale); LOGGER.debug("center: " + center.x + " " + center.y); final MapRenderer mr = new MapRenderer(range, projection); - ct.mark("pre bg"); image = images.getMapBackground(projection, range, width, scale); - ct.mark("bg"); mr.setLocation(INSET, INSET, width); mr.setMapImage(image); mr.setGeoLabelSet(labels); @@ -687,12 +677,9 @@ private BufferedImage updateMapRenderer() { final Plot plot = new Plot(); plot.setSize(mapImagePanel.getWidth(), mapImagePanel.getHeight()); plot.addRenderer(renderer); - ct.mark("pre plot"); mi = plot.getAsBufferedImage(false); - ct.mark("plot"); dragDX = Integer.MAX_VALUE; dragDY = Integer.MAX_VALUE; - ct.stopAndReport(); } catch (final Exception e) { LOGGER.error("Exception during map creation. {}", e); } finally { @@ -701,6 +688,14 @@ private BufferedImage updateMapRenderer() { return mi; } + @Override + public void setVisible(boolean isVisible) { + for (MapLayer layer : layers) { + layer.setVisible(isVisible); + } + } + + private Pair, List> updateMiniPanels() { final List compsToAdd = new ArrayList(); final List linesToAdd = new ArrayList(); @@ -833,6 +828,7 @@ public Object construct() { return new Boolean(true); } + @Override public void finished() { @@ -955,6 +951,7 @@ private void paintGreatCircleRoute(final Graphics2D g2) { @Override public void paintComponent(final Graphics g) { + if (renderer == null || mapImage == null) { final Dimension d = getSize(); g.drawString("Loading map...", d.width / 2 - 50, d.height / 2); @@ -1007,19 +1004,15 @@ public void paintComponent(final Graphics g) { g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); final AffineTransform at = g2.getTransform(); g2.setFont(Font.decode("dialog-plain-12")); - if (clickableLabels != null) { - for (final ClickableGeoLabel label : clickableLabels) { - final Point2D.Double xy = getXY(label.location.x, label.location.y); - if (xy != null) { - g2.translate(xy.x - dx, xy.y - dy); - label.draw(g2); - g2.translate(-xy.x + dx, -xy.y + dy); - } - } + + g2.translate(-dx, -dy); + for (MapLayer layer : layers) { + layer.draw(g2); } + g2.translate(dx, dy); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldaa); g2.setTransform(at); - } } } @@ -1028,6 +1021,30 @@ public GeoRange getRange() { return range; } + public Projection getProjection() { + return projection; + } + + public int getGraphWidth() { + int width = 0; + if (renderer != null) { + width = renderer.getGraphWidth(); + } + return width; + } + + public int getGraphHeight() { + int height = 0; + if (renderer != null) { + height = renderer.getGraphHeight(); + } + return height; + } + + public int getInset() { + return INSET; + } + public class MapMouseAdapter extends MouseAdapter { @Override public void mouseExited(final MouseEvent e) { @@ -1036,19 +1053,16 @@ public void mouseExited(final MouseEvent e) { @Override public void mouseClicked(final MouseEvent e) { - if (clickableLabels != null) { - for (final ClickableGeoLabel label : clickableLabels) { - final Rectangle r = label.getClickBox(); - final Point2D.Double xy = getXY(label.location.x, label.location.y); - if (xy != null) { - r.translate((int) xy.x, (int) xy.y); - if (r.contains(e.getPoint())) - label.mouseClicked(e); - } + if (layers != null) { + boolean handled = false; + Iterator it = layers.iterator(); + while (it.hasNext() && handled == false) { + MapLayer layer = it.next(); + handled = layer.mouseClicked(e); } } } - + @Override public void mousePressed(final MouseEvent e) { requestFocusInWindow(); @@ -1107,7 +1121,21 @@ public void mouseMoved(final MouseEvent e) { final Point2D.Double latLon = getLonLat(e.getX(), e.getY()); if (latLon != null) MapFrame.getInstance().setStatusText(Util.lonLatToString(latLon)); + + if (layers != null) { + boolean handled = false; + Iterator it = layers.iterator(); + while (it.hasNext() && handled == false) { + MapLayer layer = it.next(); + handled = layer.mouseMoved(e); + } + if (handled == true) { + repaint(); + } + } + } + public void mouseDragged(final MouseEvent e) { mouseNow = e.getPoint(); diff --git a/src/main/java/gov/usgs/volcanoes/swarm/map/MapSettingsDialog.java b/src/main/java/gov/usgs/volcanoes/swarm/map/MapSettingsDialog.java index 50f258ef..0a517d51 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/map/MapSettingsDialog.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/map/MapSettingsDialog.java @@ -8,235 +8,231 @@ import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JColorChooser; +import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JTextField; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.jgoodies.forms.builder.DefaultFormBuilder; import com.jgoodies.forms.layout.FormLayout; -import gov.usgs.volcanoes.swarm.SwarmConfig; -import gov.usgs.volcanoes.swarm.SwarmDialog; +import gov.usgs.volcanoes.swarm.Icons; +import gov.usgs.volcanoes.swarm.SwarmModalDialog; import gov.usgs.volcanoes.swarm.map.MapPanel.LabelSetting; +import gov.usgs.volcanoes.swarm.map.hypocenters.HypocenterSource; /** * * @author Dan Cervelli */ -public class MapSettingsDialog extends SwarmDialog { - public static final long serialVersionUID = -1; - - private MapFrame map; - - private JPanel dialogPanel; - - private JTextField scale; - private JTextField longitude; - private JTextField latitude; - - private JTextField lineWidth; - private JTextField refreshInterval; - private ButtonGroup labelGroup; - private JRadioButton someLabels; - private JRadioButton allLabels; - private JRadioButton noLabels; - private JButton mapLine; - private JColorChooser lineChooser; - - private JTextField labelSource; - - private static MapSettingsDialog dialog; - - private MapSettingsDialog() { - super(applicationFrame, "Map Settings", true); - createUI(); - setSizeAndLocation(); - } - - private void createFields() { - latitude = new JTextField(); - longitude = new JTextField(); - scale = new JTextField(); - lineWidth = new JTextField(); - refreshInterval = new JTextField(); - labelGroup = new ButtonGroup(); - someLabels = new JRadioButton("Some"); - allLabels = new JRadioButton("All"); - noLabels = new JRadioButton("None"); - labelGroup.add(someLabels); - labelGroup.add(allLabels); - labelGroup.add(noLabels); - labelSource = new JTextField(); - mapLine = new JButton(); - - lineChooser = new JColorChooser(); - lineChooser.setPreviewPanel(new MapLinePreview()); - - final ActionListener okActionListener = new ActionListener() { - public void actionPerformed(ActionEvent actionEvent) { -// swarmConfig.mapLineColor = lineChooser.getC - } - }; - - mapLine.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - JDialog dialog = JColorChooser.createDialog(applicationFrame, "Map Line Settings", true, lineChooser, - okActionListener, null); - dialog.setVisible(true); - } - }); - - } - - protected void createUI() { - super.createUI(); - createFields(); - - FormLayout layout = new FormLayout("right:max(30dlu;pref), 3dlu, 50dlu, 3dlu, 30dlu", ""); - - DefaultFormBuilder builder = new DefaultFormBuilder(layout); - builder.setDefaultDialogBorder(); - - builder.appendSeparator("Location"); - builder.append("Longitude:"); - builder.append(longitude); - builder.append("degrees"); - builder.nextLine(); - builder.append("Latitude:"); - builder.append(latitude); - builder.append("degrees"); - builder.nextLine(); - builder.append("Scale:"); - builder.append(scale); - builder.append("m/pixel"); - builder.nextLine(); - - builder.appendSeparator("Options"); - builder.append("Line:"); - builder.append(mapLine); - builder.nextLine(); - builder.append("Refresh Interval:"); - builder.append(refreshInterval); - builder.append(" seconds"); - builder.nextLine(); - builder.append("Channel Labels:"); - builder.append(noLabels); - builder.nextLine(); - builder.append(" "); - builder.append(someLabels); - builder.nextLine(); - builder.append(" "); - builder.append(allLabels); - builder.nextLine(); - builder.append("Click Labels:"); - builder.append(labelSource, 3); - - dialogPanel = builder.getPanel(); - mainPanel.add(dialogPanel, BorderLayout.CENTER); - } - - public static MapSettingsDialog getInstance(MapFrame mf) { - if (dialog == null) - dialog = new MapSettingsDialog(); - - dialog.setMapFrame(mf); - dialog.setToCurrent(); - return dialog; - } - - public void setVisible(boolean b) { - if (b) - this.getRootPane().setDefaultButton(okButton); - super.setVisible(b); +public class MapSettingsDialog extends SwarmModalDialog { + public static final long serialVersionUID = -1; + + private static final Logger LOGGER = LoggerFactory.getLogger(MapSettingsDialog.class); + + private JPanel dialogPanel; + + private JTextField scale; + private JTextField longitude; + private JTextField latitude; + + private JTextField lineWidth; + private JTextField refreshInterval; + private ButtonGroup labelGroup; + private JRadioButton someLabels; + private JRadioButton allLabels; + private JRadioButton noLabels; + private JButton mapLine; + private JColorChooser lineChooser; + private JComboBox hypocenterSource; + + private MapFrame mapFrame; + + public MapSettingsDialog(MapFrame mapFrame) { + super(applicationFrame, "Map Settings", "mapSettings.md"); + this.mapFrame = mapFrame; + createUI(); + setToCurrent(); + setSizeAndLocation(); + } + + private void createFields() { + latitude = new JTextField(); + longitude = new JTextField(); + scale = new JTextField(); + lineWidth = new JTextField(); + refreshInterval = new JTextField(); + labelGroup = new ButtonGroup(); + someLabels = new JRadioButton("Some"); + allLabels = new JRadioButton("All"); + noLabels = new JRadioButton("None"); + labelGroup.add(someLabels); + labelGroup.add(allLabels); + labelGroup.add(noLabels); + mapLine = new JButton(); + hypocenterSource = new JComboBox(HypocenterSource.values()); + lineChooser = new JColorChooser(); + lineChooser.setPreviewPanel(new MapLinePreview()); + + final ActionListener okActionListener = new ActionListener() { + public void actionPerformed(ActionEvent actionEvent) { + // swarmConfig.mapLineColor = lineChooser.getC + } + }; + + mapLine.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JDialog dialog = JColorChooser.createDialog(applicationFrame, "Map Line Settings", true, + lineChooser, okActionListener, null); + dialog.setVisible(true); + } + }); + + } + + protected void createUI() { + super.createUI(); + createFields(); + + FormLayout layout = new FormLayout("right:max(30dlu;pref), 3dlu, 50dlu, 3dlu, 30dlu", ""); + + DefaultFormBuilder builder = new DefaultFormBuilder(layout); + builder.setDefaultDialogBorder(); + + builder.appendSeparator("Location"); + builder.append("Longitude:"); + builder.append(longitude); + builder.append("degrees"); + builder.nextLine(); + builder.append("Latitude:"); + builder.append(latitude); + builder.append("degrees"); + builder.nextLine(); + builder.append("Scale:"); + builder.append(scale); + builder.append("m/pixel"); + builder.nextLine(); + + builder.appendSeparator("Options"); + builder.append("Line:"); + builder.append(mapLine); + builder.nextLine(); + builder.append("Refresh Interval:"); + builder.append(refreshInterval); + builder.append(" seconds"); + builder.nextLine(); + builder.append("Channel Labels:"); + builder.append(noLabels); + builder.nextLine(); + builder.append(" "); + builder.append(someLabels); + builder.nextLine(); + builder.append(" "); + builder.append(allLabels); + + builder.nextLine(); + builder.append("NEIC Event Summary"); + builder.append(hypocenterSource, 3); + + dialogPanel = builder.getPanel(); + mainPanel.add(dialogPanel, BorderLayout.CENTER); + + } + + public void setVisible(boolean b) { + if (b) + this.getRootPane().setDefaultButton(okButton); + super.setVisible(b); + } + + public void setToCurrent() { + MapPanel panel = mapFrame.getMapPanel(); + if (panel == null) + return; + + longitude.setText(String.format("%.4f", panel.getCenter().x)); + latitude.setText(String.format("%.4f", panel.getCenter().y)); + scale.setText(String.format("%.1f", panel.getScale())); + lineWidth.setText(Integer.toString(swarmConfig.mapLineWidth)); + refreshInterval.setText(String.format("%.2f", mapFrame.getRefreshInterval() / 1000.0)); + LabelSetting ls = panel.getLabelSetting(); + switch (ls) { + case ALL: + allLabels.setSelected(true); + break; + case SOME: + someLabels.setSelected(true); + break; + case NONE: + noLabels.setSelected(true); + break; } - - public void setMapFrame(MapFrame mf) { - map = mf; - setToCurrent(); - } - - public void setToCurrent() { - MapPanel panel = map.getMapPanel(); - longitude.setText(String.format("%.4f", panel.getCenter().x)); - latitude.setText(String.format("%.4f", panel.getCenter().y)); - scale.setText(String.format("%.1f", panel.getScale())); - lineWidth.setText(Integer.toString(swarmConfig.mapLineWidth)); - refreshInterval.setText(String.format("%.2f", map.getRefreshInterval() / 1000.0)); - LabelSetting ls = panel.getLabelSetting(); - switch (ls) { - case ALL: - allLabels.setSelected(true); - break; - case SOME: - someLabels.setSelected(true); - break; - case NONE: - noLabels.setSelected(true); - break; - } - labelSource.setText(swarmConfig.labelSource); + hypocenterSource.setSelectedItem(swarmConfig.getHypocenterSource()); + } + + protected void wasOK() { + try { + MapPanel panel = mapFrame.getMapPanel(); + LabelSetting ls = LabelSetting.ALL; + if (someLabels.isSelected()) + ls = LabelSetting.SOME; + else if (noLabels.isSelected()) + ls = LabelSetting.NONE; + panel.setLabelSetting(ls); + Point2D.Double center = new Point2D.Double(); + center.x = Double.parseDouble(longitude.getText()); + center.y = Double.parseDouble(latitude.getText()); + double sc = Double.parseDouble(scale.getText()); + panel.setCenterAndScale(center, sc); + swarmConfig.mapLineWidth = Integer.parseInt(lineWidth.getText()); + mapFrame.setRefreshInterval(Math.round(Double.parseDouble(refreshInterval.getText()) * 1000)); + + swarmConfig.setHypocenterSource((HypocenterSource) hypocenterSource.getSelectedItem()); + } catch (Exception e) { + LOGGER.debug("Exception caught while accepting map options."); + e.printStackTrace(); } - - protected void wasOK() { - try { - swarmConfig.labelSource = labelSource.getText(); - MapPanel panel = map.getMapPanel(); - LabelSetting ls = LabelSetting.ALL; - if (someLabels.isSelected()) - ls = LabelSetting.SOME; - else if (noLabels.isSelected()) - ls = LabelSetting.NONE; - panel.setLabelSetting(ls); - Point2D.Double center = new Point2D.Double(); - center.x = Double.parseDouble(longitude.getText()); - center.y = Double.parseDouble(latitude.getText()); - double sc = Double.parseDouble(scale.getText()); - panel.setCenterAndScale(center, sc); - swarmConfig.mapLineWidth = Integer.parseInt(lineWidth.getText()); - map.setRefreshInterval(Math.round(Double.parseDouble(refreshInterval.getText()) * 1000)); - panel.loadLabels(); - } catch (Exception e) { - e.printStackTrace(); - // don't do anything here since all validation should occur in - // allowOK() -- this is just worst case. - } - } - - protected boolean allowOK() { - String message = null; - try { - message = "Invalid refresh interval; legal values are between 0 and 3600, 0 to refresh continuously."; - double ri = Double.parseDouble(refreshInterval.getText()); - if (ri < 0 || ri > 3600) - throw new NumberFormatException(); - - message = "Invalid longitude; legal values are between -180 and 180."; - double lon = Double.parseDouble(longitude.getText()); - if (lon < -180 || lon > 180) - throw new NumberFormatException(); - - message = "Invalid latitude; legal values are between -90 and 90."; - double lat = Double.parseDouble(latitude.getText()); - if (lat < -90 || lat > 90) - throw new NumberFormatException(); - - message = "Invalid scale; legal values are greater than 0."; - double sc = Double.parseDouble(scale.getText()); - if (sc <= 0) - throw new NumberFormatException(); - - message = "Invalid line width; legal values are integers greater than 0."; - int i = Integer.parseInt(lineWidth.getText()); - if (i <= 0) - throw new NumberFormatException(); - - return true; - } catch (Exception e) { - JOptionPane.showMessageDialog(this, message, "Options Error", JOptionPane.ERROR_MESSAGE); - } - return false; + } + + protected boolean allowOK() { + String message = null; + try { + message = + "Invalid refresh interval; legal values are between 0 and 3600, 0 to refresh continuously."; + double ri = Double.parseDouble(refreshInterval.getText()); + if (ri < 0 || ri > 3600) + throw new NumberFormatException(); + + message = "Invalid longitude; legal values are between -180 and 180."; + double lon = Double.parseDouble(longitude.getText()); + if (lon < -180 || lon > 180) + throw new NumberFormatException(); + + message = "Invalid latitude; legal values are between -90 and 90."; + double lat = Double.parseDouble(latitude.getText()); + if (lat < -90 || lat > 90) + throw new NumberFormatException(); + + message = "Invalid scale; legal values are greater than 0."; + double sc = Double.parseDouble(scale.getText()); + if (sc <= 0) + throw new NumberFormatException(); + + message = "Invalid line width; legal values are integers greater than 0."; + int i = Integer.parseInt(lineWidth.getText()); + if (i <= 0) + throw new NumberFormatException(); + + return true; + } catch (Exception e) { + JOptionPane.showMessageDialog(this, message, "Options Error", JOptionPane.ERROR_MESSAGE); } + return false; + } } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/map/NationalMapLayer.java b/src/main/java/gov/usgs/volcanoes/swarm/map/NationalMapLayer.java new file mode 100644 index 00000000..723c6125 --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/map/NationalMapLayer.java @@ -0,0 +1,32 @@ +package gov.usgs.volcanoes.swarm.map; + +/** + * + * http://basemap.nationalmap.gov/arc + * + * @author Tom Parker + * + */ +public enum NationalMapLayer { + + TOPO("Topo", "http://basemap.nationalmap.gov/arcgis/services/USGSTopo/MapServer/WMSServer?"), + SHADED_RELIEF("Shaded Relief", "http://basemap.nationalmap.gov/arcgis/services/USGSShadedReliefOnly/MapServer/WMSServer?"), + IMAGERY_ONLY( "Imagery Only", "http://basemap.nationalmap.gov/arcgis/services/USGSImageryOnly/MapServer/WMSServer?"), + IMAGERY_TOPO("Imagery Topo", "http://basemap.nationalmap.gov/arcgis/services/USGSTopo/USGSImageryTopo/WMSServer?"), + ; + + public final String server; + public final String layer = "0"; + public final String sytels = ""; + + private final String title; + + private NationalMapLayer(String title, String server) { + this.title = title; + this.server = server; + } + + public String toString() { + return title; + } +} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/map/hypocenters/HypocenterLayer.java b/src/main/java/gov/usgs/volcanoes/swarm/map/hypocenters/HypocenterLayer.java new file mode 100644 index 00000000..29cd5d09 --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/map/hypocenters/HypocenterLayer.java @@ -0,0 +1,342 @@ +package gov.usgs.volcanoes.swarm.map.hypocenters; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.event.MouseEvent; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import gov.usgs.plot.render.DataPointRenderer; +import gov.usgs.proj.GeoRange; +import gov.usgs.proj.Projection; +import gov.usgs.volcanoes.core.quakeml.Event; +import gov.usgs.volcanoes.core.quakeml.Magnitude; +import gov.usgs.volcanoes.core.quakeml.Origin; +import gov.usgs.volcanoes.core.quakeml.QuakemlObserver; +import gov.usgs.volcanoes.core.quakeml.QuakemlSource; +import gov.usgs.volcanoes.core.time.J2kSec; +import gov.usgs.volcanoes.core.time.Time; +import gov.usgs.volcanoes.swarm.ConfigListener; +import gov.usgs.volcanoes.swarm.Swarm; +import gov.usgs.volcanoes.swarm.SwarmConfig; +import gov.usgs.volcanoes.swarm.map.MapFrame; +import gov.usgs.volcanoes.swarm.map.MapLayer; +import gov.usgs.volcanoes.swarm.map.MapPanel; + +public final class HypocenterLayer implements MapLayer, ConfigListener, QuakemlObserver { + private static final Logger LOGGER = LoggerFactory.getLogger(HypocenterLayer.class); + private static final int REFRESH_INTERVAL = 5 * 60 * 1000; + private static final int ONE_HOUR = 60 * 60 * 1000; + private static final int ONE_DAY = ONE_HOUR * 24; + private static final int ONE_WEEK = ONE_DAY * 7; + + private static final int POPUP_PADDING = 2; + + private static final int[] markerSize = { 5, 7, 9, 11, 13, 17, 21, 25 }; + + private static final Color ORANGE = new Color(225, 175, 0, 200); + private static final Color RED = new Color(200, 0, 0, 200); + private static final Color YELLOW = new Color(225, 225, 0, 200); + private static final Color WHITE = new Color(200, 200, 200, 200); + private static final Color GREEN = new Color(0, 200, 0, 200); + + private final Map events; + + private MapPanel panel; + + private final SwarmConfig swarmConfig; + private final DataPointRenderer renderer; + private QuakemlSource quakemlSource; + private Event hoverEvent; + + public HypocenterLayer() throws MalformedURLException { + events = new ConcurrentHashMap(); + swarmConfig = SwarmConfig.getInstance(); + swarmConfig.addListener(this); + + renderer = new DataPointRenderer(); + renderer.antiAlias = true; + renderer.stroke = new BasicStroke(1f); + renderer.filled = true; + renderer.color = Color.BLACK; + + HypocenterSource hypocenterSource = swarmConfig.getHypocenterSource(); + + if (hypocenterSource != HypocenterSource.NONE) { + URL quakemlUrl = new URL(hypocenterSource.getUrl()); + if (quakemlUrl != null) { + quakemlSource = new QuakemlSource(quakemlUrl, (long) REFRESH_INTERVAL); + quakemlSource.addObserver(this); + update(quakemlSource); + } + } + } + + public void setMapPanel(MapPanel mapPanel) { + panel = mapPanel; + } + + public void draw(Graphics2D g2) { + if (events.size() < 1) + return; + + GeoRange range = panel.getRange(); + Projection projection = panel.getProjection(); + int widthPx = panel.getGraphWidth(); + int heightPx = panel.getGraphHeight(); + int insetPx = panel.getInset(); + + for (final Event event : events.values()) { + Origin origin = event.getPreferredOrigin(); + + Point2D.Double originLoc = new Point2D.Double(origin.getLongitude(), origin.getLatitude()); + if (!range.contains(originLoc)) { + continue; + } + + final Point2D.Double xy = projection.forward(originLoc); + + final double[] ext = range.getProjectedExtents(projection); + final double dx = (ext[1] - ext[0]); + final double dy = (ext[3] - ext[2]); + final Point2D.Double res = new Point2D.Double(); + res.x = (((xy.x - ext[0]) / dx) * widthPx + insetPx); + res.y = ((1 - (xy.y - ext[2]) / dy) * heightPx + insetPx); + + g2.translate(res.x, res.y); + + double mag = event.getPerferredMagnitude().getMag(); + float diameter; + + diameter = getMarkerSize(mag); + + long age = J2kSec.asEpoch(J2kSec.now()) - origin.getTime(); + renderer.shape = new Ellipse2D.Float(0f, 0f, diameter, diameter); + Color markerColor; + if (event == hoverEvent) { + markerColor = GREEN; + } else if (age < ONE_HOUR) { + markerColor = RED; + } else if (age < ONE_DAY) { + markerColor = ORANGE; + } else if (age < ONE_WEEK) { + markerColor = YELLOW; + } else { + markerColor = WHITE; + } + + // int alpha = 0x80FFFFFF; + // renderer.paint = new Color(alpha & markerColor.getRGB(), true); + renderer.paint = markerColor; + renderer.renderAtOrigin(g2); + + g2.translate(-res.x, -res.y); + } + + drawPopup(g2); + } + + private int getMarkerSize(double mag) { + int markerMag = Math.max((int) mag, 0); + markerMag = Math.min(markerMag, markerSize.length); + return markerSize[markerMag]; + } + + private void drawPopup(Graphics2D g2) { + if (hoverEvent == null) { + return; + } + Origin origin = hoverEvent.getPreferredOrigin(); + GeoRange range = panel.getRange(); + Projection projection = panel.getProjection(); + List text = generatePopupText(origin); + + FontMetrics fm = g2.getFontMetrics(); + int popupHeight = 2 * POPUP_PADDING; + int popupWidth = 0; + for (String string : text) { + Rectangle2D bounds = fm.getStringBounds(string, g2); + popupHeight += (int) (Math.ceil(bounds.getHeight()) + 2); + popupWidth = Math.max(popupWidth, (int) (Math.ceil(bounds.getWidth()) + 2 * POPUP_PADDING)); + } + + int widthPx = panel.getGraphWidth(); + int heightPx = panel.getGraphHeight(); + int insetPx = panel.getInset(); + + final Point2D.Double xy = projection.forward(new Point2D.Double(origin.getLongitude(), origin.getLatitude())); + final double[] ext = range.getProjectedExtents(projection); + final double dx = (ext[1] - ext[0]); + final double dy = (ext[3] - ext[2]); + final Point2D.Double res = new Point2D.Double(); + + res.x = (((xy.x - ext[0]) / dx) * widthPx + insetPx); + int maxX = widthPx - popupWidth + POPUP_PADDING; + res.x = Math.min(res.x, maxX); + + res.y = ((1 - (xy.y - ext[2]) / dy) * heightPx + insetPx); + if (res.y < (insetPx + popupHeight)) { + res.y += popupHeight; + } + + g2.translate(res.x, res.y); + + g2.setStroke(new BasicStroke(1.2f)); + g2.setColor(new Color(0, 0, 0, 128)); + g2.drawRect(0, -(popupHeight - POPUP_PADDING), popupWidth, popupHeight); + g2.fillRect(0, -(popupHeight - POPUP_PADDING), popupWidth, popupHeight); + + g2.setColor(Color.WHITE); + int baseY = POPUP_PADDING; + for (String string : text) { + g2.drawString(string, POPUP_PADDING, -baseY); + Rectangle2D bounds = fm.getStringBounds(string, g2); + baseY += (int) (Math.ceil(bounds.getHeight()) + 2); + } + g2.translate(-res.x, -res.y); + + } + + private List generatePopupText(Origin origin) { + List text = new ArrayList(3); + + Magnitude magElement = hoverEvent.getPerferredMagnitude(); + String mag = String.format("%.2f %s at %.2f km depth", magElement.getMag(), magElement.getType(), + (origin.getDepth() / 1000)); + text.add(mag); + + String date = Time.format(Time.STANDARD_TIME_FORMAT, new Date(origin.getTime())); + text.add(date + " UTC"); + + String description = hoverEvent.getDescription(); + text.add(description); + + return text; + } + + public boolean mouseClicked(final MouseEvent e) { + boolean handled = false; + + if (hoverEvent != null) { + LOGGER.debug("Opening event {}", hoverEvent.getEvid()); + Swarm.openEvent(hoverEvent); + handled = true; + hoverEvent = null; + } + + return handled; + } + + public void settingsChanged() { + LOGGER.debug("hypocenter plotter sees changed settings."); + if (quakemlSource != null) { + quakemlSource.stop(); + } + + HypocenterSource hypocenterSource = swarmConfig.getHypocenterSource(); + if (hypocenterSource == HypocenterSource.NONE) { + events.clear(); + if (MapFrame.getInstance() != null) { + MapFrame.getInstance().repaint(); + } + return; + } + + try { + LOGGER.debug("New hypocenter source: {}", hypocenterSource); + quakemlSource = new QuakemlSource(new URL(hypocenterSource.getUrl()), (long) REFRESH_INTERVAL); + quakemlSource.start(); + quakemlSource.addObserver(this); + update(quakemlSource); + } catch (MalformedURLException ex) { + LOGGER.error("Unable to load hypocenter URL.", ex); + } + } + + public boolean mouseMoved(MouseEvent e) { + if (events.size() < 1) { + return false; + } + + GeoRange range = panel.getRange(); + Projection projection = panel.getProjection(); + if (projection == null) { + return false; + } + + int widthPx = panel.getGraphWidth(); + int heightPx = panel.getGraphHeight(); + int insetPx = panel.getInset(); + + Iterator it = events.values().iterator(); + boolean handled = false; + while (it.hasNext() && handled == false) { + Event event = it.next(); + + Origin origin = event.getPreferredOrigin(); + if (origin == null) { + continue; + } + + Point2D.Double originLoc = new Point2D.Double(origin.getLongitude(), origin.getLatitude()); + if (!range.contains(originLoc)) { + continue; + } + + int markerDiameter = getMarkerSize(event.getPerferredMagnitude().getMag()); + final Rectangle r = new Rectangle(0, 0, markerDiameter, markerDiameter); + + final Point2D.Double xy = projection + .forward(new Point2D.Double(origin.getLongitude(), origin.getLatitude())); + final double[] ext = range.getProjectedExtents(projection); + final double dx = (ext[1] - ext[0]); + final double dy = (ext[3] - ext[2]); + final Point2D.Double res = new Point2D.Double(); + res.x = (((xy.x - ext[0]) / dx) * widthPx + insetPx); + res.y = ((1 - (xy.y - ext[2]) / dy) * heightPx + insetPx); + + r.translate((int) res.x, (int) res.y); + if (r.contains(e.getPoint())) { + LOGGER.debug("set hover event {}", event.publicId); + hoverEvent = event; + handled = true; + } else if (event == hoverEvent) { + LOGGER.debug("unset hover event {}", event.publicId); + + hoverEvent = null; + handled = true; + } + } + return handled; + } + + public void update(QuakemlSource source) { + events.clear(); + events.putAll(source.getEventSet()); + if (MapFrame.getInstance() != null) { + MapFrame.getInstance().repaint(); + } + } + + public void setVisible(boolean isVisible) { + LOGGER.debug("Setting hypocenter update to {}", isVisible); + if (quakemlSource != null) { + quakemlSource.doUpdate(isVisible); + } + } +} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/map/hypocenters/HypocenterSource.java b/src/main/java/gov/usgs/volcanoes/swarm/map/hypocenters/HypocenterSource.java new file mode 100644 index 00000000..2a293218 --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/map/hypocenters/HypocenterSource.java @@ -0,0 +1,50 @@ +package gov.usgs.volcanoes.swarm.map.hypocenters; + +public enum HypocenterSource { + NONE("None", null), + HR_SIG("1 Hour - Significant", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/significant_hour.quakeml"), + HR_45("1 Hour - M4.5+", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/4.5_hour.quakeml"), + HR_25("1 Hour - M2.5+", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_hour.quakeml"), + HR_1("1 Hour - M1.0+", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/1.0_hour.quakeml") , + HR_ALL("1 Hour - All", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.quakeml"), + DAY_SIG("1 Day - Significant", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/significant_day.quakeml"), + DAY_45("1 Day - M4.5+", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/4.5_day.quakeml"), + DAY_25("1 Day - M2.5+", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.quakeml"), + DAY_1("1 Day - M1.0+", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/1.0_day.quakeml"), + DAY_ALL("1 Day - All", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.quakeml"), + WEEK_SIG("1 Week - Significant", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/significant_week.quakeml"), + WEEK_45("1 Week - M4.5+", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/4.5_week.quakeml"), + WEEK_25("1 Week - M2.5+", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_week.quakeml"), + WEEK_1("1 Week - M1.0+", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/1.0_week.quakeml"), + WEEK_ALL("1 Week - All", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.quakeml"), + MONTH_SIG("1 Month - Significant", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/significant_month.quakeml"), + MONTH_45("1 Month - M4.5+", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/4.5_month.quakeml"), + MONTH_25("1 Month - M2.5+", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_month.quakeml"), + MONTH_1("1 Month - M1.0+", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/1.0_month.quakeml"), + MONTH_ALL("1 Month - All", "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.quakeml"); + + private String label; + private String url; + + private HypocenterSource(String label, String url) { + this.label = label; + this.url = url; + } + + public String toString() { + return label; + } + + public String getUrl() { + return url; + } + + public static HypocenterSource fromUrl(String hypocenterUrl) { + for (HypocenterSource source : HypocenterSource.values()) { + if (source.url.equals(hypocenterUrl)) { + return source; + } + } + throw new RuntimeException("Cannot find hypo source for " + hypocenterUrl); + } +} \ No newline at end of file diff --git a/src/main/java/gov/usgs/volcanoes/swarm/picker/Event.java b/src/main/java/gov/usgs/volcanoes/swarm/picker/Event.java deleted file mode 100644 index 8655619d..00000000 --- a/src/main/java/gov/usgs/volcanoes/swarm/picker/Event.java +++ /dev/null @@ -1,76 +0,0 @@ -package gov.usgs.volcanoes.swarm.picker; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class Event { - private static final Logger LOGGER = LoggerFactory.getLogger(Event.class); - private final Map channels; - private final List observers; - - public Event() { - channels = new LinkedHashMap(); - observers = new ArrayList(); - } - - public void setPhase(String channel, Phase phase) { - EventChannel eChan = channels.get(channel); - if (eChan == null) { - eChan = new EventChannel(); - channels.put(channel, eChan); - } - eChan.setPhase(phase); - if (eChan.isEmpty()) { - channels.remove(channel); - } - notifyObservers(); - } - - public void clearPhase(String channel, Phase.PhaseType type) { - EventChannel eChan = channels.get(channel); - if (eChan != null) { - eChan.clearPhase(type); - if (eChan.isEmpty()) { - channels.remove(channel); - } - notifyObservers(); - } - } - - public Phase getPhase(String channel, Phase.PhaseType type) { - EventChannel eChan = channels.get(channel); - if (eChan != null) { - return eChan.getPhase(type); - } else { - return null; - } - } - - public Map getChannels() { - return channels; - } - - public void addObserver(EventObserver observer) { - observers.add(observer); - } - - public void notifyObservers() { - for (EventObserver observer : observers) { - observer.updateEvent(); - } - } - - public void remove(String channel) { - channels.remove(channel); - notifyObservers(); - } - - public boolean isEmpty() { - return channels.isEmpty(); - } -} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/picker/EventChannel.java b/src/main/java/gov/usgs/volcanoes/swarm/picker/EventChannel.java deleted file mode 100644 index 2d1d8b6e..00000000 --- a/src/main/java/gov/usgs/volcanoes/swarm/picker/EventChannel.java +++ /dev/null @@ -1,54 +0,0 @@ -package gov.usgs.volcanoes.swarm.picker; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class EventChannel { - private final static Logger LOGGER = LoggerFactory.getLogger(EventChannel.class); - private Phase pPhase; - private Phase sPhase; - - private int maxAmplitude; - private long duration; - - public void setPhase(Phase phase) { - switch (phase.phaseType) { - case P: - pPhase = phase; - break; - case S: - sPhase = phase; - break; - default: - throw new RuntimeException("Unknown phase type."); - } - } - - public void clearPhase(Phase.PhaseType type) { - switch (type) { - case P: - pPhase = null; - break; - case S: - sPhase = null; - break; - default: - throw new RuntimeException("Unknown phase type."); - } - } - - public Phase getPhase(Phase.PhaseType type) { - switch (type) { - case P: - return pPhase; - case S: - return sPhase; - default: - throw new RuntimeException("Unknown phase type."); - } - } - - public boolean isEmpty() { - return pPhase == null && sPhase == null; - } -} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/picker/EventObserver.java b/src/main/java/gov/usgs/volcanoes/swarm/picker/EventObserver.java deleted file mode 100644 index 900ecbc4..00000000 --- a/src/main/java/gov/usgs/volcanoes/swarm/picker/EventObserver.java +++ /dev/null @@ -1,5 +0,0 @@ -package gov.usgs.volcanoes.swarm.picker; - -public interface EventObserver { - public void updateEvent(); -} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/picker/Phase.java b/src/main/java/gov/usgs/volcanoes/swarm/picker/Phase.java deleted file mode 100644 index b13815c2..00000000 --- a/src/main/java/gov/usgs/volcanoes/swarm/picker/Phase.java +++ /dev/null @@ -1,87 +0,0 @@ -package gov.usgs.volcanoes.swarm.picker; - -import gov.usgs.volcanoes.swarm.picker.Phase.Builder; -import gov.usgs.volcanoes.swarm.picker.Phase.FirstMotion; - -public class Phase { - public static enum Onset { - i, e - } - - public static enum PhaseType { - P, S - } - - public static enum FirstMotion { - UP, DOWN, COMPRESSION, DIALATION, POOR_UP, POOR_DOWN, POOR_COMPRESSION, POOR_DIALATION, NOISY, NOT_READABLE; - } - - public final Onset onset; - public final PhaseType phaseType; - public final FirstMotion firstMotion; - public final int weight; - public final long time; - - public static class Builder { - private String channel; - private Onset onset; - private PhaseType phaseType; - private FirstMotion firstMotion; - private int weight; - private long time; - - public Builder() {} - public Builder(Phase phase) { - onset = phase.onset; - phaseType = phase.phaseType; - firstMotion = phase.firstMotion; - weight = phase.weight; - time = phase.time; - } - - public Builder onset(Onset onset) { - this.onset = onset; - return this; - } - - public Builder phaseType(PhaseType phaseType) { - this.phaseType = phaseType; - return this; - } - - public Builder firstMotion(FirstMotion firstMotion) { - this.firstMotion = firstMotion; - return this; - } - - public Builder weight(int weight) { - this.weight = weight; - return this; - } - - public Builder time(long time) { - this.time = time; - return this; - } - - public Phase build() { - return new Phase(this); - } - - } - - private Phase(Builder builder) { - onset = builder.onset; - phaseType = builder.phaseType; - firstMotion = builder.firstMotion; - weight = builder.weight; - time = builder.time; - } - - public String tag() { - StringBuffer sb = new StringBuffer(); - sb.append(onset).append(phaseType).append(weight); - - return sb.toString(); - } -} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/picker/PhasePopup.java b/src/main/java/gov/usgs/volcanoes/swarm/picker/PhasePopup.java deleted file mode 100644 index 7ebfef31..00000000 --- a/src/main/java/gov/usgs/volcanoes/swarm/picker/PhasePopup.java +++ /dev/null @@ -1,167 +0,0 @@ -package gov.usgs.volcanoes.swarm.picker; - -import java.awt.Component; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; -import javax.swing.SwingUtilities; - -public class PhasePopup extends JPopupMenu { - private JMenu p; - private JMenu s; - private final String channel; - private final long time; - private final Event event; - private Component parent; - - public PhasePopup(final Event event, final String channel, final long time) { - this.channel = channel; - this.time = time; - this.event = event; - - // final Component parentFrame = SwingUtilities.getAncestorNamed("mainPanel", this.getParent()); - p = new JMenu("P"); - add(p); - - - JMenuItem ip0 = new JMenuItem("iP0"); - ip0.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Phase phase = new Phase.Builder().onset(Phase.Onset.i).phaseType(Phase.PhaseType.P) - .firstMotion(Phase.FirstMotion.UP).time(time).weight(0).build(); - event.setPhase(channel, phase); - parent.validate(); - parent.repaint(); - } - }); - p.add(ip0); - - JMenuItem ip1 = new JMenuItem("iP1"); - ip1.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Phase phase = new Phase.Builder().onset(Phase.Onset.i).phaseType(Phase.PhaseType.P) - .firstMotion(Phase.FirstMotion.UP).time(time).weight(1).build(); - event.setPhase(channel, phase); - parent.validate(); - parent.repaint(); - } - }); - p.add(ip1); - - JMenuItem ep2 = new JMenuItem("eP2"); - ep2.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Phase phase = new Phase.Builder().onset(Phase.Onset.e).phaseType(Phase.PhaseType.P) - .firstMotion(Phase.FirstMotion.UP).time(time).weight(2).build(); - event.setPhase(channel, phase); - parent.validate(); - parent.repaint(); - } - }); - p.add(ep2); - - JMenuItem ep3 = new JMenuItem("eP3"); - ep3.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Phase phase = new Phase.Builder().onset(Phase.Onset.e).phaseType(Phase.PhaseType.P) - .firstMotion(Phase.FirstMotion.UP).time(time).weight(3).build(); - event.setPhase(channel, phase); - parent.validate(); - parent.repaint(); - } - }); - p.add(ep3); - - JMenuItem clearP = new JMenuItem("Clear P"); - clearP.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - event.clearPhase(channel, Phase.PhaseType.P); - parent.validate(); - parent.repaint(); - } - }); - p.add(clearP); - - s = new JMenu("S"); - add(s); - - JMenuItem is0 = new JMenuItem("iS0"); - is0.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Phase phase = new Phase.Builder().onset(Phase.Onset.i).phaseType(Phase.PhaseType.S) - .firstMotion(Phase.FirstMotion.UP).time(time).weight(0).build(); - event.setPhase(channel, phase); - parent.validate(); - parent.repaint(); - } - }); - s.add(is0); - - JMenuItem is1 = new JMenuItem("iS1"); - is1.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Phase phase = new Phase.Builder().onset(Phase.Onset.i).phaseType(Phase.PhaseType.S) - .firstMotion(Phase.FirstMotion.UP).time(time).weight(1).build(); - event.setPhase(channel, phase); - parent.validate(); - parent.repaint(); - } - }); - s.add(is1); - - JMenuItem es2 = new JMenuItem("eS2"); - es2.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Phase phase = new Phase.Builder().onset(Phase.Onset.e).phaseType(Phase.PhaseType.S) - .firstMotion(Phase.FirstMotion.UP).time(time).weight(2).build(); - event.setPhase(channel, phase); - parent.validate(); - parent.repaint(); - } - }); - s.add(es2); - - JMenuItem es3 = new JMenuItem("eS3"); - es3.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - Phase phase = new Phase.Builder().onset(Phase.Onset.e).phaseType(Phase.PhaseType.S) - .firstMotion(Phase.FirstMotion.UP).time(time).weight(3).build(); - event.setPhase(channel, phase); - parent.validate(); - parent.repaint(); - } - }); - s.add(es3); - - JMenuItem clearS = new JMenuItem("Clear S"); - clearS.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - event.clearPhase(channel, Phase.PhaseType.S); - parent.validate(); - parent.repaint(); - } - }); - s.add(clearS); - - JMenuItem clearAll = new JMenuItem("Clear"); - clearAll.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - event.clearPhase(channel, Phase.PhaseType.P); - event.clearPhase(channel, Phase.PhaseType.S); - parent.validate(); - parent.repaint(); - } - }); - add(clearAll); - - - } - - public void setParent(Component parent) { - this.parent = parent; - } -} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/picker/PickListPanel.java b/src/main/java/gov/usgs/volcanoes/swarm/picker/PickListPanel.java deleted file mode 100644 index bdf40936..00000000 --- a/src/main/java/gov/usgs/volcanoes/swarm/picker/PickListPanel.java +++ /dev/null @@ -1,274 +0,0 @@ -package gov.usgs.volcanoes.swarm.picker; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.font.TextAttribute; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import javax.swing.BorderFactory; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.SwingConstants; -import javax.swing.border.Border; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import gov.usgs.volcanoes.core.time.Time; - -public class PickListPanel extends JPanel implements EventObserver { - private static final Logger LOGGER = LoggerFactory.getLogger(PickListPanel.class); - private static final Color BACKGROUND_COLOR = new Color(255, 255, 255); - private static final Color SELECTED_BACKGROUND_COLOR = new Color(255, 248, 220); - // private static final Color SELECTED_BACKGROUND_COLOR = new Color(0, 0, 220); - private static final Font TABLE_FONT = new Font(Font.MONOSPACED, Font.PLAIN, 12); - private final Event event; - private JPanel pickList; - private Set selected; - private Component parent; - - public PickListPanel(Event event) { - super(); - this.event = event; - LOGGER.debug("GOT EVENT: {}", event); - selected = new HashSet(); - - setLayout(new BorderLayout()); - LOGGER.debug("Event: " + event); - event.addObserver(this); - pickList = new JPanel(); - pickList.setLayout(new GridBagLayout()); - - writeList(pickList); - add(pickList, BorderLayout.PAGE_START); - } - - private void writeList(JPanel pickList) { - // pickList.setBorder(BorderFactory.createLineBorder(Color.black)); - pickList.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 3)); - pickList.setBackground(BACKGROUND_COLOR); - JLabel label; - GridBagConstraints c = new GridBagConstraints(); - c.fill = GridBagConstraints.HORIZONTAL; - c.weightx = .5; - c.anchor = GridBagConstraints.CENTER; - c.gridy = 0; - c.gridx = GridBagConstraints.RELATIVE; - c.ipadx = 3; - c.ipady = 2; - - Border border = BorderFactory.createMatteBorder(0, 0, 1, 0, Color.BLACK); - - label = new JLabel("Time", SwingConstants.CENTER); - label.setBorder(border); - pickList.add(label, c); - - label = new JLabel("Type", SwingConstants.CENTER); - label.setBorder(border); - pickList.add(label, c); - - label = new JLabel("Channel", SwingConstants.CENTER); - label.setBorder(border); - pickList.add(label, c); - - label = new JLabel("Weight", SwingConstants.CENTER); - label.setBorder(border); - pickList.add(label, c); - - label = new JLabel("Onset", SwingConstants.CENTER); - label.setBorder(border); - pickList.add(label, c); - - label = new JLabel("First Motion", SwingConstants.CENTER); - label.setBorder(border); - pickList.add(label, c); - - LOGGER.debug("event: {}", event); - Map channels = event.getChannels(); - String[] keys = channels.keySet().toArray(new String[0]); - int idx = keys.length; - while (idx-- > 0) { - c.gridy++; - String chan = keys[idx]; - - EventChannel eventChannel = channels.get(chan); - - Phase phase; - - phase = eventChannel.getPhase(Phase.PhaseType.P); - if (phase != null) { - writePhase(pickList, chan, phase, c); - } - - phase = eventChannel.getPhase(Phase.PhaseType.S); - if (phase != null) { - writePhase(pickList, chan, phase, c); - } - } - - c.weighty = 1; - c.gridy++; - JPanel filler = new JPanel(); - filler.setBackground(BACKGROUND_COLOR); - pickList.add(filler, c); - } - - public void writePhase(final JPanel pickList, final String channel, final Phase phase, - final GridBagConstraints c) { - - boolean isSelected = selected.contains(channel); - - c.gridy++; - - String time = Time.toDateString(phase.time); - pickList.add(getLabel(time, isSelected), c); - - String phaseT = phase.phaseType.toString(); - pickList.add(getLabel(phaseT, isSelected), c); - - pickList.add(getLabel(channel, isSelected), c); - - c.fill = GridBagConstraints.NONE; - final JComboBox weight = new JComboBox(new Integer[] {0, 1, 2, 3, 4}); - weight.setFont(TABLE_FONT); - weight.setSelectedIndex(phase.weight); - weight.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - Phase newPhase = new Phase.Builder(phase).weight(weight.getSelectedIndex()).build(); - event.setPhase(channel, newPhase); - parent.validate(); - parent.repaint(); - } - }); - pickList.add(weight, c); - c.fill = GridBagConstraints.HORIZONTAL; - - c.fill = GridBagConstraints.NONE; - Phase.Onset[] onsets = Phase.Onset.values(); - final JComboBox onset = new JComboBox(onsets); - onset.setFont(TABLE_FONT); - int i = 0; - while (i < onsets.length && onsets[i] != phase.onset) { - i++; - } - onset.setSelectedIndex(i); - onset.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - Phase newPhase = - new Phase.Builder(phase).onset((Phase.Onset) onset.getSelectedItem()).build(); - event.setPhase(channel, newPhase); - parent.validate(); - parent.repaint(); - } - }); - pickList.add(onset, c); - c.fill = GridBagConstraints.HORIZONTAL; - - if (phase.phaseType == Phase.PhaseType.P) { - c.fill = GridBagConstraints.NONE; - Phase.FirstMotion[] firstMotions = Phase.FirstMotion.values(); - final JComboBox firstMotion = - new JComboBox(firstMotions); - firstMotion.setFont(TABLE_FONT); - int idx = 0; - while (idx < firstMotions.length && firstMotions[idx] != phase.firstMotion) { - idx++; - } - firstMotion.setSelectedIndex(idx); - firstMotion.addActionListener(new ActionListener() { - - public void actionPerformed(ActionEvent e) { - Phase newPhase = new Phase.Builder(phase) - .firstMotion((Phase.FirstMotion) firstMotion.getSelectedItem()).build(); - event.setPhase(channel, newPhase); - parent.validate(); - parent.repaint(); - } - }); - pickList.add(firstMotion, c); - c.fill = GridBagConstraints.HORIZONTAL; - } else { - pickList.add(new JLabel(""), c); - } - } - - public void updateEvent() { - if (event == null) { - return; - } - - LOGGER.debug("Updating pick list"); - JPanel newList = new JPanel(); - newList.setLayout(new GridBagLayout()); - writeList(newList); - remove(pickList); - pickList = newList; - add(pickList, BorderLayout.CENTER); - // parent.repaint(); - } - - public void deselect(String channel) { - LOGGER.debug("deselecting {}", channel); - selected.remove(channel); - } - - public void deselectAll() { - LOGGER.debug("deselecting all"); - selected = new HashSet(); - - } - - public void select(String channel) { - LOGGER.debug("selecting {}", channel); - selected.add(channel); - } - - public void remove(String channel) { - LOGGER.debug("removing {}", channel); - - selected.remove(channel); - event.remove(channel); - } - - private JLabel getLabel(String string, boolean selected) { - JLabel label = new JLabel(string, SwingConstants.CENTER); - label.setFont(TABLE_FONT); - label.setOpaque(true); - if (selected) { - // label.setBackground(SELECTED_BACKGROUND_COLOR); - label.setBackground(BACKGROUND_COLOR); - Font font = label.getFont(); - font = font - .deriveFont(Collections.singletonMap(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD)); - // TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE)); - - label.setFont(font); - } else { - label.setBackground(BACKGROUND_COLOR); - } - - return label; - } - - public void setParent(Component parent) { - this.parent = parent; - } - - public void repaint() { - updateEvent(); - super.repaint(); - } -} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/picker/PickerFrame.java b/src/main/java/gov/usgs/volcanoes/swarm/picker/PickerFrame.java deleted file mode 100644 index 1de7a623..00000000 --- a/src/main/java/gov/usgs/volcanoes/swarm/picker/PickerFrame.java +++ /dev/null @@ -1,1098 +0,0 @@ -package gov.usgs.volcanoes.swarm.picker; - -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.datatransfer.UnsupportedFlavorException; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.awt.event.MouseEvent; -import java.awt.image.BufferedImage; -import java.beans.PropertyVetoException; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; -import java.util.TimeZone; - -import javax.swing.AbstractAction; -import javax.swing.BorderFactory; -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.ButtonGroup; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JFileChooser; -import javax.swing.JInternalFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JRadioButtonMenuItem; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JToolBar; -import javax.swing.ScrollPaneConstants; -import javax.swing.SwingUtilities; -import javax.swing.TransferHandler; -import javax.swing.WindowConstants; -import javax.swing.event.InternalFrameAdapter; -import javax.swing.event.InternalFrameEvent; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import gov.usgs.plot.data.Wave; -import gov.usgs.plot.data.file.FileType; -import gov.usgs.plot.data.file.SeismicDataFile; -import gov.usgs.util.Pair; -import gov.usgs.volcanoes.core.contrib.PngEncoder; -import gov.usgs.volcanoes.core.contrib.PngEncoderB; -import gov.usgs.volcanoes.core.time.J2kSec; -import gov.usgs.volcanoes.core.ui.ExtensionFileFilter; -import gov.usgs.volcanoes.core.util.UiUtils; -import gov.usgs.volcanoes.swarm.FileChooser; -import gov.usgs.volcanoes.swarm.Icons; -import gov.usgs.volcanoes.swarm.Metadata; -import gov.usgs.volcanoes.swarm.Swarm; -import gov.usgs.volcanoes.swarm.SwarmConfig; -import gov.usgs.volcanoes.swarm.SwarmFrame; -import gov.usgs.volcanoes.swarm.SwarmUtil; -import gov.usgs.volcanoes.swarm.SwingWorker; -import gov.usgs.volcanoes.swarm.Throbber; -import gov.usgs.volcanoes.swarm.chooser.DataChooser; -import gov.usgs.volcanoes.swarm.data.CachedDataSource; -import gov.usgs.volcanoes.swarm.data.SeismicDataSource; -import gov.usgs.volcanoes.swarm.internalFrame.InternalFrameListener; -import gov.usgs.volcanoes.swarm.internalFrame.SwarmInternalFrames; -import gov.usgs.volcanoes.swarm.time.TimeListener; -import gov.usgs.volcanoes.swarm.time.WaveViewTime; -import gov.usgs.volcanoes.swarm.wave.AbstractWavePanel; -import gov.usgs.volcanoes.swarm.wave.WaveViewPanelAdapter; -import gov.usgs.volcanoes.swarm.wave.WaveViewPanelListener; -import gov.usgs.volcanoes.swarm.wave.WaveViewSettingsToolbar; - -/** - * The picker internal frame. Adapted from the WaveClipboardFrame. - * - * @author Tom Parker - */ -public class PickerFrame extends SwarmFrame implements EventObserver { - private static final Logger LOGGER = LoggerFactory.getLogger(PickerFrame.class); - - public static final long serialVersionUID = -1; - private static final Color SELECT_COLOR = new Color(200, 220, 241); - private static final Color BACKGROUND_COLOR = new Color(0xf7, 0xf7, 0xf7); - - private JScrollPane scrollPane; - private Box waveBox; - private PickListPanel pickList; - private JSplitPane mainPanel; - private PickerWavePanel baseWave; - private final List waves; - private final Set selectedSet; - private JToolBar toolbar; - private JLabel statusLabel; - private JButton sizeButton; - private JButton saveButton; - private JButton captureButton; - private JButton histButton; - private JButton particleMotionButton; - - private final DateFormat saveAllDateFormat; - - private WaveViewPanelListener selectListener; - private WaveViewSettingsToolbar waveToolbar; - - private JButton upButton; - private JButton downButton; - private JButton removeButton; - private JButton compXButton; - private JButton expXButton; - private JButton forwardButton; - private JButton backButton; - private JButton gotoButton; - - private JPopupMenu popup; - - private final Map> histories; - - private Throbber throbber; - - private int waveHeight = -1; - - private int lastClickedIndex = -1; - - private Event event; - - public PickerFrame() { - super("Picker", true, true, true, false); - event = new Event(); - event.addObserver(this); - - this.setFocusable(true); - this.setVisible(true); - selectedSet = new HashSet(); - saveAllDateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); - saveAllDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); - waves = new ArrayList(); - histories = new HashMap>(); - createUI(); - LOGGER.debug("Finished creating picker frame."); - } - - - private void createUI() { - this.setFrameIcon(Icons.ruler); - this.setSize(swarmConfig.clipboardWidth, swarmConfig.clipboardHeight); - this.setLocation(swarmConfig.clipboardX, swarmConfig.clipboardY); - this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); - LOGGER.debug("picker frame: {} @ {}", this.getSize(), this.getLocation()); - - toolbar = SwarmUtil.createToolBar(); - - JPanel wavePanel = new JPanel(); - wavePanel.setLayout(new BoxLayout(wavePanel, BoxLayout.PAGE_AXIS)); - - JPanel tablePanel = new JPanel(); - tablePanel.setLayout(new BoxLayout(tablePanel, BoxLayout.PAGE_AXIS)); - - mainPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, wavePanel, tablePanel); - mainPanel.setOneTouchExpandable(true); - - createMainButtons(); - createWaveButtons(); - wavePanel.add(toolbar); - - waveBox = new Box(BoxLayout.Y_AXIS); - waveBox.setTransferHandler(new TransferHandler("") { - public boolean canImport(TransferSupport supp) { - return supp.isDataFlavorSupported(DataFlavor.stringFlavor); - } - - public boolean importData(TransferSupport supp) { - if (!canImport(supp)) { - return false; - } - - Transferable t = supp.getTransferable(); - String data = null; - try { - data = (String) t.getTransferData(DataFlavor.stringFlavor); - } catch (UnsupportedFlavorException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - - // Fetch the drop location - DropLocation loc = supp.getDropLocation(); - - // Insert the data at this location - LOGGER.debug("DROP {} @ {}", data, loc); - String[] chans = data.split("\n"); - for (String chan : chans) { - addWave(chan); - } - return true; - } - - }); - scrollPane = new JScrollPane(waveBox); - scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); - scrollPane.getVerticalScrollBar().setUnitIncrement(40); - wavePanel.add(scrollPane); - - JPanel statusPanel = new JPanel(); - statusPanel.setLayout(new BorderLayout()); - statusLabel = new JLabel(" "); - statusLabel.setBorder(BorderFactory.createEtchedBorder()); - statusPanel.add(statusLabel); - statusPanel - .setMaximumSize(new Dimension(Integer.MAX_VALUE, statusPanel.getPreferredSize().height)); - wavePanel.add(statusPanel); - - pickList = new PickListPanel(event); - pickList.setParent(mainPanel); - - scrollPane = new JScrollPane(pickList); - scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); - scrollPane.getVerticalScrollBar().setUnitIncrement(40); - tablePanel.add(scrollPane); - - mainPanel.setResizeWeight(.75); - this.setContentPane(mainPanel); - - createListeners(); - doButtonEnables(); - } - - private void createMainButtons() { - saveButton = - SwarmUtil.createToolBarButton(Icons.save, "Save selected wave", new SaveActionListener()); - saveButton.setEnabled(false); - toolbar.add(saveButton); - - toolbar.addSeparator(); - - sizeButton = - SwarmUtil.createToolBarButton(Icons.resize, "Set wave height", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - doSizePopup(); - } - }); - toolbar.add(sizeButton); - - toolbar.addSeparator(); - captureButton = SwarmUtil.createToolBarButton(Icons.camera, "Save pick image (P)", - new CaptureActionListener()); - UiUtils.mapKeyStrokeToButton(this, "P", "capture", captureButton); - toolbar.add(captureButton); - } - - // TODO: don't write image on event thread - // TODO: unify with MapFrame.CaptureActionListener - class CaptureActionListener implements ActionListener { - public void actionPerformed(final ActionEvent e) { - if (waves == null || waves.size() == 0) - return; - - final JFileChooser chooser = FileChooser.getFileChooser(); - final File lastPath = new File(swarmConfig.lastPath); - chooser.setCurrentDirectory(lastPath); - chooser.setSelectedFile(new File("clipboard.png")); - chooser.setDialogTitle("Save Clipboard Screen Capture"); - final int result = chooser.showSaveDialog(applicationFrame); - File f = null; - if (result == JFileChooser.APPROVE_OPTION) { - f = chooser.getSelectedFile(); - - if (f.exists()) { - final int choice = JOptionPane.showConfirmDialog(applicationFrame, - "File exists, overwrite?", "Confirm", JOptionPane.YES_NO_OPTION); - if (choice != JOptionPane.YES_OPTION) - return; - } - swarmConfig.lastPath = f.getParent(); - } - if (f == null) - return; - - int height = 0; - final int width = waves.get(0).getWidth(); - for (final AbstractWavePanel panel : waves) - height += panel.getHeight(); - - final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR); - final Graphics g = image.getGraphics(); - for (final AbstractWavePanel panel : waves) { - panel.paint(g); - g.translate(0, panel.getHeight()); - } - try { - final PngEncoderB png = new PngEncoderB(image, false, PngEncoder.FILTER_NONE, 7); - final FileOutputStream out = new FileOutputStream(f); - final byte[] bytes = png.pngEncode(); - out.write(bytes); - out.close(); - } catch (final Exception ex) { - ex.printStackTrace(); - } - } - } - - private void createWaveButtons() { - toolbar.addSeparator(); - - backButton = SwarmUtil.createToolBarButton(Icons.left, "Scroll back time 20% (Left arrow)", - new ActionListener() { - public void actionPerformed(final ActionEvent e) { - shiftTime(-0.20); - } - }); - UiUtils.mapKeyStrokeToButton(this, "LEFT", "backward1", backButton); - toolbar.add(backButton); - - forwardButton = SwarmUtil.createToolBarButton(Icons.right, - "Scroll forward time 20% (Right arrow)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - shiftTime(0.20); - } - }); - toolbar.add(forwardButton); - UiUtils.mapKeyStrokeToButton(this, "RIGHT", "forward1", forwardButton); - - gotoButton = - SwarmUtil.createToolBarButton(Icons.gototime, "Go to time (Ctrl-G)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - final String t = JOptionPane.showInputDialog(applicationFrame, - "Input time in 'YYYYMMDDhhmm[ss]' format:", "Go to Time", - JOptionPane.PLAIN_MESSAGE); - if (t != null) - gotoTime(t); - } - }); - toolbar.add(gotoButton); - UiUtils.mapKeyStrokeToButton(this, "ctrl G", "goto", gotoButton); - - compXButton = SwarmUtil.createToolBarButton(Icons.xminus, - "Shrink sample time 20% (Alt-left arrow, +)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - scaleTime(0.20); - } - }); - toolbar.add(compXButton); - UiUtils.mapKeyStrokeToButton(this, "alt LEFT", "compx", compXButton); - UiUtils.mapKeyStrokeToButton(this, "EQUALS", "compx2", compXButton); - UiUtils.mapKeyStrokeToButton(this, "shift EQUALS", "compx2", compXButton); - - expXButton = SwarmUtil.createToolBarButton(Icons.xplus, - "Expand sample time 20% (Alt-right arrow, -)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - scaleTime(-0.20); - } - }); - toolbar.add(expXButton); - UiUtils.mapKeyStrokeToButton(this, "alt RIGHT", "expx", expXButton); - UiUtils.mapKeyStrokeToButton(this, "MINUS", "expx", expXButton); - - histButton = SwarmUtil.createToolBarButton(Icons.timeback, "Last time settings (Backspace)", - new ActionListener() { - public void actionPerformed(final ActionEvent e) { - back(); - } - }); - UiUtils.mapKeyStrokeToButton(this, "BACK_SPACE", "back", histButton); - toolbar.add(histButton); - toolbar.addSeparator(); - - waveToolbar = new WaveViewSettingsToolbar(null, toolbar, this); - - toolbar.addSeparator(); - - upButton = SwarmUtil.createToolBarButton(Icons.up, "Move wave up in clipboard (Up arrow)", - new ActionListener() { - public void actionPerformed(final ActionEvent e) { - moveUp(); - } - }); - UiUtils.mapKeyStrokeToButton(this, "UP", "up", upButton); - toolbar.add(upButton); - - downButton = SwarmUtil.createToolBarButton(Icons.down, - "Move wave down in clipboard (Down arrow)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - moveDown(); - } - }); - UiUtils.mapKeyStrokeToButton(this, "DOWN", "down", downButton); - toolbar.add(downButton); - - removeButton = SwarmUtil.createToolBarButton(Icons.delete, - "Remove wave from clipboard (Delete)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - remove(); - } - }); - UiUtils.mapKeyStrokeToButton(this, "DELETE", "remove", removeButton); - toolbar.add(removeButton); - - particleMotionButton = SwarmUtil.createToolBarButton(Icons.particle_motion, "Particle Motion", - new ActionListener() { - public void actionPerformed(final ActionEvent e) { - particleMotionPlot(); - } - - private void particleMotionPlot() { - // TODO Auto-generated method stub - - } - }); - toolbar.add(particleMotionButton); - - toolbar.add(Box.createHorizontalGlue()); - - throbber = new Throbber(); - toolbar.add(throbber); - - UiUtils.mapKeyStrokeToAction(this, "control A", "selectAll", new AbstractAction() { - private static final long serialVersionUID = 1L; - - public void actionPerformed(final ActionEvent e) { - for (final PickerWavePanel wave : waves) - select(wave); - } - }); - } - - private void createListeners() { - this.addInternalFrameListener(new InternalFrameAdapter() { - @Override - public void internalFrameActivated(final InternalFrameEvent e) {} - - @Override - public void internalFrameDeiconified(final InternalFrameEvent e) { - resizeWaves(); - } - - @Override - public void internalFrameClosing(final InternalFrameEvent e) { - setVisible(false); - } - - @Override - public void internalFrameClosed(final InternalFrameEvent e) {} - }); - - this.addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(final ComponentEvent e) { - resizeWaves(); - } - }); - - WaveViewTime.addTimeListener(new TimeListener() { - public void timeChanged(final double j2k) { - for (final AbstractWavePanel panel : waves) { - if (panel != null) - panel.setCursorMark(j2k); - } - } - }); - - selectListener = new WaveViewPanelAdapter() { - public void mousePressed(final AbstractWavePanel src, final MouseEvent e, - final boolean dragging) { - PickerWavePanel panel = (PickerWavePanel) src; - LOGGER.debug("wave selected."); - requestFocusInWindow(); - final int thisIndex = getWaveIndex(src); - if (!e.isControlDown() && !e.isShiftDown() && !e.isAltDown()) { - deselectAll(); - select(panel); - } else if (e.isControlDown()) { - if (selectedSet.contains(src)) - deselect(src); - else - select(panel); - } else if (e.isShiftDown()) { - if (lastClickedIndex == -1) - select(panel); - else { - deselectAll(); - final int min = Math.min(lastClickedIndex, thisIndex); - final int max = Math.max(lastClickedIndex, thisIndex); - for (int i = min; i <= max; i++) { - final PickerWavePanel ps = (PickerWavePanel) waveBox.getComponent(i); - select(ps); - } - } - } - lastClickedIndex = thisIndex; - event.notifyObservers(); - mainPanel.validate(); - mainPanel.repaint(); - } - - @Override - public void waveZoomed(final AbstractWavePanel src, final double st, final double et, - final double nst, final double net) { - final double[] t = new double[] {st, et}; - addHistory(src, t); - for (final AbstractWavePanel wvp : selectedSet) { - if (wvp != src) { - addHistory(wvp, t); - wvp.zoom(nst, net); - } - } - } - - @Override - public void waveClosed(final AbstractWavePanel src) { - LOGGER.debug("Removing wave: {}", src.getChannel()); - remove(src); - } - }; - } - - private int calculateWaveHeight() { - if (waveHeight > 0) - return waveHeight; - - final int w = scrollPane.getViewport().getSize().width; - int h = (int) Math.round(w * 60.0 / 300.0); - h = Math.min(200, h); - h = Math.max(h, 80); - return h; - } - - private void setWaveHeight(final int s) { - waveHeight = s; - resizeWaves(); - } - - private void doSizePopup() { - if (popup == null) { - final String[] labels = new String[] {"Auto", null, "Tiny", "Small", "Medium", "Large"}; - final int[] sizes = new int[] {-1, -1, 50, 100, 160, 230}; - popup = new JPopupMenu(); - final ButtonGroup group = new ButtonGroup(); - for (int i = 0; i < labels.length; i++) { - if (labels[i] != null) { - final int size = sizes[i]; - final JRadioButtonMenuItem mi = new JRadioButtonMenuItem(labels[i]); - mi.addActionListener(new ActionListener() { - public void actionPerformed(final ActionEvent e) { - setWaveHeight(size); - } - }); - if (waveHeight == size) - mi.setSelected(true); - group.add(mi); - popup.add(mi); - } else - popup.addSeparator(); - } - } - popup.show(sizeButton.getParent(), sizeButton.getX(), sizeButton.getY()); - } - - private class SaveActionListener implements ActionListener { - public void actionPerformed(final ActionEvent e) { - final AbstractWavePanel selected = getSingleSelected(); - if (selected == null) - return; - - final JFileChooser chooser = FileChooser.getFileChooser(); - chooser.resetChoosableFileFilters(); - chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); - chooser.setMultiSelectionEnabled(false); - chooser.setDialogTitle("Save Wave"); - - for (final FileType ft : FileType.values()) { - if (ft == FileType.UNKNOWN) - continue; - - final ExtensionFileFilter f = new ExtensionFileFilter(ft.extension, ft.description); - chooser.addChoosableFileFilter(f); - } - - chooser.setFileFilter(chooser.getAcceptAllFileFilter()); - - final File lastPath = new File(swarmConfig.lastPath); - chooser.setCurrentDirectory(lastPath); - final String fileName = selected.getChannel().replace(' ', '_') + ".sac"; - chooser.setSelectedFile(new File(fileName)); - final int result = chooser.showSaveDialog(applicationFrame); - if (result == JFileChooser.APPROVE_OPTION) { - final File f = chooser.getSelectedFile(); - boolean confirm = true; - if (f.exists()) { - if (f.isDirectory()) { - JOptionPane.showMessageDialog(applicationFrame, - "You can not select an existing directory.", "Error", JOptionPane.ERROR_MESSAGE); - return; - } - confirm = false; - final int choice = JOptionPane.showConfirmDialog(applicationFrame, - "File exists, overwrite?", "Confirm", JOptionPane.YES_NO_OPTION); - if (choice == JOptionPane.YES_OPTION) - confirm = true; - } - - if (confirm) { - try { - swarmConfig.lastPath = f.getParent(); - final String fn = f.getPath(); - final SeismicDataFile file = SeismicDataFile.getFile(fn); - final Wave wave = selected.getWave(); - file.putWave(selected.getChannel(), wave); - file.write(); - } catch (final FileNotFoundException ex) { - JOptionPane.showMessageDialog(Swarm.getApplicationFrame(), "Directory does not exist.", - "Error", JOptionPane.ERROR_MESSAGE); - } catch (final IOException ex) { - JOptionPane.showMessageDialog(Swarm.getApplicationFrame(), "Error writing file.", - "Error", JOptionPane.ERROR_MESSAGE); - } - } - } - } - } - - - - private void doButtonEnables() { - final boolean enable = (waves == null || waves.size() == 0); - - final boolean allowSingle = (selectedSet.size() == 1); - upButton.setEnabled(allowSingle); - downButton.setEnabled(allowSingle); - - final boolean allowMulti = (selectedSet.size() > 0); - backButton.setEnabled(allowMulti); - expXButton.setEnabled(allowMulti); - compXButton.setEnabled(allowMulti); - backButton.setEnabled(allowMulti); - forwardButton.setEnabled(allowMulti); - histButton.setEnabled(allowMulti); - removeButton.setEnabled(allowMulti); - gotoButton.setEnabled(allowMulti); - } - - public synchronized PickerWavePanel getSingleSelected() { - if (selectedSet.size() != 1) - return null; - - PickerWavePanel p = null; - for (final PickerWavePanel panel : selectedSet) - p = panel; - - return p; - } - - public synchronized void syncChannels() { - final AbstractWavePanel p = getSingleSelected(); - if (p == null) - return; - - final double st = p.getStartTime(); - final double et = p.getEndTime(); - - // TODO: thread bug here. must synch iterator below - final SwingWorker worker = new SwingWorker() { - @Override - public Object construct() { - List copy = null; - synchronized (PickerFrame.this) { - copy = new ArrayList(waves); - } - for (final AbstractWavePanel wvp : copy) { - if (wvp != p) { - if (wvp.getDataSource() != null) { - addHistory(wvp, new double[] {wvp.getStartTime(), wvp.getEndTime()}); - final Wave sw = wvp.getDataSource().getWave(wvp.getChannel(), st, et); - wvp.setWave(sw, st, et); - } - } - } - return null; - } - - @Override - public void finished() { - repaint(); - } - }; - worker.start(); - } - - public void setStatusText(final String s) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - statusLabel.setText(s); - } - }); - } - - public synchronized void setBaseWave(final PickerWavePanel p) { - baseWave = p; - addWave(p); - doButtonEnables(); - waveBox.validate(); - - String channel = p.getChannel(); - - final List> nrst = - Metadata.findNearest(SwarmConfig.getInstance().getMetadata(), channel); - - final SwingWorker worker = new SwingWorker() { - @Override - public Object construct() { - for (Pair item : nrst) { - if (item.item1 > 20000) { - break; - } else if (item.item2.matches(".*(B|E|H)H(Z|E|N).*")) { - PickerWavePanel p2 = new PickerWavePanel(p); - p2.setChannel(item.item2); - SeismicDataSource sds = p2.getDataSource(); - double st = p2.getStartTime(); - double et = p2.getEndTime(); - Wave wave = sds.getWave(item.item2, st, et); - if (wave != null && wave.isData()) { - p2.setWave(wave, st, et); - p2.getWaveViewSettings().autoScaleAmpMemory = false; - addWave(p2); - System.out.println(String.format("%s (%.1f km)", item.item2, item.item1 / 1000)); - } else { - LOGGER.debug("Skipping {}, no data.", item.item2); - } - } - } - return null; - } - - @Override - public void finished() { - waveBox.validate(); - validate(); - repaint(); - } - }; - worker.start(); - } - - public synchronized void addWave(final String chan) { - PickerWavePanel p2 = new PickerWavePanel(baseWave); - p2.setChannel(chan); - SeismicDataSource sds = p2.getDataSource(); - double st = p2.getStartTime(); - double et = p2.getEndTime(); - Wave wave = sds.getWave(chan, st, et); - if (wave != null && wave.isData()) { - p2.setWave(wave, st, et); - p2.getWaveViewSettings().autoScaleAmpMemory = false; - addWave(p2); - } - } - - public synchronized void addWave(final PickerWavePanel p) { - p.addListener(selectListener); - p.setOffsets(54, 8, 21, 19); - p.setAllowClose(true); - p.setStatusLabel(statusLabel); - p.setAllowDragging(true); - p.setDisplayTitle(true); - p.setEvent(event); - p.setParent(mainPanel); - final int w = scrollPane.getViewport().getSize().width; - p.setSize(w, calculateWaveHeight()); - p.setBottomBorderColor(Color.GRAY); - p.createImage(); - waveBox.add(p); - waves.add(p); - LOGGER.debug("{} panels; {} waves", waveBox.getComponentCount(), waves.size()); - } - - private synchronized void deselect(final AbstractWavePanel p) { - selectedSet.remove(p); - pickList.deselect(p.getChannel()); - waveToolbar.removeSettings(p.getSettings()); - p.setBackgroundColor(BACKGROUND_COLOR); - p.createImage(); - doButtonEnables(); - } - - private synchronized void deselectAll() { - final AbstractWavePanel[] panels = selectedSet.toArray(new AbstractWavePanel[0]); - pickList.deselectAll(); - for (final AbstractWavePanel p : panels) { - deselect(p); - } - } - - private synchronized void select(final PickerWavePanel p) { - if (p == null || selectedSet.contains(p)) - return; - - pickList.select(p.getChannel()); - selectedSet.add(p); - doButtonEnables(); - p.setBackgroundColor(SELECT_COLOR); - DataChooser.getInstance().setNearest(p.getChannel()); - p.createImage(); - waveToolbar.addSettings(p.getSettings()); - } - - private synchronized void remove(final AbstractWavePanel p) { - event.remove(p.getChannel()); - int i = 0; - for (i = 0; i < waveBox.getComponentCount(); i++) { - if (p == waveBox.getComponent(i)) - break; - } - - p.removeListener(selectListener); - p.getDataSource().close(); - setStatusText(" "); - waveBox.remove(i); - waves.remove(p); - histories.remove(p); - doButtonEnables(); - waveBox.validate(); - selectedSet.remove(p); - pickList.remove(p.getChannel()); - lastClickedIndex = Math.min(lastClickedIndex, waveBox.getComponentCount() - 1); - waveToolbar.removeSettings(p.getSettings()); - event.notifyObservers(); - validate(); - repaint(); - } - - protected int getWaveIndex(final AbstractWavePanel p) { - int i = 0; - for (i = 0; i < waveBox.getComponentCount(); i++) { - if (p == waveBox.getComponent(i)) - break; - } - return i; - } - - public synchronized void remove() { - final PickerWavePanel[] panels = selectedSet.toArray(new PickerWavePanel[0]); - for (final PickerWavePanel p : panels) { - LOGGER.debug("removing panel {}", p); - waveBox.remove(p); - waves.remove(p); - event.remove(p.getChannel()); - // pickList.remove(p); - validate(); - repaint(); - } - } - - public synchronized void moveDown() { - final PickerWavePanel p = getSingleSelected(); - if (p == null) - return; - - final int i = waves.indexOf(p); - if (i == waves.size() - 1) - return; - - waves.remove(i); - waves.add(i + 1, p); - waveBox.remove(p); - waveBox.add(p, i + 1); - waveBox.validate(); - repaint(); - } - - public synchronized void moveUp() { - final PickerWavePanel p = getSingleSelected(); - if (p == null) - return; - - final int i = waves.indexOf(p); - if (i == 0) - return; - - waves.remove(i); - waves.add(i - 1, p); - waveBox.remove(p); - waveBox.add(p, i - 1); - waveBox.validate(); - repaint(); - } - - public void resizeWaves() { - final SwingWorker worker = new SwingWorker() { - @Override - public Object construct() { - final int w = scrollPane.getViewport().getSize().width; - for (final AbstractWavePanel wave : waves) { - wave.setSize(w, calculateWaveHeight()); - wave.createImage(); - } - return null; - } - - @Override - public void finished() { - waveBox.validate(); - validate(); - repaint(); - } - }; - worker.start(); - } - - public void removeWaves() { - while (waves.size() > 0) - remove(waves.get(0)); - - waveBox.validate(); - scrollPane.validate(); - doButtonEnables(); - repaint(); - } - - private void addHistory(final AbstractWavePanel wvp, final double[] t) { - Stack history = histories.get(wvp); - if (history == null) { - history = new Stack(); - histories.put(wvp, history); - } - history.push(t); - } - - public void gotoTime(final AbstractWavePanel wvp, String t) { - double j2k = Double.NaN; - try { - if (t.length() == 12) - t = t + "30"; - - j2k = J2kSec.parse("yyyyMMddHHmmss", t); - } catch (final Exception e) { - JOptionPane.showMessageDialog(applicationFrame, "Illegal time value.", "Error", - JOptionPane.ERROR_MESSAGE); - } - - if (!Double.isNaN(j2k)) { - double dt = 60; - if (wvp.getWave() != null) { - final double st = wvp.getStartTime(); - final double et = wvp.getEndTime(); - final double[] ts = new double[] {st, et}; - addHistory(wvp, ts); - dt = (et - st); - } - - final double tzo = - swarmConfig.getTimeZone(wvp.getChannel()).getOffset(System.currentTimeMillis()) / 1000; - - final double nst = j2k - tzo - dt / 2; - final double net = nst + dt; - - fetchNewWave(wvp, nst, net); - } - } - - public void gotoTime(final String t) { - for (final AbstractWavePanel p : selectedSet) - gotoTime(p, t); - } - - public void scaleTime(final AbstractWavePanel wvp, final double pct) { - final double st = wvp.getStartTime(); - final double et = wvp.getEndTime(); - final double[] t = new double[] {st, et}; - addHistory(wvp, t); - final double dt = (et - st) * (1 - pct); - final double mt = (et - st) / 2 + st; - final double nst = mt - dt / 2; - final double net = mt + dt / 2; - fetchNewWave(wvp, nst, net); - } - - public void scaleTime(final double pct) { - for (final AbstractWavePanel p : selectedSet) - scaleTime(p, pct); - } - - public void back(final AbstractWavePanel wvp) { - final Stack history = histories.get(wvp); - if (history == null || history.empty()) - return; - - final double[] t = history.pop(); - fetchNewWave(wvp, t[0], t[1]); - } - - public void back() { - for (final AbstractWavePanel p : selectedSet) - back(p); - } - - private void shiftTime(final AbstractWavePanel wvp, final double pct) { - final double st = wvp.getStartTime(); - final double et = wvp.getEndTime(); - final double[] t = new double[] {st, et}; - addHistory(wvp, t); - final double dt = (et - st) * pct; - final double nst = st + dt; - final double net = et + dt; - fetchNewWave(wvp, nst, net); - } - - public void shiftTime(final double pct) { - for (final AbstractWavePanel p : selectedSet) - shiftTime(p, pct); - } - - public void repositionWaves(final double st, final double et) { - for (final AbstractWavePanel wave : waves) { - fetchNewWave(wave, st, et); - } - } - - public Throbber getThrobber() { - return throbber; - } - - // TODO: This isn't right, this should be a method of waveviewpanel - private void fetchNewWave(final AbstractWavePanel wvp, final double nst, final double net) { - final SwingWorker worker = new SwingWorker() { - @Override - public Object construct() { - throbber.increment(); - final SeismicDataSource sds = wvp.getDataSource(); - // Hacky fix for bug #84 - Wave sw = null; - if (sds instanceof CachedDataSource) - sw = ((CachedDataSource) sds).getBestWave(wvp.getChannel(), nst, net); - else - sw = sds.getWave(wvp.getChannel(), nst, net); - wvp.setWave(sw, nst, net); - wvp.repaint(); - return null; - } - - @Override - public void finished() { - throbber.decrement(); - repaint(); - } - }; - worker.start(); - } - - @Override - public void setMaximum(final boolean max) throws PropertyVetoException { - if (max) { - swarmConfig.clipboardX = getX(); - swarmConfig.clipboardY = getY(); - } - super.setMaximum(max); - } - - @Override - public void paint(final Graphics g) { - super.paint(g); - if (waves.size() == 0) { - final Dimension dim = this.getSize(); - g.setColor(Color.black); - g.drawString("Picker empty.", dim.width / 2 - 40, dim.height / 2); - } - } - - @Override - public void setVisible(final boolean isVisible) { - LOGGER.debug("Visible = {}", isVisible); - super.setVisible(isVisible); - if (isVisible) - toFront(); - } - - - public void updateEvent() { - LOGGER.debug("event is empty? {}", event.getChannels().isEmpty()); - saveButton.setEnabled(!event.getChannels().isEmpty()); - } -} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/picker/PickerWavePanel.java b/src/main/java/gov/usgs/volcanoes/swarm/picker/PickerWavePanel.java deleted file mode 100644 index e4941c63..00000000 --- a/src/main/java/gov/usgs/volcanoes/swarm/picker/PickerWavePanel.java +++ /dev/null @@ -1,112 +0,0 @@ -package gov.usgs.volcanoes.swarm.picker; - -import java.awt.Color; -import java.awt.Component; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics2D; -import java.awt.event.MouseEvent; -import java.awt.geom.Line2D; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import gov.usgs.volcanoes.core.time.J2kSec; -import gov.usgs.volcanoes.swarm.time.WaveViewTime; -import gov.usgs.volcanoes.swarm.wave.AbstractWavePanel; - -public class PickerWavePanel extends AbstractWavePanel implements EventObserver { - - private static final Logger LOGGER = LoggerFactory.getLogger(PickerWavePanel.class); - - private static final Font ANNOTATION_FONT = new Font("Monospaced", Font.BOLD, 12); - private static final Color P_BACKGROUND = new Color(128, 255, 128, 192); - private static final Color S_BACKGROUND = new Color(128, 128, 255, 192); - - private Event event; - private Component parent; - - public PickerWavePanel(AbstractWavePanel insetWavePanel) { - super(insetWavePanel); - } - - @Override - protected void processRightMousePress(MouseEvent e) { - double[] t = getTranslation(); - int x = e.getX(); - double cursorTime = x * t[0] + t[1]; - LOGGER.debug("New phase: {} @ {}", channel, J2kSec.toDateString(cursorTime)); - - PhasePopup phasePopup = new PhasePopup(event, channel, J2kSec.asEpoch(cursorTime)); - phasePopup.setParent(parent); - phasePopup.show(e.getComponent(), e.getX(), e.getY()); - pauseCursorMark = true; - WaveViewTime.fireTimeChanged(cursorTime); - - } - - public void setEvent(Event event) { - this.event = event; - } - - @Override - protected void annotateImage(Graphics2D g2) { - for (Phase.PhaseType type : Phase.PhaseType.values()) { - Phase phase = event.getPhase(channel, type); - if (phase != null) { - markPhase(g2, phase); - } - } - // repaint(); - } - - private void markPhase(Graphics2D g2, Phase phase) { - double j2k = J2kSec.fromEpoch(phase.time); - double[] t = getTranslation(); - if (t == null) - return; - - double x = 2 + (j2k - t[1]) / t[0]; - g2.setColor(DARK_GREEN); - g2.draw(new Line2D.Double(x, yOffset, x, getHeight() - bottomHeight - 1)); - - String tag = phase.tag(); - Font oldFont = g2.getFont(); - g2.setFont(ANNOTATION_FONT); - - FontMetrics fm = g2.getFontMetrics(); - int width = fm.stringWidth(tag); - int height = fm.getAscent(); - - int offset = 2; - int lw = width + 2 * offset; - - Color background; - if (phase.phaseType == Phase.PhaseType.P) { - background = P_BACKGROUND; - } else { - background = S_BACKGROUND; - } - g2.setColor(background); - - g2.fillRect((int) x, 3, lw, height + 2 * offset); - g2.setColor(Color.black); - g2.drawRect((int) x, 3, lw, height + 2 * offset); - - g2.drawString(tag, (int) x + offset, 3 + (fm.getAscent() + offset)); - g2.setFont(oldFont); - } - - @Override - protected void processRightMouseRelease(MouseEvent e) { - pauseCursorMark = false; - } - - public void updateEvent() { - repaint(); - } - - public void setParent(Component parent) { - this.parent = parent; - } -} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/rsam/RsamViewSettingsDialog.java b/src/main/java/gov/usgs/volcanoes/swarm/rsam/RsamViewSettingsDialog.java index cd9b8099..fd38fed1 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/rsam/RsamViewSettingsDialog.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/rsam/RsamViewSettingsDialog.java @@ -2,7 +2,7 @@ import gov.usgs.math.BinSize; import gov.usgs.volcanoes.swarm.Icons; -import gov.usgs.volcanoes.swarm.SwarmDialog; +import gov.usgs.volcanoes.swarm.SwarmModalDialog; import gov.usgs.volcanoes.swarm.rsam.RsamViewSettings.ViewType; import java.awt.BorderLayout; @@ -29,7 +29,7 @@ * * @author Tom Parker */ -public class RsamViewSettingsDialog extends SwarmDialog { +public class RsamViewSettingsDialog extends SwarmModalDialog { private static final long serialVersionUID = 1L; private JPanel dialogPanel; @@ -60,7 +60,7 @@ public class RsamViewSettingsDialog extends SwarmDialog { private JTextField scaleMin; private RsamViewSettingsDialog() { - super(applicationFrame, "RSAM Settings", true); + super(applicationFrame, "RSAM Settings"); this.setIconImage(Icons.rsam_values.getImage()); createUI(); setSizeAndLocation(); diff --git a/src/main/java/gov/usgs/volcanoes/swarm/wave/AbstractWavePanel.java b/src/main/java/gov/usgs/volcanoes/swarm/wave/AbstractWavePanel.java index 6f5c8078..b983cd69 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/wave/AbstractWavePanel.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/wave/AbstractWavePanel.java @@ -2,9 +2,6 @@ -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; @@ -12,6 +9,7 @@ import java.awt.Graphics2D; import java.awt.Image; import java.awt.Paint; +import java.awt.Rectangle; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; @@ -22,9 +20,13 @@ import javax.swing.JComponent; import javax.swing.JLabel; +import javax.swing.JScrollPane; import javax.swing.SwingUtilities; import javax.swing.event.EventListenerList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import gov.usgs.math.Filter; import gov.usgs.plot.Plot; import gov.usgs.plot.PlotException; @@ -86,7 +88,7 @@ public abstract class AbstractWavePanel extends JComponent { */ protected SeismicDataSource source; /** - * A flag to indicate wheter the plot should display a title. Currently used when the plot is on + * A flag to indicate whether the plot should display a title. Currently used when the plot is on * the clipboard or monitor. */ protected boolean displayTitle; @@ -120,14 +122,24 @@ public abstract class AbstractWavePanel extends JComponent { protected static final Color DARK_GREEN = new Color(0, 168, 0); protected boolean pauseCursorMark; + protected double time; - public AbstractWavePanel(WaveViewSettings s) { + + public AbstractWavePanel() { + super(); swarmConfig = SwarmConfig.getInstance(); - settings = s; - s.view = this; pauseCursorMark = false; backgroundColor = new Color(0xf7, 0xf7, 0xf7); + settings = new WaveViewSettings(); + setupMouseHandler(); + + } + + public AbstractWavePanel(WaveViewSettings s) { + this(); + settings = s; + s.view = this; } /** @@ -137,8 +149,8 @@ public AbstractWavePanel(WaveViewSettings s) { * @param p the source WaveViewPanel */ public AbstractWavePanel(AbstractWavePanel p) { + this(); - swarmConfig = SwarmConfig.getInstance(); channel = p.channel; source = p.source; startTime = p.startTime; @@ -146,21 +158,22 @@ public AbstractWavePanel(AbstractWavePanel p) { bias = p.bias; maxSpectraPower = p.maxSpectraPower; maxSpectrogramPower = p.maxSpectrogramPower; - translation = new double[8]; - if (p.translation != null) - System.arraycopy(p.translation, 0, translation, 0, 8); timeSeries = p.timeSeries; allowDragging = p.allowDragging; - settings = new WaveViewSettings(p.settings); - settings.view = this; wave = p.wave; displayTitle = p.displayTitle; backgroundColor = p.backgroundColor; mark1 = p.mark1; mark2 = p.mark2; - setupMouseHandler(); - processSettings(); + translation = new double[8]; + if (p.translation != null) + System.arraycopy(p.translation, 0, translation, 0, 8); + + settings = new WaveViewSettings(p.settings); + settings.view = this; + + processSettings(); } public void setOffsets(int xo, int yo, int rw, int bh) { @@ -482,11 +495,11 @@ public boolean processMousePosition(int x, int y) { Dimension size = getSize(); double[] t = getTranslation(); - double j2k = Double.NaN; + time = Double.NaN; if (wave != null && t != null && y > yOffset && y < (size.height - bottomHeight) && x > xOffset && x < size.width - rightWidth) { - j2k = x * t[0] + t[1]; + time = x * t[0] + t[1]; double yi = y * -t[2] + t[3]; int[] dataRange = wave.getDataRange(); @@ -497,13 +510,13 @@ public boolean processMousePosition(int x, int y) { dataRange[1]); if (timeSeries) { - String utc = J2kSec.format(Time.STANDARD_TIME_FORMAT_MS, j2k); + String utc = J2kSec.format(Time.STANDARD_TIME_FORMAT_MS, time); TimeZone tz = swarmConfig.getTimeZone(channel); - double tzo = tz.getOffset(J2kSec.asEpoch(j2k)) / 1000; + double tzo = tz.getOffset(J2kSec.asEpoch(time)) / 1000; if (tzo != 0) { - String tza = tz.getDisplayName(tz.inDaylightTime(J2kSec.asDate(j2k)), TimeZone.SHORT); - status = J2kSec.format(Time.STANDARD_TIME_FORMAT_MS, j2k + tzo) + " (" + tza + "), " + utc - + " (UTC)"; + String tza = tz.getDisplayName(tz.inDaylightTime(J2kSec.asDate(time)), TimeZone.SHORT); + status = J2kSec.format(Time.STANDARD_TIME_FORMAT_MS, time + tzo) + " (" + tza + "), " + + utc + " (UTC)"; } else status = utc; @@ -532,7 +545,7 @@ public boolean processMousePosition(int x, int y) { } else { - double xi = j2k; + double xi = time; if (settings.viewType == ViewType.SPECTRA && settings.logFreq) xi = Math.pow(10.0, xi); if (settings.viewType == ViewType.SPECTRA && settings.logPower) @@ -544,7 +557,7 @@ public boolean processMousePosition(int x, int y) { } if (!pauseCursorMark) - WaveViewTime.fireTimeChanged(j2k); + WaveViewTime.fireTimeChanged(time); if (status == null) status = " "; @@ -558,7 +571,7 @@ public boolean processMousePosition(int x, int y) { else status = pre; } - + if (status != null && statusLabel != null) { final String st = status; SwingUtilities.invokeLater(new Runnable() { @@ -642,8 +655,10 @@ public void finished() { } }; worker.start(); - } else + } else { + r.run(); + } } /** @@ -701,6 +716,9 @@ private void filter(Wave w) { * @param g the graphics context */ public void paint(Graphics g) { +if (getVisibleRect().isEmpty()) { + return; +} Graphics2D g2 = (Graphics2D) g; Dimension dim = this.getSize(); if (wave == null) { @@ -995,11 +1013,6 @@ private void paintDragBox(Graphics2D g2) { g2.setPaint(pnt); } - - public AbstractWavePanel() { - super(); - } - public void setCursorMark(double j2k) { cursorMark = j2k; repaint(); diff --git a/src/main/java/gov/usgs/volcanoes/swarm/wave/MultiMonitor.java b/src/main/java/gov/usgs/volcanoes/swarm/wave/MultiMonitor.java index a51b2198..4ee1a309 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/wave/MultiMonitor.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/wave/MultiMonitor.java @@ -156,6 +156,8 @@ public void processLayout(final ConfigFile cf) { final String w = "wave-" + i; final String channel = cf.getString(w + ".channel"); final ConfigFile scf = cf.getSubConfig(w); + + //TODO: why is wvp discarded? final WaveViewPanel wvp = addChannel(channel); wvp.getSettings().set(scf); } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/wave/MultiMonitorSettingsDialog.java b/src/main/java/gov/usgs/volcanoes/swarm/wave/MultiMonitorSettingsDialog.java index d47cb67c..7c64465f 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/wave/MultiMonitorSettingsDialog.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/wave/MultiMonitorSettingsDialog.java @@ -10,7 +10,7 @@ import com.jgoodies.forms.builder.DefaultFormBuilder; import com.jgoodies.forms.layout.FormLayout; -import gov.usgs.volcanoes.swarm.SwarmDialog; +import gov.usgs.volcanoes.swarm.SwarmModalDialog; /** * A dialog for Monitor Mode Settings. @@ -18,7 +18,7 @@ * * @author Dan Cervelli */ -public class MultiMonitorSettingsDialog extends SwarmDialog { +public class MultiMonitorSettingsDialog extends SwarmModalDialog { public static final long serialVersionUID = -1; private MultiMonitor monitor; @@ -32,7 +32,7 @@ public class MultiMonitorSettingsDialog extends SwarmDialog { private static MultiMonitorSettingsDialog dialog; private MultiMonitorSettingsDialog() { - super(applicationFrame, "Monitor Settings", true); + super(applicationFrame, "Monitor Settings"); createUI(); // setToCurrent(); // setCurrentValues(); diff --git a/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveClipboardFrame.java b/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveClipboardFrame.java index 692c441b..5c25e2f6 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveClipboardFrame.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveClipboardFrame.java @@ -27,6 +27,7 @@ import java.util.Set; import java.util.Stack; import java.util.TimeZone; +import java.util.TreeSet; import javax.swing.AbstractAction; import javax.swing.BorderFactory; @@ -79,1130 +80,1137 @@ * @author Dan Cervelli */ public class WaveClipboardFrame extends SwarmFrame { - public static final long serialVersionUID = -1; - private static final Color SELECT_COLOR = new Color(200, 220, 241); - private static final Color BACKGROUND_COLOR = new Color(0xf7, 0xf7, 0xf7); - - private JScrollPane scrollPane; - private Box waveBox; - private final List waves; - private final Set selectedSet; - private JToolBar toolbar; - private JPanel mainPanel; - private JLabel statusLabel; - private JToggleButton linkButton; - private JButton sizeButton; - private JButton syncButton; - private JButton sortButton; - private JButton removeAllButton; - private JButton saveButton; - private JButton saveAllButton; - private JButton openButton; - private JButton captureButton; - private JButton histButton; - private final DateFormat saveAllDateFormat; - - private WaveViewPanelListener selectListener; - private WaveViewSettingsToolbar waveToolbar; - - private JButton upButton; - private JButton downButton; - private JButton removeButton; - private JButton compXButton; - private JButton expXButton; - private JButton copyButton; - private JButton forwardButton; - private JButton backButton; - private JButton gotoButton; - - private JPopupMenu popup; - - private final Map> histories; - - private final HelicorderViewPanelListener linkListener; - - private boolean heliLinked = true; - - private Throbber throbber; - - private int waveHeight = -1; - - private int lastClickedIndex = -1; - - private WaveClipboardFrame() { - super("Wave Clipboard", true, true, true, false); - this.setFocusable(true); - selectedSet = new HashSet(); - saveAllDateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); - saveAllDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); - waves = new ArrayList(); - histories = new HashMap>(); - createUI(); - linkListener = new HelicorderViewPanelListener() { - public void insetCreated(final double st, final double et) { - if (heliLinked) - repositionWaves(st, et); - } - }; - } - - public static WaveClipboardFrame getInstance() { - return WaveClipboardFrameHolder.waveClipiboardFrame; - } - - public HelicorderViewPanelListener getLinkListener() { - return linkListener; - } - - private void createUI() { - this.setFrameIcon(Icons.clipboard); - this.setSize(swarmConfig.clipboardWidth, swarmConfig.clipboardHeight); - this.setLocation(swarmConfig.clipboardX, swarmConfig.clipboardY); - this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); - - toolbar = SwarmUtil.createToolBar(); - mainPanel = new JPanel(new BorderLayout()); - - createMainButtons(); - createWaveButtons(); - - mainPanel.add(toolbar, BorderLayout.NORTH); - - waveBox = new Box(BoxLayout.Y_AXIS); - scrollPane = new JScrollPane(waveBox); - scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); - scrollPane.getVerticalScrollBar().setUnitIncrement(40); - mainPanel.add(scrollPane, BorderLayout.CENTER); - - statusLabel = new JLabel(" "); - statusLabel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 1)); - mainPanel.add(statusLabel, BorderLayout.SOUTH); - - mainPanel.setBorder(BorderFactory.createEmptyBorder(0, 2, 1, 2)); - this.setContentPane(mainPanel); - - createListeners(); - doButtonEnables(); - } - - private void createMainButtons() { - openButton = - SwarmUtil.createToolBarButton(Icons.open, "Open a saved wave", new OpenActionListener()); - toolbar.add(openButton); - - saveButton = - SwarmUtil.createToolBarButton(Icons.save, "Save selected wave", new SaveActionListener()); - saveButton.setEnabled(false); - toolbar.add(saveButton); - - saveAllButton = - SwarmUtil.createToolBarButton(Icons.saveall, "Save all waves", new SaveAllActionListener()); - saveAllButton.setEnabled(false); - toolbar.add(saveAllButton); - - toolbar.addSeparator(); - - linkButton = SwarmUtil.createToolBarToggleButton(Icons.helilink, - "Synchronize times with helicorder wave", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - heliLinked = linkButton.isSelected(); - } - }); - linkButton.setSelected(heliLinked); - toolbar.add(linkButton); - - syncButton = SwarmUtil.createToolBarButton(Icons.clock, "Synchronize times with selected wave", - new ActionListener() { - public void actionPerformed(final ActionEvent e) { - syncChannels(); - } - }); - syncButton.setEnabled(false); - toolbar.add(syncButton); - - sortButton = SwarmUtil.createToolBarButton(Icons.geosort, - "Sort waves by nearest to selected wave", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - sortChannelsByNearest(); - } - }); - sortButton.setEnabled(false); - toolbar.add(sortButton); - - toolbar.addSeparator(); - - sizeButton = SwarmUtil.createToolBarButton(Icons.resize, "Set clipboard wave size", - new ActionListener() { - public void actionPerformed(final ActionEvent e) { - doSizePopup(sizeButton.getX(), sizeButton.getY() + 2 * sizeButton.getHeight()); - } - }); - toolbar.add(sizeButton); - - removeAllButton = SwarmUtil.createToolBarButton(Icons.deleteall, - "Remove all waves from clipboard", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - removeWaves(); - } - }); - removeAllButton.setEnabled(false); - toolbar.add(removeAllButton); - - toolbar.addSeparator(); - captureButton = SwarmUtil.createToolBarButton(Icons.camera, "Save clipboard image (P)", - new CaptureActionListener()); - UiUtils.mapKeyStrokeToButton(this, "P", "capture", captureButton); - toolbar.add(captureButton); - } - - // TODO: don't write image on event thread - // TODO: unify with MapFrame.CaptureActionListener - class CaptureActionListener implements ActionListener { - public void actionPerformed(final ActionEvent e) { - if (waves == null || waves.size() == 0) - return; - - final JFileChooser chooser = FileChooser.getFileChooser(); - final File lastPath = new File(swarmConfig.lastPath); - chooser.setCurrentDirectory(lastPath); - chooser.setSelectedFile(new File("clipboard.png")); - chooser.setDialogTitle("Save Clipboard Screen Capture"); - final int result = chooser.showSaveDialog(applicationFrame); - File f = null; - if (result == JFileChooser.APPROVE_OPTION) { - f = chooser.getSelectedFile(); - - if (f.exists()) { - final int choice = JOptionPane.showConfirmDialog(applicationFrame, - "File exists, overwrite?", "Confirm", JOptionPane.YES_NO_OPTION); - if (choice != JOptionPane.YES_OPTION) - return; - } - swarmConfig.lastPath = f.getParent(); - } - if (f == null) - return; - - int height = 0; - final int width = waves.get(0).getWidth(); - for (final AbstractWavePanel panel : waves) - height += panel.getHeight(); - - final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR); - final Graphics g = image.getGraphics(); - for (final AbstractWavePanel panel : waves) { - panel.paint(g); - g.translate(0, panel.getHeight()); - } - try { - final PngEncoderB png = new PngEncoderB(image, false, PngEncoder.FILTER_NONE, 7); - final FileOutputStream out = new FileOutputStream(f); - final byte[] bytes = png.pngEncode(); - out.write(bytes); - out.close(); - } catch (final Exception ex) { - ex.printStackTrace(); - } - } - } - - private void createWaveButtons() { - toolbar.addSeparator(); - - backButton = SwarmUtil.createToolBarButton(Icons.left, "Scroll back time 20% (Left arrow)", - new ActionListener() { - public void actionPerformed(final ActionEvent e) { - shiftTime(-0.20); - } - }); - UiUtils.mapKeyStrokeToButton(this, "LEFT", "backward1", backButton); - toolbar.add(backButton); - - forwardButton = SwarmUtil.createToolBarButton(Icons.right, - "Scroll forward time 20% (Right arrow)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - shiftTime(0.20); - } - }); - toolbar.add(forwardButton); - UiUtils.mapKeyStrokeToButton(this, "RIGHT", "forward1", forwardButton); - - gotoButton = - SwarmUtil.createToolBarButton(Icons.gototime, "Go to time (Ctrl-G)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - final String t = JOptionPane.showInputDialog(applicationFrame, - "Input time in 'YYYYMMDDhhmm[ss]' format:", "Go to Time", - JOptionPane.PLAIN_MESSAGE); - if (t != null) - gotoTime(t); - } - }); - toolbar.add(gotoButton); - UiUtils.mapKeyStrokeToButton(this, "ctrl G", "goto", gotoButton); - - compXButton = SwarmUtil.createToolBarButton(Icons.xminus, - "Shrink sample time 20% (Alt-left arrow, +)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - scaleTime(0.20); - } - }); - toolbar.add(compXButton); - UiUtils.mapKeyStrokeToButton(this, "alt LEFT", "compx", compXButton); - UiUtils.mapKeyStrokeToButton(this, "EQUALS", "compx2", compXButton); - UiUtils.mapKeyStrokeToButton(this, "shift EQUALS", "compx2", compXButton); - - expXButton = SwarmUtil.createToolBarButton(Icons.xplus, - "Expand sample time 20% (Alt-right arrow, -)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - scaleTime(-0.20); - } - }); - toolbar.add(expXButton); - UiUtils.mapKeyStrokeToButton(this, "alt RIGHT", "expx", expXButton); - UiUtils.mapKeyStrokeToButton(this, "MINUS", "expx", expXButton); - - histButton = SwarmUtil.createToolBarButton(Icons.timeback, "Last time settings (Backspace)", - new ActionListener() { - public void actionPerformed(final ActionEvent e) { - back(); - } - }); - UiUtils.mapKeyStrokeToButton(this, "BACK_SPACE", "back", histButton); - toolbar.add(histButton); - toolbar.addSeparator(); - - waveToolbar = new WaveViewSettingsToolbar(null, toolbar, this); - - copyButton = SwarmUtil.createToolBarButton(Icons.clipboard, - "Place another copy of wave on clipboard (C or Ctrl-C)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - // TODO: implement - // if (selected != null) - // { - // WaveViewPanel wvp = new WaveViewPanel(selected); - // wvp.setBackgroundColor(BACKGROUND_COLOR); - // addWave(wvp); - // } - } - }); - UiUtils.mapKeyStrokeToButton(this, "C", "clipboard1", copyButton); - UiUtils.mapKeyStrokeToButton(this, "control C", "clipboard2", copyButton); - toolbar.add(copyButton); - - toolbar.addSeparator(); - - upButton = SwarmUtil.createToolBarButton(Icons.up, "Move wave up in clipboard (Up arrow)", - new ActionListener() { - public void actionPerformed(final ActionEvent e) { - moveUp(); - } - }); - UiUtils.mapKeyStrokeToButton(this, "UP", "up", upButton); - toolbar.add(upButton); - - downButton = SwarmUtil.createToolBarButton(Icons.down, - "Move wave down in clipboard (Down arrow)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - moveDown(); - } - }); - UiUtils.mapKeyStrokeToButton(this, "DOWN", "down", downButton); - toolbar.add(downButton); - - removeButton = SwarmUtil.createToolBarButton(Icons.delete, - "Remove wave from clipboard (Delete)", new ActionListener() { - public void actionPerformed(final ActionEvent e) { - remove(); - } - }); - UiUtils.mapKeyStrokeToButton(this, "DELETE", "remove", removeButton); - toolbar.add(removeButton); - - toolbar.add(Box.createHorizontalGlue()); - - throbber = new Throbber(); - toolbar.add(throbber); - - UiUtils.mapKeyStrokeToAction(this, "control A", "selectAll", new AbstractAction() { - private static final long serialVersionUID = 1L; - - public void actionPerformed(final ActionEvent e) { - for (final AbstractWavePanel wave : waves) - select(wave); - } - }); - } - - private void createListeners() { - this.addInternalFrameListener(new InternalFrameAdapter() { - @Override - public void internalFrameActivated(final InternalFrameEvent e) {} - - @Override - public void internalFrameDeiconified(final InternalFrameEvent e) { - resizeWaves(); - } - - @Override - public void internalFrameClosing(final InternalFrameEvent e) { - setVisible(false); - } - - @Override - public void internalFrameClosed(final InternalFrameEvent e) {} - }); - - this.addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(final ComponentEvent e) { - resizeWaves(); - } - }); - - WaveViewTime.addTimeListener(new TimeListener() { - public void timeChanged(final double j2k) { - for (final AbstractWavePanel panel : waves) { - if (panel != null) - panel.setCursorMark(j2k); - } - } - }); - - selectListener = new WaveViewPanelAdapter() { - @Override - public void mousePressed(final AbstractWavePanel src, final MouseEvent e, - final boolean dragging) { - requestFocusInWindow(); - final int thisIndex = getWaveIndex(src); - if (!e.isControlDown() && !e.isShiftDown() && !e.isAltDown()) { - deselectAll(); - select(src); - } else if (e.isControlDown()) { - if (selectedSet.contains(src)) - deselect(src); - else - select(src); - } else if (e.isShiftDown()) { - if (lastClickedIndex == -1) - select(src); - else { - deselectAll(); - final int min = Math.min(lastClickedIndex, thisIndex); - final int max = Math.max(lastClickedIndex, thisIndex); - for (int i = min; i <= max; i++) { - final WaveViewPanel ps = (WaveViewPanel) waveBox.getComponent(i); - select(ps); - } - } - } - lastClickedIndex = thisIndex; - } - - @Override - public void waveZoomed(final AbstractWavePanel src, final double st, final double et, - final double nst, final double net) { - final double[] t = new double[] {st, et}; - addHistory(src, t); - for (final AbstractWavePanel wvp : selectedSet) { - if (wvp != src) { - addHistory(wvp, t); - wvp.zoom(nst, net); - } - } - } - - @Override - public void waveClosed(final AbstractWavePanel src) { - remove(src); - } - }; - } - - private int calculateWaveHeight() { - if (waveHeight > 0) - return waveHeight; - - final int w = scrollPane.getViewport().getSize().width; - int h = (int) Math.round(w * 60.0 / 300.0); - h = Math.min(200, h); - h = Math.max(h, 80); - return h; - } - - private void setWaveHeight(final int s) { - waveHeight = s; - resizeWaves(); - } - - private void doSizePopup(final int x, final int y) { - if (popup == null) { - final String[] labels = new String[] {"Auto", null, "Tiny", "Small", "Medium", "Large"}; - final int[] sizes = new int[] {-1, -1, 50, 100, 160, 230}; - popup = new JPopupMenu(); - final ButtonGroup group = new ButtonGroup(); - for (int i = 0; i < labels.length; i++) { - if (labels[i] != null) { - final int size = sizes[i]; - final JRadioButtonMenuItem mi = new JRadioButtonMenuItem(labels[i]); - mi.addActionListener(new ActionListener() { - public void actionPerformed(final ActionEvent e) { - setWaveHeight(size); - } - }); - if (waveHeight == size) - mi.setSelected(true); - group.add(mi); - popup.add(mi); - } else - popup.addSeparator(); - } - } - popup.show(this, x, y); - } - - private class OpenActionListener implements ActionListener { - public void actionPerformed(final ActionEvent e) { - final JFileChooser chooser = FileChooser.getFileChooser(); - chooser.resetChoosableFileFilters(); - for (final FileType ft : FileType.getKnownTypes()) { - final ExtensionFileFilter f = new ExtensionFileFilter(ft.extension, ft.description); - chooser.addChoosableFileFilter(f); - } - chooser.setDialogTitle("Open Wave"); - chooser.setFileFilter(chooser.getAcceptAllFileFilter()); - final File lastPath = new File(swarmConfig.lastPath); - chooser.setCurrentDirectory(lastPath); - chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); - chooser.setMultiSelectionEnabled(true); - final int result = chooser.showOpenDialog(applicationFrame); - if (result == JFileChooser.APPROVE_OPTION) { - final File[] fs = chooser.getSelectedFiles(); - - for (int i = 0; i < fs.length; i++) { - if (fs[i].isDirectory()) { - final File[] dfs = fs[i].listFiles(); - if (dfs == null) - continue; - for (int j = 0; j < dfs.length; j++) - openFile(dfs[j]); - swarmConfig.lastPath = fs[i].getParent(); - } else { - openFile(fs[i]); - swarmConfig.lastPath = fs[i].getParent(); - } - } - } - } - } - - private class SaveActionListener implements ActionListener { - public void actionPerformed(final ActionEvent e) { - final AbstractWavePanel selected = getSingleSelected(); - if (selected == null) - return; - - final JFileChooser chooser = FileChooser.getFileChooser(); - chooser.resetChoosableFileFilters(); - chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); - chooser.setMultiSelectionEnabled(false); - chooser.setDialogTitle("Save Wave"); - - for (final FileType ft : FileType.values()) { - if (ft == FileType.UNKNOWN) - continue; - - final ExtensionFileFilter f = new ExtensionFileFilter(ft.extension, ft.description); - chooser.addChoosableFileFilter(f); - } - - chooser.setFileFilter(chooser.getAcceptAllFileFilter()); - - final File lastPath = new File(swarmConfig.lastPath); - chooser.setCurrentDirectory(lastPath); - final String fileName = selected.getChannel().replace(' ', '_') + ".sac"; - chooser.setSelectedFile(new File(fileName)); - final int result = chooser.showSaveDialog(applicationFrame); - if (result == JFileChooser.APPROVE_OPTION) { - final File f = chooser.getSelectedFile(); - boolean confirm = true; - if (f.exists()) { - if (f.isDirectory()) { - JOptionPane.showMessageDialog(applicationFrame, - "You can not select an existing directory.", "Error", JOptionPane.ERROR_MESSAGE); - return; - } - confirm = false; - final int choice = JOptionPane.showConfirmDialog(applicationFrame, - "File exists, overwrite?", "Confirm", JOptionPane.YES_NO_OPTION); - if (choice == JOptionPane.YES_OPTION) - confirm = true; - } - - if (confirm) { - try { - swarmConfig.lastPath = f.getParent(); - final String fn = f.getPath(); - final SeismicDataFile file = SeismicDataFile.getFile(fn); - // String channel = selected.getChannel().replace(' ', '_'); - final Wave wave = selected.getWave(); - // file.putWave(channel, wave); - file.putWave(selected.getChannel(), wave); - file.write(); - } catch (final FileNotFoundException ex) { - JOptionPane.showMessageDialog(Swarm.getApplicationFrame(), "Directory does not exist.", - "Error", JOptionPane.ERROR_MESSAGE); - } catch (final IOException ex) { - JOptionPane.showMessageDialog(Swarm.getApplicationFrame(), "Error writing file.", - "Error", JOptionPane.ERROR_MESSAGE); - } - } - } - } - } - - private class SaveAllActionListener implements ActionListener { - public void actionPerformed(final ActionEvent e) { - if (waves.size() <= 0) - return; - - final FileTypeDialog dialog = new FileTypeDialog(); - FileType fileType = FileType.UNKNOWN; - if (!dialog.isOpen() || (dialog.isOpen() && !dialog.isAssumeSame())) { - dialog.setVisible(true); - - if (dialog.isCancelled()) - fileType = FileType.UNKNOWN; - else - fileType = dialog.getFileType(); - - } - - final JFileChooser chooser = FileChooser.getFileChooser(); - chooser.resetChoosableFileFilters(); - chooser.setMultiSelectionEnabled(false); - chooser.setDialogTitle("Save All Files"); - final File lastPath = new File(swarmConfig.lastPath); - chooser.setCurrentDirectory(lastPath); - - if (!fileType.isCollective) - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - else - chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); - - final int result = chooser.showSaveDialog(applicationFrame); - if (result == JFileChooser.CANCEL_OPTION) - return; - - final File f = chooser.getSelectedFile(); - - if (f == null) { - JOptionPane.showMessageDialog(applicationFrame, "Save location not understood.", "Error", - JOptionPane.ERROR_MESSAGE); - return; - } - - if (result == JFileChooser.APPROVE_OPTION) { - try { - if (f.exists() && !f.isDirectory()) - return; - if (!f.exists()) - f.mkdir(); - for (final AbstractWavePanel wvp : waves) { - Wave sw = wvp.getWave(); - - if (sw != null) { - sw = sw.subset(wvp.getStartTime(), wvp.getEndTime()); - final String date = saveAllDateFormat.format(J2kSec.asDate(sw.getStartTime())); - final File dir = new File(f.getPath() + File.separatorChar + date); - if (!dir.exists()) - dir.mkdir(); - - swarmConfig.lastPath = f.getParent(); - final String fn = - dir + File.separator + wvp.getChannel().replace(' ', '_') + fileType.extension; - final SeismicDataFile file = SeismicDataFile.getFile(fn); - file.putWave(wvp.getChannel(), sw); - file.write(); - } - } - swarmConfig.lastPath = f.getPath(); - } catch (final FileNotFoundException ex) { - JOptionPane.showMessageDialog(Swarm.getApplicationFrame(), "Directory does not exist.", - "Error", JOptionPane.ERROR_MESSAGE); - } catch (final IOException ex) { - JOptionPane.showMessageDialog(Swarm.getApplicationFrame(), "Error writing file.", "Error", - JOptionPane.ERROR_MESSAGE); - } - } - } - } - - public void openFile(final File f) { - SeismicDataFile file = SeismicDataFile.getFile(f); - if (file == null) { - final FileTypeDialog dialog = new FileTypeDialog(); - FileType fileType = FileType.UNKNOWN; - if (!dialog.isOpen() || (dialog.isOpen() && !dialog.isAssumeSame())) { - dialog.setVisible(true); - - if (dialog.isCancelled()) - fileType = FileType.UNKNOWN; - else - fileType = dialog.getFileType(); - - } - file = SeismicDataFile.getFile(f, fileType); - } - - if (file == null) { - JOptionPane.showMessageDialog(applicationFrame, - "There was an error opening the file, '" + f.getName() + "'.", "Error", - JOptionPane.ERROR_MESSAGE); - return; - } - - try { - file.read(); - } catch (final IOException e) { - JOptionPane.showMessageDialog(applicationFrame, - "There was an error opening the file, '" + f.getName() + "'.\n" + e.getMessage(), "Error", - JOptionPane.ERROR_MESSAGE); - } - - for (final String channel : file.getChannels()) { - final WaveViewPanel wvp = new WaveViewPanel(); - wvp.setChannel(channel); - final CachedDataSource cache = CachedDataSource.getInstance(); - - final Wave wave = file.getWave(channel); - cache.putWave(channel, wave); - wvp.setDataSource(cache); - wvp.setWave(wave, wave.getStartTime(), wave.getEndTime()); - WaveClipboardFrame.this.addWave(new WaveViewPanel(wvp)); - } - } - - private void doButtonEnables() { - final boolean enable = (waves == null || waves.size() == 0); - saveButton.setEnabled(!enable); - sortButton.setEnabled(!enable); - saveAllButton.setEnabled(!enable); - syncButton.setEnabled(!enable); - removeAllButton.setEnabled(!enable); - saveAllButton.setEnabled(!enable); - - final boolean allowSingle = (selectedSet.size() == 1); - upButton.setEnabled(allowSingle); - downButton.setEnabled(allowSingle); - sortButton.setEnabled(allowSingle); - syncButton.setEnabled(allowSingle); - saveButton.setEnabled(allowSingle); - - final boolean allowMulti = (selectedSet.size() > 0); - backButton.setEnabled(allowMulti); - expXButton.setEnabled(allowMulti); - compXButton.setEnabled(allowMulti); - backButton.setEnabled(allowMulti); - forwardButton.setEnabled(allowMulti); - histButton.setEnabled(allowMulti); - removeButton.setEnabled(allowMulti); - gotoButton.setEnabled(allowMulti); - } - - public synchronized void sortChannelsByNearest() { - final AbstractWavePanel p = getSingleSelected(); - if (p == null) - return; - - final ArrayList sorted = new ArrayList(waves.size()); - for (final AbstractWavePanel wave : waves) - sorted.add(wave); - - final Metadata smd = swarmConfig.getMetadata(p.getChannel()); - if (smd == null || Double.isNaN(smd.getLongitude()) || Double.isNaN(smd.getLatitude())) - return; - - Collections.sort(sorted, new Comparator() { - public int compare(final AbstractWavePanel wvp1, final AbstractWavePanel wvp2) { - Metadata md = swarmConfig.getMetadata(wvp1.getChannel()); - final double d1 = smd.distanceTo(md); - md = swarmConfig.getMetadata(wvp2.getChannel()); - final double d2 = smd.distanceTo(md); - return Double.compare(d1, d2); - } - }); - - removeWaves(); - for (final AbstractWavePanel wave : sorted) - addWave(wave); - select(p); - } - - public synchronized AbstractWavePanel getSingleSelected() { - if (selectedSet.size() != 1) - return null; - - AbstractWavePanel p = null; - for (final AbstractWavePanel panel : selectedSet) - p = panel; - - return p; - } - - public synchronized void syncChannels() { - final AbstractWavePanel p = getSingleSelected(); - if (p == null) - return; - - final double st = p.getStartTime(); - final double et = p.getEndTime(); - - // TODO: thread bug here. must synch iterator below - final SwingWorker worker = new SwingWorker() { - @Override - public Object construct() { - List copy = null; - synchronized (WaveClipboardFrame.this) { - copy = new ArrayList(waves); - } - for (final AbstractWavePanel wvp : copy) { - if (wvp != p) { - if (wvp.getDataSource() != null) { - addHistory(wvp, new double[] {wvp.getStartTime(), wvp.getEndTime()}); - final Wave sw = wvp.getDataSource().getWave(wvp.getChannel(), st, et); - wvp.setWave(sw, st, et); - } - } - } - return null; - } - - @Override - public void finished() { - repaint(); - } - }; - worker.start(); - } - - public void setStatusText(final String s) { - SwingUtilities.invokeLater(new Runnable() { - public void run() { - statusLabel.setText(s); - } - }); - } - - public WaveViewPanel getSelected() { - return null; - // return selected; - } - - public synchronized void addWave(final AbstractWavePanel p) { - p.addListener(selectListener); - p.setOffsets(54, 8, 21, 19); - p.setAllowClose(true); - p.setStatusLabel(statusLabel); - p.setAllowDragging(true); - p.setDisplayTitle(true); - final int w = scrollPane.getViewport().getSize().width; - p.setSize(w, calculateWaveHeight()); - p.setBottomBorderColor(Color.GRAY); - p.createImage(); - waveBox.add(p); - waves.add(p); - doButtonEnables(); - waveBox.validate(); - } - - private synchronized void deselect(final AbstractWavePanel p) { - selectedSet.remove(p); - waveToolbar.removeSettings(p.getSettings()); - p.setBackgroundColor(BACKGROUND_COLOR); - p.createImage(); - doButtonEnables(); - } - - private synchronized void deselectAll() { - final WaveViewPanel[] panels = selectedSet.toArray(new WaveViewPanel[0]); - for (final WaveViewPanel p : panels) - deselect(p); - } - - private synchronized void select(final AbstractWavePanel p) { - if (p == null || selectedSet.contains(p)) - return; - - selectedSet.add(p); - doButtonEnables(); - p.setBackgroundColor(SELECT_COLOR); - DataChooser.getInstance().setNearest(p.getChannel()); - p.createImage(); - waveToolbar.addSettings(p.getSettings()); - } - - private synchronized void remove(final WaveViewPanel p) { - int i = 0; - for (i = 0; i < waveBox.getComponentCount(); i++) { - if (p == waveBox.getComponent(i)) - break; - } - - p.removeListener(selectListener); - p.getDataSource().close(); - setStatusText(" "); - waveBox.remove(i); - waves.remove(p); - histories.remove(p); - doButtonEnables(); - waveBox.validate(); - selectedSet.remove(p); - lastClickedIndex = Math.min(lastClickedIndex, waveBox.getComponentCount() - 1); - waveToolbar.removeSettings(p.getSettings()); - repaint(); - } - - protected int getWaveIndex(final AbstractWavePanel p) { - int i = 0; - for (i = 0; i < waveBox.getComponentCount(); i++) { - if (p == waveBox.getComponent(i)) - break; - } - return i; - } - - public synchronized void remove() { - final WaveViewPanel[] panels = selectedSet.toArray(new WaveViewPanel[0]); - for (final WaveViewPanel p : panels) - remove(p); - } - - public synchronized void moveDown() { - final AbstractWavePanel p = getSingleSelected(); - if (p == null) - return; - - final int i = waves.indexOf(p); - if (i == waves.size() - 1) - return; - - waves.remove(i); - waves.add(i + 1, p); - waveBox.remove(p); - waveBox.add(p, i + 1); - waveBox.validate(); - repaint(); - } - - public synchronized void moveUp() { - final AbstractWavePanel p = getSingleSelected(); - if (p == null) - return; - - final int i = waves.indexOf(p); - if (i == 0) - return; - - waves.remove(i); - waves.add(i - 1, p); - waveBox.remove(p); - waveBox.add(p, i - 1); - waveBox.validate(); - repaint(); - } - - public void resizeWaves() { - final SwingWorker worker = new SwingWorker() { - @Override - public Object construct() { - final int w = scrollPane.getViewport().getSize().width; - for (final AbstractWavePanel wave : waves) { - wave.setSize(w, calculateWaveHeight()); - wave.createImage(); - } - return null; - } - - @Override - public void finished() { - waveBox.validate(); - validate(); - repaint(); - } - }; - worker.start(); - } - - public void removeWaves() { - while (waves.size() > 0) { - remove(waves.get(0)); - waves.remove(0); - } - - waveBox.validate(); - scrollPane.validate(); - doButtonEnables(); - repaint(); - } - - private void addHistory(final AbstractWavePanel wvp, final double[] t) { - Stack history = histories.get(wvp); - if (history == null) { - history = new Stack(); - histories.put(wvp, history); - } - history.push(t); - } - - public void gotoTime(final AbstractWavePanel wvp, String t) { - double j2k = Double.NaN; - try { - if (t.length() == 12) - t = t + "30"; - - j2k = J2kSec.parse("yyyyMMddHHmmss", t); - } catch (final Exception e) { - JOptionPane.showMessageDialog(applicationFrame, "Illegal time value.", "Error", - JOptionPane.ERROR_MESSAGE); - } - - if (!Double.isNaN(j2k)) { - double dt = 60; - if (wvp.getWave() != null) { - final double st = wvp.getStartTime(); - final double et = wvp.getEndTime(); - final double[] ts = new double[] {st, et}; - addHistory(wvp, ts); - dt = (et - st); - } - - final double tzo = - swarmConfig.getTimeZone(wvp.getChannel()).getOffset(System.currentTimeMillis()) / 1000; - - final double nst = j2k - tzo - dt / 2; - final double net = nst + dt; - - fetchNewWave(wvp, nst, net); - } - } - - public void gotoTime(final String t) { - for (final AbstractWavePanel p : selectedSet) - gotoTime(p, t); - } - - public void scaleTime(final AbstractWavePanel wvp, final double pct) { - final double st = wvp.getStartTime(); - final double et = wvp.getEndTime(); - final double[] t = new double[] {st, et}; - addHistory(wvp, t); - final double dt = (et - st) * (1 - pct); - final double mt = (et - st) / 2 + st; - final double nst = mt - dt / 2; - final double net = mt + dt / 2; - fetchNewWave(wvp, nst, net); - } - - public void scaleTime(final double pct) { - for (final AbstractWavePanel p : selectedSet) - scaleTime(p, pct); - } - - public void back(final AbstractWavePanel wvp) { - final Stack history = histories.get(wvp); - if (history == null || history.empty()) - return; - - final double[] t = history.pop(); - fetchNewWave(wvp, t[0], t[1]); - } - - public void back() { - for (final AbstractWavePanel p : selectedSet) - back(p); - } - - private void shiftTime(final AbstractWavePanel wvp, final double pct) { - final double st = wvp.getStartTime(); - final double et = wvp.getEndTime(); - final double[] t = new double[] {st, et}; - addHistory(wvp, t); - final double dt = (et - st) * pct; - final double nst = st + dt; - final double net = et + dt; - fetchNewWave(wvp, nst, net); - } - - public void shiftTime(final double pct) { - for (final AbstractWavePanel p : selectedSet) - shiftTime(p, pct); - } - - public void repositionWaves(final double st, final double et) { - for (final AbstractWavePanel wave : waves) { - fetchNewWave(wave, st, et); - } - } - - public Throbber getThrobber() { - return throbber; - } - - // TODO: This isn't right, this should be a method of waveviewpanel - private void fetchNewWave(final AbstractWavePanel wvp, final double nst, final double net) { - final SwingWorker worker = new SwingWorker() { - @Override - public Object construct() { - throbber.increment(); - final SeismicDataSource sds = wvp.getDataSource(); - // Hacky fix for bug #84 - Wave sw = null; - if (sds instanceof CachedDataSource) - sw = ((CachedDataSource) sds).getBestWave(wvp.getChannel(), nst, net); - else - sw = sds.getWave(wvp.getChannel(), nst, net); - wvp.setWave(sw, nst, net); - wvp.repaint(); - return null; - } - - @Override - public void finished() { - throbber.decrement(); - repaint(); - } - }; - worker.start(); - } - - @Override - public void setMaximum(final boolean max) throws PropertyVetoException { - if (max) { - swarmConfig.clipboardX = getX(); - swarmConfig.clipboardY = getY(); - } - super.setMaximum(max); - } - - @Override - public void paint(final Graphics g) { - super.paint(g); - if (waves.size() == 0) { - final Dimension dim = this.getSize(); - g.setColor(Color.black); - g.drawString("Clipboard empty.", dim.width / 2 - 40, dim.height / 2); - } - } - - @Override - public void setVisible(final boolean isVisible) { - super.setVisible(isVisible); - if (isVisible) - toFront(); - } - - private static class WaveClipboardFrameHolder { - public static WaveClipboardFrame waveClipiboardFrame = new WaveClipboardFrame(); - } + public static final long serialVersionUID = -1; + private static final Color SELECT_COLOR = new Color(200, 220, 241); + private static final Color BACKGROUND_COLOR = new Color(0xf7, 0xf7, 0xf7); + + private JScrollPane scrollPane; + private Box waveBox; + private final List waves; + private final Set selectedSet; + private JToolBar toolbar; + private JPanel mainPanel; + private JLabel statusLabel; + private JToggleButton linkButton; + private JButton sizeButton; + private JButton syncButton; + private JButton sortButton; + private JButton removeAllButton; + private JButton saveButton; + private JButton saveAllButton; + private JButton openButton; + private JButton captureButton; + private JButton histButton; + private final DateFormat saveAllDateFormat; + + private WaveViewPanelListener selectListener; + private WaveViewSettingsToolbar waveToolbar; + + private JButton upButton; + private JButton downButton; + private JButton removeButton; + private JButton compXButton; + private JButton expXButton; + private JButton copyButton; + private JButton forwardButton; + private JButton backButton; + private JButton gotoButton; + + private JPopupMenu popup; + + private final Map> histories; + + private final HelicorderViewPanelListener linkListener; + + private boolean heliLinked = true; + + private Throbber throbber; + + private int waveHeight = -1; + + private int lastClickedIndex = -1; + + private WaveClipboardFrame() { + super("Wave Clipboard", true, true, true, false); + this.setFocusable(true); + selectedSet = new HashSet(); + saveAllDateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); + saveAllDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); + waves = new ArrayList(); + histories = new HashMap>(); + createUI(); + linkListener = new HelicorderViewPanelListener() { + public void insetCreated(final double st, final double et) { + if (heliLinked) + repositionWaves(st, et); + } + }; + } + + public static WaveClipboardFrame getInstance() { + return WaveClipboardFrameHolder.waveClipiboardFrame; + } + + public HelicorderViewPanelListener getLinkListener() { + return linkListener; + } + + private void createUI() { + this.setFrameIcon(Icons.clipboard); + this.setSize(swarmConfig.clipboardWidth, swarmConfig.clipboardHeight); + this.setLocation(swarmConfig.clipboardX, swarmConfig.clipboardY); + this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + + toolbar = SwarmUtil.createToolBar(); + mainPanel = new JPanel(new BorderLayout()); + + createMainButtons(); + createWaveButtons(); + + mainPanel.add(toolbar, BorderLayout.NORTH); + + waveBox = new Box(BoxLayout.Y_AXIS); + scrollPane = new JScrollPane(waveBox); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + scrollPane.getVerticalScrollBar().setUnitIncrement(40); + mainPanel.add(scrollPane, BorderLayout.CENTER); + + statusLabel = new JLabel(" "); + statusLabel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 1)); + mainPanel.add(statusLabel, BorderLayout.SOUTH); + + mainPanel.setBorder(BorderFactory.createEmptyBorder(0, 2, 1, 2)); + this.setContentPane(mainPanel); + + createListeners(); + doButtonEnables(); + } + + private void createMainButtons() { + openButton = SwarmUtil.createToolBarButton(Icons.open, "Open a saved wave", new OpenActionListener()); + toolbar.add(openButton); + + saveButton = SwarmUtil.createToolBarButton(Icons.save, "Save selected wave", new SaveActionListener()); + saveButton.setEnabled(false); + toolbar.add(saveButton); + + saveAllButton = SwarmUtil.createToolBarButton(Icons.saveall, "Save all waves", new SaveAllActionListener()); + saveAllButton.setEnabled(false); + toolbar.add(saveAllButton); + + toolbar.addSeparator(); + + linkButton = SwarmUtil.createToolBarToggleButton(Icons.helilink, "Synchronize times with helicorder wave", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + heliLinked = linkButton.isSelected(); + } + }); + linkButton.setSelected(heliLinked); + toolbar.add(linkButton); + + syncButton = SwarmUtil.createToolBarButton(Icons.clock, "Synchronize times with selected wave", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + syncChannels(); + } + }); + syncButton.setEnabled(false); + toolbar.add(syncButton); + + sortButton = SwarmUtil.createToolBarButton(Icons.geosort, "Sort waves by nearest to selected wave", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + sortChannelsByNearest(); + } + }); + sortButton.setEnabled(false); + toolbar.add(sortButton); + + toolbar.addSeparator(); + + sizeButton = SwarmUtil.createToolBarButton(Icons.resize, "Set clipboard wave size", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + doSizePopup(sizeButton.getX(), sizeButton.getY() + 2 * sizeButton.getHeight()); + } + }); + toolbar.add(sizeButton); + + removeAllButton = SwarmUtil.createToolBarButton(Icons.deleteall, "Remove all waves from clipboard", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + removeWaves(); + } + }); + removeAllButton.setEnabled(false); + toolbar.add(removeAllButton); + + toolbar.addSeparator(); + captureButton = SwarmUtil.createToolBarButton(Icons.camera, "Save clipboard image (P)", + new CaptureActionListener()); + UiUtils.mapKeyStrokeToButton(this, "P", "capture", captureButton); + toolbar.add(captureButton); + } + + // TODO: don't write image on event thread + // TODO: unify with MapFrame.CaptureActionListener + class CaptureActionListener implements ActionListener { + public void actionPerformed(final ActionEvent e) { + if (waves == null || waves.size() == 0) + return; + + final JFileChooser chooser = FileChooser.getFileChooser(); + final File lastPath = new File(swarmConfig.lastPath); + chooser.setCurrentDirectory(lastPath); + chooser.setSelectedFile(new File("clipboard.png")); + chooser.setDialogTitle("Save Clipboard Screen Capture"); + final int result = chooser.showSaveDialog(applicationFrame); + File f = null; + if (result == JFileChooser.APPROVE_OPTION) { + f = chooser.getSelectedFile(); + + if (f.exists()) { + final int choice = JOptionPane.showConfirmDialog(applicationFrame, "File exists, overwrite?", + "Confirm", JOptionPane.YES_NO_OPTION); + if (choice != JOptionPane.YES_OPTION) + return; + } + swarmConfig.lastPath = f.getParent(); + } + if (f == null) + return; + + int height = 0; + final int width = waves.get(0).getWidth(); + for (final AbstractWavePanel panel : waves) + height += panel.getHeight(); + + final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR); + final Graphics g = image.getGraphics(); + for (final AbstractWavePanel panel : waves) { + panel.paint(g); + g.translate(0, panel.getHeight()); + } + try { + final PngEncoderB png = new PngEncoderB(image, false, PngEncoder.FILTER_NONE, 7); + final FileOutputStream out = new FileOutputStream(f); + final byte[] bytes = png.pngEncode(); + out.write(bytes); + out.close(); + } catch (final Exception ex) { + ex.printStackTrace(); + } + } + } + + private void createWaveButtons() { + toolbar.addSeparator(); + + backButton = SwarmUtil.createToolBarButton(Icons.left, "Scroll back time 20% (Left arrow)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + shiftTime(-0.20); + } + }); + UiUtils.mapKeyStrokeToButton(this, "LEFT", "backward1", backButton); + toolbar.add(backButton); + + forwardButton = SwarmUtil.createToolBarButton(Icons.right, "Scroll forward time 20% (Right arrow)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + shiftTime(0.20); + } + }); + toolbar.add(forwardButton); + UiUtils.mapKeyStrokeToButton(this, "RIGHT", "forward1", forwardButton); + + gotoButton = SwarmUtil.createToolBarButton(Icons.gototime, "Go to time (Ctrl-G)", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + final String t = JOptionPane.showInputDialog(applicationFrame, + "Input time in 'YYYYMMDDhhmm[ss]' format:", "Go to Time", JOptionPane.PLAIN_MESSAGE); + if (t != null) + gotoTime(t); + } + }); + toolbar.add(gotoButton); + UiUtils.mapKeyStrokeToButton(this, "ctrl G", "goto", gotoButton); + + compXButton = SwarmUtil.createToolBarButton(Icons.xminus, "Shrink sample time 20% (Alt-left arrow, +)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + scaleTime(0.20); + } + }); + toolbar.add(compXButton); + UiUtils.mapKeyStrokeToButton(this, "alt LEFT", "compx", compXButton); + UiUtils.mapKeyStrokeToButton(this, "EQUALS", "compx2", compXButton); + UiUtils.mapKeyStrokeToButton(this, "shift EQUALS", "compx2", compXButton); + + expXButton = SwarmUtil.createToolBarButton(Icons.xplus, "Expand sample time 20% (Alt-right arrow, -)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + scaleTime(-0.20); + } + }); + toolbar.add(expXButton); + UiUtils.mapKeyStrokeToButton(this, "alt RIGHT", "expx", expXButton); + UiUtils.mapKeyStrokeToButton(this, "MINUS", "expx", expXButton); + + histButton = SwarmUtil.createToolBarButton(Icons.timeback, "Last time settings (Backspace)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + back(); + } + }); + UiUtils.mapKeyStrokeToButton(this, "BACK_SPACE", "back", histButton); + toolbar.add(histButton); + toolbar.addSeparator(); + + waveToolbar = new WaveViewSettingsToolbar(null, toolbar, this); + + copyButton = SwarmUtil.createToolBarButton(Icons.clipboard, + "Place another copy of wave on clipboard (C or Ctrl-C)", new ActionListener() { + public void actionPerformed(final ActionEvent e) { + // TODO: implement + // if (selected != null) + // { + // WaveViewPanel wvp = new WaveViewPanel(selected); + // wvp.setBackgroundColor(BACKGROUND_COLOR); + // addWave(wvp); + // } + } + }); + UiUtils.mapKeyStrokeToButton(this, "C", "clipboard1", copyButton); + UiUtils.mapKeyStrokeToButton(this, "control C", "clipboard2", copyButton); + toolbar.add(copyButton); + + toolbar.addSeparator(); + + upButton = SwarmUtil.createToolBarButton(Icons.up, "Move wave(s) up in clipboard (Up arrow)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + moveWaves(-1); + } + }); + UiUtils.mapKeyStrokeToButton(this, "UP", "up", upButton); + toolbar.add(upButton); + + downButton = SwarmUtil.createToolBarButton(Icons.down, "Move wave down in clipboard (Down arrow)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + moveWaves(1); + } + }); + UiUtils.mapKeyStrokeToButton(this, "DOWN", "down", downButton); + toolbar.add(downButton); + + removeButton = SwarmUtil.createToolBarButton(Icons.delete, "Remove wave from clipboard (Delete)", + new ActionListener() { + public void actionPerformed(final ActionEvent e) { + remove(); + } + }); + UiUtils.mapKeyStrokeToButton(this, "DELETE", "remove", removeButton); + toolbar.add(removeButton); + + toolbar.add(Box.createHorizontalGlue()); + + throbber = new Throbber(); + toolbar.add(throbber); + + UiUtils.mapKeyStrokeToAction(this, "control A", "selectAll", new AbstractAction() { + private static final long serialVersionUID = 1L; + + public void actionPerformed(final ActionEvent e) { + for (final AbstractWavePanel wave : waves) + select(wave); + } + }); + } + + private void createListeners() { + this.addInternalFrameListener(new InternalFrameAdapter() { + @Override + public void internalFrameActivated(final InternalFrameEvent e) { + } + + @Override + public void internalFrameDeiconified(final InternalFrameEvent e) { + resizeWaves(); + } + + @Override + public void internalFrameClosing(final InternalFrameEvent e) { + setVisible(false); + } + + @Override + public void internalFrameClosed(final InternalFrameEvent e) { + } + }); + + this.addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(final ComponentEvent e) { + resizeWaves(); + } + }); + + WaveViewTime.addTimeListener(new TimeListener() { + public void timeChanged(final double j2k) { + for (final AbstractWavePanel panel : waves) { + if (panel != null) + panel.setCursorMark(j2k); + } + } + }); + + selectListener = new WaveViewPanelAdapter() { + @Override + public void mousePressed(final AbstractWavePanel src, final MouseEvent e, final boolean dragging) { + requestFocusInWindow(); + final int thisIndex = getWaveIndex(src); + if (!e.isControlDown() && !e.isShiftDown() && !e.isAltDown()) { + deselectAll(); + select(src); + } else if (e.isControlDown()) { + if (selectedSet.contains(src)) + deselect(src); + else + select(src); + } else if (e.isShiftDown()) { + if (lastClickedIndex == -1) + select(src); + else { + deselectAll(); + final int min = Math.min(lastClickedIndex, thisIndex); + final int max = Math.max(lastClickedIndex, thisIndex); + for (int i = min; i <= max; i++) { + final WaveViewPanel ps = (WaveViewPanel) waveBox.getComponent(i); + select(ps); + } + } + } + lastClickedIndex = thisIndex; + } + + @Override + public void waveZoomed(final AbstractWavePanel src, final double st, final double et, final double nst, + final double net) { + final double[] t = new double[] { st, et }; + addHistory(src, t); + for (final AbstractWavePanel wvp : selectedSet) { + if (wvp != src) { + addHistory(wvp, t); + wvp.zoom(nst, net); + } + } + } + + @Override + public void waveClosed(final AbstractWavePanel src) { + remove(src); + } + }; + } + + private int calculateWaveHeight() { + if (waveHeight > 0) + return waveHeight; + + final int w = scrollPane.getViewport().getSize().width; + int h = (int) Math.round(w * 60.0 / 300.0); + h = Math.min(200, h); + h = Math.max(h, 80); + return h; + } + + private void setWaveHeight(final int s) { + waveHeight = s; + resizeWaves(); + } + + private void doSizePopup(final int x, final int y) { + if (popup == null) { + final String[] labels = new String[] { "Auto", null, "Tiny", "Small", "Medium", "Large" }; + final int[] sizes = new int[] { -1, -1, 50, 100, 160, 230 }; + popup = new JPopupMenu(); + final ButtonGroup group = new ButtonGroup(); + for (int i = 0; i < labels.length; i++) { + if (labels[i] != null) { + final int size = sizes[i]; + final JRadioButtonMenuItem mi = new JRadioButtonMenuItem(labels[i]); + mi.addActionListener(new ActionListener() { + public void actionPerformed(final ActionEvent e) { + setWaveHeight(size); + } + }); + if (waveHeight == size) + mi.setSelected(true); + group.add(mi); + popup.add(mi); + } else + popup.addSeparator(); + } + } + popup.show(this, x, y); + } + + private class OpenActionListener implements ActionListener { + public void actionPerformed(final ActionEvent e) { + final JFileChooser chooser = FileChooser.getFileChooser(); + chooser.resetChoosableFileFilters(); + for (final FileType ft : FileType.getKnownTypes()) { + final ExtensionFileFilter f = new ExtensionFileFilter(ft.extension, ft.description); + chooser.addChoosableFileFilter(f); + } + chooser.setDialogTitle("Open Wave"); + chooser.setFileFilter(chooser.getAcceptAllFileFilter()); + final File lastPath = new File(swarmConfig.lastPath); + chooser.setCurrentDirectory(lastPath); + chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + chooser.setMultiSelectionEnabled(true); + final int result = chooser.showOpenDialog(applicationFrame); + if (result == JFileChooser.APPROVE_OPTION) { + final File[] fs = chooser.getSelectedFiles(); + + for (int i = 0; i < fs.length; i++) { + if (fs[i].isDirectory()) { + final File[] dfs = fs[i].listFiles(); + if (dfs == null) + continue; + for (int j = 0; j < dfs.length; j++) + openFile(dfs[j]); + swarmConfig.lastPath = fs[i].getParent(); + } else { + openFile(fs[i]); + swarmConfig.lastPath = fs[i].getParent(); + } + } + } + } + } + + private class SaveActionListener implements ActionListener { + public void actionPerformed(final ActionEvent e) { + final AbstractWavePanel selected = getSingleSelected(); + if (selected == null) + return; + + final JFileChooser chooser = FileChooser.getFileChooser(); + chooser.resetChoosableFileFilters(); + chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + chooser.setMultiSelectionEnabled(false); + chooser.setDialogTitle("Save Wave"); + + for (final FileType ft : FileType.values()) { + if (ft == FileType.UNKNOWN) + continue; + + final ExtensionFileFilter f = new ExtensionFileFilter(ft.extension, ft.description); + chooser.addChoosableFileFilter(f); + } + + chooser.setFileFilter(chooser.getAcceptAllFileFilter()); + + final File lastPath = new File(swarmConfig.lastPath); + chooser.setCurrentDirectory(lastPath); + final String fileName = selected.getChannel().replace(' ', '_') + ".sac"; + chooser.setSelectedFile(new File(fileName)); + final int result = chooser.showSaveDialog(applicationFrame); + if (result == JFileChooser.APPROVE_OPTION) { + final File f = chooser.getSelectedFile(); + boolean confirm = true; + if (f.exists()) { + if (f.isDirectory()) { + JOptionPane.showMessageDialog(applicationFrame, "You can not select an existing directory.", + "Error", JOptionPane.ERROR_MESSAGE); + return; + } + confirm = false; + final int choice = JOptionPane.showConfirmDialog(applicationFrame, "File exists, overwrite?", + "Confirm", JOptionPane.YES_NO_OPTION); + if (choice == JOptionPane.YES_OPTION) + confirm = true; + } + + if (confirm) { + try { + swarmConfig.lastPath = f.getParent(); + final String fn = f.getPath(); + final SeismicDataFile file = SeismicDataFile.getFile(fn); + // String channel = selected.getChannel().replace(' ', + // '_'); + final Wave wave = selected.getWave(); + // file.putWave(channel, wave); + file.putWave(selected.getChannel(), wave); + file.write(); + } catch (final FileNotFoundException ex) { + JOptionPane.showMessageDialog(Swarm.getApplicationFrame(), "Directory does not exist.", "Error", + JOptionPane.ERROR_MESSAGE); + } catch (final IOException ex) { + JOptionPane.showMessageDialog(Swarm.getApplicationFrame(), "Error writing file.", "Error", + JOptionPane.ERROR_MESSAGE); + } + } + } + } + } + + private class SaveAllActionListener implements ActionListener { + public void actionPerformed(final ActionEvent e) { + if (waves.size() <= 0) + return; + + final FileTypeDialog dialog = new FileTypeDialog(); + FileType fileType = FileType.UNKNOWN; + if (!dialog.isOpen() || (dialog.isOpen() && !dialog.isAssumeSame())) { + dialog.setVisible(true); + + if (dialog.isCancelled()) + fileType = FileType.UNKNOWN; + else + fileType = dialog.getFileType(); + + } + + final JFileChooser chooser = FileChooser.getFileChooser(); + chooser.resetChoosableFileFilters(); + chooser.setMultiSelectionEnabled(false); + chooser.setDialogTitle("Save All Files"); + final File lastPath = new File(swarmConfig.lastPath); + chooser.setCurrentDirectory(lastPath); + + if (!fileType.isCollective) + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + else + chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + + final int result = chooser.showSaveDialog(applicationFrame); + if (result == JFileChooser.CANCEL_OPTION) + return; + + final File f = chooser.getSelectedFile(); + + if (f == null) { + JOptionPane.showMessageDialog(applicationFrame, "Save location not understood.", "Error", + JOptionPane.ERROR_MESSAGE); + return; + } + + if (result == JFileChooser.APPROVE_OPTION) { + try { + if (f.exists() && !f.isDirectory()) + return; + if (!f.exists()) + f.mkdir(); + for (final AbstractWavePanel wvp : waves) { + Wave sw = wvp.getWave(); + + if (sw != null) { + sw = sw.subset(wvp.getStartTime(), wvp.getEndTime()); + final String date = saveAllDateFormat.format(J2kSec.asDate(sw.getStartTime())); + final File dir = new File(f.getPath() + File.separatorChar + date); + if (!dir.exists()) + dir.mkdir(); + + swarmConfig.lastPath = f.getParent(); + final String fn = dir + File.separator + wvp.getChannel().replace(' ', '_') + + fileType.extension; + final SeismicDataFile file = SeismicDataFile.getFile(fn); + file.putWave(wvp.getChannel(), sw); + file.write(); + } + } + swarmConfig.lastPath = f.getPath(); + } catch (final FileNotFoundException ex) { + JOptionPane.showMessageDialog(Swarm.getApplicationFrame(), "Directory does not exist.", "Error", + JOptionPane.ERROR_MESSAGE); + } catch (final IOException ex) { + JOptionPane.showMessageDialog(Swarm.getApplicationFrame(), "Error writing file.", "Error", + JOptionPane.ERROR_MESSAGE); + } + } + } + } + + public void openFile(final File f) { + SeismicDataFile file = SeismicDataFile.getFile(f); + if (file == null) { + final FileTypeDialog dialog = new FileTypeDialog(); + FileType fileType = FileType.UNKNOWN; + if (!dialog.isOpen() || (dialog.isOpen() && !dialog.isAssumeSame())) { + dialog.setVisible(true); + + if (dialog.isCancelled()) + fileType = FileType.UNKNOWN; + else + fileType = dialog.getFileType(); + + } + file = SeismicDataFile.getFile(f, fileType); + } + + if (file == null) { + JOptionPane.showMessageDialog(applicationFrame, + "There was an error opening the file, '" + f.getName() + "'.", "Error", JOptionPane.ERROR_MESSAGE); + return; + } + + try { + file.read(); + } catch (final IOException e) { + JOptionPane.showMessageDialog(applicationFrame, + "There was an error opening the file, '" + f.getName() + "'.\n" + e.getMessage(), "Error", + JOptionPane.ERROR_MESSAGE); + } + + for (final String channel : file.getChannels()) { + final WaveViewPanel wvp = new WaveViewPanel(); + wvp.setChannel(channel); + final CachedDataSource cache = CachedDataSource.getInstance(); + + final Wave wave = file.getWave(channel); + cache.putWave(channel, wave); + wvp.setDataSource(cache); + wvp.setWave(wave, wave.getStartTime(), wave.getEndTime()); + WaveClipboardFrame.this.addWave(new WaveViewPanel(wvp)); + } + } + + private void doButtonEnables() { + final boolean enable = (waves == null || waves.size() == 0); + saveButton.setEnabled(!enable); + sortButton.setEnabled(!enable); + saveAllButton.setEnabled(!enable); + syncButton.setEnabled(!enable); + removeAllButton.setEnabled(!enable); + saveAllButton.setEnabled(!enable); + + final boolean allowSingle = (selectedSet.size() == 1); + sortButton.setEnabled(allowSingle); + syncButton.setEnabled(allowSingle); + saveButton.setEnabled(allowSingle); + + final boolean allowMulti = (selectedSet.size() > 0); + upButton.setEnabled(allowMulti); + downButton.setEnabled(allowMulti); + backButton.setEnabled(allowMulti); + expXButton.setEnabled(allowMulti); + compXButton.setEnabled(allowMulti); + backButton.setEnabled(allowMulti); + forwardButton.setEnabled(allowMulti); + histButton.setEnabled(allowMulti); + removeButton.setEnabled(allowMulti); + gotoButton.setEnabled(allowMulti); + } + + public synchronized void sortChannelsByNearest() { + final AbstractWavePanel p = getSingleSelected(); + if (p == null) + return; + + final ArrayList sorted = new ArrayList(waves.size()); + for (final AbstractWavePanel wave : waves) + sorted.add(wave); + + final Metadata smd = swarmConfig.getMetadata(p.getChannel()); + if (smd == null || Double.isNaN(smd.getLongitude()) || Double.isNaN(smd.getLatitude())) + return; + + Collections.sort(sorted, new Comparator() { + public int compare(final AbstractWavePanel wvp1, final AbstractWavePanel wvp2) { + Metadata md = swarmConfig.getMetadata(wvp1.getChannel()); + final double d1 = smd.distanceTo(md); + md = swarmConfig.getMetadata(wvp2.getChannel()); + final double d2 = smd.distanceTo(md); + return Double.compare(d1, d2); + } + }); + + removeWaves(); + for (final AbstractWavePanel wave : sorted) + addWave(wave); + select(p); + } + + public synchronized AbstractWavePanel getSingleSelected() { + if (selectedSet.size() != 1) + return null; + + AbstractWavePanel p = null; + for (final AbstractWavePanel panel : selectedSet) + p = panel; + + return p; + } + + public synchronized void syncChannels() { + final AbstractWavePanel p = getSingleSelected(); + if (p == null) + return; + + final double st = p.getStartTime(); + final double et = p.getEndTime(); + + // TODO: thread bug here. must synch iterator below + final SwingWorker worker = new SwingWorker() { + @Override + public Object construct() { + List copy = null; + synchronized (WaveClipboardFrame.this) { + copy = new ArrayList(waves); + } + for (final AbstractWavePanel wvp : copy) { + if (wvp != p) { + if (wvp.getDataSource() != null) { + addHistory(wvp, new double[] { wvp.getStartTime(), wvp.getEndTime() }); + final Wave sw = wvp.getDataSource().getWave(wvp.getChannel(), st, et); + wvp.setWave(sw, st, et); + } + } + } + return null; + } + + @Override + public void finished() { + repaint(); + } + }; + worker.start(); + } + + public void setStatusText(final String s) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + statusLabel.setText(s); + } + }); + } + + public WaveViewPanel getSelected() { + return null; + // return selected; + } + + public synchronized void addWave(final AbstractWavePanel p) { + p.addListener(selectListener); + p.setOffsets(54, 8, 21, 19); + p.setAllowClose(true); + p.setStatusLabel(statusLabel); + p.setAllowDragging(true); + p.setDisplayTitle(true); + final int w = scrollPane.getViewport().getSize().width; + p.setSize(w, calculateWaveHeight()); + p.setBottomBorderColor(Color.GRAY); + p.createImage(); + waveBox.add(p); + waves.add(p); + doButtonEnables(); + waveBox.validate(); + } + + private synchronized void deselect(final AbstractWavePanel p) { + selectedSet.remove(p); + waveToolbar.removeSettings(p.getSettings()); + p.setBackgroundColor(BACKGROUND_COLOR); + p.createImage(); + doButtonEnables(); + } + + private synchronized void deselectAll() { + final WaveViewPanel[] panels = selectedSet.toArray(new WaveViewPanel[0]); + for (final WaveViewPanel p : panels) + deselect(p); + } + + private synchronized void select(final AbstractWavePanel p) { + if (p == null || selectedSet.contains(p)) + return; + + selectedSet.add(p); + doButtonEnables(); + p.setBackgroundColor(SELECT_COLOR); + DataChooser.getInstance().setNearest(p.getChannel()); + p.createImage(); + waveToolbar.addSettings(p.getSettings()); + } + + private synchronized void remove(final AbstractWavePanel p) { + int i = 0; + for (i = 0; i < waveBox.getComponentCount(); i++) { + if (p == waveBox.getComponent(i)) + break; + } + + p.removeListener(selectListener); + p.getDataSource().close(); + setStatusText(" "); + waveBox.remove(i); + waves.remove(p); + histories.remove(p); + doButtonEnables(); + waveBox.validate(); + selectedSet.remove(p); + lastClickedIndex = Math.min(lastClickedIndex, waveBox.getComponentCount() - 1); + waveToolbar.removeSettings(p.getSettings()); + repaint(); + } + + protected int getWaveIndex(final AbstractWavePanel p) { + int i = 0; + for (i = 0; i < waveBox.getComponentCount(); i++) { + if (p == waveBox.getComponent(i)) + break; + } + return i; + } + + public synchronized void remove() { + final WaveViewPanel[] panels = selectedSet.toArray(new WaveViewPanel[0]); + for (final WaveViewPanel p : panels) + remove(p); + } + + public synchronized void moveDown() { + final AbstractWavePanel p = getSingleSelected(); + if (p == null) + return; + + final int i = waves.indexOf(p); + if (i == waves.size() - 1) + return; + + waves.remove(i); + waves.add(i + 1, p); + waveBox.remove(p); + waveBox.add(p, i + 1); + waveBox.validate(); + repaint(); + } + + public synchronized void moveWaves(int i) { + final WaveViewPanel[] panels = selectedSet.toArray(new WaveViewPanel[0]); + + List panelIdx = new ArrayList(); + for (int idx = 0; idx < panels.length; idx++) { + panelIdx.add(waves.indexOf(panels[idx])); + } + + Collections.sort(panelIdx); + if (i > 0) { + Collections.reverse(panelIdx); + if (panelIdx.get(0) + i > waves.size() - 1) { + return; + } + } else { + if (panelIdx.get(0) + i < 0) { + return; + } + } + + for (int idx : panelIdx) { + AbstractWavePanel p = waves.get(idx); + waves.remove(idx); + waves.add(idx + i, p); + waveBox.remove(p); + waveBox.add(p, idx + i); + } + + waveBox.validate(); + repaint(); + } + + public void resizeWaves() { + final SwingWorker worker = new SwingWorker() { + @Override + public Object construct() { + final int w = scrollPane.getViewport().getSize().width; + for (final AbstractWavePanel wave : waves) { + wave.setSize(w, calculateWaveHeight()); + wave.createImage(); + } + return null; + } + + @Override + public void finished() { + waveBox.validate(); + validate(); + repaint(); + } + }; + worker.start(); + } + + public void removeWaves() { + while (waves.size() > 0) { + remove(waves.get(0)); + } + + waveBox.validate(); + scrollPane.validate(); + doButtonEnables(); + repaint(); + } + + private void addHistory(final AbstractWavePanel wvp, final double[] t) { + Stack history = histories.get(wvp); + if (history == null) { + history = new Stack(); + histories.put(wvp, history); + } + history.push(t); + } + + public void gotoTime(final AbstractWavePanel wvp, String t) { + double j2k = Double.NaN; + try { + if (t.length() == 12) + t = t + "30"; + + j2k = J2kSec.parse("yyyyMMddHHmmss", t); + } catch (final Exception e) { + JOptionPane.showMessageDialog(applicationFrame, "Illegal time value.", "Error", JOptionPane.ERROR_MESSAGE); + } + + if (!Double.isNaN(j2k)) { + double dt = 60; + if (wvp.getWave() != null) { + final double st = wvp.getStartTime(); + final double et = wvp.getEndTime(); + final double[] ts = new double[] { st, et }; + addHistory(wvp, ts); + dt = (et - st); + } + + final double tzo = swarmConfig.getTimeZone(wvp.getChannel()).getOffset(System.currentTimeMillis()) / 1000; + + final double nst = j2k - tzo - dt / 2; + final double net = nst + dt; + + fetchNewWave(wvp, nst, net); + } + } + + public void gotoTime(final String t) { + for (final AbstractWavePanel p : selectedSet) + gotoTime(p, t); + } + + public void scaleTime(final AbstractWavePanel wvp, final double pct) { + final double st = wvp.getStartTime(); + final double et = wvp.getEndTime(); + final double[] t = new double[] { st, et }; + addHistory(wvp, t); + final double dt = (et - st) * (1 - pct); + final double mt = (et - st) / 2 + st; + final double nst = mt - dt / 2; + final double net = mt + dt / 2; + fetchNewWave(wvp, nst, net); + } + + public void scaleTime(final double pct) { + for (final AbstractWavePanel p : selectedSet) + scaleTime(p, pct); + } + + public void back(final AbstractWavePanel wvp) { + final Stack history = histories.get(wvp); + if (history == null || history.empty()) + return; + + final double[] t = history.pop(); + fetchNewWave(wvp, t[0], t[1]); + } + + public void back() { + for (final AbstractWavePanel p : selectedSet) + back(p); + } + + private void shiftTime(final AbstractWavePanel wvp, final double pct) { + final double st = wvp.getStartTime(); + final double et = wvp.getEndTime(); + final double[] t = new double[] { st, et }; + addHistory(wvp, t); + final double dt = (et - st) * pct; + final double nst = st + dt; + final double net = et + dt; + fetchNewWave(wvp, nst, net); + } + + public void shiftTime(final double pct) { + for (final AbstractWavePanel p : selectedSet) + shiftTime(p, pct); + } + + public void repositionWaves(final double st, final double et) { + for (final AbstractWavePanel wave : waves) { + fetchNewWave(wave, st, et); + } + } + + public Throbber getThrobber() { + return throbber; + } + + // TODO: This isn't right, this should be a method of waveviewpanel + private void fetchNewWave(final AbstractWavePanel wvp, final double nst, final double net) { + final SwingWorker worker = new SwingWorker() { + @Override + public Object construct() { + throbber.increment(); + final SeismicDataSource sds = wvp.getDataSource(); + // Hacky fix for bug #84 + Wave sw = null; + if (sds instanceof CachedDataSource) + sw = ((CachedDataSource) sds).getBestWave(wvp.getChannel(), nst, net); + else + sw = sds.getWave(wvp.getChannel(), nst, net); + wvp.setWave(sw, nst, net); + wvp.repaint(); + return null; + } + + @Override + public void finished() { + throbber.decrement(); + repaint(); + } + }; + worker.start(); + } + + @Override + public void setMaximum(final boolean max) throws PropertyVetoException { + if (max) { + swarmConfig.clipboardX = getX(); + swarmConfig.clipboardY = getY(); + } + super.setMaximum(max); + } + + @Override + public void paint(final Graphics g) { + super.paint(g); + if (waves.size() == 0) { + final Dimension dim = this.getSize(); + g.setColor(Color.black); + g.drawString("Clipboard empty.", dim.width / 2 - 40, dim.height / 2); + } + } + + @Override + public void setVisible(final boolean isVisible) { + super.setVisible(isVisible); + if (isVisible) + toFront(); + } + + private static class WaveClipboardFrameHolder { + public static WaveClipboardFrame waveClipiboardFrame = new WaveClipboardFrame(); + } } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveToolBarListener.java b/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveToolBarListener.java new file mode 100644 index 00000000..d0a1bd3e --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveToolBarListener.java @@ -0,0 +1,5 @@ +package gov.usgs.volcanoes.swarm.wave; + +public interface WaveToolBarListener { + +} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveViewPanelAdapter.java b/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveViewPanelAdapter.java index 83f73630..57799109 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveViewPanelAdapter.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveViewPanelAdapter.java @@ -4,16 +4,9 @@ /** * - * $Log: not supported by cvs2svn $ - * Revision 1.1 2006/08/01 23:45:23 cervelli - * Moved package. - * - * Revision 1.1 2006/06/05 18:06:49 dcervelli - * Major 1.3 changes. - * * @author Dan Cervelli */ -public class WaveViewPanelAdapter implements WaveViewPanelListener +public abstract class WaveViewPanelAdapter implements WaveViewPanelListener { public void waveZoomed(AbstractWavePanel src, double st, double et, double nst, double net) {} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveViewSettingsDialog.java b/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveViewSettingsDialog.java index 54c3cc4f..e40df672 100644 --- a/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveViewSettingsDialog.java +++ b/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveViewSettingsDialog.java @@ -1,7 +1,7 @@ package gov.usgs.volcanoes.swarm.wave; import gov.usgs.volcanoes.core.math.Butterworth.FilterType; -import gov.usgs.volcanoes.swarm.SwarmDialog; +import gov.usgs.volcanoes.swarm.SwarmModalDialog; import gov.usgs.volcanoes.swarm.wave.WaveViewSettings.ViewType; import java.awt.BorderLayout; @@ -24,7 +24,7 @@ * * @author Dan Cervelli */ -public class WaveViewSettingsDialog extends SwarmDialog { +public class WaveViewSettingsDialog extends SwarmModalDialog { private static final long serialVersionUID = 1L; private JPanel dialogPanel; @@ -72,7 +72,7 @@ public class WaveViewSettingsDialog extends SwarmDialog { private int settingsCount; private WaveViewSettingsDialog() { - super(applicationFrame, "Wave Settings", true); + super(applicationFrame, "Wave Settings"); createUI(); setSizeAndLocation(); } diff --git a/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveViewToolBar.java b/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveViewToolBar.java new file mode 100644 index 00000000..2c942340 --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveViewToolBar.java @@ -0,0 +1,85 @@ +package gov.usgs.volcanoes.swarm.wave; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.AbstractAction; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JToggleButton; +import javax.swing.JToolBar; +import javax.swing.KeyStroke; + +import gov.usgs.volcanoes.core.ui.UiUtils; +import gov.usgs.volcanoes.swarm.Icons; +import gov.usgs.volcanoes.swarm.SwarmUtil; +import gov.usgs.volcanoes.swarm.wave.WaveViewSettings.ViewType; + +public class WaveViewToolBar { + private JButton waveSet; + private JToggleButton waveToggle; + private JToggleButton spectraToggle; + private JToggleButton spectrogramToggle; + private ButtonGroup waveTypes; + private WaveViewToolBarListener listener; + + public WaveViewToolBar(WaveViewSettings s, JToolBar dest, WaveViewToolBarListener listener) { + this.listener = listener; + createUI(dest); + } + + public void createUI(JToolBar dest) { + waveSet = SwarmUtil.createToolBarButton(Icons.wavesettings, "Wave view settings (?)", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + listener.displaySettingsDialog(); + } + }); + listener.mapKeyStroke("shift SLASH", "settings", waveSet); + dest.add(waveSet); + + waveToggle = + SwarmUtil.createToolBarToggleButton(Icons.wave, "Wave view (W or ,)", new ActionListener() { + public void actionPerformed(ActionEvent e) { + listener.setType(ViewType.WAVE); + } + }); + listener.mapKeyStroke("COMMA", "wave1", waveToggle); + listener.mapKeyStroke("W", "wave2", waveToggle); + dest.add(waveToggle); + + spectraToggle = SwarmUtil.createToolBarToggleButton(Icons.spectra, "Spectra view (S or .)", + new ActionListener() { + public void actionPerformed(ActionEvent e) { + listener.setType(ViewType.SPECTRA); + } + }); + listener.mapKeyStroke("PERIOD", "spectra1", spectraToggle); + listener.mapKeyStroke("S", "spectra2", spectraToggle); + dest.add(spectraToggle); + + spectrogramToggle = SwarmUtil.createToolBarToggleButton(Icons.spectrogram, + "Spectrogram view (G or /)", new ActionListener() { + public void actionPerformed(ActionEvent e) { + listener.setType(ViewType.SPECTROGRAM); + } + }); + listener.mapKeyStroke("SLASH", "spectrogram1", spectrogramToggle); + listener.mapKeyStroke("G", "spectrogram2", spectrogramToggle); + dest.add(spectrogramToggle); + + waveTypes = new ButtonGroup(); + waveTypes.add(waveToggle); + waveTypes.add(spectraToggle); + waveTypes.add(spectrogramToggle); + } + + public void setEnabled(boolean enable) { + waveSet.setEnabled(enable); + waveToggle.setEnabled(enable); + spectraToggle.setEnabled(enable); + spectrogramToggle.setEnabled(enable); + + } +} diff --git a/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveViewToolBarListener.java b/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveViewToolBarListener.java new file mode 100644 index 00000000..5575e0d3 --- /dev/null +++ b/src/main/java/gov/usgs/volcanoes/swarm/wave/WaveViewToolBarListener.java @@ -0,0 +1,11 @@ +package gov.usgs.volcanoes.swarm.wave; + +import javax.swing.AbstractButton; + +import gov.usgs.volcanoes.swarm.wave.WaveViewSettings.ViewType; + +public interface WaveViewToolBarListener { + public void displaySettingsDialog(); + public void mapKeyStroke(String keyStroke,String name, AbstractButton button); + public void setType(ViewType viewType); +} diff --git a/src/main/java/nz/org/geonet/data/RSSHypocenters.java b/src/main/java/nz/org/geonet/data/RSSHypocenters.java deleted file mode 100644 index 086d305c..00000000 --- a/src/main/java/nz/org/geonet/data/RSSHypocenters.java +++ /dev/null @@ -1,106 +0,0 @@ -package nz.org.geonet.data; - -import gov.usgs.util.ResourceReader; -import gov.usgs.util.xml.SimpleXMLParser; -import gov.usgs.util.xml.XMLDocHandler; -import gov.usgs.util.xml.XMLToMap; -import gov.usgs.volcanoes.core.time.J2kSec; -import gov.usgs.volcanoes.core.time.Time; -import gov.usgs.volcanoes.swarm.map.ClickableGeoLabel; -import gov.usgs.volcanoes.swarm.map.Hypocenter; -import gov.usgs.volcanoes.swarm.map.LabelSource; - -import java.awt.geom.Point2D; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * A class for getting hypocenter information from the GeoNet RSS feed. - * - * $Log: not supported by cvs2svn $ - * @author Dan Cervelli - * @version $Id: RSSHypocenters.java,v 1.1 2007-05-21 02:59:38 dcervelli Exp $ - */ -public class RSSHypocenters implements LabelSource -{ - public static final String RSS_URL = "http://www.geonet.org.nz/feeds/earthquake/geonet-recent-quakes-1.0.xml"; - public static final String QUAKE_URL = "http://www.geonet.org.nz/services/quake/"; - - private static class GetQuakeIDs implements XMLDocHandler - { - public List ids = new ArrayList(); - private boolean idTag; - - public void endDocument() throws Exception - { - } - - public void endElement(String tag) throws Exception - { - } - - public void startDocument() throws Exception - { - } - - public void startElement(String tag, Map h) throws Exception - { - if ("quake:id".equals(tag)) - idTag = true; - } - - public void text(String str) throws Exception - { - if (idTag) - { - ids.add(str); - idTag = false; - } - } - } - - public List getLabels() - { - ResourceReader rr = ResourceReader.getResourceReader(RSS_URL); - List hypos = new ArrayList(); - try - { - GetQuakeIDs q = new GetQuakeIDs(); - SimpleXMLParser.parse(q, rr.getReader()); - for (String id : q.ids) - { - System.out.println(id); - XMLToMap qm = new XMLToMap(); - ResourceReader qr = ResourceReader.getResourceReader(QUAKE_URL + "/" + id); - SimpleXMLParser.parse(qm, qr.getReader()); - Hypocenter h = new Hypocenter(); - double lat = Double.parseDouble(qm.text.get("report/lat")); - double lon = Double.parseDouble(qm.text.get("report/lon")); - h.location = new Point2D.Double(lon, lat); - h.text = "M" + qm.text.get("report/mag"); - h.depth = Double.parseDouble(qm.text.get("report/depth")); - String year = qm.text.get("report/uttime/year"); - String mo = qm.text.get("report/uttime/month"); - String day = qm.text.get("report/uttime/day"); - String hour = qm.text.get("report/uttime/hour"); - String minute = qm.text.get("report/uttime/minute"); - String second = qm.text.get("report/uttime/second"); - String ms = qm.text.get("report/uttime/msec"); - String ds = String.format("%s-%s-%s %s:%s:%s.%s", year, mo, day, hour, minute, second, ms); - h.time = J2kSec.parse(Time.STANDARD_TIME_FORMAT_MS, ds); - hypos.add(h); - } - } - catch (Exception e) - { - e.printStackTrace(); - } - return hypos; - } - - public static void main(String[] args) - { - new RSSHypocenters().getLabels(); - } -} diff --git a/IRIS_networks.txt b/src/main/resources/IRIS_networks.txt similarity index 100% rename from IRIS_networks.txt rename to src/main/resources/IRIS_networks.txt diff --git a/src/main/resources/help/mapSettings.md b/src/main/resources/help/mapSettings.md new file mode 100644 index 00000000..136b2f30 --- /dev/null +++ b/src/main/resources/help/mapSettings.md @@ -0,0 +1,15 @@ +# Map Settings # + +## Location ## +Specifies the center of the map and the scale in pixels per meter. + + +## Options ## +### Line ### +Specifies the color and width of the line connecting an inset map pannel with its marker. + + +### NEIC Event Summary ### +Swarm can plot real-time events using data from the [NEIC real-time feed](http://earthquake.usgs.gov/earthquakes/feed/). Events are updated every five minutes. + +Event markers are colored according to the event age. Events shown in red have occurred within the past hour; events shown in blue have occurred within the past day; events shown in yellow have occurred within the past week. \ No newline at end of file diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties index 84a7fbad..9bcabac9 100644 --- a/src/main/resources/log4j.properties +++ b/src/main/resources/log4j.properties @@ -2,7 +2,7 @@ log4j.rootLogger=debug, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.Threshold=debug +log4j.appender.stdout.Threshold=info # Pattern to output the caller's file name and line number. log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd hh:mm:ss} %5p - %m%n diff --git a/src/main/resources/log4jDebug.properties b/src/main/resources/log4jDebug.properties new file mode 100644 index 00000000..ff2cf672 --- /dev/null +++ b/src/main/resources/log4jDebug.properties @@ -0,0 +1,19 @@ +log4j.rootLogger=ALL, stdout + +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.Threshold=DEBUG + +# Pattern to output the caller's file name and line number. +log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd hh:mm:ss} %5p - %m%n + +log4j.appender.R=org.apache.log4j.RollingFileAppender +log4j.appender.R.File=swarm.log +log4j.appender.R.MaxFileSize=100KB +# Keep one backup file +log4j.appender.R.MaxBackupIndex=9 + +log4j.appender.R.layout=org.apache.log4j.PatternLayout +log4j.appender.R.layout.ConversionPattern=%d{yyyy-MM-dd hh:mm:ss} %5p - %m (%F:%L)%n + +#log4j.logger.gov.usgs.volcanoes.swarm.map=info \ No newline at end of file diff --git a/src/main/resources/networks.csv b/src/main/resources/networks.csv new file mode 100644 index 00000000..637995fd --- /dev/null +++ b/src/main/resources/networks.csv @@ -0,0 +1,1212 @@ +# Network Code,Network Name,Network Operator,Network Country (ISO & Name),Network Website,Deployment Country (if different),Digital Object Identifier (DOI) +1A,NCISP6,"Institute of Geology and Geophysics, CAS",CN (China),http://english.igg.cas.cn,China,doi:10.7914/SN/1A_2007 +1A,Arlita,Institut des Sciences de la Terre,FR (France),,France, +1A,Waste Isolation Pilot Plant Noise Analysis,"New Mexico Tech (NMT Mining & Technology NM,USA)",US (United States),,United States,doi:10.7914/SN/1A_2013 +1A,"Mining-induced seismicity network at mine Prosper-Haniel, Bottrop",Ruhr Universitaet Bochum (RUB Germany),DE (Germany),,Germany,doi:10.7914/SN/1A_2014 +1B,UGANDA,University of Frankfurt,DE (Germany),,Germany, +1B,Gulf of Alaska Active Source Experiment,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +1B,Assured Arctic Awareness,University of Washington,US (United States),,United States,doi:10.7914/SN/1B_2013 +1B,Sweetwater Array,IRIS HQ (DC),US (United States),,United States, +1C,Urban Seismology 2,Karlsruhe Institute of Technology (KIT GIP Germany),DE (Germany),,Germany, +1C,"Seismic Characterization of Menengai Crater, Kenya",University of Texas at El Paso (UTEP),US (United States),,United States,doi:10.7914/SN/1C_2011 +1C,Antithesis 2,Geoazur,FR (France),,France, +1D,Elevation change anomalies in West Antarctica and dynamics of subglacial water beneath ice streams and their tributaries,UC Santa Cruz,US (United States),,United States,doi:10.7914/SN/1D_2010 +1D,MSH Node Array,University of New Mexico (UNM),US (United States),,United States,doi:10.7914/SN/1D_2014 +1D,UKANET: UK Antarctic Network,University of Leeds (UK),GB (United Kingdom),,United Kingdom,doi:10.7914/SN/1D_2016 +1E,ELLITE,University of Aberdeen (UK),GB (United Kingdom),,United Kingdom, +1E,Crustal Magma Plumbing of the Santorini Volcanic System - OBS Component,University of Oregon,US (United States),,United States,doi:10.7914/SN/1E_2015 +1F,"Workshops on Volcanoes 2016, Santiaguito, Guatemala","University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States,doi:10.7914/SN/1F_2015 +1G,ScanArray core,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +2A,"Gravity Lineations, Intraplate Melting, Petrologic and Seismic Expedition",Brown University,US (United States),,United States, +2A,"COGEAR Temporary Deployments, Valais, Switzerland",Swiss Seismological Service,CH (Switzerland),,Switzerland, +2B,"PUDEL Network, Argentina, 2007-2009","GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +2B,OBSIP: Bering Sea Active Source Experiment,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +2B,East_Staithes,Durham University,GB (United Kingdom),,United Kingdom, +2C,Geophysical Study of Ice Stream Stick-slip dynamics,Central Washington University,US (United States),,United States,doi:10.7914/SN/2C_2010 +2C,CoLiBrEA,University Montpellier II (UM2 UMSF France),FR (France),,Tanzania,doi:10.7914/SN/2C_2014 +2D,Albacore,UC San Diego,US (United States),,United States, +2D,N2,University of Bergen (UiB Norway) Inst of Geophysics,NO (Norway),,Norway, +2E,"Seismic Coherence and Site Effect Studies at Precarious Rock Locations in Riverside, California","USGS Caltech, Pasadena",US (United States),,United States,doi:10.7914/SN/2E_2010 +2E,Norway Microbarom Study 2014,"Los Alamos National Laboratory (LANL, NM)",US (United States),,United States,doi:10.7914/SN/2E_2014 +2F,OMBRA seismic network,Istituto Nazionale di Geofisica e Vulcanologia (INGV),IT (Italy),http://ombra.bo.ingv.it,Italy, +2F,LONGMEN,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany,10.14470/7S7567431325 +2F,PFO Accelerometer Test,IGPP University of California Systemwide Institute of Geophysics and Planetary Physics,US (United States),,United States, +2G,Testing of the effectiveness of incorporating seismic data in seismic hazard assessment within a traditional field course,Indiana University Bloomington (IU Bloomington),US (United States),,United States,doi:10.7914/SN/2G_2010 +3A,ARC-Vanuatu,"Observatoire de Grenoble, Grenoble",FR (France),,France, +3A,Maule Aftershock Deployment (UK),University of Liverpool,GB (United Kingdom),,United Kingdom, +3A,The celerity of microbaroms,"Los Alamos National Laboratory (LANL, NM)",US (United States),,United States,doi:10.7914/SN/3A_2013 +3B,Desert Peak EGS,"Lawrence Berkeley Laboratory (LBNL, CA USA)",US (United States),,United States, +3B,"Integrated Geosciences in the Merida Andes, Venezuela","Fundación Venezolana de Investigaciones Sismológicas (FUNVISIS), Caracas",VE (Venezuela),,Venezuela, +3C,Collaborative Proposal: Glacier seismicity and its relationship to basal motion,Central Washington University,US (United States),,United States,doi:10.7914/SN/3C_2010 +3C,Arizona Rapid Array Array Mobilization Program,Arizona Geological Survey,US (United States),,United States,doi:10.7914/SN/3C_2014 +3D,Morocco-Muenster,Munster University (WWU-Germany) Westfalische Wilhelms-Universitat Munster,DE (Germany),,Germany, +3D,HART Pisagua Earthquake,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +3E,Exploring Seismic Velocity of Sediments in the Mississippi Embayment,"University of Memphis, CERI",US (United States),,United States,doi:10.7914/SN/3E_2010 +3E,Crustal Magma Plumbing of the Santorini Volcanic System - Land Component,University of Oregon,US (United States),,United States,doi:10.7914/SN/3E_2015 +3F,USGS flume seismic monitoring,USGS Vancouver WA,US (United States),,United States,doi:10.7914/SN/3F_2010 +3F,HUMMING WOMBAT,Weston Geophysical Corp,US (United States),,United States,doi:10.7914/SN/3F_2013 +3G,TÜBITAK Marmara Research Center Earth Sciences Institution Network,TÜBITAK Marmara Research Center,TR (Turkey),,Turkey,doi:10.7914/SN/3G_2007 +4A,Investigating mechanisms of subglacial hydrology and basal shear stress beneath Whillans Ice Stream using passive seismology,UC Santa Cruz,US (United States),,United States,doi:10.7914/SN/4A_2008 +4A,"Darfield RAMP Aftershock Deployment, NZ","University of Wisconsin, Madison",US (United States),,United States,doi:10.7914/SN/4A_2010 +4A,Pollino Seismic Experiment,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +4B,TASK FORCE KIRGISTAN,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +4B,Raft River EGS,"Lawrence Berkeley Laboratory (LBNL, CA USA)",US (United States),,United States, +4C,Semipalatinsk Test Site Network,Inst. of Geophysical Research National Nuclear Center of Republic of Kazakhstan,KZ (Kazakhstan),,Kazakhstan, +4C,NERA-JRA1-A,"Observatoire de Grenoble, Grenoble",FR (France),,France, +4C,GeoGirls outreach experiment at Mount St. Helens,USGS Vancouver WA,US (United States),,United States, +4D,Seismological Experiments on Swiss Glaciers,Swiss Seismological Service,CH (Switzerland),,Switzerland, +4D,Parkfield Experiment to Record Microseismicity and Tremor,Karlsruhe Institute of Technology (KIT GIP Germany),DE (Germany),,Germany, +4D,Seismological Experiments on Swiss Glaciers,Swiss Seismological Service,CH (Switzerland),,Switzerland, +4E,MARS/Testing of cabled broadband OBS using MARS test facility,UC San Diego,US (United States),,United States, +4E,Field Research Station broadband seismometer network,University of Bristol (UK),GB (United Kingdom),,United Kingdom,doi:10.7914/SN/4E_2015 +4F,Askja 2007,University of Cambridge (UK) Earth Sciences,GB (United Kingdom),,United Kingdom, +4F,North Texas Earthquake Study,Southern Methodist University (SMU TX),US (United States),,United States,doi:10.7914/SN/4F_2015 +5A,Samoa Lithosphere Integrated Seismic-Petrologic Expedition,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States,doi:10.7914/SN/5A_2005 +5A,Crustal-Scale Geometry of Active Continental Normal Faults,Oregon State University (OSU USA),US (United States),,United States,doi:10.7914/SN/5A_2010 +5A,Eastern North American Margin Explosives Test,University of Texas at El Paso (UTEP),US (United States),,United States,doi:10.7914/SN/5A_2013 +5A,Subduction Reversal and the Ontong Java Plateau,University of Southampton (UK),GB (United Kingdom),,United Kingdom, +5B,TaskForce L Aquila 2009,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +5B,Brady Hot Spring EGS,"Lawrence Berkeley Laboratory (LBNL, CA USA)",US (United States),,United States, +5C,"Dynamics of Lake-Calving Glaciers: Yakutat Glacier, Alaska","University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States,doi:10.7914/SN/5C_2009 +5C,TIPTIMON TAJIKISTAN,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +5D,LuxBB,Karlsruhe Institute of Technology (KIT GIP Germany),DE (Germany),,Germany, +5E,MINAS,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany,doi:10.14470/ab466166 +5F,"Magnetotelluric and seismic investigation of arc melt generation, delivery, and storage beneath Okmok Volcano","University of Wisconsin, Madison",US (United States),,United States,doi:10.7914/SN/5F_2015 +6A,Rohrbach/Vogtland seismic array 2008,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +6A,Walvis Ridge Passive Source Seismic Experiment,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +6A,Seismic Array iNegrated Detection for a Window of Indian Continental Head,"Institute of Geology and Geophysics, CAS",CN (China),,China,doi:10.7914/SN/6A_2013 +6B,North China seismic Array,Institute of Geophysics China Earthquake Administration (IGPCEA),CN (China),http://www.chinarraydmc.org/,China,doi:10.7914/SN/6B_2006 +6B,New York Canyon EGS,"Lawrence Berkeley Laboratory (LBNL, CA USA)",US (United States),,United States, +6C,Ferghana,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +6C,Seismic Cluster Investigation in the Southern Rio Grande Rift,University of Texas at El Paso (UTEP),US (United States),,United States,doi:10.7914/SN/6C_2011 +6C,TIPTIMON AFGHANISTAN,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +6D,Telica Seismic and Deformation Network,Carnegie Institute of Washington / Science (DTM CIW),US (United States),,United States, +6E,Nisyros network,Ruhr Universitaet Bochum (RUB Germany),DE (Germany),,Germany, +6E,Wabash Valley Seismic Zone,Southern Illinois University Carbondale (SIUC),US (United States),,United States,doi:10.7914/SN/6E_2013 +6F,"WIsconsin, new Zealand, And Rpi Deployment, Deep Fault Drilling Project","University of Wisconsin, Madison",US (United States),,United States,doi:10.7914/SN/6F_2012 +7A,BTL,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7A,Lake Toba,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +7A,Cascadia- Keck,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +7A,Mid-Atlantic Geophysical Integrative Collaboration,Yale University,US (United States),,United States,doi:10.7914/SN/7A_2013 +7B,SKIPPY,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7B,TIPAGE,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +7B,Dixie Valley EGS,"Lawrence Berkeley Laboratory (LBNL, CA USA)",US (United States),,United States, +7C,YE,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7C,DORA,Institut des Sciences de la Terre,FR (France),,France, +7C,The Mackenzie Mountains Transect: Active Deformation from Margin to Craton,Colorado State University (CSU),US (United States),,United States,doi:10.7914/SN/7C_2015 +7D,KIMBA97,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7D,Yakima Landslide RAMP,"University of Memphis, CERI",US (United States),,United States,doi:10.7914/SN/7D_2009 +7D,Cascadia Initiative Community Experiment - OBS Component,IRIS HQ (DC),US (United States),,United States,doi:10.7914/SN/7D_2011 +7E,KIMBA98,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7E,PASSEQ,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany,10.14470/2R383989 +7E,Glacioseismic Monitoring of Rumblings in Jakobshavn Glacier,University of North Carolina at Chapel Hill (UNC),US (United States),,United States,doi:10.7914/SN/7E_2010 +7E,Reykjanet,"Institute of Geophysics, Academy of Sciences of the Czech Republic",CZ (Czech Republic),http://www.ig.cas.cz/en/reykjanet,Czech Republic,doi:10.7914/SN/7E_2013 +7F,QUOLL,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7F,Central Arkansas Induced Eathquakes 2010-2011,"University of Memphis, CERI",US (United States),,United States, +7F,Casey - Davis region,University of Tasmania,AU (Australia),,Australia, +7G,WA-CRATON,Auburn University,US (United States),,United States, +7G,"Youngstown, Ohio, induced earthquakes","Lamont Doherty Earth Observatory (LDEO), Columbia University",US (United States),,United States, +7H,TIGGER,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7H,"W-Alps landslide experiments Char d Osset, Chamousset, Pont Bourquin",Institut des Sciences de la Terre,FR (France),,France, +7I,TASMAL,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7I,Test data of Insight Seismomoter,Institut de Physique du Globe de Paris,FR (France),,France, +7J,CAPRAL,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7J,"Correlating seismic and visual (TLS) signals at Breidamerkurjokull, Iceland",University of South Florida (USF),US (United States),,United States,doi:10.7914/SN/7J_2011 +7K,SOC,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7M,LF98,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7N,MB99,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7O,AFOO,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7P,TIGGER-SP,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7Q,SEAL,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7R,EVA,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7S,SETA,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7T,SEAL2,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +7U,SEAL3,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +8A,IPY: Stability of Larsen C Ice Shelf in a Warming Climate,IRIS PASSCAL Instrument Center @ New Mexico Tech,US (United States),,United States,doi:10.7914/SN/8A_2008 +8A,West Iberia Lithosphere and Astenosphere Structure,Instituto Dom Luiz (FCUL formerly CGUL-LX Portugal),PT (Portugal),,Portugal, +8A,AfricaArray - Namibia,Penn State University,US (United States),,United States, +8B,NCISP7,"Institute of Geology and Geophysics, CAS",CN (China),http://english.igg.cas.cn,China,doi:10.7914/SN/8B_2008 +8B,Newberry EGS,"Lawrence Berkeley Laboratory (LBNL, CA USA)",US (United States),,United States, +8C,Lucky Strike OBS Network,,,,, +8D,Swiss Seismological Service Aftershock Deployments,Swiss Seismological Service,CH (Switzerland),,Switzerland, +8E,Mammoth Mountain Broadband Experiment,USGS Menlo Park,US (United States),,United States, +8F,Hikurangi Ocean Bottom Investigation of Tremor and Slow Slip,University of Texas at Austin,US (United States),,United States,doi:10.7914/SN/8F_2015 +9A,COBO,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +9A,Loihi Seismicity,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +9A,East Texas Earthquake Monitoring,Weston Geophysical Corp,US (United States),,United States,doi:10.7914/SN/9A_2013 +9A,Makushin Volcano Plumbing,"University of Wisconsin, Madison",US (United States),,United States,doi:10.7914/SN/9A_2015 +9B,Volcano Seismology and Digital Signal Processing Short Course (GEOP539),"New Mexico Tech (NMT Mining & Technology NM,USA)",US (United States),,United States,doi:10.7914/SN/9B_2008 +9B,"Volcano Geophysical Field Methods Course at Kilauea Volcano, Hawaii","New Mexico Tech (NMT Mining & Technology NM,USA)",US (United States),,United States,doi:10.7914/SN/9B_2010 +9B,Tomographic Imaging of Silali and Paka Volcanoes,University of Texas at El Paso (UTEP),US (United States),,United States, +9B,"Cape Verde, Fogo Temporary Seismic Network ","Instituto Português do Mar e da Atmosfera, I.P.",PT (Portugal),,Portugal, +9C,High Resolution Seismological Profiling across Sierra Nevad,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +9C,Aleutian Array of Arrays,UC Riverside,US (United States),,United States,doi:10.7914/SN/9C_2014 +9D,Ice-ocean interaction at Nuuk tidewater glaciers,"University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States,doi:10.7914/SN/9D_2010 +9D,Seismic Array of the Sevilleta,,,,,doi:10.7914/SN/9D_2015 +9E,"Short term noise tests / structural / foundation monitoring at SED, ETHZ",Swiss Seismological Service,CH (Switzerland),,Switzerland, +9F,Southern Alps microearthquake Borehole Array- part 2,Victoria University of Wellington (VUW New Zealand),NZ (New Zealand),,New Zealand,doi:10.7914/SN/9F_2008 +A,Generic Asian Strong Motion Network,INGV Instituto Nazionale di Geofisica e Vulcanologia (Italy),IT (Italy),,Asia, +A1,Southern African Co-located Academic Network,Pangaea Geophyics and Geodesy Working Group,ZA (South Africa),,South Africa, +AA,Anchorage Strong Motion Network,"University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States, +AB,National Seismic Network of Azerbaijan,Republic Center of Seismic Survey/Acad. of Sciences of Azerbaijan,AZ (Azerbaijan),,Azerbaijan, +AC,Albanian Seismological Network,"Institute of Geosciences, Polytechnic Univ. of Tirana",AL (Albania),,Albania, +AD,ACROSS Strong Motion Network,Central Asian Institute for Applied Geosciences,KG (Kyrgyzstan),,Kyrgyzstan, +AE,Arizona Broadband Seismic Network,Arizona Geological Survey,US (United States),,United States,doi:10.7914/SN/AE +AF,Africa Array,Penn State University,US (United States),http://www.africaarray.psu.edu/,United States, +AG,Arkansas Seismic Network,Arkansas Geological Survey (AGS),US (United States),,United States, +AH,Arkhangelsk Seismic Network,Institute of Environmental Problems of the North UB RAS,RU (Russian Federation),,Russian Federation, +AI,Antarctic Seismographic Argentinean Italian Network,Istituto Nazionale di Oceanografia e di Geofisica Sperimentale,IT (Italy),http://www.crs.inogs.it/asain/,Antarctica,doi:10.7914/SN/AI +AK,Alaska Regional Network,"Alaska Earthquake Center, Univ. of Alaska Fairbanks",US (United States),http://earthquake.alaska.edu,United States,doi:10.7914/SN/AK +AL,Alaskan Long Period Array,"University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States, +AM,Public Seismic Network,Other, (),http://psn.quake.net/,, +AN,Altay-Sayan Regional Seismic Network,Geophysical Survey Siberian Branch Russian Academy of Sciences (GS SB RAS),RU (Russian Federation),,Russian Federation, +AO,Arkansas Seismic Observatory,University of Arkansas at Little Rock (UALR),US (United States),,United States, +AQ,Central Queensland Seismic Network,Central Queensland Seismology Research Group,AU (Australia),,Australia,doi:10.7914/SN/AQ +AR,Northern Arizona Network,Northern Arizona University (NAU AEIC Arizona),US (United States),,United States, +AS,Modified High Gain Long Period Observatory,Albuquerque Seismological Laboratory (ASL)/USGS,US (United States),,Global,doi:10.7914/SN/AS +AT,National Tsunami Warning Center Alaska Seismic Network,NOAA National Oceanic and Atmospheric Administration (USA),US (United States),http://ntwc.arh.noaa.gov/,Alaska,doi:10.7914/SN/AT +AU,Australian National Seismograph Network,Geoscience Australia,AU (Australia),,Australia, +AV,Alaska Volcano Observatory,USGS Alaska Fairbanks,US (United States),http://www.avo.alaska.edu/,United States, +AW,AWI Network Antarctica,Alfred Wegener Institute for Polar and Marine Research,DE (Germany),,Antarctica, +AX,Meteo Aruba,Departamento Meterologico Aruba,AW (Aruba),http://www.meteo.aw/,Aruba, +AY,Haitian Seismic Network,Bureau of Mines and Energy (Haiti),HT (Haiti),,Haiti, +AZ,ANZA Regional Network,UC San Diego,US (United States),http://eqinfo.ucsd.edu/,United States, +BA,UniBAS,Universita della Basilicata,IT (Italy),,Italy, +BB,Brunei Darussalam National Seismic Network,National Seismic Center of Brunei,BN (Brunei),,Brunei, +BC,Red Sismica del Noroeste de Mexico,"Centro de Investigación Científica y de Educación Superior de Ensenada (CICESE), Ensenada",MX (Mexico),,Mexico, +BD,Bakun Dam Micro-Seismic Monitoring Network,Kinemetrics,US (United States),,United States, +BE,Belgian Seismic Network,Royal Observatory of Belgium,BE (Belgium),http://www.seismology.be,Belgium,doi:10.7914/SN/BE +BF,Black Forest Observatory,Universities of Karlsruhe and Stuttgart,DE (Germany),,Germany, +BG,Berkeley Geysers Network,UC Berkeley,US (United States),,United States, +BH,Bay Area Urban Hazards,USGS Golden Colorado,US (United States),,United States, +BI,University of Dhaka Seismographic Network,University of Dhaka,BD (Bangladesh),,Bangladesh, +BK,Berkeley Digital Seismograph Network,UC Berkeley,US (United States),http://seismo.berkeley.edu/bdsn/,United States,doi:10.7932/BDSN +BL,Brazilian Lithospheric Seismic Project,"Universidade de Sao Paulo, USP",BR (Brazil),,Brazil, +BN,"UK-Net, Blacknest Array",Blacknest (AWE UK),GB (United Kingdom),,United Kingdom, +BO,Bosai-Ken Network,National Research Institute for Earth Science and Disaster Prevention (NEID JAPAN),JP (Japan),http://www.bosai.go.jp/,Japan, +BP,Berkeley Parkfield High Resolution Seismic Network,UC Berkeley,US (United States),http://seismo.berkeley.edu/bdsn/hrsn_overview.html,United States,doi:10.7932/HRSN +BR,University of Brasilia Seismic Network,University of Brasilia,BR (Brazil),,Brazil, +BS,National Seismic Network of Bulgaria,Bulgarian Academy of Sciences Geophysical Institute (GPhl Bulgaria),BG (Bulgaria),,Bulgaria, +BT,Red Sismológica de la Sabana de Bogotá,Universidad Nacional de Colombia (UN-Colombia),CO (Colombia),,Colombia, +BU,Boise State University Seismic Network,Boise State University,US (United States),,United States, +BW,BayernNetz,"Department of Earth and Environmental Sciences, Geophysical Observatory, University of Munchen",DE (Germany),,Germany, +BX,Botswana Seismological Network,Department of Geological Survey of Botswana,BW (Botswana),,Botswana, +BY,Baikal Digital Seismograph Network,Baikal Regional Seismological Center,RU (Russian Federation),,Russian Federation, +C,Chilean National Seismic Network,"Universidad de Chile, Dept de Geofisica (DGF UChile Chile)",CL (Chile),,Chile, +C1,Red Sismologica Nacional,"Universidad de Chile, Dept de Geofisica (DGF UChile Chile)",CL (Chile),http://www.sismologia.cl/,Chile, +CA,Catalan Seismic Network,Institut Cartogràfic i Geològic de Catalunya-Institut d'Estudis Catalans,ES (Spain),http://www.icgc.cat/xarxasismica,Spain,doi:10.7914/SN/CA +CB,"Data Management Centre of China National Seismic Network at Institute of Geophysics, CEA",Institute of Geophysics China Earthquake Administration (IGPCEA),CN (China),http://www.seisdmc.ac.cn,China,doi:10.7914/SN/CB +CC,Cascade Chain Volcano Monitoring,Cascades Volcano Observatory/USGS,US (United States),,United States, +CD,China Digital Seismograph Network,"China Seismological Bureau, Institute of Geophysics, Beijing",CN (China),,China,doi:10.7914/SN/CD +CE,California Strong Motion Instrumentation Program,California Division of Mines and Geology (CDMG),US (United States),,United States, +CF,Red Acelerografica Nacional de la Comision Federal de Electricidad (CFE),Comision Federal de Electricidad (CFE),MX (Mexico),,Mexico, +CG,Coso Microearthquake Network,Geothermal Program Office/US Navy,US (United States),,United States, +CH,Switzerland Seismological Network,Swiss Seismological Service,CH (Switzerland),,Switzerland, +CI,Southern California Seismic Network,California Institute of Technology (Caltech),US (United States),http://www.scsn.org/,United States,doi:10.7914/SN/CI +CJ,Community Seismic Network,California Institute of Technology (Caltech),US (United States),http://csn.caltech.edu/,United States, +CK,CAREMON Central Asian Cross-border network,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Central Asia, +CL,Corinth Riff Laboratory,"Sismos a l'Ecole, Geosciences Azur",FR (France),,France, +CM,Red Sismologica Nacional de Colombia,INGEOMINAS - Servicio Geologico Colombiano (SGC Colombia),CO (Colombia),,Colombia, +CN,Canadian National Seismic Network,Geological Survey of Canada,CA (Canada),http://www.earthquakescanada.nrcan.gc.ca/stndon/CNSN-RNSC/,Canada, +CO,South Carolina Seismic Network,University of South Carolina,US (United States),http://www.seis.sc.edu/projects/SCSN/,United States,doi:10.7914/SN/CO +CP,CIVISA Seismo-Volcanic Monitoring Network-Azores Islands,University of Azores,PT (Portugal),,Portugal, +CQ,Cyprus Broadband Seismological Network,Geological Survey Department Cyprus,CY (Cyprus),,Cyprus, +CR,Croatian Seismograph Network,University of Zagreb,HR (Croatia),,Croatia, +CS,Caucusus Array,"Lamont Doherty Earth Observatory (LDEO), Columbia University",US (United States),,Russian Federation, +CT,California Transect Network,UC Santa Cruz,US (United States),,United States, +CU,Caribbean Network,USGS National Earthquake Information Center (NEIC),US (United States),,Caribbean region, +CV,"Vigil Network, Cape Verde Islands","Instituto Superior Tecnico (IST Universidade Tecnica de Lisboa, Portugal)",PT (Portugal),,Portugal, +CW,Servicio Sismologico Nacional de Cuba (SSNC),National Centre for Seismological Research (CENAIS Cuba),CU (Cuba),,Cuba, +CX,Plate Boundary Observatory Network Northern Chile,GFZ / IGPP / CSN / UdC / UCNA,M7 (Multiple Countries),http://www.ipoc-network.org/index.php/data/cx-net.html,Multiple Countries,10.14470/PK615318 +CY,Cayman Islands,Cayman Islands Government,KY (Cayman Islands),,Cayman Islands, +CZ,Czech Regional Seismic Network,"Institute of Geophysics, Academy of Sciences of the Czech Republic",CZ (Czech Republic),http://www.ig.cas.cz/en/structure/observatories/czech-regional-seismological-network,Czech Republic,doi:10.7914/SN/CZ +D0,Kamchatka Network of Seismic Stations,Geophysical Survey Kamchatkan EMSD (KEMSD GS RAS - Russia),RU (Russian Federation),,Russian Federation, +DE,Nanometrics Research Network,Nanometrics Seismological Instruments,CA (Canada),,Global, +DH,DOHAD,Research Association on Planateral Acitivities of Nature (DOHAD),TR (Turkey),,Turkey, +DK,Danish Seismological Network,National Survey and Cadastre,DK (Denmark),,Denmark, +DM,Public Seismic Network - Dominica,,,http://dpsninc.org/,Dominica, +DN,Dubai Seismological Network,Dubai Municipality,AE (United Arab Emirates),http://www.seismo.geodesy.ae/,United Arab Emirates, +DO,Dense Oceanfloor Network System for Earthquakes & Tsunamis,Japan Marine Science and Technology Center (JAMSTEC),JP (Japan),http://www.jamstec.go.jp/donet,Japan, +DR,Dominican Republic Seismic Network,Universidad Autonoma de Santo Domingo (ISU/UASD Dominican Republic),DO (Dominican Republic),,Dominican Republic, +DW,Digital World-Wide Standardized Seismograph Network,Albuquerque Seismological Laboratory (ASL)/USGS,US (United States),,Global,doi:10.7914/SN/DW +DZ,REALSAS,"CRAAG (Research Center of Astronomy, Astrophysics & Geophysics) Algeria",DZ (Algeria),,Algeria, +E,Rete ENEA,Agenzia Nazionale per le Nuove Technolgie (ENEA),IT (Italy),,Italy, +EA,IRIS Education and Research Network,IRIS HQ (DC),US (United States),,United States, +EB,Ebre Observatory Regional Seismic Network,"Observatori de l'Ebre, Tarragona",ES (Spain),,Spain, +EC,Ecuador Seismic Network,Instituto Geofisico Escuela Politecnica Nacional (IG-EPN Ecuador),EC (Ecuador),,Ecuador, +ED,French Educational Seismological Network,"Sismos a l'Ecole, Geosciences Azur",FR (France),,France, +EE,Estonian Seismic Network,Geological Survey of Estonia (GSE),EE (Estonia),,Estonia, +EG,EUROSEISTEST Strong Motion Network,Aristotle University of Thessaloniki,GR (Greece),http://euroseisdb.civil.auth.gr/,Greece, +EH,Ethiopian Seismic Network,Addis Ababa University,ET (Ethiopia),,Ethiopia,doi:10.7914/SN/EH +EI,Irish National Seismic Network (INSN),Dublin Institute for Advanced Studies,IE (Ireland),,Ireland, +EM,Electromagnetic Studies of the Continents,Oregon State University (OSU USA),US (United States),http://NGF@oregonstate.edu,United States,doi:10.7914/SN/EM +EN,CalEnergy Subnetwork,"USGS Caltech, Pasadena",US (United States),,United States, +EP,UTEP Seismic Network,University of Texas at El Paso (UTEP),US (United States),,United States, +EQ,Nevada Educational Seismic Network,University of Nevada (UNR Reno),US (United States),,United States, +ER,Mount Erebus Volcano Observatory Seismic Network,"New Mexico Tech (NMT Mining & Technology NM,USA)",US (United States),http://erebus.nmt.edu/,Antartica, +ES,Spanish Digital Seismic Network,Subdireccion General De Geodesia Y Geofisica,ES (Spain),,Spain, +ET,CERI Southern Appalachian Seismic Network,"University of Memphis, CERI",US (United States),http://www.ceri.memphis.edu/,United States, +EU,Generic European Strong Motion Network,INGV Instituto Nazionale di Geofisica e Vulcanologia (Italy),IT (Italy),,Europe, +EV,Collalto Seismic Network - Rete Sismica di Collalto,Istituto Nazionale di Oceanografia e di Geofisica Sperimentale,IT (Italy),http://rete-collalto.crs.inogs.it/,Italy,doi:10.7914/SN/EV +EX,EXTREMA Network,Istituto Nazionale di Geofisica e Vulcanologia (INGV),IT (Italy),,Italy, +FA,UCLA Seismic Network,UC Los Angeles,US (United States),,United States, +FB,Seismic Network of Federation of Bosnia and Herzegovina,Federal Hydrometeorological Institute (Bosnia),BA (Bosnia and Herzegovina),,Bosnia and Herzegovina, +FC,Generic African Strong Motion Network,INGV Instituto Nazionale di Geofisica e Vulcanologia (Italy),IT (Italy),,Africa, +FG,Fallon Microearthquake Network,Geothermal Program Office/US Navy,US (United States),,United States, +FN,Northern Finland Seismological Network,Sodankyla Geophysical Observatory / University of Oulu (Finland),FI (Finland),,Finland, +FR,RESIF and other Broad-band and accelerometric permanent networks in metropolitan France,Ecole et Observatoire des Sciences de la Terre (EOST),FR (France),https://portal.resif.fr/?FR-French-broadband-and,France,10.15778/RESIF.FR +FV,Friuli and Veneto Short Period Network,OGS (Istituto Nazionale di Oceanografia e di Geofisica Sperimentale),IT (Italy),http://oasis.crs.inogs.it/,Italy,doi:10.7914/SN/FV +G,GEOSCOPE,Institut de Physique du Globe de Paris,FR (France),,Global, +GA,GARNET Seismic Network,Geological Survey of Japan (GSJ AIST),JP (Japan),,Japan, +GB,Great Britain Seismograph Network,British Geological Survey,GB (United Kingdom),,United Kingdom, +GC,UCLA Geonet Network,UC Los Angeles,US (United States),http://lecs.cs.ucla.edu/wiki/index.php/GeoNet,United States, +GD,Global Positioning System (GPS) Displacement Measurements, None, ( ),,Global , +GE,GEOFON,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),http://geofon.gfz-potsdam.de/,Global,doi:10.14470/TR560404 +GF,GONAF,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),http://www.gonaf.de/,Turkey, +GG,Greenland Ice Sheet Monitoring Network,IRIS HQ (DC),US (United States),http://glisn.info/,Greenland, +GH,Ghana Digital Seismic Network,Ghana Geological Survey,GH (Ghana),,Ghana, +GI,Red Sismologica Nacional- Guatemala,"Instituto Nacional de Sismologia, Vulcanologia,Meteorologia e Hidrologia (INSIVUMEH)",GT (Guatemala),,Guatemala, +GL,Guadeloupe Seismic and Volcano Observatory Network,Institut de Physique du Globe de Paris,FR (France),http://www.ipgp.fr/ovsg,"Guadeloupe, Lesser Antilles, Caribbean", +GN,Garni Dense Array,USGS Menlo Park,US (United States),http://nsmp.wr.usgs.gov/geos/GARNI/garni.html,Armenia, +GO,National Seismic Network of Georgia,Ilia State University- Seismic Monitoring Centre of Georgia (Georgia),GE (Georgia),,Georgia, +GP,GPS Data Products for Solid Earth Science,California Institute of Technology (Caltech),US (United States),,United States, +GR,German Regional Seismic Network,Seismogisches Zentralobseratorium GRF,DE (Germany),,Germany, +GS,US Geological Survey Networks,USGS Golden Colorado,US (United States),,Global, +GT,Global Telemetered Seismograph Network (USAF/USGS),Albuquerque Seismological Laboratory (ASL)/USGS,US (United States),,"South America, Africa and Antarctica ",doi:10.7914/SN/GT +GU,Regional Seismic Network of North Western Italy,University of Genova,IT (Italy),http://www.distav.unige.it/rsni,Italy,doi:10.7914/SN/GU +GY,"Geyokcha Array, Turkmenistan",UC San Diego,US (United States),,Turkmenistan, +GZ,ANKARA-NET,"Gazi University, Earthquake Engineering Implementation and Research Center",TR (Turkey),,Turkey, +H2,Hawaii 2 Observatory,University of Hawaii (UH),US (United States),http://www.soest.hawaii.edu/H2O/,United States, +HA,Hellenic Seismological Network,University of Athens,GR (Greece),,Greece, +HC,Seismological Network of Crete,Technological Educational Institute of Crete,GR (Greece),,Greece, +HE,Finnish National Seismic Network,University of Helsinki,FI (Finland),,Finland, +HF,Swedish Seismic Array Network,Swedish Defence Research Agency (FOI),SE (Sweden),http://snsn.geofys.uu.se/,Sweden, +HG,High-Gain Long-Period Network,Albuquerque Seismological Laboratory (ASL)/USGS,US (United States),,Global ,doi:10.7914/SN/HG +HI,ITSAK Strong Motion Network,Institute of Engineering Seismology and Earthquake Engineering (ITSAK),GR (Greece),,Greece, +HK,Hong Kong Seismograph Network,Hong Kong Observatory,CN (China),http://www.hko.gov.hk/gts/equake/sp_seismo_network_intro_e.htm,China, +HL,National Observatory of Athens Seismic Network,"National Observatory of Athens, Institute of Geodynamics, Athens",GR (Greece),http://bbnet.gein.noa.gr,Greece,doi:10.7914/SN/HL +HM,GeoRisk Seismic Network, GeoRisk Earthquake Engineering,HU (Hungary),http://www.foldrenges.hu,Hungary,doi:10.7914/SN/HM +HP,"University of Patras, Seismological Laboratory",University of Patras,GR (Greece),http://seismo.geology.upatras.gr/,Greece, +HS,Hessischer Erdbebendienst (HED),"Hessisches Landesamt für Umwelt und Geologie, Wiesbaden",DE (Germany),,Germany, +HT,Aristotle University of Thessaloniki Seismological Network,Aristotle University of Thessaloniki,GR (Greece),http://geophysics.geo.auth.gr/the_seisnet/WEBSITE_2005/station_index_en.html,Greece,doi:10.7914/SN/HT +HU,Hungarian National Seismological Network,Geodetic and Geophysical Research Institute,HU (Hungary),http://www.seismology.hu/index.php/en/observatory/stations/51-the-hungarian-national-seismological-network,Hungary,10.14470/UH028726 +HV,Hawaiian Volcano Observatory Network,USGS Hawaiian Volcano Observatory (HVO),US (United States),http://hvo.wr.usgs.gov,United States,doi:10.7914/SN/HV +HW,Hanford Washington Seismic Network,Pacific Northwest National Laboratory (PNL),US (United States),,United States, +I1,Iranian Strong Motion Network,Ministry of Road and Urban Development (BHRC),IR (Iran),,Iran, +I2,Iranian National Broadband Seismic Network,Internationa Institute of Earthquake Engineering and Seismology,JP (Japan),,Japan, +IA,InaTEWS Indonesia Tsunami Early Warning System,"Meteorological, Climatological and Geophysical Agency (BMKG Indonesia)",ID (Indonesia),,Indonesia, +IB,IberArray,"Institute Earth Sciences ""Jaume Almera"" CSIC (ICTJA Spain)",ES (Spain),http://iberarray.ictja.csic.es/,Spain, +IC,New China Digital Seismograph Network,Albuquerque Seismological Laboratory (ASL)/USGS,US (United States),,China,doi:10.7914/SN/IC +ID,Project IDA VLP Seismometer Network (UCSD/SIO/IGPP),Scripps Institution of Oceanography,US (United States),http://ida.ucsd.edu/,Global,doi:10.7914/SN/ID +IE,Idaho National Engineering Laboratory,University of Utah,US (United States),,United States, +IG,Southern Spain Broadband Seismic Network,"Instituto Andaluz de Geofisica, Granada University",ES (Spain),,Spain, +IH,Red Sismica Dominicana del INDRHI,Instituto Nacional de Recursos Hidraulicos,DO (Dominican Republic),,Dominican Republic, +II,IRIS/IDA Seismic Network,Scripps Institution of Oceanography,US (United States),http://ida.ucsd.edu/,Global,doi:10.7914/SN/II +IK,IISERK Seismic Network,Indian Institute of Science Education and Research Kolkata (IISER),IN (India),http://www.iiserkol.ac.in/,India, +IL,Iranian Long Period Array,"Institute of Geophysics (University of Tehran, Iranian Seis Center-Iran)",IR (Iran),,Iran, +IM,International Miscellaneous Stations, None, ( ),,Global, +IN,National Seismic Network of India,India Meteorological Department,IN (India),,India, +IO,International OGS Network,Istituto Nazionale di Oceanografia e di Geofisica Sperimentale,IT (Italy),http://www.inogs.it/asain/evn,Global,doi:10.7914/SN/IO +IP,Instituto Superior Tecnico Broadband Seismic Network,"Instituto Superior Tecnico (IST Universidade Tecnica de Lisboa, Portugal)",PT (Portugal),,Portugal, +IQ,Inquique Local Network,University of Kiel,DE (Germany),http://www.ipoc-network.org/index.php/observatory/seismology/iquique-local-network.html,Chile, +IR,International Registry,"International Seismological Centre (ISC, UK)",GB (United Kingdom),,United Kingdom, +IS,Israel National Seismic Network,Geophysical Institute of Israel (GII Israel),IL (Israel),,Israel, +IT,Italian Strong Motion Network,Presidency of Counsil of Ministers - Civil Protection Department ,IT (Italy),http://ran.protezionecivile.it/IT/index.php,Italy,doi:10.7914/SN/IT +IU,Global Seismograph Network (GSN - IRIS/USGS),Albuquerque Seismological Laboratory (ASL)/USGS,US (United States),http://earthquake.usgs.gov/monitoring/gsn/,Global,doi:10.7914/SN/IU +IV,Italian National Seismic Network,Istituto Nazionale di Geofisica e Vulcanologia (INGV),IT (Italy),http://terremoti.ingv.it/,Italy, +IW,Intermountain West Seismic Network,USGS National Earthquake Information Center (NEIC),US (United States),http://earthquake.usgs.gov/monitoring/anss/regions/imw/,United States, +IX,ISNet - Irpinia Seismic Network,INGV Instituto Nazionale di Geofisica e Vulcanologia (Italy),IT (Italy),http://isnet.fisica.unina.it/,Italy, +IZ,Italian Experimental Seismic Network,Other, (),http://www.iesn.org/,, +JA,Red Sísmica y Acelerométrica de Jalisco,Universidad De Guadalajara (UDG Mexico),MX (Mexico),,Mexico, +JI,Jorhat India Seismic Network,"North East Institute of Science and Technology, Jorhat (NEIST India)",IN (India),,India, +JM,Jamaica Seismograph Network,University of the West Indies - Mona (Jamaica),JM (Jamaica),http://www.mona.uwi.edu/earthquake/jamseisnet.htm,Jamaica, +JP,Japan Meteorological Agency Seismic Network,JMA Japan Meteorological Agency,JP (Japan),http://www.jma.go.jp/en/quake/,Japan, +JS,Jordan Seismic Network,Jordan Seismo Obs & Geo Studies / Natural Resources Authority (NRA Jordan),JO (Jordan),,Jordan, +KA,Karthala Volcano Monitoring Network,Institut de Physique du Globe de Paris,FR (France),,Comoros, +KB,Karlsruhe BroadBand Array,Karlsruhe Institute of Technology (KIT GIP Germany),DE (Germany),http://www.gpi.kit.edu/english/KABBA.php,Global, +KC,Central Asian Seismic Network of CAIAG,Central Asian Institute for Applied Geosciences,KG (Kyrgyzstan),http://www.caiag.kg,Central Asia,doi:10.7914/SN/KC +KG,Korean Seismic Network (KIGAM),"Korean Institute of Geology, Mining and Materials (KIGAM)","KR (Korea, South)",,"Korea, South", +KI,Kandilli Observatory Strong Motion Network,"Bogazici University (Istanbul, Turkey)",TR (Turkey),,Turkey, +KK,Ọbáfémi Awólówò University Digital Seismograph Network,"Geology Department, Ọbáfémi Awólówò University, Ilé-Ifè, Osun State, Nigeria",NG (Nigeria),,Nigeria, +KM,Kansas Seismic Monitoring Network,Kansas Geological Survey,US (United States),,United States, +KN,Kyrgyz Seismic Telemetry Network,"Kyrgyz Institute of Seismology, IVTAN/KIS ",KG (Kyrgyzstan),,Kyrgyzstan, +KO,Bogazici University Kandilli Observatory And Earthquake Research Institute,"Kandilli Observatory and Earthquake Research Institute, Bosphorus Univ.",TR (Turkey),http://bdtim.koeri.boun.edu.tr/,Turkey,doi:10.7914/SN/KO +KP,Korea Polar Seismic Network,Korea Polar Research Institute (KOPRI),"KR (Korea, South)",,Greenland,doi:10.7914/SN/KP +KR,Kyrgyz Digital Network,"Institute of Seismology, National Academy of Sciences of Kyrgyz Republic (KIS)",KG (Kyrgyzstan),http://www.seismo.kg,Kyrgyzstan,doi:10.7914/SN/KR +KS,Korea National Seismography Network (KNSN-KMA),"Korea Meteorological Administration, Seoul","KR (Korea, South)",,"Korea, South", +KU,King Saud University Network,King Saud University,SA (Saudi Arabia),,Saudi Arabia, +KW,Kuwait National Seismic Network,Kuwait Institute for Scienctific Research (KISR),KW (Kuwait),,Kuwait, +KY,Kentucky Seismic and Strong Motion Network,Kentucky Geological Survey/Univ. of Kentucky,US (United States),http://www.uky.edu/KGS/geologichazards/equake3.htm,United States,doi:10.7914/SN/KY +KZ,Kazakhstan Network,KNDC/Institute of Geophysical Research (Kazakhstan),KZ (Kazakhstan),,Kazakhstan,doi:10.7914/SN/KZ +LA,Los Angeles Basin Seismic Network,USC - University of Southern California,US (United States),,United States, +LB,Leo Brady Network,Sandia National Laboratories,US (United States),,United States, +LC,LSC (Laboratorio Subterraneo Canfranc) ,"Institut de Ciencies de la Terra ""Jaume Almera"" (CSIC ICTJA Spain)",ES (Spain),,Spain, +LD,Lamont-Doherty Cooperative Seismographic Network,"Lamont Doherty Earth Observatory (LDEO), Columbia University",US (United States),http://www.ldeo.columbia.edu/LCSN/,United States, +LE,Landeserdbebendienst Baden-Wuerttemberg,"Landesamt fuer Geologie, Rohstoffe und Bergbau",DE (Germany),http://www.lgrb.uni-freiburg.de/lgrb/Fachbereiche/erdbebendienst,Germany, +LI,Laser Interferometer Gravitational-Wave Experiment,California Institute of Technology (Caltech),US (United States),http://www.ligo.caltech.edu/,United States,doi:10.7914/SN/LI +LL,LLNL NTS Network,Lawrence Livermore National Laboratory (LLNL),US (United States),,United States, +LN,Los Alamos Seismic Network (LASN),"Los Alamos National Laboratory (LANL, NM)",US (United States),http://www.ees.lanl.gov/ees11/geophysics/lasn/lasn.shtml,United States, +LO,Observatorio Sismologico Loyola,Instituto Politecnico Loyola,DO (Dominican Republic),,Dominican Republic, +LS,Large Aperture Seismic Array (LASA), None, ( ),, , +LT,Timor Leste Seismic Network,Other, (),http://ipg.tl,Timor-Leste,doi:10.7914/SN/LT +LV,seismic data in North West of UK,Liverpool University,GB (United Kingdom),,United Kingdom,doi:10.7914/SN/LV +LX,University of Lisbon Seismic Network,Universidade de Lisboa (Lisbon Portugal),PT (Portugal),,Portugal, +LZ,Zambia Seismic Network,Geological Survey Department of Zambia,ZM (Zambia),http://www.mewd.gov.zm,Zambia,doi:10.7914/SN/LZ +M,Generic American Strong Motion Network,Istituto Nazionale di Geofisica e Vulcanologia (INGV),IT (Italy),,Italy, +MA,Macedonian Seismological Network,Macedonia Seismological Observatory (Macedonia),MK (Macedonia),,Macedonia, +MB,Montana Regional Seismic Network,"Montana Bureau of Mines and Geology/Montana Tech (MBMG, MT USA)",US (United States),http://mbmgquake.mtech.edu,United States,doi:10.7914/SN/MB +MC,Montserrat CALIPSO Borehole Network,University of Texas-Arlington,US (United States),http://www.uta.edu/faculty/mattioli/research/CALIPSO/Intro.html,Montserrat, +MD,Moldova Digital Seismic Network,Geological and Seismological Institute of Moldova,MD (Moldova),,Moldova, +ME,Montenegrian Seismic Network,Montenegro Seismological Observatory,ME (Montenegro),,Montenegro, +MF,Laboratorio de Ingeniería Sísmica,Universidad de Costa Rica,CR (Costa Rica),http://www.lis.ucr.ac.cr,Costa Rica,doi:10.7914/SN/MF +MG,Seismic Network of the NorthEastern Mexico,"Universidad Nacional Autónoma de México (UNAM), Mexico City, D.F.",MX (Mexico),,Mexico, +MI,USGS Northern Mariana Islands Network,USGS Alaska Anchorage,US (United States),http://volcanoes.usgs.gov/nmi/,Northern Mariana Islands,doi:10.7914/SN/MI +ML,Malta Seismic Network,University of Malta (Malta),MT (Malta),,Malta, +MM,Myanmar National Seismic Network,Department of Meteorology and Hydrology - National Earthquake Data Center,MM (Myanmar),http://www.dmh.gov.mm/,Myanmar,doi:10.7914/SN/MM +MN,MEDNET Project,Istituto Nazionale di Geofisica e Vulcanologia (INGV),IT (Italy),http://mednet.rm.ingv.it/,Mediterrean Sea region, +MO,Morocco Seismic Network,"Institut National de Géophysique (CNRST), Rabat",MA (Morocco),,Morocco, +MQ,Martinique Seismic and Volcano Observatory Network ,Institut de Physique du Globe de Paris,FR (France),http://www.ipgp.fr/fr/ovsm,Martinique, +MR,Mauritius Seismic Network,Mauritius Meteorological Services,MU (Mauritius),,Mauritius, +MS,Singapore Seismological Network,Meteorological Service of Singapore,SG (Singapore),,Singapore, +MT,Observatory for Multidisciplinary monitoring of Instability of Versant,"Observatoire de Grenoble, Grenoble",FR (France),,France, +MU,Miami University Seismic Network,"Miami University (Ohio, USA)",US (United States),,United States, +MV,Montserrat Volcano Observatory,University of the West Indies (Seismic Research Centre Trinidad),TT (Trinidad and Tobago),,Trinidad and Tobago, +MW,Malawi National Seismic Network,Geological Survey Department of Malawi,MW (Malawi),,Eastern Region,doi:10.7914/SN/MW +MX,Mexican National Seismic Network,"Universidad Nacional Autónoma de México (UNAM), Mexico City, D.F.",MX (Mexico),,Mexico, +MY,Malaysian National Seismic Network,"Malaysian Meteorological Service (Ministry of Science, Tech and Innovation, MOSTI-MMD-MET)",MY (Malaysia),,Malaysia, +N4,Central and Eastern US Network,UC San Diego,US (United States),http://ceusn.ucsd.edu/,United States,doi:10.7914/SN/N4 +NA,Netherlands Antilles Seismic Network,"ORFEUS (KNMI) Data Center, Royal Netherlands Meteorological Institute",NL (Netherlands),,Netherlands, +NB,Northeastern Brazil UFRN,Universidade Federal do Rio Grande do Norte (UFRN Brazil),BR (Brazil),http://www.sistemas.ufrn.br/,Brazil, +NC,USGS Northern California Regional Network,USGS Menlo Park,US (United States),http://www.ncedc.org/ncsn/,United States, +ND,New CaleDonia Broadband Seismic Network,"Centre IRD de Noumea, Nouvelle-Caledonie",NC (New Caledonia),http://www.seisme.nc,New Caledonia,doi:10.7914/SN/ND +NE,New England Seismic Network,Boston College,US (United States),http://aki.bc.edu/nesn.htm,North East United States,doi:10.7914/SN/NE +NF,North Fork Valley Seismic Network,"National Institute for Occupational and Health (NIOSH WA,USA)",US (United States),,United States, +NG,Reserved for Papua New Guinea Seismic Network,,,,, +NH,Geological Survey of North Rhine - Westphalia,Geological Survey of North Rhine - Westphalia,DE (Germany),http://www.gd.nrw.de/gg_le.htm,Germany, +NI,North-East Italy Broadband Network,OGS (Istituto Nazionale di Oceanografia e di Geofisica Sperimentale) and University of Trieste,IT (Italy),,Italy,doi:10.7914/SN/NI +NJ,Nigerian National Network of Seismographi Stations (NNNSS),Centre for Geodesy and Geodynamics,NG (Nigeria),,Nigeria,doi:10.7914/SN/NJ +NL,Netherlands Seismic Network,"ORFEUS (KNMI) Data Center, Royal Netherlands Meteorological Institute",NL (Netherlands),http://www.knmi.nl/research/seismology/nat_seism_network.html,Netherlands, +NM,Cooperative New Madrid Seismic Network,Saint Louis University,US (United States),http://www.eas.slu.edu/eqc/eqcnetwork.html,United States, +NN,Nevada Seismic Network,University of Nevada (UNR Reno),US (United States),http://www.seismo.unr.edu,United States,doi:10.7914/SN/NN +NO,Norwegian Seismic Array Network,Norwegian Seismic Array (NORSAR),NO (Norway),,Norway, +NP,United States National Strong-Motion Network,USGS National Strong Motion Program -Fresno,US (United States),,United States, +NQ,NetQuakes,USGS Menlo Park,US (United States),http://earthquake.usgs.gov/monitoring/netquakes/,United States, +NR,NARS Array,Utrecht University (UU Netherlands),NL (Netherlands),http://www.data.scec.org/NARS-Baja/,Mexico, +NS,Norwegian National Seismic Network,University of Bergen (UiB Norway) Inst of Geophysics,NO (Norway),http://www.geo.uib.no/seismo/nnsn/,Norway, +NT,USGS Geomagnetism Program,USGS Golden Colorado,US (United States),http://geomag.usgs.gov/,United States, +NU,Nicaraguan Seismic Network,INETER (Instituto Nicarag¿ense de Estudios Territoriales),NI (Nicaragua),,Nicaragua, +NV,Neptune Canada,Geological Survey of Canada,CA (Canada),http://www.neptunecanada.com/,Canada, +NW,Northwestern University Seismic Network,Northwestern University,US (United States),http://www.earth.northwestern.edu/,United States, +NX,Nanometrics Research Network,Nanometrics Seismological Instruments,CA (Canada),,Canada,doi:10.7914/SN/NX +NY,Yukon-Northwest Seismic Network (YNSN),University of Ottawa (uOttawa Canada),CA (Canada),,Canada, +NZ,New Zealand National Seismograph Network,Institute of Geological & Nuclear Sciences Ltd (GNS New Zealand),NZ (New Zealand),http://info.geonet.org.nz/display/equip/New+Zealand+National+Seismograph+Network,New Zealand, +OB,"Osservatorio Sismico ""A. Bina""","Osservatorio Sismico ""A. Bina"", Perugia",IT (Italy),,Italy, +OE,Austrian Seismic Network,ZAMG - Central Institute for Meteorology and Geodynamics,AT (Austria),,Austria, +OG,Sismalp,"Observatoire de Grenoble, Grenoble",FR (France),,France, +OH,Ohio Seismic Network,Ohio Geological Survey,US (United States),,United States, +OK,Oklahoma Seismic Network,University of Oklahoma (OU Oklahoma USA),US (United States),http://www.okgeosurvey1.gov/pages/seismic-stations.php,United States, +OL,Cornegliano Laudense Seismic Network,Istituto Nazionale di Oceanografia e di Geofisica Sperimentale,IT (Italy),,Italy,doi:10.7914/SN/OL +OM,Earthquake Monitoring Program of Oman,Sultan Qaboos University,OM (Oman),http://www.squ.edu.om/emc,Oman, +ON,Rede Sismográfica do Sul e do Sudeste,"Observatório Nacional, Rio de Janeiro, RJ",BR (Brazil),http://www.rsis1.on.br/,Brazil,doi:10.7914/SN/ON +OO,Ocean Observatories Initiative,Rutgers University,US (United States),http://www.oceanobservatories.org/,Juan de Fuca Plate,doi:10.7914/SN/OO +OS,"Seismological Observatory, Brasilia Brazil",University of Brasilia,BR (Brazil),http://www.obsis.unb.br/,Brazil, +OT,OTRIONS,"University of Bari ""Aldo Moro""",IT (Italy),http://www.otrions.uniba.it,Italy,doi:10.7914/SN/OT +OV,Observatorio Vulcanológico y Sismológico de Costa Rica,Universidad Nacional Costa Rica (UNA Heredia OVISCORI Costa Rica) ,CR (Costa Rica),http://www.ovsicori.una.ac.cr/,Costa Rica, +OX,North-East Italy Seismic Network,OGS (Istituto Nazionale di Oceanografia e di Geofisica Sperimentale),IT (Italy),http://rts.crs.inogs.it/,Italy,doi:10.7914/SN/OX +OZ,SRC eqServer,Seismology Research Centre,AU (Australia),,Australia, +PA,ChiriNet,Observatorio Sismico del Occidente de Panamá (OSOP),PA (Panama),,Panama, +PB,Plate Boundary Observatory Borehole Seismic Network,UNAVCO,US (United States),,United States, +PC,Panama Canal Network,Autoridad del Canal de Panama (Panama Canal Authority),PA (Panama),,Panama, +PD,Polish Seismic Monitoring Network,Polish Academy of Sciences (PAN) Polskiej Akademii Nauk,PL (Poland),http://www.igf.edu.pl/en/monitoring_geofizyczny/monitoring_sejsmiczny_polski,Poland, +PE,Penn State Network,Penn State University,US (United States),http://eqseis.geosc.psu.edu/,United States, +PF,Piton de la Fournaise Volcano Observatory Network (Reunion Island),Institut de Physique du Globe de Paris,FR (France),http://www.ipgp.fr/ovpf,Réunion, +PG,"Central Coast Seismic Network, PG&E",Pacific Gas and Electric (PG&E),US (United States),http://ncedc.org/pge/,United States, +PH,Puna Geothermal Network,Duke University,US (United States),,United States, +PI,PASSCAL Pier Test Data,IRIS/PASSCAL, (),,, +PK,Micro Seismic Monitoring System of Northern Areas of Pakistan,Water and Power Development Authority (WAPDA),PK (Pakistan),,Pakistan, +PL,Polish Seismological Network,Polish Academy of Sciences (PAN) Polskiej Akademii Nauk,PL (Poland),http://www.igf.edu.pl/pl/zaklady_naukowe/sejsmologii_i_fizyki_wnetrza_ziemi/stacje_i_obserwatoria_sejsmologiczne,Poland, +PM,Portuguese National Seismic Network,"Instituto Português do Mar e da Atmosfera, I.P.",PT (Portugal),http://www.meteo.pt/en/sismologia/redes/,Portugal, +PN,PEPP-Indiana,Indiana University Bloomington (IU Bloomington),US (United States),http://www.indiana.edu/~pepp/,United States, +PO,Portable Observatories for Lithospheric Analysis and Research Investigating Seismicity,Geological Survey of Canada,CA (Canada),http://www.polarisnet.ca/network/polaris-network-locations.html,Canada, +PP,Princeton Earth Physics Program,Princeton University,US (United States),,United States, +PR,Puerto Rico Seismic Network (PRSN) & Puerto Rico Strong Motion Program (PRSMP),University of Puerto Rico/Mayaguez Campus (UPRM),PR (Puerto Rico),http://redsismica.uprm.edu/,Puerto Rico, +PS,Pacific21 ,"University of Tokyo, Earthquake Research Institute (Todai, ERI, Japan)",JP (Japan),,Japan, +PT,Pacific Tsunami Warning Seismic System,Pacific Tsunami Warning Center,US (United States),,United States, +PU,INGEMMET,"Instituto Geologico Minero Y Metalurgico, Lima",PE (Peru),,Peru, +PV,Philippine Seismic Network,Philippine Institution of Volcanology and Seismology,PH (Philippines),,Philippines, +PW,Pacific Northwest Geodetic Array,Central Washington University,US (United States),http://www.panga.cwu.edu/,United States,doi:10.7914/SN/PW +PX,Parkfield Experiment Array,Duke University,US (United States),,United States, +PY,Pinyon Flats Observatory (PFO) Array,UC San Diego,US (United States),,United States, +PZ,Prince Islands Real-time Earthquake monitoring System (PIRES),"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Turkey, +QA,Qatar Seismic Network,Qatar Civil Aviation Authority,QA (Qatar),http://www.caa.gov.qa/en/meteorology,Qatar, +QC,Quake Catcher Network,Stanford University,US (United States),http://qcn.stanford.edu/,United States, +RA,Réseau Accélérométrique Permanent (French Accelerometrique Network),Universite Joseph Fourier - Grenoble (LGIT-UJF-France),FR (France),http://www-rap.obs.ujf-grenoble.fr/,France, +RB,Red Sísmica de Banda Ancha (RESBAN),"Centro de Investigación Científica y de Educación Superior de Ensenada (CICESE), Ensenada",MX (Mexico),http://sismologia.cicese.mx/Redes/Resban.html,Mexico, +RC,BYU-Idaho Network,Brigham Young Univ-Idaho (BYU Idaho),US (United States),,United States,doi:10.7914/SN/RC +RD,CEA/DASE Seismic Network,Departement d'Analyse et Surveillance de l'Environnement du CEA,PF (French Polynesia),http://www-dase.cea.fr/public/dossiers_thematiques/alerte_aux_forts_seismes/description_en.html,French Polynesia, +RE,US Bureau of Reclamation Seismic Networks,U.S. State Department,US (United States),,United States, +RF,Friuli Venezia Giulia Accelerometric Network,University of Trieste,IT (Italy),http://rtweb.units.it/,Italy,doi:10.7914/SN/RF +RG,Upper-Rhine Graben Geothermal Network,ES-Géothermie,FR (France),,France,doi:10.7914/SN/RG +RI,National Seismic Network of Argentina,Instituto Nacional de Prevención Sísmica (INPRES),AR (Argentina),,Argentina, +RJ,Rede Sísmica e Geomagnética do Rio de Janeiro,"Observatório Nacional, Rio de Janeiro, RJ",BR (Brazil),,Brazil, +RM,Regional Integrated Multi-Hazard Early Warning System,Regional Integrated Multi-Hazard Early Warning System (RIMES Thailand),TH (Thailand),http://www.rimes.int/,Asia,doi:10.7914/SN/RM +RN,RuhrNet,Ruhr Universitaet Bochum (RUB Germany),DE (Germany),http://www.gmg.ruhr-uni-bochum.de/geophysik/seismology/research/,Germany, +RO,Romanian Seismic Network,National Institute for Earth Physics (NIEP Romania),RO (Romania),http://www.infp.ro/retea-seismica-nationala/,Romania,doi:10.7914/SN/RO +RP,Universidad de Panama Seismic Network,"Instituto de Geociencias, Universidad de Panamá",PA (Panama),,Panama, +RS,Regional Seismic Test Network,USGS National Earthquake Information Center (NEIC),US (United States),,North America,doi:10.7914/SN/RS +RT,Kamchatka Tsunami Warning Sesimic Network,Geophysical Survey Kamchatkan EMSD (KEMSD GS RAS - Russia),RU (Russian Federation),,Russian Federation, +RV,Regional Alberta Seismic Observatory for Earthquake Studies Network,Alberta Geological Survey (AGS Canada),CA (Canada),http://ags.gov.ab.ca/geohazards/earthquakes_project.html,"Alberta, Canada",doi:10.7914/SN/RV +RY,Yakutain Branch of Russian Seismic Network,"Yakutian Branch, Geological Survey, Siberian Branch, Russian Academy of Sciences",RU (Russian Federation),,Russian Federation, +S,Seismographs in Schools Network,Swiss Seismological Service,CH (Switzerland),http://www.iris.edu/hq/ssn/schools,Global, +SA,Saudi Arabia Broadband Seismic Network,"Institute for Astronomical and Geophysical Research, Saudi Arabia (KACST)",SA (Saudi Arabia),,Saudi Arabia, +SB,UC Santa Barbara Engineering Seismology Network,UC Santa Barbara,US (United States),http://www.nees.ucsb.edu/,United States, +SC,New Mexico Tech Seismic Network,"New Mexico Tech (NMT Mining & Technology NM,USA)",US (United States),http://www.ees.nmt.edu/outside/Geop/NMTSO.html,United States, +SE,Southeastern Appalachian Cooperative Seismic Network, None, ( ),, , +SF,San Andreas Fault Observatory at Depth,Stanford University,US (United States),http://www.earthscope.org/observatories/safod,United States, +SG,Global Geodynamics Project,Multiple Operators,BE (Belgium),http://www.eas.slu.edu/GGP/ggphome.html,Global,doi:10.7914/SN/SG +SH,Shumagin-East Aleutian Network,"Lamont Doherty Earth Observatory (LDEO), Columbia University",US (United States),,"Alaska, United States",doi:10.7914/SN/SH +SI,Province Südtirol,ZAMG - Central Institute for Meteorology and Geodynamics,AT (Austria),,Austria, +SJ,Serbian Seismological Network,Seismological Survey of Serbia,RS (Serbia),,Serbia, +SK,Slovak National Seismic Network,"Geophysical Institute, Slovak Academy of Sciences",SK (Slovakia),http://www.seismology.sk/National_Network/national_network_A.html,Slovakia, +SL,Slovenia Seismic Network,Geological Survey of Slovenia (GEoZS Slovenia),SI (Slovenia),http://www.geo-zs.si/podrocje.aspx?id=0,Slovenia, +SM,Icelandic Strong Motion Network,University of Iceland,IS (Iceland),http://www.isesd.hi.is/,Iceland, +SN,Southern Great Basin Network,University of Nevada (UNR Reno),US (United States),http://www.seismo.unr.edu,United States,doi:10.7914/SN/SN +SP,South Carolina Earth Physics Project,University of South Carolina,US (United States),http://www.seis.sc.edu/scepp/,United States,doi:10.7914/SN/SP +SR,Seismic Research Observatory,Albuquerque Seismological Laboratory (ASL)/USGS,US (United States),,Global,doi:10.7914/SN/SR +SS,SINGLE STATION, None, ( ),, , +ST,Trentino Seismic Network,Geological Survey-Provincia Autonoma di Trento,IT (Italy),http://www.protezionecivile.tn.it,Italy,doi:10.7914/SN/ST +SU,Sandia Seismo-Acoustic Network,Sandia National Laboratories,US (United States),,United States, +SV,"Servicio Nacional de Estudios Territoriales (SNET), El Salvador",Servicio Nacional de Estudios Territoriales (SNET El Salvador),SV (El Salvador),http://www.snet.gob.sv/ver/sismologia/,El Salvador, +SX,SXNET Saxon Seismic Network,University of Leipzig,DE (Germany),,Germany, +SY,Synthetic Seismograms,IRIS DMC (Seattle),US (United States),,United States, +TA,USArray Transportable Array,IRIS Transportable Array,US (United States),http://www.earthscope.org/observatories/usarray,United States,doi:10.7914/SN/TA +TC,Red Sismológica Nacional de Costa Rica,Universidad de Costa Rica,CR (Costa Rica),http://www.rsn.ucr.ac.cr/,Costa Rica,doi:10.15517/TC +TD,TransAlta Monitoring Network,TransAlta Corporation,CA (Canada),http://www.transalta.com/,Canada, +TE,EGAT Dams Seismic Monitoring System,Electricity Generating Authority of Thailand,TH (Thailand),,Thailand,doi:10.7914/SN/TE +TG,Royal Irrigation Department,Royal Irrigation Department,TH (Thailand),,Thailand,doi:10.7914/SN/TG +TH,Thüringer Seismisches Netz (TSN),"Institut fuer Geowissenschaften, Friedrich-Schiller-Universitaet Jena",DE (Germany),http://www.geophysik.uni-jena.de/Einrichtungen+_+Labore/Thüringer+Seismisches+Netz_TSN.html,Germany, +TI,Centro Sismologico de America Central,Universidad de Costa Rica,CR (Costa Rica),,Costa Rica, +TJ,Tajikistan National Seismic Network,PMP International (Tajikistan),TJ (Tajikistan),,Tajikistan,doi:10.7914/SN/TJ +TK,National Strong-Motion Network of Turkey (TR-NSMN),"Prime Ministry Disaster & Emergency Managment Presidency Earthquake Department (AFAD), Ankara",TR (Turkey),,Turkey, +TL,Armutlu Local Seismic Network,Kocaeli University,TR (Turkey),http://www.kocaeli.edu.tr/yubam/,Turkey, +TM,Thai Seismic Monitoring Network,Thailand Seismological Bureau,TH (Thailand),,Thailand, +TN,Eastern Tennessee Seismic Network,Joint Institute for Energy and the Evnironment,US (United States),,United States, +TO,"Tectonic Observatory -MASE, VEOX , PeruSE, CCSE",California Institute of Technology (Caltech),US (United States),http://web.gps.caltech.edu/~clay/MexWeb/MexSubduction.html,Mexico, +TR,Eastern Caribbean Seismograph Network,University of the West Indies (Seismic Research Centre Trinidad),TT (Trinidad and Tobago),,Eastern Caribbean, +TS,TERRAscope (Southern California Seismic Network),California Institute of Technology (Caltech),US (United States),,United States, +TT,Seismic Network of Tunisia,"Institut National de la Météorologie, Tunis",TN (Tunisia),,Tunisia, +TU,National Seismic Network of Turkey (DDA),Disaster and Emergency Management Presidency (AFAD Turkey),TR (Turkey),,Turkey, +TV,INGV experiments network,INGV Instituto Nazionale di Geofisica e Vulcanologia (Italy),IT (Italy),,Italy, +TW,Broadband Array in Taiwan for Seismology,"Academia Sinica, Institute of Earth Sciences",TW (Taiwan),http://bats.earth.sinica.edu.tw/,Taiwan,doi:10.7914/SN/TW +TX,Texas Seismological Network,"Bureau of Economic Geology, The University of Texas at Austin",US (United States),,"United States, Texas",doi:10.7914/SN/TX +UA,Caribbean Andesite Lava Island Precision Seismo-geodetic Observatory,University of Arkansas - Fayetteville,US (United States),,Montserrat, +UC,Colima Seismic Network,Universidad de Colima (Mexico),MX (Mexico),http://www.ucol.mx/volcan/monitoreo.php,Mexico, +UE,Seismological Network of United Arab Emirates,National Center of Meteorology and Seismology,AE (United Arab Emirates),,United Arab Emirates, +UG,Unocal Geothermal Northern California Network,Unocal Corporation,US (United States),http://www.ncedc.org/geysers/,United States, +UH,UH Infrasound Network,University of Hawaii (UH),US (United States),http://www.isla.hawaii.edu/,United States, +UI,North Idaho Seismic Network,University of Idaho,US (United States),,United States, +UK,UK Schools Seismic Network,University of Leicester (SEIS UK),GB (United Kingdom),http://www.le.ac.uk/seis-uk/school_resources.html,United Kingdom, +UL,USGS Low Frequency Geophysical Data Network,USGS Menlo Park,US (United States),http://quake.geo.berkeley.edu/ul/ul.overview.html,United States, +UO,University of Oregon Regional Network,University of Oregon,US (United States),,United States, +UP,University of Uppsala Network,University of Uppsala,SE (Sweden),,Sweden, +UR,UKArray,British Geological Survey,GB (United Kingdom),,United Kingdom,doi:10.7914/SN/UR +US,United States National Seismic Network,USGS National Earthquake Information Center (NEIC),US (United States),,United States,doi:10.7914/SN/US +UU,University of Utah Regional Seismic Network,University of Utah,US (United States),http://www.seis.utah.edu/,United States,doi:10.7914/SN/UU +UW,Pacific Northwest Regional Seismic Network,University of Washington,US (United States),http://www.pnsn.org/,United States, +UY,Observatorio Geofisico de Uruguay,Universidad de la Republica,UY (Uruguay),http://geotectonica.fcien.edu.uy/oga,Uruguay, +UZ,Uzbekistan Digital Seismic Network,"Uzbekistan Institute of Seismology, Tashkent",UZ (Uzbekistan),,Uzbekistan, +VC,Red Nacional de Vigilancia Volcánica Chile (VC),Servicio Nacional de Geología y Minería,CL (Chile),http://www.sernageomin.cl/,Chile, +VE,Red Sismologica Nacional con Apertura Continental,"Fundación Venezolana de Investigaciones Sismológicas (FUNVISIS), Caracas",VE (Venezuela),,Venezuela, +VG,Seismic Network of the Indonesian Center for Volcanology and Geological Hazard Mitigation,Pusat Vulkanologi dan Mitigasi Bencana Geologi,ID (Indonesia),,Indonesia,doi:10.7914/SN/VG +VI,Icelandic National Digital Seismograph Network,Icelandic Meteorological Office,IS (Iceland),http://en.vedur.is/earthquakes-and-volcanism/earthquakes/,Iceland, +VU,Vanuatu Seismic Network,Vanuatu Geoscience Observatory,VU (Vanuatu),,Vanuatu, +WA,West Central Argentina Network,"Universidad Nacional de San Juan (UNSJ, Argentina)",AR (Argentina),,Argentina, +WB,West Bohemia Local Seismic Network,"Institute of Geophysics, Academy of Sciences of the Czech Republic",CZ (Czech Republic),http://www.ig.cas.cz/struktura/oddeleni/seismika/zapadoceska-zemetreseni,Czech Republic,doi:10.7914/SN/WB +WC,Curacao Seismic Network,Meteorologische Dienst Curacao,CW (Curaçao),,Curacao, +WI,West Indies French Seismic Network,Institut de Physique du Globe de Paris,FR (France),,West Indies, +WK,Wake Island Hydrophone Array,University of Hawaii (UH),US (United States),,United States, +WM,Western Mediterranean Seismic Network,Real Instituto y Observatorio de la Armada (Spain),ES (Spain),,Spain, +WR,California Division of Water Resources,California Division of Water Resources,US (United States),,United States, +WS,Seismic Network of Repuplika Srpska,Republicki Hidrometeorološki Zavod,RS (Serbia),,Serbia, +WU,The Southern Ontario Seismic Network,University of Western Ontario (UWO Canada),CA (Canada),http://www.gp.uwo.ca/docs/stationmap.html,Canada, +WY,Yellowstone Wyoming Seismic Network,University of Utah,US (United States),,United States,doi:10.7914/SN/WY +X1,"Development and Testing of a Deep-Water, Acoustically-Linked, Moored- Buoy Seafloor Observatory",Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +X1,Aysen Chile Aftershock Deployment,University of Liverpool,GB (United Kingdom),,United Kingdom, +X1,Carbon Sequestration Technical Assessemnt-Pennsylvania,Penn State University,US (United States),,United States, +X1,Byrd Lake Reflection Seismics - Cresis,Penn State University,US (United States),,United States,doi:10.7914/SN/X1_2011 +X1,Réunion Hotspot and Upper Mantle Project,University of Frankfurt,DE (Germany),,Germany, +X2,NCISP4,"Institute of Geology and Geophysics, CAS",CN (China),http://english.igg.cas.cn,China,doi:10.7914/SN/X2_2005 +X2,Martinique 2007 Aftershock Experiment,"Observatoire de Grenoble, Grenoble",FR (France),,France, +X2,Wellington Geophysical Transect (aka SAHKE),Victoria University of Wellington (VUW New Zealand),NZ (New Zealand),,New Zealand,doi:10.7914/SN/X2_2009 +X2,Haida Gwaii OBS Experiment,Natural Resources Canada (NRCAN Canada),CA (Canada),,Canada, +X2,Sunset Seismic Experiment,Carnegie Institute of Washington / Science (DTM CIW),US (United States),,United States, +X3,"Understanding the current thinning and accelerration of the Jakobshavns Isbrae, Greenland","University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States,doi:10.7914/SN/X3_2007 +X3,MARINER: Seismic Investigation of the Rainbow Hydrothermal Field,University of Hawaii (UH),US (United States),,United States,doi:10.7914/SN/X3_2013 +X4,Deep Structure of the Northeastern Tibet Collision Zone: INDEPTH IV,Missouri University of Science & Technology(Former UM-Rolla)(Missouri S&T USA),US (United States),,United States,doi:10.7914/SN/X4_2007 +X4,Gulf of Mexico Basin Opening,Texas Tech University,US (United States),,United States, +X5,Hudson Bay Lithospheric Experiment,University of Bristol (UK),GB (United Kingdom),,United Kingdom, +X5,IGC Geothermal Test Site,Ruhr Universitaet Bochum (RUB Germany),DE (Germany),,Germany, +X6,SLIP - Seismic Lithospheric Imaging of the Puna Plateau,University of Missouri (MU) (Mizzou),US (United States),,United States,doi:10.7914/SN/X6_2007 +X6,MonaSeis,Institute for Geosciences - Goethe University,DE (Germany),,Germany, +X6,Evolution and hydration of the Juan de Fuca crust and uppermost mantle,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States,doi:10.7914/SN/X6_2012 +X6,Collaborative Research: Unlocking the Seismic Signature of Rivers,UC Santa Cruz,US (United States),,United States,doi:10.7914/SN/X6_2013 +X6,"Homestake Gold Mine Three-dimensional, Broadband Array",Indiana University Bloomington (IU Bloomington),US (United States),,United States,doi:10.7914/SN/X6_2014 +X7,Seismic Monitoring of Active Rock Fall Source Areas in the Yosemite National Park,UC Berkeley,US (United States),,United States,doi:10.7914/SN/X7_2007 +X7,Pyrope,Institut des Sciences de la Terre,FR (France),,France, +X8,Studies of Earthquakes & Rapid Motions of Ice,"Lamont Doherty Earth Observatory (LDEO), Columbia University",US (United States),,United States, +X8,Deep Structure of Three Continental Sutures in Eastern North America,Rutgers University,US (United States),,United States,doi:10.7914/SN/X8_2012 +X9,"Monitoring an active rift system at the front of the Amery Ice Shelf, East Antarctica",University of Chicago,US (United States),,United States,doi:10.7914/SN/X9_2004 +X9,Dallas Earthquake Swarm,Southern Methodist University (SMU TX),US (United States),,United States,doi:10.7914/SN/X9_2008 +X9,Plate Boundary Evolution and Physics at an Oceanic Transform Fault System,Oregon State University (OSU USA),US (United States),,United States, +XA,Apollo Lunar Seismic,NASA Goddard Space Flight Center (GSFC),US (United States),,United States, +XA,"PANDA -- San Juan, Argentina Experiment","University of Memphis, CERI",US (United States),,United States, +XA,Pinyon Flat High Frequency Array,IRIS/PASSCAL, (),,, +XA,Pinyon Flat Broadband Triggered Array,IRIS/PASSCAL, (),,, +XA,Joshua Tree Aftershocks,IRIS/PASSCAL, (),,, +XA,Non Proliferation Experiment (Chemical Kiloton at NTS),IRIS/PASSCAL, (),,, +XA,Northridge Aftershocks,IRIS/PASSCAL, (),,, +XA,Missouri to Massachusetts Transect,IRIS/PASSCAL, (),,, +XA,"Anatomy of an Archean Craton, South Africa",IRIS/PASSCAL, (),,, +XA,LIGO Noise Study,IRIS/PASSCAL, (),,,doi:10.7914/SN/XA_2000 +XA,Bhutan Pilot Experiment,IRIS/PASSCAL, (),,,doi:10.7914/SN/XA_2002 +XA,Collaborative Research: Mechanics of Dry-Land Calving of Ice Glaciers,IRIS/PASSCAL, (),,,doi:10.7914/SN/XA_2004 +XA,Monitgoring seismicity associated with a possible asperity on the Cascadia megathrust,Oregon State University (OSU USA),US (United States),,United States,doi:10.7914/SN/XA_2008 +XA,Using Fault-Zone Trapped Waves to Document Subsuface Rock Damage Caused by the M6.3 Earthquake in Ne,USC - University of Southern California,US (United States),,United States,doi:10.7914/SN/XA_2011 +XA,GANSSER: Swiss BB seismic experiment in Bhutan,Swiss Seismological Service,CH (Switzerland),,Switzerland, +XB,Viking Mission,Institut de Physique du Globe de Paris,FR (France),,France, +XB,UC Mammoth/Long Valley network, None, ( ),, , +XB,Landers Aftershocks,IRIS/PASSCAL, (),,, +XB,Southwest Pacific Seismic Experiment,IRIS/PASSCAL, (),,, +XB,Abitibi Experiment,IRIS/PASSCAL, (),,, +XB,Seismic Experiment in Patagonia and Antarctica,IRIS/PASSCAL, (),,, +XB,Array studies of anisotropy and converted phases in the Marlborough Fault Zone of New Zealand,IRIS/PASSCAL, (),,,doi:10.7914/SN/XB_2000 +XB,Magdalena Ridge Observatory site noise study,IRIS/PASSCAL, (),,,doi:10.7914/SN/XB_2003 +XB,Broadband Seismic Investigation of the Cameroon Volcanic Line,IRIS/PASSCAL, (),,,doi:10.7914/SN/XB_2005 +XB,Costa Rica Active Source OBS Experiment,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +XB,Program to Investigate Convective Alboran Sea System Overturn,University of Oregon,US (United States),,United States,doi:10.7914/SN/XB_2009 +XB,Sweetwater Array,IRIS PASSCAL Instrument Center @ New Mexico Tech,US (United States),,United States,doi:10.7914/SN/XB_2014 +XB,INSIGHT,Institut de Physique du Globe de Paris,FR (France),,France, +XC,RESERVED,IRIS/PASSCAL, (),,, +XC,Tibetan Plateau Broadband Experiment,IRIS/PASSCAL, (),,, +XC,PANDA -- Hawaii Experiment,"University of Memphis, CERI",US (United States),,United States, +XC,Puget Sound BB Array,IRIS/PASSCAL, (),,, +XC,Southern Alps Passive Seismic Experiment,IRIS/PASSCAL, (),,, +XC,Namibia,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +XC,Geodynamics of the Yellowstone Hotspot from Seismic and GPS imaging,IRIS/PASSCAL, (),,,doi:10.7914/SN/XC_2000 +XC,Geodynamics of Indentor Corners,IRIS/PASSCAL, (),,,doi:10.7914/SN/XC_2002 +XC,Geodynamics of the Tibetan Plateau and the Origin of Great Earthquakes in Western China,Albuquerque Seismological Laboratory (ASL)/USGS,US (United States),,United States, +XC,Collaborative Research: Understanding the causes of continental intraplate tectonomagmatism: A case study in the Pacific Northwest,Arizona State University,US (United States),,United States,doi:10.7914/SN/XC_2006 +XC,Pacaya Tremor,"Michigan Technological University (MTU, Michigan USA)",US (United States),,United States,doi:10.7914/SN/XC_2011 +XC,Observing Pine Island Glacier (PIG) ice shelf deformation and fracture using a GPS and Seismic Network,New York University (NYU USA),US (United States),,United States,doi:10.7914/SN/XC_2012 +XC,"Pantanal, Chaco and Paraná (PCPB) structural studies network","IAG-USP (Institute of Astronomy, Geophysics & Atmospheric Science -Univ. of Sao Paulo)",BR (Brazil),http://www.moho.iag.usp.br/,Brazil, +XD,"Tararua BB Array, New Zealand", None, ( ),, , +XD,Tanzania BB Experiment,IRIS/PASSCAL, (),,, +XD,Iceland Hotspot,IRIS/PASSCAL, (),,, +XD,"Woodlark - D_Entrecasteaux Rift, PNG",IRIS/PASSCAL, (),,, +XD,"Broadband Seismic Studies of Dome Growth at Merapi Volcano, Indonesia",IRIS/PASSCAL, (),,,doi:10.7914/SN/XD_2001 +XD,Broadband Seismic Experiment in Various Regions in China,IRIS/PASSCAL, (),,,doi:10.7914/SN/XD_2002 +XD,Development of A Power and Communication for Remote Autonomous GPS and Seismic Stations in Antarctica,IRIS/PASSCAL, (),,,doi:10.7914/SN/XD_2007 +XD,Passive seismic study of a magma-dominated rift: the Salton Trough,Stanford University,US (United States),,United States,doi:10.7914/SN/XD_2011 +XD,Collaborative Research: Illuminating the architecture of the greater Mount St. Helens magmatic systems from slab to surface,University of Washington,US (United States),,United States,doi:10.7914/SN/XD_2014 +XE,PANDA -- New Madrid Experiment,"University of Memphis, CERI",US (United States),,United States, +XE,BBand Andean Joint Exp. / Seismic Exploration of Deep Andes,IRIS/PASSCAL, (),,, +XE,Askja/Bardardalur Iceland ,IRIS/PASSCAL, (),,, +XE,Eifel Plume,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +XE,Broadband Experiment Across Alaskan Range,IRIS/PASSCAL, (),,, +XE,Torfajokull 2002,Nordic Volcanological Institute (NORDVULK Iceland),IS (Iceland),,Iceland, +XE,Geodynamics of Indentor Corners,IRIS/PASSCAL, (),,,doi:10.7914/SN/XE_2003 +XE,Sierra Nevada EarthScope Project,EarthScope Transportable Array, (),,,doi:10.7914/SN/XE_2005 +XE,TICO-CAVA,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +XE,Sierra Negra Array Galapagos,University of Rochester,US (United States),,United States,doi:10.7914/SN/XE_2009 +XE,"Seismic Activity of Low Angle Normal Faults in Death Valley, Caifornia.",University of Washington,US (United States),,United States,doi:10.7914/SN/XE_2012 +XE,Study of the Socorro Magma Body,University of Nevada (UNR Reno),US (United States),,United States,doi:10.7914/SN/XE_2015 +XF,Anza Borehole Experiment,IRIS/PASSCAL, (),,, +XF,"Penninsular Ranges, California",IRIS/PASSCAL, (),,, +XF,Antarctic Microearthquake Project,IRIS/PASSCAL, (),,, +XF,GEDEPTH-II,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +XF,Laramie Telemetered Broad-band Array,IRIS/PASSCAL, (),,, +XF,Collaborative Research: Lithospheric Scale Dynamics of Active Mountain Building along the Himalayan-Tibetan Collision Zone,IRIS/PASSCAL, (),,,doi:10.7914/SN/XF_2002 +XF,Mapping the Rivera Subduction Zone,IRIS/PASSCAL, (),,,doi:10.7914/SN/XF_2006 +XF,Red Seismological Experiment,"Universidad de Chile, Dept de Geofisica (DGF UChile Chile)",CL (Chile),,Chile, +XF,"Collaborative Research: Relating glacier-generated seismicity to ice motion, basal processes and iceberg calving.","University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States,doi:10.7914/SN/XF_2009 +XF,Mantle serpentinization and water cycling through the Mariana Trench and Forearc,Washington University in St. Louis (WUSTL),US (United States),,United States,doi:10.7914/SN/XF_2012 +XF,Understanding Precambrian to Present Assembly of Greenland,Columbia University,US (United States),,United States,doi:10.7914/SN/XF_2014 +XG,Rocky Mountain Front,IRIS/PASSCAL, (),,, +XG,Anisotropy AI,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +XG,Nanga Parbat Pakistan ,IRIS/PASSCAL, (),,, +XG,Amazon,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +XG,Eastern Turkey Seismic Experiment,IRIS/PASSCAL, (),,, +XG,A Comparative Study of Natural and Man-Induced Seismicity in the Yanquing-Huailai Basin and the Haicheng Area,IRIS/PASSCAL, (),,,doi:10.7914/SN/XG_2002 +XG,Cascadia Array of Arrays,University of Washington,US (United States),,United States,doi:10.7914/SN/XG_2009 +XG,Underground Array,University of Minnesota-Twin Cities,US (United States),,United States, +XG,Barcelonnette 2014 aftershock sequence,"Sismos a l'Ecole, Geosciences Azur",FR (France),,France, +XG,Colorado River Runoff Pilot Project,"New Mexico Tech (NMT Mining & Technology NM,USA)",US (United States),,United States,doi:10.7914/SN/XG_2015 +XH,Colorado Plateau-Great Basin Network ,IRIS/PASSCAL, (),,, +XH,Bolivian Experiment,IRIS/PASSCAL, (),,, +XH,TransAlp-1,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +XH,ISSA2000,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +XH,START,IRIS/PASSCAL, (),,, +XH,Integrated Seismological Studies of Crust and Upper Mantle Structure in Western Anatolia,IRIS/PASSCAL, (),,,doi:10.7914/SN/XH_2002 +XH,Seismic Array Studies of Cascadia Deep Tremor,IRIS/PASSCAL, (),,,doi:10.7914/SN/XH_2004 +XH,Cascade Tiltmeter Array,Central Washington University,US (United States),,United States, +XH,Hawaiian South Flank Slow EQ Project,"University of Wisconsin, Madison",US (United States),,United States,doi:10.7914/SN/XH_2007 +XH,Lithospheric Structure above the variably dipping Nazca Slab,Purdue University,US (United States),,United States,doi:10.7914/SN/XH_2008 +XH,Seismic Survey of Greeland Icesheet Margins,"ETHZ (Swiss Federal Institute of Technology, Zurich Switzerland)",CH (Switzerland),,Switzerland, +XH,Small aperture array for observing a slow slip event on Kilauea volcano.,"University of Wisconsin, Madison",US (United States),,United States,doi:10.7914/SN/XH_2012 +XH,Collaborative Research: Mantle Structure and Dynamics of the Ross Sea from a Passive Seismic Deployment on the Ross Ice Shelf,Scripps Institution of Oceanography,US (United States),,United States,doi:10.7914/SN/XH_2014 +XI,GEDEPTH 1,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +XI,Saudi Arabia Broadband Array,IRIS/PASSCAL, (),,, +XI,NE China Seismic Experiment,IRIS/PASSCAL, (),,, +XI,Kenya Broadband Seismic Experiment,IRIS/PASSCAL, (),,,doi:10.7914/SN/XI_2000 +XI,TAG Hydrothermal Mound Passive Seismic Experiment,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +XI,3D Tomographic Imaging of the Chicxulub Crater,Imperial College London (Imperial UK),GB (United Kingdom),,United Kingdom, +XI,"Characterization of High Frequency Noise Generated at the South Pole Station, Antarctica",IRIS HQ (DC),US (United States),,United States,doi:10.7914/SN/XI_2006 +XI,Collision of the Burma Arc accretionary prism and foldbelt with the Ganges-Brahmaputra Delta in Bangladesh,"Lamont Doherty Earth Observatory (LDEO), Columbia University",US (United States),,United States,doi:10.7914/SN/XI_2007 +XI,Superior Province Rifting Earthscope Experiment,Northwestern University,US (United States),,United States,doi:10.7914/SN/XI_2011 +XI,Long Beach Broadband or LA Syncline Seismic Interferometry Experiment,NodalSeismic,US (United States),,United States,doi:10.7914/SN/XI_2014 +XI,Enid OK Network,Cornell University,US (United States),,United States, +XJ,RESERVED,IRIS/PASSCAL, (),,, +XJ,Adirondack Broadband Array,IRIS/PASSCAL, (),,, +XJ,Sierran Paradox ,IRIS/PASSCAL, (),,, +XJ,Side Edge of Kamchatka Subduction Zone,IRIS/PASSCAL, (),,, +XJ,Shear-wave Splitting in the Snake River Plain,IRIS/PASSCAL, (),,,doi:10.7914/SN/XJ_2000 +XJ,Ethiopian Afar Geophysical Lithospheric Experiment,"Royal Holloway, University of London",GB (United Kingdom),,United Kingdom, +XJ,Seismic Experiment in the Aisen Region of Chile,University of Cambridge (UK) Earth Sciences,GB (United Kingdom),,United Kingdom, +XJ,Tomo TEIDEVS,University of Liverpool,GB (United Kingdom),,United Kingdom, +XJ,"Wells, Nevada Aftershock Recording",University of Nevada (UNR Reno),US (United States),,United States,doi:10.7914/SN/XJ_2008 +XJ,L Aquila Aftershock Experiment,"Observatoire de Grenoble, Grenoble",FR (France),,France, +XJ,High-resolution imaging of magma transport at the summit of Kilauea Volcano,USGS Hawaiian Volcano Observatory (HVO),US (United States),,United States,doi:10.7914/SN/XJ_2010 +XJ,Gulf of Mexico Hydrates Research Consortium,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +XJ,Fuego Volcano VLP and tilt experiment,"Michigan Technological University (MTU, Michigan USA)",US (United States),,United States,doi:10.7914/SN/XJ_2012 +XJ,Magadi-Natron Magmatic Rifting Studies,University of Rochester,US (United States),,United States,doi:10.7914/SN/XJ_2013 +XJ,Impact of subglacial discharge on turbulent plume dynamics and ocean-glacier heat and mass transfer,University of Alaska Southeast (UAS),US (United States),,United States,doi:10.7914/SN/XJ_2016 +XK,RESERVED,IRIS/PASSCAL, (),,, +XK,"ICEMELT, Anarctica",IRIS/PASSCAL, (),,, +XK,"Chicxulub Impact Crater, Yucatan",IRIS/PASSCAL, (),,, +XK,Western Superior Teleseismic Experiment,IRIS/PASSCAL, (),,, +XK,Merapi and Semeru Volcano Project,IRIS/PASSCAL, (),,, +XK,CDROM (Colorado),IRIS/PASSCAL, (),,, +XK,Reflections Under the Scottish Highlands: A Broadband Investigation of Upper Mantle Reflectors in Scotland,IRIS/PASSCAL, (),,,doi:10.7914/SN/XK_2001 +XK,RISTRA 1.5 -- Colorado Plateau Extension,IRIS/PASSCAL, (),,,doi:10.7914/SN/XK_2004 +XK,POLENET/LAPNET,University of Oslo (Norway),NO (Norway),,Norway, +XK,Big Horn Project Regional Array Deployment,"Los Alamos National Laboratory (LANL, NM)",US (United States),,United States, +XK,"Passive seismic for geothermal exploration, Fairmont, Montana","Montana Bureau of Mines and Geology/Montana Tech (MBMG, MT USA)",US (United States),,United States,doi:10.7914/SN/XK_2011 +XK,"Passive seismic study of early rifting in Botswana, Zambia, and Malawi",Missouri University of Science & Technology(Former UM-Rolla)(Missouri S&T USA),US (United States),,United States,doi:10.7914/SN/XK_2012 +XK,Seismic Array of the Sevilleta,University of New Mexico (UNM),US (United States),,United States, +XL,Southern Seirra - Death Valley,IRIS/PASSCAL, (),,, +XL,North Korea Seismic Deployment,IRIS/PASSCAL, (),,, +XL,Deep Probe (Archean-Proterozoic),IRIS/PASSCAL, (),,, +XL,Santa Clara Valley,IRIS/PASSCAL, (),,, +XL,Oakland San Francisco Urban Seismic Array,IRIS/PASSCAL, (),,, +XL,COnsortium for Arizona Reconnaissance Seismic Experiment, None, ( ),, , +XL,Collaborative Research: Dynamic controls on tidewater glacier retreat,University of Colorado Boulder (CU),US (United States),,United States,doi:10.7914/SN/XL_2008 +XL,"Hangay Dome, Mongolia",Lehigh University,US (United States),,United States,doi:10.7914/SN/XL_2012 +XM,S. Island (N. Zealand) Pilot BB Network,IRIS/PASSCAL, (),,, +XM,Wabash Seismic Experiment,IRIS/PASSCAL, (),,, +XM,TOMOG 3D,IRIS/PASSCAL, (),,, +XM,Ocean Seismic Network Exp.,IRIS/PASSCAL, (),,,doi:10.7914/SN/XM_1998 +XM,Rio Grande Seismic Transect,IRIS/PASSCAL, (),http://www.iris.washington.edu/gmap/XM?timewindow=1999-2001,,doi:10.7914/SN/XM_1999 +XM,Ethiopia-Afar Geoscientific Lithospheric Experiment 6TD Passive Array,University of Leicester (SEIS UK),GB (United Kingdom),,United Kingdom, +XM,Vestmanna04 (SeiFaBa Project),University of Cambridge (UK) Earth Sciences,GB (United Kingdom),,United Kingdom, +XM,ALPASS,IRIS/PASSCAL, (),,, +XM,Western Tibet passive array deployment,IRIS/PASSCAL, (),,, +XM,Broadband recording at the site of great earthquake rupture in the Alaska Megathrust,University of Oklahoma (OU Oklahoma USA),US (United States),,United States,doi:10.7914/SN/XM_2011 +XM,ARGOS - Alutu and Regional Geophysical Observation Study SEIS-UK,University of Bristol (UK),GB (United Kingdom),,United Kingdom, +XM,Poroelastic Tomography at Brady Geothermal Field,"University of Wisconsin, Madison",US (United States),,United States,doi:10.7914/SN/XM_2016 +XN,TOR-0,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +XN,Biak Aftershock Survey,IRIS/PASSCAL, (),,, +XN,LARSE II - Passive Phase,IRIS/PASSCAL, (),,, +XN,Parkfield Passive Seismic Array,"University of Wisconsin, Madison",US (United States),,United States,doi:10.7914/SN/XN_2000 +XN,Collaborative Research: Canadian Northwest Seismic Experiment,IRIS/PASSCAL, (),,,doi:10.7914/SN/XN_2003 +XN,TaskForce_Java,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +XN,Bolivar: Western Venezuela,Rice University,US (United States),,United States,doi:10.7914/SN/XN_2008 +XN,Monitgoring seismicity associated with a possible asperity on the Cascadia megathrust,Oregon State University (OSU USA),US (United States),,United States,doi:10.7914/SN/XN_2010 +XN,joint summer field camp BSU/CSM/Imperial College of London,Boise State University,US (United States),,United States,doi:10.7914/SN/XN_2011 +XN,Dynamic Response of the Ross Ice Shelf to to Wave Induced Vibrations,Scripps Institution of Oceanography,US (United States),,United States,doi:10.7914/SN/XN_2014 +XN,Gorkha Earthquake 2015,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +XO,CINCA95,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +XO,ANCORP96-AN,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +XO,NOMAD,Brown University,US (United States),,United States,doi:10.7914/SN/XO_1997 +XO,Santa Clara Valley 2000,USGS Menlo Park,US (United States),,United States,doi:10.7914/SN/XO_2000 +XO,US-Japan Collaborative Research: Multi-scale seismic imaging of the Mariana Subduction Factory,IRIS/PASSCAL, (),,,doi:10.7914/SN/XO_2003 +XO,Eastern Black Sea Seismic Experiment,University of Southampton (UK),GB (United Kingdom),,United Kingdom, +XO,NOISY,Stanford University,US (United States),,United States, +XO,INDEPTH IV ,University of Cambridge (UK) Earth Sciences,GB (United Kingdom),,United Kingdom, +XO,"Deep Fault Drilling Project, Alpine Fault, New Zealand: Whataroa-Wanganui Passive Seismology Experiment 2010",Victoria University of Wellington (VUW New Zealand),NZ (New Zealand),,New Zealand,doi:10.7914/SN/XO_2010 +XO,Structure and Dynamics of the North American Craton,Indiana University Bloomington (IU Bloomington),US (United States),,United States,doi:10.7914/SN/XO_2011 +XP,"Taupo Volcanic Zone, New Zealand. Panda project",IRIS/PASSCAL, (),,, +XP,TaskForce Adana 1998,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +XP,A Broadband Seismic Investigation of Deep Continental Structure Across the East-West Antarctic Boundary,IRIS/PASSCAL, (),,,doi:10.7914/SN/XP_2000 +XP,Discrete vs. Continuous Continental Deformation and the Role of the Lower Crust in the Tien Shan,IRIS/PASSCAL, (),,,doi:10.7914/SN/XP_2005 +XP,Colorado Rockies Experiment and Seismic Transect,"New Mexico Tech (NMT Mining & Technology NM,USA)",US (United States),,United States,doi:10.7914/SN/XP_2008 +XP,Investigating the relationship between pluton growth and volcanism at two active intrusions in the central Andes,"University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States,doi:10.7914/SN/XP_2010 +XP,VocArray,"Observatoire de Grenoble, Grenoble",FR (France),,France, +XP,Seismic Experiment for Imaging Structure beneath Connecticut,Yale University,US (United States),,United States, +XQ,Green River Experiment,IRIS/PASSCAL, (),,, +XQ,Hikurangi Subduction System (New Zealand) Seismic Transects,IRIS/PASSCAL, (),,,doi:10.7914/SN/XQ_2001 +XQ,Oregon Array for Teleseismic Study,"University of Wisconsin, Madison",US (United States),,United States, +XQ,Collaborative Research: Understanding Fault Zone Compliance by Seismic Probing of InSAR Anomalies,IRIS/PASSCAL, (),,,doi:10.7914/SN/XQ_2006 +XQ,Seismic and Geodetic Investigations of Mendocino Triple Junction Dynamics,University of Oregon,US (United States),,United States,doi:10.7914/SN/XQ_2007 +XQ,Pismo BeachNet,Cuesta Community college,US (United States),,United States, +XQ,NEESR-CR: Topographic Effects in Strong Ground Motion - From Physical and Numerical Modeling to Design,University of Arkansas - Fayetteville,US (United States),,United States,doi:10.7914/SN/XQ_2011 +XQ,Pre-Hydrofracking Regional Assessment of Central Carolina Seismicity,University of North Carolina at Chapel Hill (UNC),US (United States),,United States, +XQ,"Rapid response to the Mw 7.9 earthquake of April 25, 2015 in Nepal",Oregon State University (OSU USA),US (United States),,United States, +XR,INDEPTH II,IRIS/PASSCAL, (),,, +XR,INDEPTH III,IRIS/PASSCAL, (),,, +XR,The Florida to Edmonton Broadband Experiment,IRIS/PASSCAL, (),,,doi:10.7914/SN/XR_2001 +XR,Mammoth 2003,IRIS/PASSCAL, (),,, +XR,CSEDI: Observational and Theoretical Constraints on the Structure and Rotation of the Inner Core,IRIS/PASSCAL, (),,,doi:10.7914/SN/XR_2004 +XR,Seismic Investigation of Edge Driven Convection Associated with the Rio Grande Rift.,University of Texas at Austin,US (United States),,United States,doi:10.7914/SN/XR_2008 +XR,Deployment of a simultaneous broadband gradiometers to quantify the effects of aperture and near-surface geology on gradient-der,East Carolina University (ECU),US (United States),,United States,doi:10.7914/SN/XR_2012 +XR,"Bowdoin glacier, NE Greenland","ETHZ (Swiss Federal Institute of Technology, Zurich Switzerland)",CH (Switzerland),http://www.vaw.ethz.ch/people/gz/funk/projects/data/gz_bowdoin,Switzerland, +XR,Seismicity near the Nemaha fault in northern Oklahoma,Cornell University,US (United States),,United States,doi:10.7914/SN/XR_2016 +XS,"Erebus, Antarctica",IRIS/PASSCAL, (),,, +XS,"Sangay Volcano, Ecuador",IRIS/PASSCAL, (),,, +XS,Montana BB Array,IRIS/PASSCAL, (),,, +XS,Embayment Seismic Excitation Experiment,"University of Memphis, CERI",US (United States),,United States,doi:10.7914/SN/XS_2002 +XS,Source Phenomenology Experiments in Arizona,IRIS/PASSCAL, (),,,doi:10.7914/SN/XS_2003 +XS,Tungurahua 2004,IRIS/PASSCAL, (),,, +XS,San Jose Microtremor 2005,IRIS/PASSCAL, (),,, +XS,Multi-disciplinary Experiments for Dynamic Understanding of Subduction under the Aegean Sea,"Massachusetts Institute of Technology (MIT, USA)",US (United States),,United States,doi:10.7914/SN/XS_2006 +XS,Maule Earthquake (Chile) Aftershock Experiment,"Observatoire de Grenoble, Grenoble",FR (France),,France, +XS,Geysers Broadband Network,ARRAY Information Technology (MD),US (United States),,United States, +XS,Passive Imaging of the Lithosphere-Asthenosphere Boundary,University of Southampton (UK),GB (United Kingdom),,United Kingdom, +XT,Micronesia Seismic Network,IRIS/PASSCAL, (),,, +XT,Colorado BB Array - Lodore,IRIS/PASSCAL, (),,, +XT,SIGAR (Kenya),IRIS/PASSCAL, (),,, +XT,Tonga-Fiji Dual Array Experiment,IRIS/PASSCAL, (),,,doi:10.7914/SN/XT_2001 +XT,Crust-Mantle Interactions during Continental Growth and High-Pressure Rock Exhumation at an Oblique Arc-Continent Collision Zone: SE Caribbean Margin,IRIS/PASSCAL, (),,,doi:10.7914/SN/XT_2003 +XT,Oregon Array for Teleseismic Study II,UC Berkeley,US (United States),,United States, +XT,Fuego Volcano 2009,"Michigan Technological University (MTU, Michigan USA)",US (United States),,United States,doi:10.7914/SN/XT_2009 +XT,"Retrieval of reflections from seismic background-noise measurements for geophysical imaging of Fault Systems in Reno, NV",University of Nevada (UNR Reno),US (United States),,United States,doi:10.7914/SN/XT_2010 +XT,Western Idaho Shear Zone - Passive,University of Florida,US (United States),,United States,doi:10.7914/SN/XT_2011 +XT,EASI - Eastern Alpine Seismic Investigation,Swiss Seismological Service,CH (Switzerland),,Switzerland, +XU,New Zealand Seismic Experiment,IRIS/PASSCAL, (),,, +XU,Trans-Antarctic ,Institute of Geological and Nuclear Science (GNS - New Zealand),NZ (New Zealand),,New Zealand, +XU,Monitoring MEQ activity during pulsed injection at geothermal reservoirs,"Lawrence Berkeley Laboratory (LBNL, CA USA)",US (United States),,United States, +XU,TaskForce Alsdorf 2002,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +XU,"2003 Fort Payne, AL, Aftershock Network","University of Memphis, CERI",US (United States),,United States, +XU,Deception Island Passive/Active Source,University of Washington,US (United States),,United States, +XU,"Collaborative Research: Earthscope integrated investigations of Cascadia subduction zone tremor, structure and process",University of Washington,US (United States),,United States,doi:10.7914/SN/XU_2006 +XU,Jakarta Experiment,"Australian National University (ANU, Australia)",AU (Australia),,Australia, +XU,Greeley Colorado RAMP Deployment 2014,University of Colorado Boulder (CU),US (United States),,United States,doi:10.7914/SN/XU_2014 +XU,USGS NEHRP Proposal 2016-0180 - Greeley,University of Colorado,US (United States),,United States,doi:10.7914/SN/XU_2016 +XV,Panama Canal Experiment,IRIS/PASSCAL, (),,, +XV,Kilauea East Rift,IRIS/PASSCAL, (),,, +XV,Tacoma Array,USGS Menlo Park,US (United States),,United States, +XV,Collaborative research of the Earth's largest icebergs,IRIS/PASSCAL, (),,,doi:10.7914/SN/XV_2003 +XV,Glacier Seismicity and High Resolution Motion Records: Relation to Glacier Erosion,"University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States,doi:10.7914/SN/XV_2007 +XV,Collaborative Research: Geometry and kinematics of basement-involved foreland arches:Insights into continental processes from Earthscope.,Colorado College,US (United States),,United States,doi:10.7914/SN/XV_2009 +XV,"INVESTIGATION OF SOURCES OF INTRAPLATE VOLCANISM USING PASSCAL BROADBAND INSTRUMENTS IN MADAGASCAR, THE COMORES, AND MOZAMBIQUE",Washington University in St. Louis (WUSTL),US (United States),,United States,doi:10.7914/SN/XV_2011 +XV,Fault Locations and Alaska Tectonics from Seismicity,"University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States,doi:10.7914/SN/XV_2014 +XW,Tien Shan Continental Dynamics,IRIS/PASSCAL, (),,, +XW,"Siple Coast Antarctica, Microearthquake Project",IRIS/PASSCAL, (),,, +XW,Seismic Imaging of the Mantle Beneath the Anatolian-Aegan Domain,"Observatoire de Grenoble, Grenoble",FR (France),,France, +XW,Young Conjugate Margins Lab in the Gulf of Aden,University of Southampton (UK),GB (United Kingdom),,United Kingdom,doi:10.7914/SN/XW_2009 +XW,CRESIS Greenland 2012,Penn State University,US (United States),,United States,doi:10.7914/SN/XW_2012 +XW,ESG Bayou Corne,ESG Solutions (Canada),CA (Canada),,Canada, +XW,Chile as a laboratory for understanding the impact of geology on slip prior to and during megathrust,,,,, +XY,Northern Baja Transect,IRIS/PASSCAL, (),,, +XY,"Nicoya, Costa Rica",IRIS/PASSCAL, (),,, +XY,Mongolia-Baikal Lithospheric Transect,"Sismos a l'Ecole, Geosciences Azur",FR (France),,France, +XY,"Seismic Imaging of Fractures at Krafla, Iceland",IRIS/PASSCAL, (),,,doi:10.7914/SN/XY_2004 +XY,Magma Accretion and the Formation of Batholiths,IRIS/PASSCAL, (),,,doi:10.7914/SN/XY_2005 +XY,Seismic Imagining of the Mantle Beneath the Anatolian-Aegan Domain,"Observatoire de Grenoble, Grenoble",FR (France),,France, +XY,RAMP response for 2010 earthquake,University of Florida,US (United States),,United States,doi:10.7914/SN/XY_2010 +XY,"Experiment to Determine Hypocenters and Focal Mechanisms of Earthquakes Occurring in Association with Imaged Faults Near Summeville, South Carolina",Virginia Polytechnic Institute and State University (VPI&SU or Virginia Tech),US (United States),,United States,doi:10.7914/SN/XY_2011 +XY,PASEIS,Penn State University,US (United States),,United States, +XZ,Cascadia/SEED reformat,Arizona State University,US (United States),,United States, +XZ,"Erebus II, Anarctica",IRIS/PASSCAL, (),,, +XZ,Spokane WA Earthquake Swarm,IRIS/PASSCAL, (),,,doi:10.7914/SN/XZ_2001 +XZ,DHOFAR Seismic Experiment,Institut de Physique du Globe de Paris,FR (France),,France, +XZ,Collaborative Research: St. Elias Erosion/Tectonics Project,"University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States,doi:10.7914/SN/XZ_2005 +XZ,Gulf of Mexico USGS Hydrates,USGS Woods Hole,US (United States),,United States, +XZ,Idaho Teton Dam,Southern Methodist University (SMU TX),US (United States),,United States,doi:10.7914/SN/XZ_2014 +XZ,Measuring ambient noise of ocean breakers,East Carolina University (ECU),US (United States),,United States,doi:10.7914/SN/XZ_2015 +XZ,Volatile recycling at the Lesser Antilles Arc: Processes and consequences,Imperial College London (Imperial UK),GB (United Kingdom),,United Kingdom, +Y1,SCANLIPS2,University of Leicester (SEIS UK),GB (United Kingdom),,United Kingdom, +Y1,Libris,Institut des Sciences de la Terre,FR (France),,France, +Y1,Rock Arches Resonance,University of Utah,US (United States),,United States, +Y2,Collaborative Research: Imaging the Upper Mantle Beneath the Western Tibetan Plateau,Rutgers University,US (United States),,United States,doi:10.7914/SN/Y2_2007 +Y2,Green County Observation,University of Pittsburgh (Pitt PA),US (United States),,United States, +Y2,Balcombe,University of Bristol (UK),GB (United Kingdom),,United Kingdom,doi:10.7914/SN/Y2_2013 +Y2,Safe-CO2,Universite Joseph Fourier - Grenoble (LGIT-UJF-France),FR (France),,France, +Y3,"Studies of crust and upper mantle structure, mantle flow and geodynamics of the Chile Ridge subduction zone",University of Florida,US (United States),,United States,doi:10.7914/SN/Y3_2007 +Y3,"Wells, Nevada Aftershock Recording","New Mexico Tech (NMT Mining & Technology NM,USA)",US (United States),,United States,doi:10.7914/SN/Y3_2008 +Y3,"Collaborative Research: Constraining Mantle Rheology, Mantle Flow, and Crust/Mantle coupling Beneath New Zealand",University of Colorado Boulder (CU),US (United States),,United States,doi:10.7914/SN/Y3_2009 +Y3,Vaughn-Lewis Icefall Seismicity Project,Stanford University,US (United States),,United States,doi:10.7914/SN/Y3_2012 +Y3,SEIS-UK instrument type noise study,Leicester University ,GB (United Kingdom),,United Kingdom,doi:10.7914/SN/Y3_2013 +Y4,Alpes04,"Observatoire de Grenoble, Grenoble",FR (France),,France, +Y4,Mt. Erebus Volcano Observatory,"New Mexico Tech (NMT Mining & Technology NM,USA)",US (United States),,United States,doi:10.7914/SN/Y4_2008 +Y4,Eastern Mediterranean Seismic,"Weston Geophysical Engineers, Inc",US (United States),,United States,doi:10.7914/SN/Y4_2010 +Y4,Brazilian Temporary Seismographic Experiments,"Universidade de Sao Paulo, USP",BR (Brazil),,Brazil, +Y4,"Extended Pollino Seismic Experiment, 2014-2015","GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +Y5,Canadian Rockies and Alberta Network,University of Alberta (UAlberta Canada),CA (Canada),,Canada, +Y6,Behavior of supraglacial lakes and their role in outlet glacier dynamics and mass balance of the Greenland Ice Sheet,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States,doi:10.7914/SN/Y6_2006 +Y6,QM-III-UK,Imperial College London (Imperial UK),GB (United Kingdom),,United Kingdom, +Y7,Seismic Experiment Active Caribbean Andesitic Lava Island Perscison Seismio-Geodetic Observatory,Penn State University,US (United States),,United States,doi:10.7914/SN/Y7_2007 +Y7,Geothermal Exploration Arkansas Valley,Colorado School of Mines (CSM),US (United States),,United States,doi:10.7914/SN/Y7_2009 +Y7,Deep OCean Test ARray experiment,Universitat Potsdam,DE (Germany),,Germany, +Y7,MOCHA MT Observations of Cascadia Using a Huge Array,Oregon State University (OSU USA),US (United States),,United States, +Y8,BBMOMAR,Institut de Physique du Globe de Paris,FR (France),,France, +Y8,Detection and location of non-volcanic tremor in the New Madrid Seismic Zone,"University of Memphis, CERI",US (United States),,United States,doi:10.7914/SN/Y8_2009 +Y8,"Passive-Source Guided Wave Study along the San Andreas Fault in San Gorgonio Pass, California",UC Riverside,US (United States),,United States,doi:10.7914/SN/Y8_2013 +Y9,Tocopilla,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +Y9,Guerrero Gap Experiment (Mexico),"Observatoire de Grenoble, Grenoble",FR (France),,France, +YA,Coso Passive Short Period Arrays,IRIS/PASSCAL, (),,, +YA,Bhuj Aftershock Study,IRIS/PASSCAL, (),,, +YA,"Collaborative research in Eastern Tibet: Evolution and dynamics of the crust and mantle, and surface topography.",IRIS/PASSCAL, (),,,doi:10.7914/SN/YA_2003 +YA,Torfajokull 2005,Nordic Volcanological Institute (NORDVULK Iceland),IS (Iceland),,Iceland, +YA,Transylvanian Basin Attenuation and Anisotropy Deployment,University of Florida,US (United States),,United States, +YA,HLP-3,Stanford University,US (United States),,United States, +YA,Undervolc,"Observatoire de Grenoble, Grenoble",FR (France),,France, +YA,Dynamics of the Lake Kivu System,University of Rochester,US (United States),,United States,doi:10.7914/SN/YA_2012 +YA,Fuego Volcano VLP and tilt experiment Continued,"Michigan State University (MSU, Michigan USA)",US (United States),,United States, +YB,Black Thunder Mine,IRIS/PASSCAL, (),,, +YB,South Africa Seismic Experiment - Kimberley Telemetered Array,IRIS/PASSCAL, (),,, +YB,Central Zagros 2001 transect,Universite Joseph Fourier - Grenoble (LGIT-UJF-France),FR (France),,France, +YB,"Puerto Plata, Dominican Republic Aftershock Study - RAMP",IRIS/PASSCAL, (),,,doi:10.7914/SN/YB_2003 +YB,Cavola Broadband Array on a Landslide,INGV Instituto Nazionale di Geofisica e Vulcanologia (Italy),IT (Italy),,Italy, +YB,Mount Saint Helens Dense Array,USGS Menlo Park,US (United States),,United States,doi:10.7914/SN/YB_2005 +YB,Haiti Earthquake Aftershock Experiment,"Observatoire de Grenoble, Grenoble",FR (France),,France, +YB,Faroe Islands Passive Seismic Experiment,University of Leeds,GB (United Kingdom),,United Kingdom, +YB,Continental Dynamics/Central Anatolian Tectonics: Surface to mantle dynamics during collision to escape,University of Missouri (MU) (Mizzou),US (United States),,United States,doi:10.7914/SN/YB_2013 +YB,Evolution of Slow-Spreading Oceanic Crust: South Atlantic,Texas A&M University,US (United States),,United States,doi:10.7914/SN/YB_2016 +YC,"Osa Penninsula, Costa Rica",IRIS/PASSCAL, (),,, +YC,Slab Geometry in the Southern Andes,IRIS/PASSCAL, (),,,doi:10.7914/SN/YC_2000 +YC,Eastern Oregon Mantle Experiment,IRIS/PASSCAL, (),,,doi:10.7914/SN/YC_2003 +YC,RABID,British Antarctic Survey (BAS UK),GB (United Kingdom),,United Kingdom, +YC,Partnership in Research and Education in Volcanology,"University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States,doi:10.7914/SN/YC_2006 +YC,RAMP Virginia,IRIS PASSCAL Instrument Center @ New Mexico Tech,US (United States),,United States,doi:10.7914/SN/YC_2011 +YC,Texas Brine Corporation Louisiana Seismic Network,Nanometrics Seismological Instruments,CA (Canada),,Canada, +YD,"Koacelli, Turkey",IRIS/PASSCAL, (),,, +YD,Calabria-Appennine-Tyrrhenian/Subduction-Collision-Accretion Network,IRIS/PASSCAL, (),,,doi:10.7914/SN/YD_2003 +YD,"Toward Understanding Eruption Seismicity: Multidiscplinary Experiment at Santiaguito and Fuego Volcanos, Guatemala",University of New Hampshire (UNH),US (United States),,United States,doi:10.7914/SN/YD_2007 +YD,Recording Mogul Events throughout the Reno Basin,University of Nevada (UNR Reno),US (United States),,United States,doi:10.7914/SN/YD_2008 +YD,South Carpathian Project,University of Leeds,GB (United Kingdom),,United Kingdom, +YD,Whillians Ice Stream Subglacial Access Research Drilling,UC Santa Cruz,US (United States),,United States,doi:10.7914/SN/YD_2012 +YE,Seattle Site Response, None, ( ),, , +YE,Upper Geyser Basin Seismicity,IRIS/PASSCAL, (),,, +YE,"Water storage and routing within glaciers via planar voids, a new model of glacier hydrology",Boise State University,US (United States),,United States,doi:10.7914/SN/YE_2007 +YE,"Title: NEESR-SG: High Fidelity site characterization by experimentation, field observation, and inversion-based modeling",UC Santa Barbara,US (United States),,United States,doi:10.7914/SN/YE_2008 +YE,SeaJade I,Geological Survey of Canada,CA (Canada),,Canada, +YE,Testing TA & FA vaults and directly buried sensor (3T),IRIS PASSCAL Instrument Center @ New Mexico Tech,US (United States),,United States,doi:10.7914/SN/YE_2011 +YF,Midsea - Mantle Investigation of the Deep Suture between Eurasia and Africa,Eidgendssisch Technische Hochschule (ETH Zurich) - Swiss Seismological Service,CH (Switzerland),,Switzerland, +YF,Yellowstone RAMP,IRIS/PASSCAL, (),,,doi:10.7914/SN/YF_2003 +YF,Northern IL. aftershock survey,"University of Memphis, CERI",US (United States),,United States, +YF,A new survey studies the Northeastern Caribbean plate: GEOPRICO-DO Project,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +YF,SCANLIPS,University of Leicester (SEIS UK),GB (United Kingdom),,United Kingdom, +YF,Recognition of the lithosphere-asthenosphere system in the transition between Proterozoic and Phanerozoic Europe - Passive Seismic Experiment in TESZ 2006-2007,"Institute of Geophysics, Polish Academy of Sciences (IGF PAN Poland)",PL (Poland),,Poland,doi:10.7914/SN/YF_2007 +YF,Seismic Signature of Landslides,USGS Menlo Park,US (United States),,United States, +YF,Carboneras Fault Experiment,University of Liverpool,GB (United Kingdom),,United Kingdom, +YF,Observation of a glacier calving event using a network of GPS and Seismic sensors,New York University (NYU USA),US (United States),,United States,doi:10.7914/SN/YF_2012 +YG,South Bay Seismic Array,IRIS/PASSCAL, (),,, +YG,Carpathian Basins Project Regional Array,University of Leeds (UK),GB (United Kingdom),,United Kingdom, +YG,Imaging the Upper Crust at Newberry Volcano Using Large Offset Reflections,University of Oregon,US (United States),,United States, +YG,Gauging Rutford Ice Stream Transients,British Antarctic Survey (BAS UK),GB (United Kingdom),,United Kingdom, +YG,San Andreas Virtual Earthquake Experiment Los Angeles,Stanford University,US (United States),,United States, +YG,Collaborative Research: Imaging the Cascadia subduction zone - a ship-to-shore opportunity,Oregon State University (OSU USA),US (United States),,United States,doi:10.7914/SN/YG_2012 +YG,Deployment for the Southern Cook Strait Earthquake Sequence,Victoria University of Wellington (VUW New Zealand),NZ (New Zealand),,New Zealand,doi:10.7914/SN/YG_2013 +YG,Fate and consequences of Yakutat terrane subduction beneath eastern Alaska and the Wrangell Volcanic Field,"University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States, +YH,Imaging Galapagos Upwelling & Neotectonics,University of Oregon,US (United States),,United States, +YH,Seismic tomography and location calibration around the SAFOD site,"University of Wisconsin, Madison",US (United States),,United States,doi:10.7914/SN/YH_2004 +YH,LaBarge Experiment,University of Arizona,US (United States),,United States,doi:10.7914/SN/YH_2008 +YH,Africa Array SE Tanzania Basin Experiment,Penn State University,US (United States),,United States, +YH,Dense Array for North Anatolia,University of Leeds (UK),GB (United Kingdom),,United Kingdom,doi:10.7914/SN/YH_2012 +YH,Hikurangi Ocean Bottom Investigation of Tremor and Slow Slip,University of Texas at Austin,US (United States),,United States,doi:10.7914/SN/YH_2014 +YI,Antarctic Network of Broadband Seismometers,IRIS/PASSCAL, (),,, +YI,"Retreating-Trench, Extension, and Accretion LTectonics: A Multidisciplinary Study of the Northern Apennines",IRIS/PASSCAL, (),,,doi:10.7914/SN/YI_2003 +YI,GEOPRICO2,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +YI,SIMBAAD-Etransect (Seismic Imaging of the Mantle Beneath the Anatolian-Aegan Domain),"Observatoire de Grenoble, Grenoble",FR (France),,France, +YI,2009 Malawi Earthquake RAMP Response,Columbia University,US (United States),,United States,doi:10.7914/SN/YI_2010 +YI,DAFNE,Sodankyla Geophysical Observatory / University of Oulu (Finland),FI (Finland),,Finland, +YI,St Guérin dam experiment,Universite Joseph Fourier - Grenoble (LGIT-UJF-France),FR (France),,France, +YJ,Turkey RAMP Deployment,IRIS/PASSCAL, (),,, +YJ,Ethiopia-Afar Geoscientific Lithospheric Experiment,IRIS/PASSCAL, (),,, +YJ,"Studies of crust and upper mantle structure, mantle flow and geodynamics of the Chile Ridge subduction zone",IRIS/PASSCAL, (),,,doi:10.7914/SN/YJ_2004 +YJ,"Record ground motion, strain changes, fault slip, temperature changes, and othere parameter close to the seismic source for earthquakess in the magnitude range expending to 3",USGS Menlo Park,US (United States),,United States,doi:10.7914/SN/YJ_2007 +YJ,Bench Glacier Monitoring,Boise State University,US (United States),,United States, +YJ,Infrasound From Earthquakes: Signal Characteristics and Depth Discrimination,University of Utah,US (United States),,United States,doi:10.7914/SN/YJ_2010 +YJ,SLIDEQUAKES,Institut de Physique du Globe de Paris,FR (France),,France, +YK,Coordinated Seismic Experiment in the Azores,Eidgendssisch Technische Hochschule (ETH Zurich) - Swiss Seismological Service,CH (Switzerland),,Switzerland, +YK,Investigation of Crustal Structure and Site Response in the San Francisco North Bay,USGS Menlo Park,US (United States),,United States,doi:10.7914/SN/YK_2003 +YL,"Himalayan Seismotectonics, Nepal and Tibet",IRIS/PASSCAL, (),,,doi:10.7914/SN/YL_2001 +YL,San Simeon Aftershocks,USGS Menlo Park,US (United States),,United States, +YL,"Continental lithospheric deformation along a major strike-slip fault zone: the central North Anatolian Fault Zone, Turkey",University of Arizona,US (United States),,United States,doi:10.7914/SN/YL_2005 +YL,Lau Basin OBS Experiment,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +YL,Santiaguito 2012,University of North Carolina at Chapel Hill (UNC),US (United States),,United States,doi:10.7914/SN/YL_2012 +YL,"Seismo_acoustic experiment and initial monitoring effort at Pagan Volcano, Commonwealth of the Northern Mariana Islands",USGS Alaska Anchorage,US (United States),,United States,doi:10.7914/SN/YL_2013 +YL,Parnaiba Basin WARR profile,University of Aberdeen (UK),GB (United Kingdom),,Northeast Brazil,doi:10.7914/SN/YL_2015 +YM,Central North Island Passive Seismic Exp, None, ( ),, , +YM,Denali RAMP,IRIS/PASSCAL, (),,,doi:10.7914/SN/YM_2002 +YM,Columbia Glacier Seismic Experiment,IRIS/PASSCAL, (),,,doi:10.7914/SN/YM_2004 +YM,Taiwan Supplement #2 to TAIGER project,IRIS/PASSCAL, (),,,doi:10.7914/SN/YM_2006 +YM,"An Integrated Analysis of Low-Frequency Seismicity at Villarrica Volcano, Chile","Michigan Technological University (MTU, Michigan USA)",US (United States),,United States,doi:10.7914/SN/YM_2010 +YM,Physical controls on ocean-terminating glacier variability in central west greenland,"University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States,doi:10.7914/SN/YM_2013 +YN,Seismic Experiment in Patagonia and Antarctica,Washington University in St. Louis (WUSTL),US (United States),,United States, +YN,Socorro Magma Body,IRIS/PASSCAL, (),,,doi:10.7914/SN/YN_2005 +YN,Reverberation method development,USGS Golden Colorado,US (United States),,United States,doi:10.7914/SN/YN_2008 +YN,Juan de Fuca Passive Source Tomography Experiment,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +YN,Collaborative Research: Structural Architecture and Evolutionary Plate-Boundary Processes along the San Jacinto Fault Zone,UC San Diego,US (United States),,United States,doi:10.7914/SN/YN_2010 +YO,Taranaki,University of Cambridge (UK) Earth Sciences,GB (United Kingdom),,United Kingdom, +YO,Imaging the mantle in the Central American Subduction Factory,IRIS/PASSCAL, (),,,doi:10.7914/SN/YO_2003 +YO,Mogul west Reno ,University of Nevada (UNR Reno),US (United States),,United States,doi:10.7914/SN/YO_2008 +YO,"Volacano Geophysics Field Course and Digital Signal Processing at Tungurahua, Ecuador",Boise State University,US (United States),,United States,doi:10.7914/SN/YO_2009 +YO,Tidewater glacier and ice marginal dynamic behavior,USACE - Cold Regions Research and Engineering Laboratory (CRREL),US (United States),,United States,doi:10.7914/SN/YO_2010 +YO,Sakurajima Infrasound Network,"University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States, +YO,Eastern North American Margin Community Seismic Experiment,University of Texas at Austin,US (United States),,United States,doi:10.7914/SN/YO_2014 +YP,Southern Apennines Tomography Experiment,Istituto Nazionale di Geofisica e Vulcanologia (INGV),IT (Italy),,Italy, +YP,NISD,Multimax Inc (MD USA),US (United States),,United States, +YP,NTS October 2008,"Los Alamos National Laboratory (LANL, NM)",US (United States),,United States, +YP,"Collaborative Research: Northeast China Extended Seismic Array: Deep subduction, mantle dynamics and lithospheric evolution beneath Northeast China",Rice University,US (United States),,United States,doi:10.7914/SN/YP_2009 +YP,CIFALPS,Institut des Sciences de la Terre,FR (France),,France, +YP,Argostoli earthquake aftershock experiment,Institut des Sciences de la Terre,FR (France),,France, +YQ,Himalaya Tectonic,IRIS/PASSCAL, (),,, +YQ,Katla 2003,Nordic Volcanological Institute (NORDVULK Iceland),IS (Iceland),,Iceland, +YQ,Deep crustal structure of the northernmost Basin and Range and its relation to extensional faulting,IRIS/PASSCAL, (),,,doi:10.7914/SN/YQ_2004 +YQ,Nicoya Peninsula Seismic Network,UC Santa Cruz,US (United States),,United States, +YQ,Lincoln Noise Study,University of Leicester (SEIS UK),GB (United Kingdom),,United Kingdom,doi:10.7914/SN/YQ_2009 +YQ,Development of a low cost method to estimate the seismic signature of a geothermal field from ambient seismic noise analysis,University of Nevada (UNR Reno),US (United States),,United States,doi:10.7914/SN/YQ_2012 +YQ,Study of Extension and maGmatism in Malawi aNd Tanzania,"Lamont Doherty Earth Observatory (LDEO), Columbia University",US (United States),,United States,doi:10.7914/SN/YQ_2013 +YR,"Horn of Africa (Ethiopa, Yemen) broad-band experiment",IPGS (Institut de Physique du Globe de Strasbourg) ,FR (France),,France, +YR,Petrophysical and Seismic Properties of Faroes Basalts,University of Cambridge (UK) Earth Sciences,GB (United Kingdom),,United Kingdom, +YR,Northern Lake Tahoe Magma Injection Seismic Monitoring Network,University of Nevada (UNR Reno),US (United States),,United States, +YR,Dhofar Seismic Experiment II,Institut de Physique du Globe de Paris,FR (France),,France, +YR,ALFA08,Institute of Geological and Nuclear Science (GNS - New Zealand),NZ (New Zealand),,New Zealand,doi:10.7914/SN/YR_2008 +YR,Eritrea Seismic Project,Imperial College London (Imperial UK),GB (United Kingdom),,United Kingdom, +YR,DOMERAPI,Institut des Sciences de la Terre,FR (France),,France, +YS,Observational Constraints on Persistent Water Level Changes Generated by Seismic Waves,IRIS/PASSCAL, (),,,doi:10.7914/SN/YS_2001 +YS,Plume Lithosphere Undersea Melt Experiment ,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +YS,Andravida (Greece) Aftershock Experiment,"Observatoire de Grenoble, Grenoble",FR (France),,France, +YS,The life cycle of Andean volcanoes:Combining space-based and field studies,Cornell University,US (United States),,United States,doi:10.7914/SN/YS_2009 +YS,Transitions in the Banda Arc-Australia continental collision,USC - University of Southern California,US (United States),,United States,doi:10.7914/SN/YS_2014 +YT,West Kunlun 2001 transect,Universite Joseph Fourier - Grenoble (LGIT-UJF-France),FR (France),,France, +YT,MOBAL 03,"Sismos a l'Ecole, Geosciences Azur",FR (France),,France, +YT,Parkfield Trapped Waves,IRIS/PASSCAL, (),,,doi:10.7914/SN/YT_2004 +YT,Herdubreid 2006,University of Cambridge (UK) Earth Sciences,GB (United Kingdom),,United Kingdom, +YT,IPY POLENET-Antarctica: Investigating links between geodynamics and ice sheets,Washington University in St. Louis (WUSTL),US (United States),,United States,doi:10.7914/SN/YT_2007 +YU,Several Greek networks,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +YU,Erta Ale,IRIS/PASSCAL, (),,, +YU,Mount Saint Helens Broadband,"University of Memphis, CERI",US (United States),,United States, +YU,Northern California Delta,USGS Menlo Park,US (United States),,United States,doi:10.7914/SN/YU_2006 +YV,North East Atlantic Tomography,University of Leicester (SEIS UK),GB (United Kingdom),,United Kingdom, +YV,Parkfield aftershocks,USGS Menlo Park,US (United States),,United States,doi:10.7914/SN/YV_2004 +YV,Seismic and geodetic imaging of subducting terranes under North America,IRIS/PASSCAL, (),,,doi:10.7914/SN/YV_2006 +YV,Bering Glacier Surge Seismic Experiment,"Michigan Technological University (MTU, Michigan USA)",US (United States),,United States,doi:10.7914/SN/YV_2010 +YV,RUM-RHUM,"Observatoire de Grenoble, Grenoble",FR (France),,France, +YW,North China Experiment 2000-2001,"Institute of Geology and Geophysics, CAS",CN (China),,China, +YW,Cape Verde Mantle Structure,University of Bristol (UK),GB (United Kingdom),,United Kingdom, +YW,Stalking Cascadia episodic tremor and slip with enhanced GPS and seismic arrays,University of Washington,US (United States),,United States,doi:10.7914/SN/YW_2005 +YW,TONGA06,Washington University in St. Louis (WUSTL),US (United States),,United States, +YW,Resolving structural control of episodic tremor and slip along the length of Cascadia,UC Berkeley,US (United States),,United States,doi:10.7914/SN/YW_2007 +YW,Nabro Urgency Array,Imperial College London (Imperial UK),GB (United Kingdom),,United Kingdom, +YW,"MIDGE: Minimally Invasive Direct Glacial Exploration of Biogeochemistry, Hydrology and Glaciology of Blood Falls, McMurdo Dry Va","University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States,doi:10.7914/SN/YW_2013 +YX,3FCorinth Tomography,"Sismos a l'Ecole, Geosciences Azur",FR (France),,France, +YX,Tides ,IRIS/PASSCAL, (),,, +YX,Samoa Lithosphere Integrated Seismic Experiment,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +YX,Seismology Rapid Response Test During the SoSAF Shakeout,UC Riverside,US (United States),,United States,doi:10.7914/SN/YX_2008 +YX,SSocorro Swarm 2009 RAMP,"New Mexico Tech (NMT Mining & Technology NM,USA)",US (United States),,United States,doi:10.7914/SN/YX_2009 +YX,Collaborative Research: 4D multi-disciplinary investigation of highly variable crustal response to continental extension in the north-central Basin and Range,Stanford University,US (United States),,United States,doi:10.7914/SN/YX_2010 +YX,Lhasa Block Top to Bottom--Lithospheric Evolution of Asia’s Leading Edge,Lehigh University,US (United States),,United States, +YY,NCISP1,"Institute of Geology and Geophysics, CAS",CN (China),http://english.igg.cas.cn,China,doi:10.7914/SN/YY_2000 +YY,Mariana Subdction Factory Experiment,Washington University in St. Louis (WUSTL),US (United States),,United States, +YY,Norway 05,Aarhus University,DK (Denmark),,Denmark, +YY,Southern Nevada Earthquake,"University of Nevada, Las Vegas",US (United States),,United States,doi:10.7914/SN/YY_2008 +YY,KRAFLA-09,University of Cambridge (UK) Earth Sciences,GB (United Kingdom),,United Kingdom, +YY,Mendenhall Glacier Outburst Flood Seismicity and Next-Generation Instrument Testing,UC Santa Cruz,US (United States),,United States,doi:10.7914/SN/YY_2012 +YY,Exploring extensional tectonics beyond the Ethiopian Rift,Cornell University,US (United States),,United States,doi:10.7914/SN/YY_2013 +YZ,"Bardwell, KY Aftershock Deployment","University of Memphis, CERI",US (United States),,United States, +YZ,Tabriz Experiment,"Observatoire de Grenoble, Grenoble",FR (France),,France, +YZ,Boina Broadband Network,"Royal Holloway, University of London",GB (United Kingdom),,United Kingdom, +YZ,Nicoya Seismogenic Zone,UC Santa Cruz,US (United States),,United States,doi:10.7914/SN/YZ_2009 +Z1,Northeast Tibet Plateu Seismic Experiment: Pilot,University of Rhode Island (URI),US (United States),,United States,doi:10.7914/SN/Z1_2006 +Z1,Mantle flow and magma supply to the Eastern Lau Spreading Center,Washington University in St. Louis (WUSTL),US (United States),,United States,doi:10.7914/SN/Z1_2009 +Z1,MT Imaging of the Cascades Volcanic Arc GeoPRISMS/ES MT Experiment,Oregon State University (OSU USA),US (United States),,United States, +Z2,HAMNET,"BGR Federal Institute for Geosciences and Natural Resources (BGR, Germany)",DE (Germany),,Germany, +Z2,NOISY,Stanford University,US (United States),,United States, +Z2,Luther OK,Cornell University,US (United States),,United States, +Z2,Galapagos2014,Geoazur,FR (France),,Ecuador,doi:10.7914/SN/Z2_2014 +Z3,EGELADOS network,Ruhr Universitaet Bochum (RUB Germany),DE (Germany),,Germany, +Z3,Mt.Carmel 2008 AFtershock Network,"University of Memphis, CERI",US (United States),,United States, +Z3,Detecting Structural changes During an ETS Event: Proof of Concept,Rice University,US (United States),,United States,doi:10.7914/SN/Z3_2009 +Z3,Jemez Pueblo geothermal project,TBA Inc,US (United States),,United States,doi:10.7914/SN/Z3_2010 +Z3,AlpArray,Eidgendssisch Technische Hochschule (ETH Zurich) - Swiss Seismological Service,CH (Switzerland),http://www.seismo.ethz.ch/alparray/,Switzerland, +Z4,DESIRE,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +Z4,Appalachian Seismic Transect,University of North Carolina at Chapel Hill (UNC),US (United States),,United States, +Z4,Chilean 2010 post-seismic response,Oregon State University (OSU USA),US (United States),,United States, +Z4,Seismic data of Santiguito volcano,University of Liverpool,GB (United Kingdom),,United Kingdom,doi:10.7914/SN/Z4_2014 +Z5,Tahiti Plume,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +Z5,Rwenzori Seismic Network,J.W. Goethe-University,DE (Germany),,Germany, +Z5,"Seismicity, Structure and Dynamics of the Gorda Deformation Zone",Oregon State University (OSU USA),US (United States),,United States,doi:10.7914/SN/Z5_2013 +Z6,Meramex,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +Z6,Mantle Investigations of Norwegian Uplift Structures,Karlsruhe Institute of Technology (KIT GIP Germany),DE (Germany),,Germany, +Z6,Forsyth Deployment near the South Shatsky Fracture Zone,UC San Diego,US (United States),,United States, +Z6,Collision of the Burma Arc accretionary prism and foldbelt with the Ganges-Brahmaputra Delta in Bangladesh,"Lamont Doherty Earth Observatory (LDEO), Columbia University",US (United States),,United States,doi:10.7914/SN/Z6_2011 +Z7,MROI vibrations,"New Mexico Tech (NMT Mining & Technology NM,USA)",US (United States),,United States,doi:10.7914/SN/Z7_2008 +Z7,Norhern Volcanic Zone,Geological Survey of Estonia (GSE),EE (Estonia),,Estonia, +Z8,NCISP5,"Institute of Geology and Geophysics, CAS",CN (China),http://english.igg.cas.cn,China,doi:10.7914/SN/Z8_2006 +Z8,"Deep Geothermal ""Hotter and Deeper"" seismic array",Institute of Geological & Nuclear Sciences Ltd (GNS New Zealand),NZ (New Zealand),,New Zealand, +Z8,Dense Virtual Seismic Array at Pacaya,"Michigan Technological University (MTU, Michigan USA)",US (United States),,United States,doi:10.7914/SN/Z8_2013 +Z9,Laramide Landslides,USGS University of Washington,US (United States),,United States, +Z9,Southeastern Suture of the Appalachian Margin Experiment,Brown University,US (United States),,United States,doi:10.7914/SN/Z9_2010 +ZA,Erzican,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZA,Killari,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZA,PISCO94 PS,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZA,Chicxulub CP,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZA,TOR-TE,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZA,REFUCA,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZA,The Colima Deep Seismic Experiment: Imaging the Magmatic Root of Colima Volcano,"University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States,doi:10.7914/SN/ZA_2006 +ZA,Russell Glacier Catchment 2009-12,Swansea University,GB (United Kingdom),,United Kingdom, +ZA,NoMelt,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +ZA,"Wellington, Kansas CO2 Sequestration Monitoring",Kansas Geological Survey,US (United States),,United States,doi:10.7914/SN/ZA_2014 +ZB,Alaska TE,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZB,PUNA97,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZB,Svekalapko,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZB,Simeulue,University of Cambridge (UK) Earth Sciences,GB (United Kingdom),,United Kingdom, +ZB,Sumatra Segmentation and Aftershocks Deployment,University of Cambridge (UK) Earth Sciences,GB (United Kingdom),,United Kingdom, +ZB,HARP,Scripps Institution of Oceanography,US (United States),,United States, +ZC,Antofagasta,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZC,TOR-TO,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZC,GLATIS,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZC,Vogtland,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZC,TEDESE,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZC,Mt. St.Helens Temporary Array 2005,University of Washington,US (United States),,United States, +ZC,Anatahan and submarine Mariana volcanos,Washington University in St. Louis (WUSTL),US (United States),,United States,doi:10.7914/SN/ZC_2006 +ZC,Seismic Detection of Avalanches,Boise State University,US (United States),,United States,doi:10.7914/SN/ZC_2010 +ZC,Greater Antilles Seismic Program,Baylor University,US (United States),,United States,doi:10.7914/SN/ZC_2013 +ZD,Crete-TE,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZD,SEISM Seychelles,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZD,Tides E 2005,IRIS/PASSCAL, (),,, +ZD,Seismcity of Quebrada-Discovery-gofur- Transforms ( QDG),Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +ZD,PerU Lithosphere and Slab Experiment,University of North Carolina at Chapel Hill (UNC),US (United States),,United States,doi:10.7914/SN/ZD_2010 +ZD,"4D Integrated Study Using Geology, Geophysics, Reservoir Modeling & Rock Mechanics to Develop Assessment Models for Potential In",University of Oklahoma (OU Oklahoma USA),US (United States),,United States,doi:10.7914/SN/ZD_2014 +ZE,ANCORP-TE,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZE,Calixto,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZE,Sultandagi,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZE,Bingoel,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZE,SIKKIM,University of Cambridge (UK) Earth Sciences,GB (United Kingdom),,United Kingdom, +ZE,AFAR,University of Rochester,US (United States),,United States,doi:10.7914/SN/ZE_2007 +ZE,Maule Aftershock Survey-GFZ,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZE,"SELASOMA Project, Madagascar 2012-2014","GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany,10.14470/MR7567431421 +ZE,Southern Alaska Lithosphere and Mantle Observation Network,"University of Alaska, Fairbanks (UAF, AK USA)",US (United States),,United States,doi:10.7914/SN/ZE_2015 +ZF,Yakutia,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZF,Hawaii Plume,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZF,Aachen,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZF,BAM Earthquake Aftershock Experiment,"Observatoire de Grenoble, Grenoble",FR (France),,France, +ZF,Tidal modulation of ice stream flow,IRIS/PASSCAL, (),,,doi:10.7914/SN/ZF_2005 +ZF,Afar Consortium Network,University of Bristol (UK),GB (United Kingdom),,United Kingdom, +ZF,Collaborative Research: East Antarctic Outlet Glacier Dynamics,Central Washington University,US (United States),,United States,doi:10.7914/SN/ZF_2012 +ZG,Cariaco,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZG,Adana,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZG,Popcatepetl,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZG,TaskForce Bingoel 2003,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZG,Tidal modulation of ice stream flow,IRIS/PASSCAL, (),,,doi:10.7914/SN/ZG_2004 +ZG,Origin of the Columbia River Basalts and Uplift of the Wallowa Mountains,University of Oregon,US (United States),,United States,doi:10.7914/SN/ZG_2006 +ZG,Central Andean Uplift and the Geodynamics of the High Topography,University of Arizona,US (United States),,United States,doi:10.7914/SN/ZG_2010 +ZG,Bringing New Tools and Techniques to Bear on Earthquake Hazard Analysis and Mitigation,IRIS HQ (DC),US (United States),,United States,doi:10.7914/SN/ZG_2013 +ZG,Sage Brush Flats Nodal Experiment,UC San Diego,US (United States),,United States, +ZG,27F2 SOW 019 (is the official name of the well),"USGS OGW BG, Storrs, CT",US (United States),http://va.water.usgs.gov/earthquakes/,United States, +ZH,Duezce,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZH,NW Zagros Transet,"Observatoire de Grenoble, Grenoble",FR (France),,France, +ZH,Tidal modulation of ice stream flow,IRIS/PASSCAL, (),,,doi:10.7914/SN/ZH_2004 +ZH,British Isles Seismic Experiment,University of Cambridge (UK) Earth Sciences,GB (United Kingdom),,United Kingdom, +ZH,Collaborative Research: Geometry and kinematics of basement-involved foreland arches:Insights into continental processes from Earthscope.,University of Colorado,US (United States),,United States,doi:10.7914/SN/ZH_2010 +ZH,Monitgoring seismicity associated with a possible asperity on the Cascadia megathrust,Oregon State University (OSU USA),US (United States),,United States,doi:10.7914/SN/ZH_2011 +ZH,Imaging the Floridan Aquifer System Using Seismic Noise,University of Florida,US (United States),,United States,doi:10.7914/SN/ZH_2013 +ZI,Izmit,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZI,Polynesian Lithosphere and Upper Mantle Experiment (PLUME),University Montpellier II (UM2 UMSF France),FR (France),http://www.gm.univ-montp2.fr/PERSO/barruol/polynesia/,France,doi:10.7914/SN/ZI_2001 +ZI,British Isles Seismic Experiment,University of Cambridge (UK) Earth Sciences,GB (United Kingdom),,United Kingdom, +ZI,Collaborative Research: Geometry and kinematics of basement-involved foreland arches: Insights into continental processes from Earthscope,University of Colorado,US (United States),,United States,doi:10.7914/SN/ZI_2010 +ZI,CoMITAC Morocco-Bristol Network,University of Bristol (UK),GB (United Kingdom),,United Kingdom, +ZI,"A community seismic experiment targeting the pre-syn, post-rift evolution of the Mid-Atlantic US",Southern Methodist University (SMU TX),US (United States),,United States, +ZJ,Alborz Transect,"Observatoire de Grenoble, Grenoble",FR (France),,France, +ZJ,Herdubreid 2005,Geological Survey of Estonia (GSE),EE (Estonia),,Estonia, +ZJ,Central China Broadband Experiment,Multimax Inc (MD USA),US (United States),,United States, +ZJ,Preparation for WISSARD in Alaska,UC Santa Cruz,US (United States),,United States,doi:10.7914/SN/ZJ_2011 +ZJ,Transantarctic Mountains Northern Network,The University of Alabama,US (United States),,United States,doi:10.7914/SN/ZJ_2012 +ZK,TOLSTOY,"Lamont Doherty Earth Observatory (LDEO), Columbia University",US (United States),,United States, +ZK,UWTremTexBS,University of Washington,US (United States),,United States,doi:10.7914/SN/ZK_2008 +ZK,"Four-Dimensional Anatomy of Continental Rifts Transitioning into Sea Floor spreading: Insights from Afar, Ethiopia for oil and gas exploration of global rift systems and passive continental margins",Missouri University of Science & Technology(Former UM-Rolla)(Missouri S&T USA),US (United States),,United States,doi:10.7914/SN/ZK_2009 +ZK,Guerrero Coast Experiment,Monitron,MX (Mexico),,Mexico, +ZK,Vatnajokull Icequake and Tremor Array: Lite,University of Cambridge (UK) Earth Sciences,GB (United Kingdom),,United Kingdom, +ZL,David Glacier 2003-2004,Institute of Geological and Nuclear Science (GNS - New Zealand),NZ (New Zealand),,New Zealand, +ZL,Sea of Cortez Ocean Bottom Array,Scripps Institution of Oceanography,US (United States),,United States, +ZL,Lithospheric Structure and Deformation of the Flat Slab Region of Argentina,University of Arizona,US (United States),,United States,doi:10.7914/SN/ZL_2007 +ZL,Shatsky Rise Active Source Experiment OBSIP,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +ZL,Northern Embayment Lithospheric Experiment,"University of Memphis, CERI",US (United States),,United States,doi:10.7914/SN/ZL_2011 +ZM,Uraban Seismology,Karlsruhe Institute of Technology (KIT GIP Germany),DE (Germany),,Germany, +ZM,"Seismicity of the Atlantis Massif, Mid Atlantic Ridge",Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +ZM,"A Broadband Seismic Experiment to Image the Lithosphere beneath the Gamburtsev Mountains, East Antarctica",Saint Louis University,US (United States),,United States,doi:10.7914/SN/ZM_2007 +ZM,UKFLUV14,Durham University,GB (United Kingdom),,United Kingdom,doi:10.7914/SN/ZM_2014 +ZN,Enola Arkansas Aftershock Study,"University of Memphis, CERI",US (United States),,United States, +ZN,SISMOVALP,Politecnico Milano,IT (Italy),,Italy, +ZN,Lithospheric structure of West Greenland,University of Leicester (SEIS UK),GB (United Kingdom),,United Kingdom, +ZN,Meteo Aruba/Rice Univ,Other, (),,, +ZN,How is Rifting Exhuming the Youngest HP/UHP Rocks on Earth?,"Lamont Doherty Earth Observatory (LDEO), Columbia University",US (United States),,United States,doi:10.7914/SN/ZN_2010 +ZN,"Investigation of subglacial hydrologic seismic signals, Breidamerkurjokull Glacier, Iceland- a WISSARD test case",UC Santa Cruz,US (United States),,United States,doi:10.7914/SN/ZN_2013 +ZN,"Site response in the Washington, DC, area",USGS National Center/Reston (United States Geological Survey),US (United States),,United States,doi:10.7914/SN/ZN_2014 +ZO,Transalp II,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZO,Seismic Monitoring of Greenland's Melting Ice Sheet,University of North Carolina at Chapel Hill (UNC),US (United States),,United States,doi:10.7914/SN/ZO_2006 +ZO,Seismo-Acoustic data collection near Salt Lake,Southern Methodist University (SMU TX),US (United States),,United States,doi:10.7914/SN/ZO_2007 +ZO,ARC-Vanuatu Seismic Experiment (Volcanoes),"Observatoire de Grenoble, Grenoble",FR (France),,France, +ZO,"Volcanic stress field analysis using non-local seismic sources at Hekla Volcano, Iceland",University of South Florida (USF),US (United States),,United States,doi:10.7914/SN/ZO_2010 +ZO,Erebus Small Array,"New Mexico Tech (NMT Mining & Technology NM,USA)",US (United States),,United States,doi:10.7914/SN/ZO_2011 +ZO,Vaigat,Durham University,GB (United Kingdom),,United Kingdom, +ZO,Bhutanepal seismic array,Institut des Sciences de la Terre,FR (France),,France, +ZP,ISSA,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZP,"3D Site Amplification at Sion, Valais, Switzerland",Swiss Seismological Service,CH (Switzerland),,Switzerland, +ZP,AfricaArray, None, ( ),, ,doi:10.7914/SN/ZP_2007 +ZP,Africa Array- Uganda/Tanzania,Penn State University,US (United States),,United States, +ZP,Ross Ice Shelf Project,University of Otago (New Zealand),NZ (New Zealand),,New Zealand,doi:10.7914/SN/ZP_2015 +ZQ,NCISP2,"Institute of Geology and Geophysics, CAS",CN (China),http://english.igg.cas.cn,China,doi:10.7914/SN/ZQ_2001 +ZQ,Cerro Blanco,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZQ,Africa Array- S.Africa mine network,Penn State University,US (United States),,United States, +ZQ,Oklahoma RAMP,University of Oklahoma (OU Oklahoma USA),US (United States),,United States,doi:10.7914/SN/ZQ_2011 +ZQ,Imaging the Matador arch using receiver funcitons from Texan dataloggers and short period geop0hones.,Texas Tech University,US (United States),,United States,doi:10.7914/SN/ZQ_2013 +ZQ,Taku Glacier GEOICE Development Initiative,Central Washington University,US (United States),,United States,doi:10.7914/SN/ZQ_2015 +ZR,DESERT,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZR,Reconnaissance survey of Bering Glacier Basal seismicity and calving,"Michigan Technological University (MTU, Michigan USA)",US (United States),,United States,doi:10.7914/SN/ZR_2007 +ZR,Bering Glacier Field Camp 2008,"Michigan Technological University (MTU, Michigan USA)",US (United States),,United States,doi:10.7914/SN/ZR_2008 +ZR,AFAR0911,University of Southampton (UK),GB (United Kingdom),,United Kingdom,doi:10.7914/SN/ZR_2009 +ZR,El Tatio Geyser Study,Stanford University,US (United States),,United States,doi:10.7914/SN/ZR_2012 +ZR,SCANLIPS3D,University of Leicester (SEIS UK),GB (United Kingdom),,United Kingdom, +ZR,Laguna del Maule seismic imaging,"University of Wisconsin, Madison",US (United States),,United States, +ZS,CENMOVE,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZS,TaskForce Yogyakarta 2006,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZS,Seismological Network in Northern Tanzania,UBO-IUEM-CNRS Domaines Oceaniques ,FR (France),,France, +ZS,CReSIS,Penn State University,US (United States),,United States,doi:10.7914/SN/ZS_2009 +ZS,"Stick Slip Motion of Engabreen Glacier, Norway",Penn State University,US (United States),,United States, +ZS,Seismicity of East Coast Submarine Landslides,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +ZS,Measuring the frequency of ocean breaker induced surface waves for determining swell direction: feasibility study,East Carolina University (ECU),US (United States),,United States,doi:10.7914/SN/ZS_2014 +ZT,Gulf of Guinea Islands,University of Texas at Austin,US (United States),,United States, +ZT,Study of the Dynamic of Utiku Landslide (NZ),"Observatoire de Grenoble, Grenoble",FR (France),,France, +ZT,"WIsconsin, new Zealand, And Rensselaer Deployment","University of Wisconsin, Madison",US (United States),,United States,doi:10.7914/SN/ZT_2012 +ZU,TaskForce Waldkirch 2004,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZU,PASSEQ,University of Leicester (SEIS UK),GB (United Kingdom),,United Kingdom, +ZU,North Tanzania,University of Rochester,US (United States),,United States, +ZU,Marine Observations of Anisotropy,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +ZU,Repeating glacier quakes (clones) on Mount Rainier,University of Washington,US (United States),,United States,doi:10.7914/SN/ZU_2011 +ZU,Grand Canyon Controlled Flood,California Institute of Technology (Caltech),US (United States),,United States,doi:10.7914/SN/ZU_2012 +ZU,Trans-Haiti,University of Southampton (UK),GB (United Kingdom),,United Kingdom, +ZU,OROGEN-X,Research Institute in Astrophysics and Planetology (IRAP France),FR (France),,France, +ZV,BOHEMA,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZV,Monitoring of Augustine Volcano Eruption using Ocean Bottom Seismometers,Woods Hole Oceanographic Institution (WHOI),US (United States),,United States, +ZV,Mid-Ocean Volcanoes and Earthquakes (during Arctic Gakkel Vents Expedition),Alfred Wegener Institute for Polar and Marine Research,DE (Germany),,Germany,doi:10.7914/SN/ZV_2007 +ZV,Northeast Tibet Plateau Seismic Experiment,University of Rhode Island (URI),US (United States),,United States,doi:10.7914/SN/ZV_2008 +ZV,Malargue seismic array,Delft University of Technology (TU Netherlands),NL (Netherlands),,Netherlands,doi:10.7914/SN/ZV_2012 +ZV,Southern Lake Tanganyika experiment,University of Southampton (UK),GB (United Kingdom),,United Kingdom, +ZW,TIPTEQ North,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZW,Erebus Tomography and Source Studies,"New Mexico Tech (NMT Mining & Technology NM,USA)",US (United States),,United States,doi:10.7914/SN/ZW_2007 +ZW,JULS,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZW,Locating North Texas Earthquakes,Southern Methodist University (SMU TX),US (United States),,United States,doi:10.7914/SN/ZW_2013 +ZX,NCISP3,"Institute of Geology and Geophysics, CAS",CN (China),http://english.igg.cas.cn,China,doi:10.7914/SN/ZX_2003 +ZX,Costa Rica Poco Sol,Rutgers University,US (United States),,United States, +ZX,Boulby Cliffs,Durham University,GB (United Kingdom),,United Kingdom, +ZX,Gisborne-Mahia Seismic Tremor Array,GNS Science,NZ (New Zealand),,New Zealand, +ZY,Portable Southern California Seismic Networks,California Institute of Technology (Caltech),US (United States),,United States, +ZZ,CYCLADES,"GEOFON Program (GFZ-Potsdam, Germany)",DE (Germany),,Germany, +ZZ,Lithospheric Structure of the Puna Plateau,Missouri University of Science & Technology(Former UM-Rolla)(Missouri S&T USA),US (United States),,United States, +ZZ,Seismology Classroom at University of Chicago,University of Chicago,US (United States),,United States,doi:10.7914/SN/ZZ_2010 +ZZ,"Multi-array observations of tectonic tremor in the Anza gap, San Jacinto fault, CA - Phase 1",California Institute of Technology (Caltech),US (United States),,United States,doi:10.7914/SN/ZZ_2011 +ZZ,Collaborative Research: Imaging the Cascadia subduction zone - a ship-to-shore opportunity,"Lamont Doherty Earth Observatory (LDEO), Columbia University",US (United States),,United States,doi:10.7914/SN/ZZ_2012 +ZZ,Virginia seismic experiment,USGS National Center/Reston (United States Geological Survey),US (United States),,United States,doi:10.7914/SN/ZZ_2013 diff --git a/swarm.sh b/swarm.sh index e0a18c55..cbf5ed32 100644 --- a/swarm.sh +++ b/swarm.sh @@ -1 +1 @@ -java -Duser.country=US -Duser.language=us -jar lib/swarm.jar $* +java -Duser.country=US -Duser.language=us -jar lib/swarm.jar "$@"