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);
});
Previous: Let"s get some real data