/** * SeismogramImageProcess.java * * @author Created by Philip Oliver-Paull */ package edu.sc.seis.sod.process.waveform; import java.awt.Dimension; import java.io.File; import java.util.List; import javax.swing.SwingUtilities; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import edu.iris.Fissures.AuditInfo; import edu.iris.Fissures.IfEvent.EventAccessOperations; import edu.iris.Fissures.IfEvent.Origin; import edu.iris.Fissures.IfNetwork.Channel; import edu.iris.Fissures.IfNetwork.Station; import edu.iris.Fissures.IfSeismogramDC.RequestFilter; import edu.iris.Fissures.model.MicroSecondDate; import edu.iris.Fissures.model.TimeInterval; import edu.iris.Fissures.model.UnitImpl; import edu.iris.Fissures.network.ChannelIdUtil; import edu.iris.Fissures.network.ChannelImpl; import edu.iris.Fissures.seismogramDC.LocalSeismogramImpl; import edu.sc.seis.TauP.Arrival; import edu.sc.seis.TauP.TauModelException; import edu.sc.seis.fissuresUtil.bag.TauPUtil; import edu.sc.seis.fissuresUtil.cache.CacheEvent; import edu.sc.seis.fissuresUtil.cache.EventUtil; import edu.sc.seis.fissuresUtil.display.DisplayUtils; import edu.sc.seis.fissuresUtil.display.PhasePhilter; import edu.sc.seis.fissuresUtil.display.SeismogramDisplay; import edu.sc.seis.fissuresUtil.display.borders.TitleBorder; import edu.sc.seis.fissuresUtil.display.configuration.BorderConfiguration; import edu.sc.seis.fissuresUtil.display.configuration.DOMHelper; import edu.sc.seis.fissuresUtil.display.configuration.SeismogramDisplayConfiguration; import edu.sc.seis.fissuresUtil.display.drawable.Flag; import edu.sc.seis.fissuresUtil.display.registrar.BasicTimeConfig; import edu.sc.seis.fissuresUtil.display.registrar.TimeConfig; import edu.sc.seis.fissuresUtil.exceptionHandler.GlobalExceptionHandler; import edu.sc.seis.fissuresUtil.time.MicroSecondTimeRange; import edu.sc.seis.fissuresUtil.xml.DataSet; import edu.sc.seis.fissuresUtil.xml.DataSetSeismogram; import edu.sc.seis.fissuresUtil.xml.MemoryDataSet; import edu.sc.seis.fissuresUtil.xml.MemoryDataSetSeismogram; import edu.sc.seis.sod.CookieJar; import edu.sc.seis.sod.SodUtil; import edu.sc.seis.sod.status.StringTreeLeaf; import edu.sc.seis.sod.subsetter.requestGenerator.PhaseRequest; public class SeismogramImageProcess implements WaveformProcess { public SeismogramImageProcess(SeismogramImageOutputLocator locator) { this.locator = locator; } public SeismogramImageProcess(Element el) throws Exception { if(DOMHelper.hasElement(el, "phaseWindow")) { phaseWindow = new PhaseWindow(SodUtil.getElement(el, "phaseWindow")); } if(DOMHelper.hasElement(el, "modelName")) { modelName = SodUtil.getNestedText(SodUtil.getElement(el, "modelName")); tauP = TauPUtil.getTauPUtil(modelName); } if(DOMHelper.hasElement(el, "dimension")) { dims = SodUtil.loadDimensions(SodUtil.getElement(el, "dimension")); } if(DOMHelper.hasElement(el, "showOnlyFirstArrivals")) { showOnlyFirst = true; } if(DOMHelper.hasElement(el, "phaseNameMappings")) { renamer = new PhasePhilter.PhaseRenamer(SodUtil.getElement(el, "phaseNameMappings")); } if(DOMHelper.hasElement(el, "phaseFlags")) { Element subEl = SodUtil.getElement(el, "phaseFlags"); NodeList flagEls = subEl.getElementsByTagName("phase"); phaseFlagNames = new String[flagEls.getLength()]; for(int j = 0; j < flagEls.getLength(); j++) { phaseFlagNames[j] = SodUtil.nodeValueOfXPath((Element)flagEls.item(j), "text()"); } } if(DOMHelper.hasElement(el, "displayConfig")) { sdc = SeismogramDisplayConfiguration.create(SodUtil.getElement(el, "displayConfig")); } if(DOMHelper.hasElement(el, "titleBorder")) { titleBorder = new BorderConfiguration(); titleBorder.configure(DOMHelper.getElement(el, "titleBorder")); titler = new SeismogramTitler(titleBorder); } locator = new SeismogramImageOutputLocator(el); } public WaveformResult accept(CacheEvent event, ChannelImpl channel, RequestFilter[] original, RequestFilter[] available, LocalSeismogramImpl[] seismograms, CookieJar cookieJar) throws Exception { return process(event, channel, original, seismograms, locator.getFileType(), cookieJar); } /** allows specifying a fileType, png or pdf. */ public WaveformResult process(CacheEvent event, Channel channel, RequestFilter[] original, LocalSeismogramImpl[] seismograms, final String fileType, CookieJar cookieJar) throws Exception { return process(event, channel, original, seismograms, fileType, phaseFlagNames, cookieJar); } /** allows specifying a fileType, png or pdf, and a list of phases. */ public WaveformResult process(CacheEvent event, Channel channel, RequestFilter[] original, LocalSeismogramImpl[] seismograms, final String fileType, String[] phases, CookieJar cookieJar) throws Exception { return process(event, channel, original, seismograms, fileType, phases, true, cookieJar); } public static MemoryDataSetSeismogram createSeis(LocalSeismogramImpl[] seismograms, RequestFilter[] original) throws Exception { MemoryDataSetSeismogram memDSS = new MemoryDataSetSeismogram(original[0], ""); memDSS.setBeginTime(DisplayUtils.firstBeginDate(original) .getFissuresTime()); memDSS.setEndTime(DisplayUtils.lastEndDate(original).getFissuresTime()); for(int i = 0; i < seismograms.length; i++) { memDSS.add(seismograms[i]); } return memDSS; } protected List<Arrival> getArrivals(Channel chan, Origin o, String[] phases) throws TauModelException { Station sta = chan.getSite().getStation(); TimeInterval filterOffset = new TimeInterval(10, UnitImpl.SECOND); List<Arrival> arrivals = PhasePhilter.filter(tauP.calcTravelTimes(sta, o, phases), filterOffset); if(showOnlyFirst) { arrivals = PhasePhilter.mindPsAndSs(arrivals); } return arrivals; } protected void addFlags(List<Arrival> arrivals, Origin o, SeismogramDisplay bsd, DataSetSeismogram seis) { MicroSecondDate originTime = new MicroSecondDate(o.getOriginTime()); for(int i = 0; i < arrivals.size(); i++) { MicroSecondDate flagTime = originTime.add(new TimeInterval(arrivals.get(i).getTime(), UnitImpl.SECOND)); bsd.add(new Flag(flagTime, renamer.rename(arrivals.get(i)), bsd.getDrawableSeismogram(seis))); } } /** allows specifying a fileType, png or pdf, and a list of phases. */ public WaveformResult process(CacheEvent event, Channel channel, RequestFilter[] original, LocalSeismogramImpl[] seismograms, String fileType, String[] phases, boolean relTime, CookieJar cookieJar) throws Exception { logger.debug("process() called"); MemoryDataSetSeismogram memDSS = createDataSetSeismogram(event, channel, original, seismograms); SeismogramDisplay bsd = createPopulatedDisplay(event, channel, new DataSetSeismogram[] {memDSS}, phases); String picFileName = locator.getLocation(event, channel, fileType); writeImage(bsd, locator.getFileType(), picFileName); return new WaveformResult(seismograms, new StringTreeLeaf(this, true)); } public SeismogramDisplay createPopulatedDisplay(EventAccessOperations event, Channel channel, DataSetSeismogram[] seis, String[] phases) throws Exception { MicroSecondTimeRange timeWindow = null; if(seis.length > 0) { timeWindow = getTimeWindow(phaseWindow, seis[0]); updateTitles(event, channel, timeWindow); } SeismogramDisplay bsd = sdc.createDisplay(); TimeConfig tc = new BasicTimeConfig(); bsd.setTimeConfig(tc); populateDisplay(bsd, event, channel, seis, phases); if(seis.length > 0) { setTimeWindow(tc, timeWindow, tc.getTime(seis[0])); } return bsd; } private void populateDisplay(SeismogramDisplay sd, EventAccessOperations event, Channel channel, DataSetSeismogram[] seis, String[] phases) throws TauModelException { Origin o = EventUtil.extractOrigin(event); addFlags(getArrivals(channel, o, phases), o, sd, seis[0]); if(seis.length > 0) { sd.add(seis); } } private MemoryDataSetSeismogram createDataSetSeismogram(EventAccessOperations event, Channel channel, RequestFilter[] original, LocalSeismogramImpl[] seismograms) throws Exception { MemoryDataSetSeismogram memDSS = createSeis(seismograms, original); DataSet dataset = new MemoryDataSet("temp", "Temp Dataset for " + memDSS.getName(), "temp", new AuditInfo[0]); dataset.addDataSetSeismogram(memDSS, new AuditInfo[0]); dataset.addParameter(DataSet.EVENT, event, new AuditInfo[0]); dataset.addParameter(DataSet.CHANNEL + ChannelIdUtil.toString(channel.get_id()), channel, new AuditInfo[0]); return memDSS; } public static void setTimeWindow(TimeConfig tc, DataSetSeismogram dss) throws Exception { setTimeWindow(tc, null, dss); } public static void setTimeWindow(TimeConfig tc, PhaseWindow pw, DataSetSeismogram dss) throws Exception { setTimeWindow(tc, getTimeWindow(pw, dss), tc.getTime(dss)); } public static void setTimeWindow(TimeConfig tc, MicroSecondTimeRange newTime, MicroSecondTimeRange currentTime) { double[] shiftNScale = DisplayUtils.getShiftAndScale(newTime, currentTime); tc.shaleTime(shiftNScale[0], shiftNScale[1]); } public static MicroSecondTimeRange getTimeWindow(PhaseWindow pw, DataSetSeismogram dss) throws Exception { RequestFilter rf; if(pw != null) { PhaseRequest pr = pw.getPhaseRequest(); rf = pr.generateRequest(dss.getEvent(), dss.getChannel()); } else { rf = dss.getRequestFilter(); } return new MicroSecondTimeRange(rf); } public void updateTitles(EventAccessOperations event, Channel channel, MicroSecondTimeRange timeRange) { if(titler != null) { titler.title(event, channel, timeRange); } } public BorderConfiguration getTitleBorder() { return titleBorder; } protected void writeImage(SeismogramDisplay disp, String fileType, String picFileName) throws Exception { SwingUtilities.invokeAndWait(new ImageWriter(disp, fileType, picFileName)); } protected class ImageWriter implements Runnable { private final SeismogramDisplay bsd; private final String fileType; private final String picFileName; private ImageWriter(SeismogramDisplay bsd, String fileType, String picFileName) { this.bsd = bsd; this.fileType = fileType; this.picFileName = picFileName; if(!(fileType.equals(PDF) || fileType.equals(PNG))) { throw new IllegalArgumentException("Unknown fileType:" + fileType); } } public void run() { logger.debug("writing " + picFileName); try { File outFile = new File(picFileName); outFile.getParentFile().mkdirs(); if(fileType.equals(PDF)) { if(titleBorder != null) { bsd.outputToPDF(outFile, (TitleBorder)titleBorder.createBorder(bsd)); } else { bsd.outputToPDF(outFile); } } else if(fileType.equals(PNG)) { bsd.outputToPNG(outFile, dims); } else { // should never happen throw new RuntimeException("Unknown fileType:" + fileType); } } catch(Throwable e) { GlobalExceptionHandler.handle("unable to save seismogram image to " + picFileName, e); } } } private SeismogramDisplayConfiguration sdc = new SeismogramDisplayConfiguration(); protected SeismogramImageOutputLocator locator; protected BorderConfiguration titleBorder; private TauPUtil tauP = TauPUtil.getTauPUtil(); private boolean showOnlyFirst; private SeismogramTitler titler; protected PhaseWindow phaseWindow; private PhasePhilter.PhaseRenamer renamer = new PhasePhilter.PhaseRenamer(); private String modelName = "iasp91"; protected String[] phaseFlagNames = DEFAULT_PHASES; protected Dimension dims = DEFAULT_DIMENSION; public static final String PDF = "pdf"; public static final String PNG = "png"; private static final String[] DEFAULT_PHASES = {}; private static Dimension DEFAULT_DIMENSION = new Dimension(500, 200); private Logger logger = LoggerFactory.getLogger(SeismogramImageProcess.class); }