Seisplotjs Tutorial

Quakes and Channels:

See it live in tutorial3.html .

It would be nicer if we could add some useful labels to this instead of just a raw seismogram. Perhaps getting some information about this particular earthquake and the station it was recorded at. We will use the IRIS FDSNWS station web service to get the station and channels and the USGS FDSNWS event web service to get the earthquake. Since we will have the locations for the quake and station, we might as well plot them on a map. We can use Leaflet , a javascript map library that creates nice looking maps and has lots of flexibility, for this. Seisplotjs includes leaflet and provides helper functions in the seisplotjs.leafletutil module.

We can add some additional styling to size the map. Because the quake and the station are more or less east west of each other, we can use a map that is relatively narrow vertically, but spans the entire window. The markers on the map can also be styled with css, using the quakeMarker and stationMarker selectors. We will change the color from the default red and blue to illustrate. Note that the station icon is actually a div that contains a triangle unicode character, \u25B2, while the quake marker is a circle created from an svg path element and so we use stoke and fill instead of color.


          <style>
            div#mapid {
              height: 300px;
              width: 100%;
            }
            div.stationMapMarker {
              color: rebeccapurple;
            }
            path.quakeMapMarker {
              fill: orange;
              stroke: yellow;
              fill-opacity: 0.25;
            }
          </style>
        

First, we will create the map, centering it on 35/-100 and at zoom level 4. The World Ocean Base tile layer gives a nice background.


import {
  fdsndataselect, fdsnevent, fdsnstation,
  seismogram, seismograph,
  seismographconfig,
  stationxml,
  util, luxon} from './seisplotjs_3.0.0-alpha.1_standalone.mjs';

const mymap = document.querySelector('sp-station-event-map');
//mymap.scrollWheelZoom.disable();


Then we will create the queries for the quake and station. Again, both of these queries are asynchronous and so we will have to use promises. We first create both the EventQuery and StationQuery objects.


let queryTimeWindow = luxon.Interval.fromDateTimes(util.isoToDateTime('2019-07-01'), util.isoToDateTime('2019-07-31'));
let eventQuery = new fdsnevent.EventQuery()
  .timeWindow(queryTimeWindow)
  .minMag(7)
  .latitude(35).longitude(-118)
  .maxRadius(3);
let stationQuery = new fdsnstation.StationQuery()
  .networkCode('CO')
  .stationCode('HODGE')
  .locationCode('00')
  .channelCode('LH?')
  .timeWindow(queryTimeWindow);

Next we call the query methods for eventQuery and stationQuery, which each return a Promise to an array of Network or Quake objects and then use those to plot the earthquakes and stations on the map. The station will be plotted as a generic triangle as the map marker, but the quake is plotted as a circle with the radius scaled by the magnitude. We also added a couple of <span> elements to hold the station codes and earthquake description. We set the these elements assuming we will only get one of each.


let stationsPromise = stationQuery.queryChannels();
let quakePromise = eventQuery.query();

We then use a separate then() for the actual seismograms. Because we will need both the earthquake and the chanenls, we use Promise.all() to ensure both have successfully completed. We then will use the more powerful POST method of the FDSNWS dataselect web service to get the seismograms. The array of SeismogramDisplayData objects works just like the StartEndDuration we saw previously but also holds the channel and the quake and will also contain the resulting seismogram. We ask for data starting at the origin time of the earthquake and with a duration of 2400 seconds.


Promise.all( [ quakePromise, stationsPromise ] )
.then( ( [ quakeList, networkList ] ) => {
  let seismogramDataList = [];
  for (const q of quakeList) {
    let timeWindow = luxon.Interval.after(q.time, luxon.Duration.fromMillis(1000*2400));
    for (const c of stationxml.allChannels(networkList)) {
      let sdd = seismogram.SeismogramDisplayData.fromChannelAndTimeWindow(c, timeWindow);
      sdd.addQuake(q);
      seismogramDataList.push(sdd);
    }
  }
  mymap.seisData = seismogramDataList;
  mymap.seisData.forEach(sdd => {
    console.log(`${sdd.quakeList.length}  ${sdd.quake}`);
  })
  let dsQuery = new fdsndataselect.DataSelectQuery();
  let sddPromise = dsQuery.postQuerySeismograms(seismogramDataList);
  return Promise.all( [ quakePromise, stationsPromise, sddPromise ] );

Once these are loaded we will plot them just like we did before. Because we have the channel, we can also plot the seismograms corrected for overall gain and in units of m/s. The default text coloring colors the seismograms and the corresponding array of strings in the title the same color, making them easier to identify. Note the units on the left hand side are now m/s.


}).then( ( [ quakeList, networkList, seismogramDataList ] ) => {
    let div = document.querySelector('div#myseismograph');
    let seisConfig = new seismographconfig.SeismographConfig();
    seisConfig.doGain = false;
    seisConfig.centeredAmp = true;
    let graphCount = new seismograph.Seismograph(seismogramDataList, seisConfig);
    div.appendChild(graphCount);

    let seisConfigGain = new seismographconfig.SeismographConfig();
    seisConfigGain.doGain = true;
    seisConfigGain.centeredAmp = true;
    let graphGain = new seismograph.Seismograph(seismogramDataList, seisConfigGain);
    div.appendChild(graphGain);
    return seismogramDataList;
}).catch( function(error) {
  const div = document.querySelector('div#myseismograph');
  div.innerHTML = `
    <p>Error loading data. ${error}</p>
  `;
  console.assert(false, error);
});

See it live in tutorial3.html .

Previous: Let"s get some real data

Next: Predicted phase arrival times