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 third 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 = seisplotjs.stationxml.extractAllChannels(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 chanTRList = 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(chanTRList) ] );
Now that we have travel times and seismograms, we can plot both. We also link the seismographs so that they keep 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);
}).catch( function(error) {
seisplotjs.d3.select("div#myseismograph").append('p').html("Error loading data." +error);
console.assert(false, error);
});
Previous: Quakes and Channels