Seisplotjs Tutorial 3.1.3

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.


          <style>
            sp-station-quake-map {
              height: 300px;
              width: 90%;
            }
            sp-seismograph {
              height: 300px;
            }
          </style>
        

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


      <sp-station-quake-map centerLat="34" centerLon="-100" zoomLevel="5" fitBounds="false"
        tileUrl="https://services.arcgisonline.com/arcgis/rest/services/Ocean/World_Ocean_Base/MapServer/tile/{z}/{y}/{x}"
        tileAttribution="Tiles © Esri — Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri" >
      </sp-station-quake-map>
      

The markers on the map can also be styled with css, using the quakeMarker and stationMarker selectors. However, because we are using custom HTML elements , they are insulated from the CSS in the containing page, so we have to inject the CSS into the element. Many of the seisplotjs elements have a addStyle() method that will put the CSS into the correct place.

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.


const mymap = document.querySelector('sp-station-quake-map');

mymap.addStyle(`
  div.stationMapMarker {
    color: rebeccapurple;
  }
  path.quakeMapMarker {
    fill: orange;
    stroke: yellow;
    fill-opacity: 0.25;
  }
`);

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. The time range on the event query means the earthquake happened within the window, but for the station query it means the station was active (or at least existed) during some part of the window, i.e. it overlaps.


let queryTimeWindow = sp.util.startEnd('2019-07-01', '2019-07-31');
let eventQuery = new sp.fdsnevent.EventQuery()
  .timeRange(queryTimeWindow)
  .minMag(7)
  .latitude(35).longitude(-118)
  .maxRadius(3);
let stationQuery = new sp.fdsnstation.StationQuery()
  .networkCode('CO')
  .stationCode('HODGE')
  .locationCode('00')
  .channelCode('LH?')
  .timeRange(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.


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 also set the content of a couple of <span> elements to hold the station codes and earthquake description, assuming we will only get one of each. The easiest way to use the more powerful POST method of the FDSNWS dataselect web service to get the seismograms is to first create an array of SeismogramDisplayData objects, which start out as just having a channel and a time range. The time range starts at the origin time of the earthquake and has a duration of 2400 seconds. Then we manually attach the earthquake. The map knows to look for station and earthquake locations within SeismogramDisplayData objects, so we can plot both on the map by setting the seisData field on the map. Passing the array of SeismogramDisplayData objects to the postQuerySeismograms() function will query the remote DataSelect service and insert the actual seismograms into each SeismogramDisplayData when the DataSelect query returns. Of course this function will return a Promise.


Promise.all( [ quakePromise, stationsPromise ] )
.then( ( [ quakeList, networkList ] ) => {
  document.querySelector("span#stationCode").textContent = networkList[0].stations[0].codes();
  document.querySelector("span#earthquakeDescription").textContent = quakeList[0].description;
  let seismogramDataList = [];
  for (const q of quakeList) {
    const timeWindow = sp.util.startDuration(q.time, 2400);
    for (const c of sp.stationxml.allChannels(networkList)) {
      let sdd = sp.seismogram.SeismogramDisplayData.fromChannelAndTimeWindow(c, timeWindow);
      sdd.addQuake(q);
      seismogramDataList.push(sdd);
    }
  }
  mymap.seisData = seismogramDataList;
  let dsQuery = new sp.fdsndataselect.DataSelectQuery();
  return dsQuery.postQuerySeismograms(seismogramDataList);

Once the Promise returns, we will plot the seismograms using the already existing sp-seismograph element. 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( seismogramDataList => {
  seismogramDataList.forEach(sdd => {
    sdd.seismogram = sp.filter.rMean(sdd.seismogram);
  });
  let graph = document.querySelector('sp-seismograph');

  let seisConfigGain = new sp.seismographconfig.SeismographConfig();
  seisConfigGain.doGain = true;
  seisConfigGain.amplitudeMode = "mean";
  graph.seismographConfig = seisConfigGain;
  graph.seisData = 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