diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..de288e1eab6a537caed290ce0fc5e70be7ae9dba --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.formatting.provider": "black" +} \ No newline at end of file diff --git a/Gradio_app.ipynb b/Gradio_app.ipynb index a3d5ff79fe658ddbeeefc6df6c467b1f1831273c..c49a0a948853ba1719cb4c7890a416f3d5c66488 100644 --- a/Gradio_app.ipynb +++ b/Gradio_app.ipynb @@ -2,42 +2,14 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 64, "metadata": {}, "outputs": [ - { - "data": { - "text/plain": [ - "'hi!'" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "'hi!'" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/anovosel/miniconda3/envs/phasehunter/lib/python3.11/site-packages/gradio/outputs.py:43: UserWarning: Usage of gradio.outputs is deprecated, and will not be supported in the future, please import your components from gradio.components\n", - " warnings.warn(\n" - ] - }, { "name": "stdout", "output_type": "stream", "text": [ - "Running on local URL: http://127.0.0.1:7862\n", + "Running on local URL: http://127.0.0.1:7904\n", "\n", "To create a public link, set `share=True` in `launch()`.\n" ] @@ -45,7 +17,7 @@ { "data": { "text/html": [ - "
" + "
" ], "text/plain": [ "" @@ -58,30 +30,9 @@ "data": { "text/plain": [] }, - "execution_count": 4, + "execution_count": 64, "metadata": {}, "output_type": "execute_result" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Error in callback (for post_execute):\n" - ] - }, - { - "ename": "KeyboardInterrupt", - "evalue": "", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m~/miniconda3/envs/phasehunter/lib/python3.11/site-packages/matplotlib/pyplot.py:120\u001b[0m, in \u001b[0;36m_draw_all_if_interactive\u001b[0;34m()\u001b[0m\n\u001b[1;32m 118\u001b[0m \u001b[39mdef\u001b[39;00m \u001b[39m_draw_all_if_interactive\u001b[39m():\n\u001b[1;32m 119\u001b[0m \u001b[39mif\u001b[39;00m matplotlib\u001b[39m.\u001b[39mis_interactive():\n\u001b[0;32m--> 120\u001b[0m draw_all()\n", - "File \u001b[0;32m~/miniconda3/envs/phasehunter/lib/python3.11/site-packages/matplotlib/_pylab_helpers.py:132\u001b[0m, in \u001b[0;36mGcf.draw_all\u001b[0;34m(cls, force)\u001b[0m\n\u001b[1;32m 130\u001b[0m \u001b[39mfor\u001b[39;00m manager \u001b[39min\u001b[39;00m \u001b[39mcls\u001b[39m\u001b[39m.\u001b[39mget_all_fig_managers():\n\u001b[1;32m 131\u001b[0m \u001b[39mif\u001b[39;00m force \u001b[39mor\u001b[39;00m manager\u001b[39m.\u001b[39mcanvas\u001b[39m.\u001b[39mfigure\u001b[39m.\u001b[39mstale:\n\u001b[0;32m--> 132\u001b[0m manager\u001b[39m.\u001b[39;49mcanvas\u001b[39m.\u001b[39;49mdraw_idle()\n", - "File \u001b[0;32m~/miniconda3/envs/phasehunter/lib/python3.11/site-packages/matplotlib/backend_bases.py:2082\u001b[0m, in \u001b[0;36mFigureCanvasBase.draw_idle\u001b[0;34m(self, *args, **kwargs)\u001b[0m\n\u001b[1;32m 2080\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_is_idle_drawing:\n\u001b[1;32m 2081\u001b[0m \u001b[39mwith\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_idle_draw_cntx():\n\u001b[0;32m-> 2082\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mdraw(\u001b[39m*\u001b[39;49margs, \u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mkwargs)\n", - "File \u001b[0;32m~/miniconda3/envs/phasehunter/lib/python3.11/site-packages/matplotlib/backends/backend_agg.py:397\u001b[0m, in \u001b[0;36mFigureCanvasAgg.draw\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 395\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mrenderer\u001b[39m.\u001b[39mclear()\n\u001b[1;32m 396\u001b[0m \u001b[39m# Acquire a lock on the shared font cache.\u001b[39;00m\n\u001b[0;32m--> 397\u001b[0m \u001b[39mwith\u001b[39;49;00m RendererAgg\u001b[39m.\u001b[39;49mlock, \\\n\u001b[1;32m 398\u001b[0m (\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mtoolbar\u001b[39m.\u001b[39;49m_wait_cursor_for_draw_cm() \u001b[39mif\u001b[39;49;00m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mtoolbar\n\u001b[1;32m 399\u001b[0m \u001b[39melse\u001b[39;49;00m nullcontext()):\n\u001b[1;32m 400\u001b[0m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mfigure\u001b[39m.\u001b[39;49mdraw(\u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mrenderer)\n\u001b[1;32m 401\u001b[0m \u001b[39m# A GUI class may be need to update a window using this draw, so\u001b[39;49;00m\n\u001b[1;32m 402\u001b[0m \u001b[39m# don't forget to call the superclass.\u001b[39;49;00m\n", - "\u001b[0;31mKeyboardInterrupt\u001b[0m: " - ] } ], "source": [ @@ -95,6 +46,8 @@ "import torch\n", "\n", "from scipy.stats import gaussian_kde\n", + "from bmi_topography import Topography\n", + "import earthpy.spatial as es\n", "\n", "import obspy\n", "from obspy.clients.fdsn import Client\n", @@ -107,6 +60,7 @@ "\n", "import matplotlib.pyplot as plt\n", "import matplotlib.dates as mdates\n", + "from matplotlib.colors import LightSource\n", "\n", "from glob import glob\n", "\n", @@ -123,21 +77,25 @@ "\n", " return processed_input, p_phase, s_phase\n", "\n", - "def mark_phases(waveform):\n", + "def mark_phases(waveform, uploaded_file):\n", + "\n", + " if uploaded_file is not None:\n", + " waveform = uploaded_file.name\n", + "\n", " processed_input, p_phase, s_phase = make_prediction(waveform)\n", "\n", " # Create a plot of the waveform with the phases marked\n", " if sum(processed_input[0][2] == 0): #if input is 1C\n", " fig, ax = plt.subplots(nrows=2, figsize=(10, 2), sharex=True)\n", "\n", - " ax[0].plot(processed_input[0][0])\n", + " ax[0].plot(processed_input[0][0], color='black', lw=1)\n", " ax[0].set_ylabel('Norm. Ampl.')\n", "\n", " else: #if input is 3C\n", " fig, ax = plt.subplots(nrows=4, figsize=(10, 6), sharex=True)\n", - " ax[0].plot(processed_input[0][0])\n", - " ax[1].plot(processed_input[0][1])\n", - " ax[2].plot(processed_input[0][2])\n", + " ax[0].plot(processed_input[0][0], color='black', lw=1)\n", + " ax[1].plot(processed_input[0][1], color='black', lw=1)\n", + " ax[2].plot(processed_input[0][2], color='black', lw=1)\n", "\n", " ax[0].set_ylabel('Z')\n", " ax[1].set_ylabel('N')\n", @@ -169,22 +127,39 @@ " plt.close(fig)\n", " return image\n", "\n", + "def bin_distances(distances, bin_size=10):\n", + " # Bin the distances into groups of `bin_size` kilometers\n", + " binned_distances = {}\n", + " for i, distance in enumerate(distances):\n", + " bin_index = distance // bin_size\n", + " if bin_index not in binned_distances:\n", + " binned_distances[bin_index] = (distance, i)\n", + " elif i < binned_distances[bin_index][1]:\n", + " binned_distances[bin_index] = (distance, i)\n", + "\n", + " # Select the first distance in each bin and its index\n", + " first_distances = []\n", + " for bin_index in binned_distances:\n", + " first_distance, first_distance_index = binned_distances[bin_index]\n", + " first_distances.append(first_distance_index)\n", + " \n", + " return first_distances\n", + "\n", "def variance_coefficient(residuals):\n", " # calculate the variance of the residuals\n", " var = residuals.var()\n", - " \n", " # scale the variance to a coefficient between 0 and 1\n", " coeff = 1 - (var / (residuals.max() - residuals.min()))\n", - " \n", " return coeff\n", "\n", - "def predict_on_section(client_name, timestamp, eq_lat, eq_lon, radius_km, source_depth_km, velocity_model):\n", + "def predict_on_section(client_name, timestamp, eq_lat, eq_lon, radius_km, source_depth_km, velocity_model, max_waveforms):\n", " distances, t0s, st_lats, st_lons, waveforms = [], [], [], [], []\n", " \n", " taup_model = TauPyModel(model=velocity_model)\n", " client = Client(client_name)\n", "\n", " window = radius_km / 111.2\n", + " max_waveforms = int(max_waveforms)\n", "\n", " assert eq_lat - window > -90 and eq_lat + window < 90, \"Latitude out of bounds\"\n", " assert eq_lon - window > -180 and eq_lon + window < 180, \"Longitude out of bounds\"\n", @@ -192,59 +167,107 @@ " starttime = obspy.UTCDateTime(timestamp)\n", " endtime = starttime + 120\n", "\n", - " inv = client.get_stations(network=\"*\", station=\"*\", location=\"*\", channel=\"*H*\", \n", - " starttime=starttime, endtime=endtime, \n", - " minlatitude=(eq_lat-window), maxlatitude=(eq_lat+window),\n", - " minlongitude=(eq_lon-window), maxlongitude=(eq_lon+window), \n", - " level='station')\n", + " try:\n", + " print('Starting to download inventory')\n", + " inv = client.get_stations(network=\"*\", station=\"*\", location=\"*\", channel=\"*H*\", \n", + " starttime=starttime, endtime=endtime, \n", + " minlatitude=(eq_lat-window), maxlatitude=(eq_lat+window),\n", + " minlongitude=(eq_lon-window), maxlongitude=(eq_lon+window), \n", + " level='station')\n", + " print('Finished downloading inventory')\n", + " except (IndexError, FDSNNoDataException, FDSNTimeoutException, FDSNInternalServerException):\n", + " fig, ax = plt.subplots()\n", + " ax.text(0.5,0.5,'Something is wrong with the data provider, try another')\n", + " fig.canvas.draw();\n", + " image = np.array(fig.canvas.renderer.buffer_rgba())\n", + " plt.close(fig)\n", + " return image\n", " \n", " waveforms = []\n", " cached_waveforms = glob(\"data/cached/*.mseed\")\n", "\n", " for network in inv:\n", + " # Skip the SYntetic networks\n", + " if network.code == 'SY':\n", + " continue\n", " for station in network:\n", - " try:\n", - " distance = locations2degrees(eq_lat, eq_lon, station.latitude, station.longitude)\n", - "\n", - " arrivals = taup_model.get_travel_times(source_depth_in_km=source_depth_km, \n", - " distance_in_degree=distance, \n", - " phase_list=[\"P\", \"S\"])\n", + " print(f\"Processing {network.code}.{station.code}...\")\n", + " distance = locations2degrees(eq_lat, eq_lon, station.latitude, station.longitude)\n", "\n", - " if len(arrivals) > 0:\n", + " arrivals = taup_model.get_travel_times(source_depth_in_km=source_depth_km, \n", + " distance_in_degree=distance, \n", + " phase_list=[\"P\", \"S\"])\n", "\n", - " starttime = obspy.UTCDateTime(timestamp) + arrivals[0].time - 15\n", - " endtime = starttime + 60\n", + " if len(arrivals) > 0:\n", "\n", + " starttime = obspy.UTCDateTime(timestamp) + arrivals[0].time - 15\n", + " endtime = starttime + 60\n", + " try:\n", " if f\"data/cached/{network.code}_{station.code}_{starttime}.mseed\" not in cached_waveforms:\n", + " print('Downloading waveform')\n", " waveform = client.get_waveforms(network=network.code, station=station.code, location=\"*\", channel=\"*\", \n", " starttime=starttime, endtime=endtime)\n", " waveform.write(f\"data/cached/{network.code}_{station.code}_{starttime}.mseed\", format=\"MSEED\")\n", + " print('Finished downloading and caching waveform')\n", " else:\n", + " print('Reading cached waveform')\n", " waveform = obspy.read(f\"data/cached/{network.code}_{station.code}_{starttime}.mseed\")\n", - " \n", - " waveform = waveform.select(channel=\"H[BH][ZNE]\")\n", - " waveform = waveform.merge(fill_value=0)\n", - " waveform = waveform[:3]\n", - " \n", - " len_check = [len(x.data) for x in waveform]\n", - " if len(set(len_check)) > 1:\n", - " continue\n", + " \n", + "\n", + " except (IndexError, FDSNNoDataException, FDSNTimeoutException, FDSNInternalServerException):\n", + " print(f'Skipping {network.code}_{station.code}_{starttime}')\n", + " continue\n", + " \n", + " waveform = waveform.select(channel=\"H[BH][ZNE]\")\n", + " waveform = waveform.merge(fill_value=0)\n", + " waveform = waveform[:3]\n", + " \n", + " len_check = [len(x.data) for x in waveform]\n", + " if len(set(len_check)) > 1:\n", + " continue\n", + "\n", + " if len(waveform) == 3:\n", + " try:\n", + " waveform = prepare_waveform(np.stack([x.data for x in waveform]))\n", "\n", - " if len(waveform) == 3:\n", - " try:\n", - " waveform = prepare_waveform(np.stack([x.data for x in waveform]))\n", - " except:\n", - " continue\n", - " \n", " distances.append(distance)\n", " t0s.append(starttime)\n", " st_lats.append(station.latitude)\n", " st_lons.append(station.longitude)\n", " waveforms.append(waveform)\n", "\n", - " except (IndexError, FDSNNoDataException, FDSNTimeoutException):\n", - " continue\n", + " print(f\"Added {network.code}.{station.code} to the list of waveforms\")\n", + "\n", + " except:\n", + " continue\n", + " \n", + " \n", + " # If there are no waveforms, return an empty plot\n", + " if len(waveforms) == 0:\n", + " fig, ax = plt.subplots()\n", + " ax.text(0.5,0.5,'No waveforms found')\n", + " fig.canvas.draw();\n", + " image = np.array(fig.canvas.renderer.buffer_rgba())\n", + " plt.close(fig)\n", + " return image\n", + " \n", + "\n", + " first_distances = bin_distances(distances, bin_size=10/111.2)\n", + "\n", + " # Edge case when there are way too many waveforms to process\n", + " selection_indexes = np.random.choice(first_distances, \n", + " np.min([len(first_distances), max_waveforms]),\n", + " replace=False)\n", + "\n", + " waveforms = np.array(waveforms)[selection_indexes]\n", + " distances = np.array(distances)[selection_indexes]\n", + " t0s = np.array(t0s)[selection_indexes]\n", + " st_lats = np.array(st_lats)[selection_indexes]\n", + " st_lons = np.array(st_lons)[selection_indexes]\n", "\n", + " waveforms = [torch.tensor(waveform) for waveform in waveforms]\n", + "\n", + " print('Starting to run predictions')\n", " with torch.no_grad():\n", " waveforms_torch = torch.vstack(waveforms)\n", " output = model(waveforms_torch)\n", @@ -256,8 +279,31 @@ " p_max_confidence = np.min([p_phases[i::len(waveforms)].std() for i in range(len(waveforms))]) \n", " s_max_confidence = np.min([s_phases[i::len(waveforms)].std() for i in range(len(waveforms))])\n", "\n", - " fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(10, 3), sharex=True)\n", + " print(f\"Starting plotting {len(waveforms)} waveforms\")\n", + " fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(10, 3))\n", + "\n", + " # Plot topography\n", + " print('Fetching topography')\n", + " params = Topography.DEFAULT.copy()\n", + " extra_window = 0.5\n", + " params[\"south\"] = np.min([st_lats.min(), eq_lat])-extra_window\n", + " params[\"north\"] = np.max([st_lats.max(), eq_lat])+extra_window\n", + " params[\"west\"] = np.min([st_lons.min(), eq_lon])-extra_window\n", + " params[\"east\"] = np.max([st_lons.max(), eq_lon])+extra_window\n", + "\n", + " topo_map = Topography(**params)\n", + " topo_map.fetch()\n", + " topo_map.load()\n", + "\n", + " print('Plotting topo')\n", + " hillshade = es.hillshade(topo_map.da[0], altitude=10)\n", + " \n", + " topo_map.da.plot(ax = ax[1], cmap='Greys', add_colorbar=False, add_labels=False)\n", + " topo_map.da.plot(ax = ax[2], cmap='Greys', add_colorbar=False, add_labels=False)\n", + " ax[1].imshow(hillshade, cmap=\"Greys\", alpha=0.5)\n", + "\n", " for i in range(len(waveforms)):\n", + " print(f\"Plotting waveform {i+1}/{len(waveforms)}\")\n", " current_P = p_phases[i::len(waveforms)]\n", " current_S = s_phases[i::len(waveforms)]\n", "\n", @@ -275,17 +321,46 @@ " ax[0].set_ylabel('Z')\n", "\n", " ax[0].xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S'))\n", - " ax[0].xaxis.set_major_locator(mdates.SecondLocator(interval=5))\n", + " ax[0].xaxis.set_major_locator(mdates.SecondLocator(interval=20))\n", + "\n", + " delta_t = t0s[i].timestamp - obspy.UTCDateTime(timestamp).timestamp\n", + "\n", + " velocity_p = (distances[i]*111.2)/(delta_t+current_P.mean()*60).item()\n", + " velocity_s = (distances[i]*111.2)/(delta_t+current_S.mean()*60).item()\n", + "\n", + " print(f\"Station {st_lats[i]}, {st_lons[i]} has P velocity {velocity_p} and S velocity {velocity_s}\")\n", " \n", + " # Generate an array from st_lat to eq_lat and from st_lon to eq_lon\n", + " x = np.linspace(st_lons[i], eq_lon, 50)\n", + " y = np.linspace(st_lats[i], eq_lat, 50)\n", + " \n", + " # Plot the array\n", + " ax[1].scatter(x, y, c=np.zeros_like(x)+velocity_p, alpha=0.5, vmin=0, vmax=8)\n", + " ax[2].scatter(x, y, c=np.zeros_like(x)+velocity_s, alpha=0.5, vmin=0, vmax=8)\n", + "\n", + " # Add legend\n", " ax[0].scatter(None, None, color='r', marker='|', label='P')\n", " ax[0].scatter(None, None, color='b', marker='|', label='S')\n", " ax[0].legend()\n", "\n", - " ax[1].scatter(st_lats, st_lons, color='b', marker='d', label='Stations')\n", - " ax[1].scatter(eq_lat, eq_lon, color='r', marker='*', label='Earthquake')\n", - " ax[1].legend()\n", - " plt.subplots_adjust(hspace=0., wspace=0.)\n", - " \n", + " print('Plotting stations')\n", + " for i in range(1,3):\n", + " ax[i].scatter(st_lons, st_lats, color='b', label='Stations')\n", + " ax[i].scatter(eq_lon, eq_lat, color='r', marker='*', label='Earthquake')\n", + "\n", + " # Generate colorbar for the velocity plot\n", + " cbar = plt.colorbar(ax[1].scatter(None, None, c=velocity_p, alpha=0.5, vmin=0, vmax=8), ax=ax[1])\n", + " cbar.set_label('P Velocity (km/s)')\n", + " ax[1].set_title('P Velocity')\n", + "\n", + " cbar = plt.colorbar(ax[2].scatter(None, None, c=velocity_s, alpha=0.5, vmin=0, vmax=8), ax=ax[2])\n", + " cbar.set_label('S Velocity (km/s)')\n", + " ax[2].set_title('S Velocity')\n", + "\n", + "\n", + "\n", + " plt.subplots_adjust(hspace=0., wspace=0.5)\n", + "\n", " fig.canvas.draw();\n", " image = np.array(fig.canvas.renderer.buffer_rgba())\n", " plt.close(fig)\n", @@ -299,30 +374,54 @@ "model.eval()\n", "\n", "with gr.Blocks() as demo:\n", - " gr.Markdown(\"# PhaseHunter\")\n", - " gr.Markdown(\"\"\"This app allows one to detect P and S seismic phases along with uncertainty of the detection. \n", - " The app can be used in three ways: either by selecting one of the sample waveforms;\n", - " or by selecting an earthquake from the global earthquake catalogue;\n", - " or by uploading a waveform of interest.\n", + " gr.HTML(\"\"\"

PhaseHunter

\n", + "

This app allows one to detect P and S seismic phases along with \n", + "\n", + "\n", + " u\n", + " n\n", + " c\n", + " e\n", + " r\n", + " t\n", + " a\n", + " i\n", + " n\n", + " t\n", + " y\n", + " \n", + "\n", + " of the detection.

\n", + "
    \n", + "
  1. By selecting one of the sample waveforms.
  2. \n", + "
  3. By uploading your own waveform.
  4. \n", + "
  5. By selecting an earthquake from the global earthquake catalogue.
  6. \n", + "
\n", + "

Please upload your waveform in .npy (numpy) format.

\n", + "

Your waveform should be sampled at 100 samples per second and have 3 (Z, N, E) or 1 (Z) channels. If your file is longer than 60 seconds, the app will only use the first 60 seconds of the waveform.

\n", " \"\"\")\n", - " with gr.Tab(\"Default example\"):\n", - " # Define the input and output types for Gradio\n", - " inputs = gr.Dropdown(\n", - " [\"data/sample/sample_0.npy\", \n", - " \"data/sample/sample_1.npy\", \n", - " \"data/sample/sample_2.npy\"], \n", - " label=\"Sample waveform\", \n", - " info=\"Select one of the samples\",\n", - " value = \"data/sample/sample_0.npy\"\n", - " )\n", + " with gr.Tab(\"Try on a single station\"):\n", + " with gr.Row(): \n", + " # Define the input and output types for Gradio\n", + " inputs = gr.Dropdown(\n", + " [\"data/sample/sample_0.npy\", \n", + " \"data/sample/sample_1.npy\", \n", + " \"data/sample/sample_2.npy\"], \n", + " label=\"Sample waveform\", \n", + " info=\"Select one of the samples\",\n", + " value = \"data/sample/sample_0.npy\"\n", + " )\n", + "\n", + " upload = gr.File(label=\"Or upload your own waveform\")\n", "\n", " button = gr.Button(\"Predict phases\")\n", - " outputs = gr.outputs.Image(label='Waveform with Phases Marked', type='numpy')\n", + " outputs = gr.Image(label='Waveform with Phases Marked', type='numpy', interactive=False)\n", " \n", - " button.click(mark_phases, inputs=inputs, outputs=outputs)\n", + " button.click(mark_phases, inputs=[inputs, upload], outputs=outputs)\n", " \n", " with gr.Tab(\"Select earthquake from catalogue\"):\n", - " gr.Markdown('TEST')\n", + " gr.Markdown(\"\"\"Select an earthquake from the global earthquake catalogue and the app will download the waveform from the FDSN client of your choice.\n", + " \"\"\")\n", " \n", " client_inputs = gr.Dropdown(\n", " choices = list(URL_MAPPINGS.keys()), \n", @@ -331,6 +430,7 @@ " value = \"IRIS\",\n", " interactive=True\n", " )\n", + "\n", " with gr.Row(): \n", "\n", " timestamp_inputs = gr.Textbox(value='2019-07-04 17:33:49',\n", @@ -364,13 +464,23 @@ " interactive=True)\n", " \n", " velocity_inputs = gr.Dropdown(\n", - " choices = ['1066a', '1066b', 'ak135', 'ak135f', 'herrin', 'iasp91', 'jb', 'prem', 'pwdk'], \n", + " choices = ['1066a', '1066b', 'ak135', \n", + " 'ak135f', 'herrin', 'iasp91', \n", + " 'jb', 'prem', 'pwdk'], \n", " label=\"1D velocity model\", \n", " info=\"Velocity model for station selection\",\n", " value = \"1066a\",\n", " interactive=True\n", " )\n", - " \n", + "\n", + " max_waveforms_inputs = gr.Slider(minimum=1,\n", + " maximum=100,\n", + " value=10,\n", + " label=\"Max waveforms per section\",\n", + " step=1,\n", + " info=\"Maximum number of waveforms to show per section\\n (to avoid long prediction times)\",\n", + " interactive=True,\n", + " )\n", " \n", " button = gr.Button(\"Predict phases\")\n", " outputs_section = gr.Image(label='Waveforms with Phases Marked', type='numpy', interactive=False)\n", @@ -378,15 +488,10 @@ " button.click(predict_on_section, \n", " inputs=[client_inputs, timestamp_inputs, \n", " eq_lat_inputs, eq_lon_inputs, \n", - " radius_inputs, source_depth_inputs, velocity_inputs],\n", + " radius_inputs, source_depth_inputs, \n", + " velocity_inputs, max_waveforms_inputs],\n", " outputs=outputs_section)\n", "\n", - " with gr.Tab(\"Predict on your own waveform\"):\n", - " gr.Markdown(\"\"\"\n", - " Please upload your waveform in .npy (numpy) format. \n", - " Your waveform should be sampled at 100 sps and have 3 (Z, N, E) or 1 (Z) channels.\n", - " \"\"\")\n", - "\n", "demo.launch()" ] }, diff --git a/app.py b/app.py index 46422a1b1507877ad783515e390fabf3b0278549..fe3bdc0934237a0c589c614e9642c0782c27d36e 100644 --- a/app.py +++ b/app.py @@ -8,6 +8,8 @@ from phasehunter.data_preparation import prepare_waveform import torch from scipy.stats import gaussian_kde +from bmi_topography import Topography +import earthpy.spatial as es import obspy from obspy.clients.fdsn import Client @@ -20,6 +22,7 @@ from obspy.clients.fdsn.header import URL_MAPPINGS import matplotlib.pyplot as plt import matplotlib.dates as mdates +from matplotlib.colors import LightSource from glob import glob @@ -36,21 +39,25 @@ def make_prediction(waveform): return processed_input, p_phase, s_phase -def mark_phases(waveform): +def mark_phases(waveform, uploaded_file): + + if uploaded_file is not None: + waveform = uploaded_file.name + processed_input, p_phase, s_phase = make_prediction(waveform) # Create a plot of the waveform with the phases marked if sum(processed_input[0][2] == 0): #if input is 1C fig, ax = plt.subplots(nrows=2, figsize=(10, 2), sharex=True) - ax[0].plot(processed_input[0][0]) + ax[0].plot(processed_input[0][0], color='black', lw=1) ax[0].set_ylabel('Norm. Ampl.') else: #if input is 3C fig, ax = plt.subplots(nrows=4, figsize=(10, 6), sharex=True) - ax[0].plot(processed_input[0][0]) - ax[1].plot(processed_input[0][1]) - ax[2].plot(processed_input[0][2]) + ax[0].plot(processed_input[0][0], color='black', lw=1) + ax[1].plot(processed_input[0][1], color='black', lw=1) + ax[2].plot(processed_input[0][2], color='black', lw=1) ax[0].set_ylabel('Z') ax[1].set_ylabel('N') @@ -82,22 +89,39 @@ def mark_phases(waveform): plt.close(fig) return image +def bin_distances(distances, bin_size=10): + # Bin the distances into groups of `bin_size` kilometers + binned_distances = {} + for i, distance in enumerate(distances): + bin_index = distance // bin_size + if bin_index not in binned_distances: + binned_distances[bin_index] = (distance, i) + elif i < binned_distances[bin_index][1]: + binned_distances[bin_index] = (distance, i) + + # Select the first distance in each bin and its index + first_distances = [] + for bin_index in binned_distances: + first_distance, first_distance_index = binned_distances[bin_index] + first_distances.append(first_distance_index) + + return first_distances + def variance_coefficient(residuals): # calculate the variance of the residuals var = residuals.var() - # scale the variance to a coefficient between 0 and 1 coeff = 1 - (var / (residuals.max() - residuals.min())) - return coeff -def predict_on_section(client_name, timestamp, eq_lat, eq_lon, radius_km, source_depth_km, velocity_model): +def predict_on_section(client_name, timestamp, eq_lat, eq_lon, radius_km, source_depth_km, velocity_model, max_waveforms): distances, t0s, st_lats, st_lons, waveforms = [], [], [], [], [] taup_model = TauPyModel(model=velocity_model) client = Client(client_name) window = radius_km / 111.2 + max_waveforms = int(max_waveforms) assert eq_lat - window > -90 and eq_lat + window < 90, "Latitude out of bounds" assert eq_lon - window > -180 and eq_lon + window < 180, "Longitude out of bounds" @@ -105,59 +129,107 @@ def predict_on_section(client_name, timestamp, eq_lat, eq_lon, radius_km, source starttime = obspy.UTCDateTime(timestamp) endtime = starttime + 120 - inv = client.get_stations(network="*", station="*", location="*", channel="*H*", - starttime=starttime, endtime=endtime, - minlatitude=(eq_lat-window), maxlatitude=(eq_lat+window), - minlongitude=(eq_lon-window), maxlongitude=(eq_lon+window), - level='station') + try: + print('Starting to download inventory') + inv = client.get_stations(network="*", station="*", location="*", channel="*H*", + starttime=starttime, endtime=endtime, + minlatitude=(eq_lat-window), maxlatitude=(eq_lat+window), + minlongitude=(eq_lon-window), maxlongitude=(eq_lon+window), + level='station') + print('Finished downloading inventory') + except (IndexError, FDSNNoDataException, FDSNTimeoutException, FDSNInternalServerException): + fig, ax = plt.subplots() + ax.text(0.5,0.5,'Something is wrong with the data provider, try another') + fig.canvas.draw(); + image = np.array(fig.canvas.renderer.buffer_rgba()) + plt.close(fig) + return image waveforms = [] cached_waveforms = glob("data/cached/*.mseed") for network in inv: + # Skip the SYntetic networks + if network.code == 'SY': + continue for station in network: - try: - distance = locations2degrees(eq_lat, eq_lon, station.latitude, station.longitude) + print(f"Processing {network.code}.{station.code}...") + distance = locations2degrees(eq_lat, eq_lon, station.latitude, station.longitude) - arrivals = taup_model.get_travel_times(source_depth_in_km=source_depth_km, - distance_in_degree=distance, - phase_list=["P", "S"]) + arrivals = taup_model.get_travel_times(source_depth_in_km=source_depth_km, + distance_in_degree=distance, + phase_list=["P", "S"]) - if len(arrivals) > 0: - - starttime = obspy.UTCDateTime(timestamp) + arrivals[0].time - 15 - endtime = starttime + 60 + if len(arrivals) > 0: + starttime = obspy.UTCDateTime(timestamp) + arrivals[0].time - 15 + endtime = starttime + 60 + try: if f"data/cached/{network.code}_{station.code}_{starttime}.mseed" not in cached_waveforms: + print('Downloading waveform') waveform = client.get_waveforms(network=network.code, station=station.code, location="*", channel="*", starttime=starttime, endtime=endtime) waveform.write(f"data/cached/{network.code}_{station.code}_{starttime}.mseed", format="MSEED") + print('Finished downloading and caching waveform') else: + print('Reading cached waveform') waveform = obspy.read(f"data/cached/{network.code}_{station.code}_{starttime}.mseed") - - waveform = waveform.select(channel="H[BH][ZNE]") - waveform = waveform.merge(fill_value=0) - waveform = waveform[:3] - - len_check = [len(x.data) for x in waveform] - if len(set(len_check)) > 1: - continue + + + except (IndexError, FDSNNoDataException, FDSNTimeoutException, FDSNInternalServerException): + print(f'Skipping {network.code}_{station.code}_{starttime}') + continue + + waveform = waveform.select(channel="H[BH][ZNE]") + waveform = waveform.merge(fill_value=0) + waveform = waveform[:3] + + len_check = [len(x.data) for x in waveform] + if len(set(len_check)) > 1: + continue + + if len(waveform) == 3: + try: + waveform = prepare_waveform(np.stack([x.data for x in waveform])) - if len(waveform) == 3: - try: - waveform = prepare_waveform(np.stack([x.data for x in waveform])) - except: - continue - distances.append(distance) t0s.append(starttime) st_lats.append(station.latitude) st_lons.append(station.longitude) waveforms.append(waveform) - except (IndexError, FDSNNoDataException, FDSNTimeoutException): - continue + print(f"Added {network.code}.{station.code} to the list of waveforms") + + except: + continue + + + # If there are no waveforms, return an empty plot + if len(waveforms) == 0: + fig, ax = plt.subplots() + ax.text(0.5,0.5,'No waveforms found') + fig.canvas.draw(); + image = np.array(fig.canvas.renderer.buffer_rgba()) + plt.close(fig) + return image + + + first_distances = bin_distances(distances, bin_size=10/111.2) + + # Edge case when there are way too many waveforms to process + selection_indexes = np.random.choice(first_distances, + np.min([len(first_distances), max_waveforms]), + replace=False) + + waveforms = np.array(waveforms)[selection_indexes] + distances = np.array(distances)[selection_indexes] + t0s = np.array(t0s)[selection_indexes] + st_lats = np.array(st_lats)[selection_indexes] + st_lons = np.array(st_lons)[selection_indexes] + waveforms = [torch.tensor(waveform) for waveform in waveforms] + + print('Starting to run predictions') with torch.no_grad(): waveforms_torch = torch.vstack(waveforms) output = model(waveforms_torch) @@ -169,8 +241,31 @@ def predict_on_section(client_name, timestamp, eq_lat, eq_lon, radius_km, source p_max_confidence = np.min([p_phases[i::len(waveforms)].std() for i in range(len(waveforms))]) s_max_confidence = np.min([s_phases[i::len(waveforms)].std() for i in range(len(waveforms))]) - fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(10, 3), sharex=True) + print(f"Starting plotting {len(waveforms)} waveforms") + fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(10, 3)) + + # Plot topography + print('Fetching topography') + params = Topography.DEFAULT.copy() + extra_window = 0.5 + params["south"] = np.min([st_lats.min(), eq_lat])-extra_window + params["north"] = np.max([st_lats.max(), eq_lat])+extra_window + params["west"] = np.min([st_lons.min(), eq_lon])-extra_window + params["east"] = np.max([st_lons.max(), eq_lon])+extra_window + + topo_map = Topography(**params) + topo_map.fetch() + topo_map.load() + + print('Plotting topo') + hillshade = es.hillshade(topo_map.da[0], altitude=10) + + topo_map.da.plot(ax = ax[1], cmap='Greys', add_colorbar=False, add_labels=False) + topo_map.da.plot(ax = ax[2], cmap='Greys', add_colorbar=False, add_labels=False) + ax[1].imshow(hillshade, cmap="Greys", alpha=0.5) + for i in range(len(waveforms)): + print(f"Plotting waveform {i+1}/{len(waveforms)}") current_P = p_phases[i::len(waveforms)] current_S = s_phases[i::len(waveforms)] @@ -188,17 +283,46 @@ def predict_on_section(client_name, timestamp, eq_lat, eq_lon, radius_km, source ax[0].set_ylabel('Z') ax[0].xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S')) - ax[0].xaxis.set_major_locator(mdates.SecondLocator(interval=5)) + ax[0].xaxis.set_major_locator(mdates.SecondLocator(interval=20)) + + delta_t = t0s[i].timestamp - obspy.UTCDateTime(timestamp).timestamp + + velocity_p = (distances[i]*111.2)/(delta_t+current_P.mean()*60).item() + velocity_s = (distances[i]*111.2)/(delta_t+current_S.mean()*60).item() + + print(f"Station {st_lats[i]}, {st_lons[i]} has P velocity {velocity_p} and S velocity {velocity_s}") + + # Generate an array from st_lat to eq_lat and from st_lon to eq_lon + x = np.linspace(st_lons[i], eq_lon, 50) + y = np.linspace(st_lats[i], eq_lat, 50) + # Plot the array + ax[1].scatter(x, y, c=np.zeros_like(x)+velocity_p, alpha=0.5, vmin=0, vmax=8) + ax[2].scatter(x, y, c=np.zeros_like(x)+velocity_s, alpha=0.5, vmin=0, vmax=8) + + # Add legend ax[0].scatter(None, None, color='r', marker='|', label='P') ax[0].scatter(None, None, color='b', marker='|', label='S') ax[0].legend() - ax[1].scatter(st_lats, st_lons, color='b', marker='d', label='Stations') - ax[1].scatter(eq_lat, eq_lon, color='r', marker='*', label='Earthquake') - ax[1].legend() - plt.subplots_adjust(hspace=0., wspace=0.) - + print('Plotting stations') + for i in range(1,3): + ax[i].scatter(st_lons, st_lats, color='b', label='Stations') + ax[i].scatter(eq_lon, eq_lat, color='r', marker='*', label='Earthquake') + + # Generate colorbar for the velocity plot + cbar = plt.colorbar(ax[1].scatter(None, None, c=velocity_p, alpha=0.5, vmin=0, vmax=8), ax=ax[1]) + cbar.set_label('P Velocity (km/s)') + ax[1].set_title('P Velocity') + + cbar = plt.colorbar(ax[2].scatter(None, None, c=velocity_s, alpha=0.5, vmin=0, vmax=8), ax=ax[2]) + cbar.set_label('S Velocity (km/s)') + ax[2].set_title('S Velocity') + + + + plt.subplots_adjust(hspace=0., wspace=0.5) + fig.canvas.draw(); image = np.array(fig.canvas.renderer.buffer_rgba()) plt.close(fig) @@ -212,30 +336,54 @@ model = Onset_picker.load_from_checkpoint("./weights.ckpt", model.eval() with gr.Blocks() as demo: - gr.Markdown("# PhaseHunter") - gr.Markdown("""This app allows one to detect P and S seismic phases along with uncertainty of the detection. - The app can be used in three ways: either by selecting one of the sample waveforms; - or by selecting an earthquake from the global earthquake catalogue; - or by uploading a waveform of interest. + gr.HTML("""

PhaseHunter

+

This app allows one to detect P and S seismic phases along with + + + u + n + c + e + r + t + a + i + n + t + y + + + of the detection.

+
    +
  1. By selecting one of the sample waveforms.
  2. +
  3. By uploading your own waveform.
  4. +
  5. By selecting an earthquake from the global earthquake catalogue.
  6. +
+

Please upload your waveform in .npy (numpy) format.

+

Your waveform should be sampled at 100 samples per second and have 3 (Z, N, E) or 1 (Z) channels. If your file is longer than 60 seconds, the app will only use the first 60 seconds of the waveform.

""") - with gr.Tab("Default example"): - # Define the input and output types for Gradio - inputs = gr.Dropdown( - ["data/sample/sample_0.npy", - "data/sample/sample_1.npy", - "data/sample/sample_2.npy"], - label="Sample waveform", - info="Select one of the samples", - value = "data/sample/sample_0.npy" - ) + with gr.Tab("Try on a single station"): + with gr.Row(): + # Define the input and output types for Gradio + inputs = gr.Dropdown( + ["data/sample/sample_0.npy", + "data/sample/sample_1.npy", + "data/sample/sample_2.npy"], + label="Sample waveform", + info="Select one of the samples", + value = "data/sample/sample_0.npy" + ) + + upload = gr.File(label="Or upload your own waveform") button = gr.Button("Predict phases") - outputs = gr.outputs.Image(label='Waveform with Phases Marked', type='numpy') + outputs = gr.Image(label='Waveform with Phases Marked', type='numpy', interactive=False) - button.click(mark_phases, inputs=inputs, outputs=outputs) + button.click(mark_phases, inputs=[inputs, upload], outputs=outputs) with gr.Tab("Select earthquake from catalogue"): - gr.Markdown('TEST') + gr.Markdown("""Select an earthquake from the global earthquake catalogue and the app will download the waveform from the FDSN client of your choice. + """) client_inputs = gr.Dropdown( choices = list(URL_MAPPINGS.keys()), @@ -244,6 +392,7 @@ with gr.Blocks() as demo: value = "IRIS", interactive=True ) + with gr.Row(): timestamp_inputs = gr.Textbox(value='2019-07-04 17:33:49', @@ -277,13 +426,23 @@ with gr.Blocks() as demo: interactive=True) velocity_inputs = gr.Dropdown( - choices = ['1066a', '1066b', 'ak135', 'ak135f', 'herrin', 'iasp91', 'jb', 'prem', 'pwdk'], + choices = ['1066a', '1066b', 'ak135', + 'ak135f', 'herrin', 'iasp91', + 'jb', 'prem', 'pwdk'], label="1D velocity model", info="Velocity model for station selection", value = "1066a", interactive=True ) - + + max_waveforms_inputs = gr.Slider(minimum=1, + maximum=100, + value=10, + label="Max waveforms per section", + step=1, + info="Maximum number of waveforms to show per section\n (to avoid long prediction times)", + interactive=True, + ) button = gr.Button("Predict phases") outputs_section = gr.Image(label='Waveforms with Phases Marked', type='numpy', interactive=False) @@ -291,13 +450,8 @@ with gr.Blocks() as demo: button.click(predict_on_section, inputs=[client_inputs, timestamp_inputs, eq_lat_inputs, eq_lon_inputs, - radius_inputs, source_depth_inputs, velocity_inputs], + radius_inputs, source_depth_inputs, + velocity_inputs, max_waveforms_inputs], outputs=outputs_section) - with gr.Tab("Predict on your own waveform"): - gr.Markdown(""" - Please upload your waveform in .npy (numpy) format. - Your waveform should be sampled at 100 sps and have 3 (Z, N, E) or 1 (Z) channels. - """) - demo.launch() \ No newline at end of file diff --git a/data/.DS_Store b/data/.DS_Store index 16006fcbc94df04cc42fd76fd9278de945d44b45..caa5dbbe5b75cc70b6f0c9673021c43c85442887 100644 Binary files a/data/.DS_Store and b/data/.DS_Store differ diff --git a/data/cached/BC_JARAX_2023-04-01T01:16:13.997267Z.mseed b/data/cached/BC_JARAX_2023-04-01T01:16:13.997267Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..b35c113bec9eaed1a4f12a6da73f84b63af85154 Binary files /dev/null and b/data/cached/BC_JARAX_2023-04-01T01:16:13.997267Z.mseed differ diff --git a/data/cached/CE_24944_2023-04-01T01:16:14.044529Z.mseed b/data/cached/CE_24944_2023-04-01T01:16:14.044529Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..2dc784e79e3e8104d0d64656838cde2d57f26409 Binary files /dev/null and b/data/cached/CE_24944_2023-04-01T01:16:14.044529Z.mseed differ diff --git a/data/cached/CE_24945_2023-04-01T01:16:13.506381Z.mseed b/data/cached/CE_24945_2023-04-01T01:16:13.506381Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..c2a336ed0300b7aaf2a82adca21a1f829a0cde0a Binary files /dev/null and b/data/cached/CE_24945_2023-04-01T01:16:13.506381Z.mseed differ diff --git a/data/cached/CI_ADO_2019-07-04T17:33:53.650962Z.mseed b/data/cached/CI_ADO_2019-07-04T17:33:53.650962Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..a6230989e3529101ec884f446dbb6c26e5a75c22 Binary files /dev/null and b/data/cached/CI_ADO_2019-07-04T17:33:53.650962Z.mseed differ diff --git a/data/cached/CI_ARV_2019-07-04T17:33:53.096986Z.mseed b/data/cached/CI_ARV_2019-07-04T17:33:53.096986Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..2ffb1552e3b6c069137c0f292dab79018f7893d2 Binary files /dev/null and b/data/cached/CI_ARV_2019-07-04T17:33:53.096986Z.mseed differ diff --git a/data/cached/CI_CJV2_2023-04-01T01:16:16.011425Z.mseed b/data/cached/CI_CJV2_2023-04-01T01:16:16.011425Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..7947ef26176d60ef9ba97d7bcce6a8e0540532dd Binary files /dev/null and b/data/cached/CI_CJV2_2023-04-01T01:16:16.011425Z.mseed differ diff --git a/data/cached/CI_CWC_2019-07-04T17:33:47.189005Z.mseed b/data/cached/CI_CWC_2019-07-04T17:33:47.189005Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..56b3f06732280ace97b8e3a5f3b2f5c17170f0d8 Binary files /dev/null and b/data/cached/CI_CWC_2019-07-04T17:33:47.189005Z.mseed differ diff --git a/data/cached/CI_EDW2_2019-07-04T17:33:49.567241Z.mseed b/data/cached/CI_EDW2_2019-07-04T17:33:49.567241Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..eaab3c2d1f83ba654b8acf59fb6fbad7f5479527 Binary files /dev/null and b/data/cached/CI_EDW2_2019-07-04T17:33:49.567241Z.mseed differ diff --git a/data/cached/CI_FUR_2019-07-04T17:33:49.310305Z.mseed b/data/cached/CI_FUR_2019-07-04T17:33:49.310305Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..105a0e19b86a346828674862512dab6e42cc1947 Binary files /dev/null and b/data/cached/CI_FUR_2019-07-04T17:33:49.310305Z.mseed differ diff --git a/data/cached/CI_GRA_2019-07-04T17:33:53.959922Z.mseed b/data/cached/CI_GRA_2019-07-04T17:33:53.959922Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..710ebeaca9ca4e8fedc78940b6e9733050feb617 Binary files /dev/null and b/data/cached/CI_GRA_2019-07-04T17:33:53.959922Z.mseed differ diff --git a/data/cached/CI_GSC_2019-07-04T17:33:47.536363Z.mseed b/data/cached/CI_GSC_2019-07-04T17:33:47.536363Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..5f075cf0bc1ad3d01fed51fd0ebccb4c6c87a174 Binary files /dev/null and b/data/cached/CI_GSC_2019-07-04T17:33:47.536363Z.mseed differ diff --git a/data/cached/CI_HEC_2019-07-04T17:33:56.148977Z.mseed b/data/cached/CI_HEC_2019-07-04T17:33:56.148977Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..1093ace445e95884d1f95afc0f75d32c14bad4fb Binary files /dev/null and b/data/cached/CI_HEC_2019-07-04T17:33:56.148977Z.mseed differ diff --git a/data/cached/CI_ISA_2019-07-04T17:33:46.297658Z.mseed b/data/cached/CI_ISA_2019-07-04T17:33:46.297658Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..3b717c03a5d7990b014e825e99826a464a691b11 Binary files /dev/null and b/data/cached/CI_ISA_2019-07-04T17:33:46.297658Z.mseed differ diff --git a/data/cached/CI_JNH2_2023-04-01T01:16:13.664044Z.mseed b/data/cached/CI_JNH2_2023-04-01T01:16:13.664044Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..760d96ea9404d2f13c8fcf8623cb698f3c9bd07f Binary files /dev/null and b/data/cached/CI_JNH2_2023-04-01T01:16:13.664044Z.mseed differ diff --git a/data/cached/CI_JRC2_2019-07-04T17:33:39.947494Z.mseed b/data/cached/CI_JRC2_2019-07-04T17:33:39.947494Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..eaa7ab64083be454219b5575e50aae2580bea6a2 Binary files /dev/null and b/data/cached/CI_JRC2_2019-07-04T17:33:39.947494Z.mseed differ diff --git a/data/cached/CI_LRL_2019-07-04T17:33:40.248999Z.mseed b/data/cached/CI_LRL_2019-07-04T17:33:40.248999Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..b01fc8ffe79b6ac5b79034eb89e40aa424d1d5d5 Binary files /dev/null and b/data/cached/CI_LRL_2019-07-04T17:33:40.248999Z.mseed differ diff --git a/data/cached/CI_LRR2_2023-04-01T01:16:15.095101Z.mseed b/data/cached/CI_LRR2_2023-04-01T01:16:15.095101Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..a644c754afbeb93f89709bfe2620d5456eb84352 Binary files /dev/null and b/data/cached/CI_LRR2_2023-04-01T01:16:15.095101Z.mseed differ diff --git a/data/cached/CI_MPM_2019-07-04T17:33:40.443346Z.mseed b/data/cached/CI_MPM_2019-07-04T17:33:40.443346Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..68d2ad59c7a54f22706ce1298929e2c30965b5f6 Binary files /dev/null and b/data/cached/CI_MPM_2019-07-04T17:33:40.443346Z.mseed differ diff --git a/data/cached/CI_OSI_2019-07-04T17:33:57.203547Z.mseed b/data/cached/CI_OSI_2019-07-04T17:33:57.203547Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..e973d4f6d3f2f815595a8f5a330cb8892060e50b Binary files /dev/null and b/data/cached/CI_OSI_2019-07-04T17:33:57.203547Z.mseed differ diff --git a/data/cached/CI_PUT_2023-04-01T01:16:16.558724Z.mseed b/data/cached/CI_PUT_2023-04-01T01:16:16.558724Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..f6e04329b1335f6bc8f15ea47f2a2befa448b5a4 Binary files /dev/null and b/data/cached/CI_PUT_2023-04-01T01:16:16.558724Z.mseed differ diff --git a/data/cached/CI_Q0013_2019-07-04T17:33:54.489098Z.mseed b/data/cached/CI_Q0013_2019-07-04T17:33:54.489098Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..0639dbb547b0b66801e22fa04a8a3ab309a6e83b Binary files /dev/null and b/data/cached/CI_Q0013_2019-07-04T17:33:54.489098Z.mseed differ diff --git a/data/cached/CI_Q0024_2019-07-04T17:33:55.620507Z.mseed b/data/cached/CI_Q0024_2019-07-04T17:33:55.620507Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..e4ed3c5655b19c342f8ca810d6ed77616ddb85ec Binary files /dev/null and b/data/cached/CI_Q0024_2019-07-04T17:33:55.620507Z.mseed differ diff --git a/data/cached/CI_Q0035_2019-07-04T17:33:56.041452Z.mseed b/data/cached/CI_Q0035_2019-07-04T17:33:56.041452Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..e0d656e405f1cba1f254f6b73499a69ae164bd31 Binary files /dev/null and b/data/cached/CI_Q0035_2019-07-04T17:33:56.041452Z.mseed differ diff --git a/data/cached/CI_Q0056_2019-07-04T17:33:50.407027Z.mseed b/data/cached/CI_Q0056_2019-07-04T17:33:50.407027Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..948151cf5e52865a72edd3113e43d31b93f09810 Binary files /dev/null and b/data/cached/CI_Q0056_2019-07-04T17:33:50.407027Z.mseed differ diff --git a/data/cached/CI_Q0061_2019-07-04T17:33:53.114695Z.mseed b/data/cached/CI_Q0061_2019-07-04T17:33:53.114695Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..fc8d7567e247270ee00cc3524ead9f9fa0628aba Binary files /dev/null and b/data/cached/CI_Q0061_2019-07-04T17:33:53.114695Z.mseed differ diff --git a/data/cached/CI_Q0068_2019-07-04T17:33:46.411249Z.mseed b/data/cached/CI_Q0068_2019-07-04T17:33:46.411249Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..19d48531b1278578b584e94f52f1aab0519cac75 Binary files /dev/null and b/data/cached/CI_Q0068_2019-07-04T17:33:46.411249Z.mseed differ diff --git a/data/cached/CI_Q0072_2019-07-04T17:33:38.390421Z.mseed b/data/cached/CI_Q0072_2019-07-04T17:33:38.390421Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..6a6650886681ed656a330708f36cca455f7273c6 Binary files /dev/null and b/data/cached/CI_Q0072_2019-07-04T17:33:38.390421Z.mseed differ diff --git a/data/cached/CI_RRX_2019-07-04T17:33:50.712219Z.mseed b/data/cached/CI_RRX_2019-07-04T17:33:50.712219Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..60d19f93de0665a17ce85b380cb35f72ef5224cc Binary files /dev/null and b/data/cached/CI_RRX_2019-07-04T17:33:50.712219Z.mseed differ diff --git a/data/cached/CI_SBB2_2023-04-01T01:16:15.609344Z.mseed b/data/cached/CI_SBB2_2023-04-01T01:16:15.609344Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..a4b3ee3c56902ef928e73c83a2812e734964e201 Binary files /dev/null and b/data/cached/CI_SBB2_2023-04-01T01:16:15.609344Z.mseed differ diff --git a/data/cached/CI_SHO_2019-07-04T17:33:51.673022Z.mseed b/data/cached/CI_SHO_2019-07-04T17:33:51.673022Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..3a988bd6febb2409f1788458f2f883f238153a84 Binary files /dev/null and b/data/cached/CI_SHO_2019-07-04T17:33:51.673022Z.mseed differ diff --git a/data/cached/CI_SLA_2019-07-04T17:33:40.190746Z.mseed b/data/cached/CI_SLA_2019-07-04T17:33:40.190746Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..01d4dae6efd50e7cca1d8411c2f5ea361c80eb17 Binary files /dev/null and b/data/cached/CI_SLA_2019-07-04T17:33:40.190746Z.mseed differ diff --git a/data/cached/CI_SRA_2023-04-01T01:16:14.887472Z.mseed b/data/cached/CI_SRA_2023-04-01T01:16:14.887472Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..ed7de4843cdb4847275ac032013217e4f5ca549c Binary files /dev/null and b/data/cached/CI_SRA_2023-04-01T01:16:14.887472Z.mseed differ diff --git a/data/cached/CI_SRT_2019-07-04T17:33:38.029990Z.mseed b/data/cached/CI_SRT_2019-07-04T17:33:38.029990Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..ba52c758c7a2a78ab2f9d63541697a471abaf5f9 Binary files /dev/null and b/data/cached/CI_SRT_2019-07-04T17:33:38.029990Z.mseed differ diff --git a/data/cached/CI_TIN_2019-07-04T17:33:55.946998Z.mseed b/data/cached/CI_TIN_2019-07-04T17:33:55.946998Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..b795b4e4b47782bab47d05708066fd6e65da50f8 Binary files /dev/null and b/data/cached/CI_TIN_2019-07-04T17:33:55.946998Z.mseed differ diff --git a/data/cached/CI_TOW2_2019-07-04T17:33:37.991033Z.mseed b/data/cached/CI_TOW2_2019-07-04T17:33:37.991033Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..87c0bf34a9e312dd52f3aea5e5deb8ee96c010a0 Binary files /dev/null and b/data/cached/CI_TOW2_2019-07-04T17:33:37.991033Z.mseed differ diff --git a/data/cached/CI_VCS_2023-04-01T01:16:15.307897Z.mseed b/data/cached/CI_VCS_2023-04-01T01:16:15.307897Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..650ee454bc6acbff1684736c283127d8466bcb16 Binary files /dev/null and b/data/cached/CI_VCS_2023-04-01T01:16:15.307897Z.mseed differ diff --git a/data/cached/CI_VTV_2019-07-04T17:33:53.688913Z.mseed b/data/cached/CI_VTV_2019-07-04T17:33:53.688913Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..9265b8e3299b4ac202ceccc84ad88182c27af361 Binary files /dev/null and b/data/cached/CI_VTV_2019-07-04T17:33:53.688913Z.mseed differ diff --git a/data/cached/CI_WBM_2019-07-04T17:33:40.063644Z.mseed b/data/cached/CI_WBM_2019-07-04T17:33:40.063644Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..d300d865ebfcff24c8849c2ed1131f1f2141001b Binary files /dev/null and b/data/cached/CI_WBM_2019-07-04T17:33:40.063644Z.mseed differ diff --git a/data/cached/CI_WLS2_2023-04-01T01:16:13.623472Z.mseed b/data/cached/CI_WLS2_2023-04-01T01:16:13.623472Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..48e515493027f90510157507972d21416f677099 Binary files /dev/null and b/data/cached/CI_WLS2_2023-04-01T01:16:13.623472Z.mseed differ diff --git a/data/cached/CI_WMF_2019-07-04T17:33:41.867962Z.mseed b/data/cached/CI_WMF_2019-07-04T17:33:41.867962Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..ada832916271dbb5ac910d906bfedaf396328d41 Binary files /dev/null and b/data/cached/CI_WMF_2019-07-04T17:33:41.867962Z.mseed differ diff --git a/data/cached/CI_WRC2_2019-07-04T17:33:38.698107Z.mseed b/data/cached/CI_WRC2_2019-07-04T17:33:38.698107Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..74e83aa1154293f3ab29fdf3ff5c67fec27e3158 Binary files /dev/null and b/data/cached/CI_WRC2_2019-07-04T17:33:38.698107Z.mseed differ diff --git a/data/cached/CI_WRV2_2019-07-04T17:33:40.843660Z.mseed b/data/cached/CI_WRV2_2019-07-04T17:33:40.843660Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..38cd289522eb61ed55ed5eec201a14b7095956f5 Binary files /dev/null and b/data/cached/CI_WRV2_2019-07-04T17:33:40.843660Z.mseed differ diff --git a/data/cached/CI_WVP2_2019-07-04T17:33:39.650418Z.mseed b/data/cached/CI_WVP2_2019-07-04T17:33:39.650418Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..b631839fd1d8632e5bf8ee22b735099656c08d4a Binary files /dev/null and b/data/cached/CI_WVP2_2019-07-04T17:33:39.650418Z.mseed differ diff --git a/data/cached/LB_DAC_2019-07-04T17:33:43.387184Z.mseed b/data/cached/LB_DAC_2019-07-04T17:33:43.387184Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..3250e7e3eaef8c424441db06034c38ea1b6745d9 Binary files /dev/null and b/data/cached/LB_DAC_2019-07-04T17:33:43.387184Z.mseed differ diff --git a/data/cached/NN_FMT_2019-07-04T17:33:51.798341Z.mseed b/data/cached/NN_FMT_2019-07-04T17:33:51.798341Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..13a412ec04dbfcdcf2d53c480b07ea40a6938bc1 Binary files /dev/null and b/data/cached/NN_FMT_2019-07-04T17:33:51.798341Z.mseed differ diff --git a/data/cached/NN_GVN_2019-07-04T17:33:54.053591Z.mseed b/data/cached/NN_GVN_2019-07-04T17:33:54.053591Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..9bda2d68a54a3adfe904ad560422051f564b17d4 Binary files /dev/null and b/data/cached/NN_GVN_2019-07-04T17:33:54.053591Z.mseed differ diff --git a/data/cached/NN_GWY_2019-07-04T17:33:48.493781Z.mseed b/data/cached/NN_GWY_2019-07-04T17:33:48.493781Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..a36dbad49a80acea6738947d3ab530405cc65871 Binary files /dev/null and b/data/cached/NN_GWY_2019-07-04T17:33:48.493781Z.mseed differ diff --git a/data/cached/NN_MCA_2019-07-04T17:33:49.255163Z.mseed b/data/cached/NN_MCA_2019-07-04T17:33:49.255163Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..88dda2eb7ec06b5232862445d28f751966a69999 Binary files /dev/null and b/data/cached/NN_MCA_2019-07-04T17:33:49.255163Z.mseed differ diff --git a/data/cached/NN_QSM_2019-07-04T17:33:45.081547Z.mseed b/data/cached/NN_QSM_2019-07-04T17:33:45.081547Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..f879b0164b8a7906b9cc3338e94371168854c3e6 Binary files /dev/null and b/data/cached/NN_QSM_2019-07-04T17:33:45.081547Z.mseed differ diff --git a/data/cached/NN_STHB_2019-07-04T17:33:55.443778Z.mseed b/data/cached/NN_STHB_2019-07-04T17:33:55.443778Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..c10d8cba8780bf92ad77dcf36390618e9207c40a Binary files /dev/null and b/data/cached/NN_STHB_2019-07-04T17:33:55.443778Z.mseed differ diff --git a/data/cached/NN_WCT_2019-07-04T17:33:54.675247Z.mseed b/data/cached/NN_WCT_2019-07-04T17:33:54.675247Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..bc58ee49894d0458edf0a39f275d69e9031423cd Binary files /dev/null and b/data/cached/NN_WCT_2019-07-04T17:33:54.675247Z.mseed differ diff --git a/data/cached/NN_WLDB_2019-07-04T17:33:54.675247Z.mseed b/data/cached/NN_WLDB_2019-07-04T17:33:54.675247Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..2ef17924fc8cc554a66124420375dec6952eb97c Binary files /dev/null and b/data/cached/NN_WLDB_2019-07-04T17:33:54.675247Z.mseed differ diff --git a/data/cached/NP_5030_2023-04-01T01:16:14.741999Z.mseed b/data/cached/NP_5030_2023-04-01T01:16:14.741999Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..97df8cb5e7cc980f38f9a2dc18497fb1b1f3e35f Binary files /dev/null and b/data/cached/NP_5030_2023-04-01T01:16:14.741999Z.mseed differ diff --git a/data/cached/NP_5419_2019-07-04T17:33:37.835873Z.mseed b/data/cached/NP_5419_2019-07-04T17:33:37.835873Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..cdee71890fc78e8c6cfa7b4f9b6c54d24f878246 Binary files /dev/null and b/data/cached/NP_5419_2019-07-04T17:33:37.835873Z.mseed differ diff --git a/data/cached/PB_B916_2019-07-04T17:33:42.213154Z.mseed b/data/cached/PB_B916_2019-07-04T17:33:42.213154Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..a4e51772844e932be75199e8fc43f7c1b0a6ed26 Binary files /dev/null and b/data/cached/PB_B916_2019-07-04T17:33:42.213154Z.mseed differ diff --git a/data/cached/PB_B916_2019-07-04T17:33:42.213172Z.mseed b/data/cached/PB_B916_2019-07-04T17:33:42.213172Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..b355a092385667c3aa14dbcc63df3c7c94e10a04 Binary files /dev/null and b/data/cached/PB_B916_2019-07-04T17:33:42.213172Z.mseed differ diff --git a/data/cached/PB_B917_2019-07-04T17:33:42.616332Z.mseed b/data/cached/PB_B917_2019-07-04T17:33:42.616332Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..688d1086d881260521562b3cabe1ed19588524d0 Binary files /dev/null and b/data/cached/PB_B917_2019-07-04T17:33:42.616332Z.mseed differ diff --git a/data/cached/PB_B917_2019-07-04T17:33:42.616354Z.mseed b/data/cached/PB_B917_2019-07-04T17:33:42.616354Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..7e0a58de8edaad6a69008a31f382899406862e0b Binary files /dev/null and b/data/cached/PB_B917_2019-07-04T17:33:42.616354Z.mseed differ diff --git a/data/cached/PB_B918_2019-07-04T17:33:38.469617Z.mseed b/data/cached/PB_B918_2019-07-04T17:33:38.469617Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..6bc62736691b905b4f79d7fcab740d338cc9c590 Binary files /dev/null and b/data/cached/PB_B918_2019-07-04T17:33:38.469617Z.mseed differ diff --git a/data/cached/PB_B918_2019-07-04T17:33:38.469631Z.mseed b/data/cached/PB_B918_2019-07-04T17:33:38.469631Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..5602faab2e96e19a94277e6310235ab0c6e7eb20 Binary files /dev/null and b/data/cached/PB_B918_2019-07-04T17:33:38.469631Z.mseed differ diff --git a/data/cached/PB_B921_2019-07-04T17:33:39.103468Z.mseed b/data/cached/PB_B921_2019-07-04T17:33:39.103468Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..ede807cee08ed319593f2e5e1a56920acf999e7b Binary files /dev/null and b/data/cached/PB_B921_2019-07-04T17:33:39.103468Z.mseed differ diff --git a/data/cached/PB_B921_2019-07-04T17:33:39.103517Z.mseed b/data/cached/PB_B921_2019-07-04T17:33:39.103517Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..eeed37e25e679aca527b7222d96b267cc8c38735 Binary files /dev/null and b/data/cached/PB_B921_2019-07-04T17:33:39.103517Z.mseed differ diff --git a/data/cached/SN_AMD_2019-07-04T17:33:54.326959Z.mseed b/data/cached/SN_AMD_2019-07-04T17:33:54.326959Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..4e115bdc63d90dff825738527ebed676ffbacaa6 Binary files /dev/null and b/data/cached/SN_AMD_2019-07-04T17:33:54.326959Z.mseed differ diff --git a/data/cached/SN_BTW_2019-07-04T17:33:57.464722Z.mseed b/data/cached/SN_BTW_2019-07-04T17:33:57.464722Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..770ce13dcbd726b42eae9f0426a7df7ec26e62e0 Binary files /dev/null and b/data/cached/SN_BTW_2019-07-04T17:33:57.464722Z.mseed differ diff --git a/data/cached/SN_DOM_2019-07-04T17:33:58.592229Z.mseed b/data/cached/SN_DOM_2019-07-04T17:33:58.592229Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..2c5713eaa59656be9f0b676ad245628c6db7c170 Binary files /dev/null and b/data/cached/SN_DOM_2019-07-04T17:33:58.592229Z.mseed differ diff --git a/data/cached/SN_HEL_2019-07-04T17:33:51.648915Z.mseed b/data/cached/SN_HEL_2019-07-04T17:33:51.648915Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..b72ea0a477ad261117d3dace65ab9e850f7aec5c Binary files /dev/null and b/data/cached/SN_HEL_2019-07-04T17:33:51.648915Z.mseed differ diff --git a/data/cached/SN_LEC_2019-07-04T17:33:51.671953Z.mseed b/data/cached/SN_LEC_2019-07-04T17:33:51.671953Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..2234c86bc7f8956ee8abf3096a45f12cb9aa49b4 Binary files /dev/null and b/data/cached/SN_LEC_2019-07-04T17:33:51.671953Z.mseed differ diff --git a/data/cached/SN_SGR_2019-07-04T17:33:54.732217Z.mseed b/data/cached/SN_SGR_2019-07-04T17:33:54.732217Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..261b3a43cba59a1d80cf7c6ec6e6541a7695e475 Binary files /dev/null and b/data/cached/SN_SGR_2019-07-04T17:33:54.732217Z.mseed differ diff --git a/data/cached/SN_SW353_2019-07-04T17:33:57.702635Z.mseed b/data/cached/SN_SW353_2019-07-04T17:33:57.702635Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..8a55e871c4d093bd4e86c0a3635c16b871105eeb Binary files /dev/null and b/data/cached/SN_SW353_2019-07-04T17:33:57.702635Z.mseed differ diff --git a/data/cached/SN_SW435_2019-07-04T17:33:56.641964Z.mseed b/data/cached/SN_SW435_2019-07-04T17:33:56.641964Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..09ec38c0325e8c18a16252c97cc56a84343bf969 Binary files /dev/null and b/data/cached/SN_SW435_2019-07-04T17:33:56.641964Z.mseed differ diff --git a/data/cached/SN_SW511_2019-07-04T17:33:55.553448Z.mseed b/data/cached/SN_SW511_2019-07-04T17:33:55.553448Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..8d2d689bc1ef05a3effbfe538728ca3dc42bfa61 Binary files /dev/null and b/data/cached/SN_SW511_2019-07-04T17:33:55.553448Z.mseed differ diff --git a/data/cached/SN_TIM_2019-07-04T17:33:58.917953Z.mseed b/data/cached/SN_TIM_2019-07-04T17:33:58.917953Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..8e5e5700f9d6a9461aa9df2c0de4ba78b18efbfa Binary files /dev/null and b/data/cached/SN_TIM_2019-07-04T17:33:58.917953Z.mseed differ diff --git a/data/cached/SN_WESTB_2019-07-04T17:34:00.722100Z.mseed b/data/cached/SN_WESTB_2019-07-04T17:34:00.722100Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..cc113efa0c588e70d2827237f66b611d8feef20f Binary files /dev/null and b/data/cached/SN_WESTB_2019-07-04T17:34:00.722100Z.mseed differ diff --git a/data/cached/SN_WESTC_2019-07-04T17:34:00.371502Z.mseed b/data/cached/SN_WESTC_2019-07-04T17:34:00.371502Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..eb6db94ac7206b33d9a5cf4bced90cdd8cb04982 Binary files /dev/null and b/data/cached/SN_WESTC_2019-07-04T17:34:00.371502Z.mseed differ diff --git a/data/cached/SN_WESTD_2019-07-04T17:34:00.103924Z.mseed b/data/cached/SN_WESTD_2019-07-04T17:34:00.103924Z.mseed new file mode 100644 index 0000000000000000000000000000000000000000..5601d539601101f7b4575560ec281ed2b81192fb Binary files /dev/null and b/data/cached/SN_WESTD_2019-07-04T17:34:00.103924Z.mseed differ diff --git a/requirements.txt b/requirements.txt index 530983d12f8212d4027360f0ec23254c215de3de..a5aed73bf40a80d015112242e964b3cea10705bf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,4 +13,5 @@ tqdm==4.65.0 webdataset==0.2.48 obspy pandas +contextily git+http://github.com/nikitadurasov/masksembles \ No newline at end of file