Seisplotjs Tutorial

Predicted Phase Arrival Times:

See it live in tutorial4.html.

It would be nice to know where the P and S wave are predicted to arrive. We can use the IRIS traveltime web service to to get travel times for seismic phases. The traveltime web service uses The TauP Toolkit under the hood, and so the documentation for it can help. We will keep things simple and just ask for P and S phases. Again, this is a remote service, therefore asynchronous, and we will need to use promises again. We put an additional then() call after we get the quake and station but before we ask for the seismograms. This allows us to use the predicted travel times to pick the time window starting 30 seconds prior to the first P arrival.


           Promise.all( [ eventQuery.query(), stationQuery.queryChannels() ] )
             .then( ( [ quakeList, networks ] ) => {
               let taupQuery = new seisplotjs.traveltime.TraveltimeQuery()
                 .latLonFromStation(networks[0].stations[0])
                 .latLonFromQuake(quakeList[0])
                 .phases("P,S");
               return Promise.all( [ quakeList, networks, taupQuery.queryJson() ] );
          

Now in the next then, we can use add the travel time to the origin time to get a start for our request. The little flags for phase arrivals are Marker objects and we put them and the quake into our SeismogramDisplayData objects. One important thing to keep in mind with time is that the moment objects from the momentjs library that we use are mutible, and so you should always create a copy before modifying like seisplotjs.moment.utc(quakeList[0].time) as otherwise you will change the origin time of the quake. The postQuerySeismograms() will parse the miniseed in the response and then create seismograms within each SeismogramDisplayData object, making it easy to associate the new waveform with the request time window, channel, and quake.


          }).then( ( [ quakeList, networks, ttimes ] ) => {
            let phaseMarkers = seisplotjs.seismograph.createMarkersForTravelTimes(quakeList[0], ttimes);
            phaseMarkers.push({
              markertype: 'predicted',
              name: "origin",
              time: seisplotjs.moment.utc(quakeList[0].time)
            });
            let allChannels = Array.from(seisplotjs.stationxml.allChannels(networks));
            let firstP = ttimes.arrivals.find(a => a.phase.startsWith('P'));
            let startTime = seisplotjs.moment.utc(quakeList[0].time)
              .add(firstP.time, 'seconds').subtract(30, 'seconds');
            let timeWindow = new seisplotjs.util.StartEndDuration(startTime, null, 1800);
            let seismogramDataList = allChannels.map(c => {
              let sdd = seisplotjs.seismogram.SeismogramDisplayData.fromChannelAndTimeWindow(c, timeWindow);
              sdd.addQuake(quakeList);
              sdd.addMarkers(phaseMarkers);
              return sdd;
            });
            let dsQuery = new seisplotjs.fdsndataselect.DataSelectQuery();
            return Promise.all( [ quakeList, networks, ttimes, dsQuery.postQuerySeismograms(seismogramDataList) ] );
          

Now that we have travel times and seismograms, we can plot both. We also link the seismographs so that they stay aligned with each other in time and amplitude.


          }).then( ( [ quakeList, networks, ttimes, seismogramDataList ] ) => {
            let div = seisplotjs.d3.select('div#myseismograph');
            let graphList = [];
            for( let sdd of seismogramDataList) {
              let seisConfig = new seisplotjs.seismographconfig.SeismographConfig();
              seisConfig.title = sdd.channel.codes();
              seisConfig.wheelZoom = false;
              seisConfig.doGain = true;
              let subdiv = div.append('div').classed('seismograph', true);
              let graph = new seisplotjs.seismograph.Seismograph(subdiv,
                                                                 seisConfig,
                                                                 sdd);
              graphList.forEach(g => graph.linkXScaleTo(g));
              graphList.forEach(g => graph.linkYScaleTo(g));
              graphList.push(graph);
              graph.draw();
            }
            seisplotjs.d3.select('span#stationCode').text(networks[0].stations[0].codes());
            seisplotjs.d3.select('span#earthquakeDescription').text(quakeList[0].description);
            return Promise.all([ quakeList, networks, ttimes, seismogramDataList ]);
          

For a little extra, we also can plot the particle motion around the P wave for these seismograms. First we need to add a div to to the html.


            <div  id="particlemotion">
            </div>
          

And some styling in the <style> at the top.


          div.particlemotionContainer  {
            float:left;
            height: 300px;
            width: 320px;
          }
          

And then javascript to create the particle motion plots. This uses 60 seconds around the S wave.


        }).then( ( [ quakeList, networks, ttimes, seismogramDataList, graphList ] ) => {
          let div = seisplotjs.d3.select('div#particlemotion');
          let firstS = ttimes.arrivals.find(a => a.phase.startsWith('S'));
          let windowDuration = 60;
          let firstSTimeWindow = new seisplotjs.util.StartEndDuration(
            seisplotjs.moment.utc(quakeList[0].time).add(firstS.time, 'seconds').subtract(windowDuration/4, 'seconds'),
            null,
            windowDuration);
          seismogramDataList.forEach(sdd => sdd.addMarkers({
            name: "pm start",
            time: firstSTimeWindow.startTime,
            type: "other",
            description: "pm start"}));
          seismogramDataList.forEach(sdd => sdd.addMarkers({
            name: "pm end",
            time: firstSTimeWindow.endTime,
            type: "other",
            description: "pm end"}));
          graphList.forEach(g => g.drawMarkers());
          let xSeisData = seismogramDataList[0].cut(firstSTimeWindow);
          let ySeisData = seismogramDataList[1].cut(firstSTimeWindow);
          let zSeisData = seismogramDataList[2].cut(firstSTimeWindow);
          let spanA = div.append('div').classed('particlemotionContainer', true);
          let spanB = div.append('div').classed('particlemotionContainer', true);
          let spanC = div.append('div').classed('particlemotionContainer', true);

          let minMax = seisplotjs.seismogram.findMinMax([ xSeisData, ySeisData, zSeisData]);

          let seismographConfig = new seisplotjs.seismographconfig.SeismographConfig();
          seismographConfig.margin.left = 60;
          seismographConfig.margin.top = seismographConfig.margin.bottom;
          seismographConfig.margin.right = seismographConfig.margin.left;
          seismographConfig.fixedTimeScale = firstSTimeWindow;
          seismographConfig.fixedYScale = minMax;

          let seisConfigA = seismographConfig.clone();
          seisConfigA.title = xSeisData.channelCode+" "+ySeisData.channelCode;
          seisConfigA.xLabel = xSeisData.channelCode;
          seisConfigA.yLabel = ySeisData.channelCode;
          let pmpA = new seisplotjs.particlemotion.ParticleMotion(spanA, seisConfigA, xSeisData, ySeisData);
          pmpA.draw();

          let seisConfigB = seismographConfig.clone();
          seisConfigB.title = xSeisData.channelCode+" "+zSeisData.channelCode;
          seisConfigB.xLabel = xSeisData.channelCode;
          seisConfigB.yLabel = zSeisData.channelCode;
          let pmpB = new seisplotjs.particlemotion.ParticleMotion(spanB, seisConfigB, xSeisData, zSeisData);
          pmpB.draw();

          let seisConfigC = seismographConfig.clone();
          seisConfigC.title = ySeisData.channelCode+" "+zSeisData.channelCode;
          seisConfigC.xLabel = ySeisData.channelCode;
          seisConfigC.yLabel = zSeisData.channelCode;
          let pmpC = new seisplotjs.particlemotion.ParticleMotion(spanC, seisConfigC, ySeisData, zSeisData);
          pmpC.draw();

          return Promise.all([ quakeList, networks, ttimes, seismogramDataList ]);
        }).catch( function(error) {
          seisplotjs.d3.select("div#myseismograph").append('p').html("Error loading data." +error);
          console.assert(false, error);
        });
          

See it live in tutorial4.html.

Previous: Quakes and Channels

Next: Deconvolution and Filtering