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.

Within the <head>, we need to add a link for the Leaflet's style sheet.


            <link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css"
              integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
              crossorigin=""/>
          

And 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>
            div#mapid {
              height: 300px;
              width: 100%;
            }
          

We just need to include the leaflet javascript file before our code.


          <script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"
             integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og=="
             crossorigin=""></script>
          

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. 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.


            const centerLat = 35;
            const centerLon = -100;
            const mapZoomLevel = 4;
            const mymap = L.map('mapid').setView([ centerLat, centerLon], mapZoomLevel);
            let OpenTopoMap = L.tileLayer('http://services.arcgisonline.com/arcgis/rest/services/Ocean/World_Ocean_Base/MapServer/tile/{z}/{y}/{x}', {
            	maxZoom: 17,
            	attribution: 'Map data: Esri, Garmin, GEBCO, NOAA NGDC, and other contributors)'
            }).addTo(mymap);

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

          

Then we call their query methods with a Promise.all() which will wait until both of these return before calling the then() function. Once we have the quake and station results, we plot them on the map. Even though we only expect one station and one quake to be returned, we will plot them as if there might be many for easier reuse. The station is a generic map marker, but the quake is plotted as a circle with the radius scaled by the magnitude. We then will use the more powerful POST method to get the seismograms. The SeismogramDisplayData works just like the StartEndDuration but also holds the channel that the time rage corresponds to and will contain the resulting seismogram. In this case the times are all the same, but might be different if we were using multiple stations and predicted arrival times. Note that we use the event time as the start and ask for a duration of 2400 seconds.


            Promise.all([ eventQuery.query(), stationQuery.queryChannels()])
            .then( ( [ quakeList, networks ] ) => {
                for (const q of quakeList) {
                  let circle = L.circleMarker([q.latitude, q.longitude], {
                    color: 'red',
                    fillColor: '#f03',
                    fillOpacity: 0.15,
                    radius: q.magnitude ? (q.magnitude.mag*5) : 3 // in case no mag
                  }).addTo(mymap);
                  circle.bindTooltip(q.time.toISOString()+" "+(q.magnitude ? (q.magnitude.mag+" "+q.magnitude.type) : "unkn"));
                }
                for (let s of seisplotjs.stationxml.allStations(networks)) {
                  let m = L.marker([s.latitude, s.longitude]);
                  m.addTo(mymap);
                  m.bindTooltip(s.codes());
                }

                let allChannels = Array.from(seisplotjs.stationxml.allChannels(networks));
                let timeWindow = new seisplotjs.util.StartEndDuration(quakeList[0].time, null, 2400);
                let seismogramDataList = allChannels.map(c => {
                  return seisplotjs.seismogram.SeismogramDisplayData.fromChannelAndTimeWindow(c, timeWindow);
                });
                let dsQuery = new seisplotjs.fdsndataselect.DataSelectQuery();
                return Promise.all([ quakeList, networks, dsQuery.postQuerySeismograms(seismogramDataList) ]);
            

Because we have the channel, we can also plot the seismograms corrected for overall gain and in units of m/s. We also added a couple of <span> elements to hold the station codes and earthquake description. 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, networks, seismogramDataList ] ) => {
            let div = seisplotjs.d3.select('div#myseismograph');
            let seisConfig = new seisplotjs.seismographconfig.SeismographConfig();
            seisConfig.title = seismogramDataList.map((sdd, i) => i===0?sdd.channel.codes():sdd.channel.channelCode);
            seisConfig.doGain = true;
            let graph = new seisplotjs.seismograph.Seismograph(div,
                                                               seisConfig,
                                                               seismogramDataList);
            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);
          });
          

See it live in tutorial3.html.

Previous: Let's get some real data

Next: Predicted phase arrival times