Commit 66d7cd0b authored by Jerome Touvier's avatar Jerome Touvier

Merge branch 'update' into 'master'

automontage bynet

See merge request !13
parents c79e0fa5 8926f721
Pipeline #49111 passed with stage
in 1 minute and 22 seconds
import argparse
import json
import logging
import os
import re
import sys
import traceback
import psycopg2
from flask import current_app
levels = [logging.CRITICAL, logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
## valid request parameters
url_keys = ("network", "station", "channel", "location", "net", "sta", "cha", "loc")
def request_parser(params, url):
for pairs in re.findall(r"[\w]+=[\w?*-]+", url):
logging.debug(pairs)
key, value = pairs.split("=")
if key in url_keys:
key = "network" if key == "net" else key
key = "station" if key == "sta" else key
key = "location" if key == "loc" else key
key = "channel" if key == "cha" else key
params[key] = value
return params
def records_to_dictlist(data):
header = ["Network", "Station", "Location", "Channel"]
dictlist = []
for row in data:
dictlist.append(dict(zip(header, row)))
return {"Channel codes": dictlist}
def is_like_or_equal(params, key):
""" Builds the condition for the specified key in the "where" clause taking into account lists or wildcards. """
subquery = list()
for param in params[key].split(","):
op = "LIKE" if re.search(r"[*?]", param) else "="
key = "s.station" if key == "station" else key
subquery.append(f"{key} {op} '{param}'")
return " OR ".join(subquery)
def sql_request(params):
""" Builds the PostgreSQL request."""
select = f"""SELECT DISTINCT network, s.station, location, channel FROM networks AS n, station AS s, channel AS c WHERE n.network_id = s.network_id AND s.station_id = c.station_id AND ({is_like_or_equal(params, "network")}) AND ({is_like_or_equal(params, "station")}) AND ({is_like_or_equal(params, "channel")}) AND ({is_like_or_equal(params, "location")})"""
select = select.replace("?", "_").replace("*", "%")
if params["limit"]:
return f"""{select} ORDER BY network, station, channel, location LIMIT {params["limit"]};"""
return f"""{select} ORDER BY network, station, channel, location;"""
def collect_data(params):
""" Get the result of the SQL query. """
with psycopg2.connect(current_app.config["DATABASE_URI"]) as conn:
logging.debug(conn.get_dsn_parameters())
logging.debug(f"Postgres version : {conn.server_version}")
with conn.cursor() as curs:
select = sql_request(params)
logging.debug(select)
curs.execute(select)
logging.debug(curs.statusmessage)
return curs.fetchall()
def extend(args=None, path=None):
""" Main function """
## parameters parsing ##
parser = argparse.ArgumentParser(
description=(
"Wildcards extender. Returns extended name of each channel containing wildcards (? and *) as atomic quadruplet (network, station, location, channel). Arguments can be provided individualy with the n(etwork), s(tation), l(ocation), c(hannel) parameters or directly parsed from an URL."
)
)
parser.add_argument(
"-n", "--network", type=str, default="*", help="network code (default all)"
)
parser.add_argument(
"-s", "--station", type=str, default="*", help="station code (default all)"
)
parser.add_argument(
"-c", "--channel", type=str, default="*", help="channel code (default all)"
)
parser.add_argument(
"-l",
"--location",
type=str,
default="*",
help="location code (default all, the double dash code (i.e. --), have to be passed as underscore (i.e. -location=_))",
)
parser.add_argument(
"--url",
type=str,
help="""An URL request that can be amended by parameters n, s, l, c if they are different from the value all (e.g. in this case n=* is discarded).""",
)
parser.add_argument(
"-d",
"--delimiter",
type=str,
default=" ",
help="output separator (default space)",
)
parser.add_argument("--limit", type=int, help="row limit (default none)")
parser.add_argument(
"--format",
type=str,
default="text",
help="text, json, standard output (stdout) or tuple (default text)",
choices=["text", "json", "stdout", "tuple"],
)
parser.add_argument(
"-v",
"--verbose",
type=int,
default=3,
help="set logging level: 0 critical, 1 error, 2 warning, 3 info, 4 debug, default info",
choices=range(0, 5),
)
if args:
args = parser.parse_args(args)
else:
args = parser.parse_args()
## logging configuration ##
logging.basicConfig(
level=levels[args.verbose],
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
## get parameters ##
logging.debug(args)
for key in vars(args):
# avoid errors with something like : -parameter=--
if getattr(args, key) == list():
logging.warning(f"Invalid parameter : {key}")
return 1
params = {"network": "*", "station": "*", "location": "*", "channel": "*"}
params["limit"] = args.limit
if args.url:
params = request_parser(params, args.url)
if args.network != "*":
params["network"] = args.network
if args.station != "*":
params["station"] = args.station
if args.location != "*":
params["location"] = "--" if args.location == "_" else args.location
if args.channel != "*":
params["channel"] = args.channel
logging.debug(f"Parameters provided to the SQL request: {params}")
## get channels ##
try:
data = collect_data(params)
if data:
res = ""
if args.format == "json":
res = json.dumps(records_to_dictlist(data))
elif args.format == "tuple":
res = data
else:
res = "\n".join(args.delimiter.join(row) for row in data)
if args.format == "stdout":
print(res)
elif not path:
return res
else:
path = os.path.splitext(path)[0]
if args.format == "json":
path = path + ".json"
else:
path = path + ".txt"
with open(path, "w") as fid:
print(res, file=fid)
else:
logging.debug("No channel found.")
except Exception:
sys.stdout.write(traceback.format_exc())
if __name__ == "__main__":
extend()
import os
# global constants
FROM_CLIENT = True
FROM_CLIENT = False
FDSN_CLIENT = "RESIF"
DATA_MOUNT_POINT = os.getenv("DATADIR")
DATA_DIR = "/mnt/auto/archive"
USER_AGENT_TIMESERIES = "resifws-timeseries"
USER_AGENT_TIMESERIES_INVENTORY = "resifws-timeseries_inventory"
USER_AGENT_TIMESERIESPLOT = "resifws-timeseriesplot"
......
......@@ -207,8 +207,10 @@ def get_output(params):
st = get_signal(params)
logging.info(f"Get data in {tictac(tic1)} seconds.")
if st is None or len(st) == 0:
if st is None:
return None
if not st:
return error_nodata(params)
npoints = sum([len(tr.data) for tr in st])
if npoints > MAX_DATA_POINTS:
......@@ -226,8 +228,6 @@ def get_output(params):
return error_nodata(params, Error.RESPONSE)
st = get_processed_signal(st, params)
if not st:
return error_nodata(params)
if params["format"] == "plot":
response = static_plots(params, st)
......
......@@ -22,8 +22,7 @@ import psycopg2
from scipy.signal import periodogram
from apps.extender import extend
from apps.globals import DATA_MOUNT_POINT
from apps.globals import DATA_DIR
from apps.globals import DPI_MIN, DPI_MAX
from apps.globals import Error
from apps.globals import FDSN_CLIENT
......@@ -297,7 +296,7 @@ def check_base_parameters(params, max_days=None):
# Search for empty selection
if params["network"] == params["station"] == params["channel"] == "*":
return error_param(params, Error.NO_SELECTION)
logging.debug(params)
return (params, {"msg": HTTP._200_, "details": Error.VALID_PARAM, "code": 200})
......@@ -388,18 +387,18 @@ def get_response(params):
)
def is_open_file(paths):
"""Embargoed files are removed from the list of paths."""
def is_open_file(filenames):
"""Embargoed files are removed from the list of filenames."""
if not paths:
if not filenames:
return None
values = ", ".join(f"""('{j}', '{k}')""" for j, k in paths)
select = f"""SELECT path, name
FROM rbud, ( SELECT * FROM (VALUES {values}) AS t(path, name) ) AS t1
WHERE source_file=t1.name AND channel_id !=0 UNION SELECT path, name
FROM rall, ( SELECT * FROM (VALUES {values}) AS t(path, name) ) AS t1
WHERE source_file=t1.name AND channel_id !=0;"""
values = ", ".join(f"'{f}'" for f in filenames)
select = f"""SELECT name
FROM rbud, ( SELECT * FROM (VALUES ({values})) AS t(name) ) AS t1
WHERE source_file=t1.name AND channel_id != 0 AND availability = 't' UNION SELECT name
FROM rall, ( SELECT * FROM (VALUES ({values})) AS t(name) ) AS t1
WHERE source_file=t1.name AND channel_id != 0 AND availability = 't';"""
with psycopg2.connect(current_app.config["DATABASE_URI"]) as conn:
logging.debug(conn.get_dsn_parameters())
......@@ -410,6 +409,32 @@ def is_open_file(paths):
return curs.fetchall()
def is_like_or_equal(params, key):
""" Builds the condition for the specified key in the "where" clause taking into account lists or wildcards. """
subquery = list()
for param in params[key].split(","):
op = "LIKE" if re.search(r"[*?]", param) else "="
key = "s.station" if key == "station" else key
subquery.append(f"{key} {op} '{param}'")
return " OR ".join(subquery)
def extend(params):
""" Get the result of the SQL query. """
select = f"""SELECT DISTINCT network, s.station, location, channel FROM networks AS n, station AS s, channel AS c WHERE n.network_id = s.network_id AND s.station_id = c.station_id AND ({is_like_or_equal(params, "network")}) AND ({is_like_or_equal(params, "station")}) AND ({is_like_or_equal(params, "channel")}) AND ({is_like_or_equal(params, "location")}) ORDER BY network, station, channel, location;"""
select = select.replace("?", "_").replace("*", "%")
with psycopg2.connect(current_app.config["DATABASE_URI"]) as conn:
logging.debug(conn.get_dsn_parameters())
logging.debug(f"Postgres version : {conn.server_version}")
with conn.cursor() as curs:
logging.debug(select)
curs.execute(select)
logging.debug(curs.statusmessage)
return curs.fetchall()
def get_signal(params):
"""Get timeseries from summer.
......@@ -419,16 +444,9 @@ def get_signal(params):
"""
logging.debug("Enter in get_signal function.")
net = f"-n={params['network']}"
sta = f"-s={params['station']}"
loc = "-l=_" if params["location"] == "--" else f"-l={params['location']}"
cha = f"-c={params['channel']}"
# Wildcards are extended into atomic quadruplets.
res = extend(args=[net, sta, loc, cha, "--format=tuple"])
res = extend(params)
logging.debug(res)
if not res:
return None
filenames = list()
delta = params["end"] - params["start"]
......@@ -443,23 +461,23 @@ def get_signal(params):
day = str(date.tm_yday).rjust(3, "0")
for net, sta, loc, cha in res:
loc = "" if loc == "--" else loc
filename = f"{net}.{sta}.{loc}.{cha}.D.{year}.{day}"
finalpath = f"{year}/{net}/{sta}/{cha}.D/"
filenames.append((finalpath, filename))
filenames.append(f"{net}.{sta}.{loc}.{cha}.D.{year}.{day}")
# We can now build the full path.
paths = list()
open_files = is_open_file(filenames)
# We can now build the full path.
if filenames:
for path in open_files:
finalpath = "".join(path)
basedir = DATA_MOUNT_POINT
validated = f"{basedir}/validated_seismic_data/{finalpath}"
bud = f"{basedir}/bud_data/{finalpath}"
if os.path.exists(validated):
paths.append(validated)
elif os.path.exists(bud):
paths.append(bud)
for filename in open_files:
path_bud = f"/rawdata/bud/{year}/{net}/{sta}/{cha}.D/"
if net in ("GL", "RD", "MQ", "WI", "MT", "CL", "ND", "FR", "PF", "G", "RA"):
path_validated = f"/data/bynet/{net}/{year}/{sta}/{cha}.D/"
else:
path_validated = f"/data/bynet/{net}{year}/{year}/{sta}/{cha}.D/"
for path in (path_bud, path_validated):
fullpath = DATA_DIR + path + filename[0]
if os.path.exists(fullpath):
paths.append(fullpath)
st = Stream()
for fullname in paths:
......@@ -473,6 +491,7 @@ def get_signal(params):
break
except Exception as ex:
logging.exception(str(ex))
pass
st.merge()
return st
......
# Webservice FDSN timeseriesplot
# Webservice timeseriesplot
The timeseriesplot service returns a graphical representation of time series data of the RESIF seismic network. The time span may be up to 31 days. Only unrestricted data are available.
......
# Webservice FDSN timeseriesplot
# Webservice timeseriesplot
Ce service permet d'obtenir une représentation graphique des signaux temporels des capteurs sismiques du réseau RESIF.
La plage demandée ne peut excéder 31 jours. Seules les données non restreintes sont accessibles.
......
# Webservice FDSN timeseries
# Webservice timeseries
This service provides access to the time series data of the RESIF seismic network. Optional signal processing may be applied and data may be returned in several formats. The time span may be up to 31 days. Only unrestricted data are available.
......
# Webservice FDSN timeseries
# Webservice timeseries
Ce service donne accès aux signaux temporels des capteurs sismiques du réseau RESIF. Des traitements supplémentaires peuvent être réalisés sur les données.
La plage demandée ne peut excéder 31 jours. Seules les données non restreintes sont accessibles.
......
......@@ -4,19 +4,21 @@
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>RESIF: RESIFWS: Timeseries Docs: v 1.0</title>
<style type="text/css">
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
<title>RESIF: RESIFWS: Timeseries Docs: v1</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
</style>
<link rel="stylesheet" href="./static/resifws.css"/>
<a href="./local=en"> <img src="./static/images/en.png" alt="Français - Anglais"> </a>
</head>
<body>
<p style="text-align:center"> <img src="./static/images/logoresif.png" alt="logoresif" width="341" height="95"></p>
<h1 id="webservice-fdsn-timeseries">Webservice FDSN timeseries</h1>
<p style="text-align:center;"> <img src="./static/images/logoresif.png" alt="logoresif" width="341" height="95"></p>
<h1 id="webservice-timeseries">Webservice timeseries</h1>
<p>Ce service donne accès aux signaux temporels des capteurs sismiques du réseau RESIF. Des traitements supplémentaires peuvent être réalisés sur les données. La plage demandée ne peut excéder 31 jours. Seules les données non restreintes sont accessibles.</p>
<h2 id="options-de-traitements-du-signal">Options de traitements du signal</h2>
<ul>
......@@ -117,8 +119,8 @@ les valeurs par défaut sont en majuscules</code></pre>
<ul>
<li>NETWORK : 1 à 2 caractères alphanumériques. Un groupe de points de mesures.</li>
<li>STATION : 1 à 5 caractères alphanumériques. Un site de mesure dans un réseau.</li>
<li>CHANNEL : 3 caractères alphanumériques. Le premier caractère indique la bande de fréquence du capteur, le second le type de l'instrument et le troisième l'orientation physique.</li>
<li>LOCATION : 2 caractères alphanumériques. Ils permettent de distinguer plusieurs flux de données d'un même canal.</li>
<li>CHANNEL : 3 caractères alphanumériques. Le premier caractère indique la bande de fréquence du capteur, le second le type de l’instrument et le troisième l’orientation physique.</li>
<li>LOCATION : 2 caractères alphanumériques. Ils permettent de distinguer plusieurs flux de données dun même canal.</li>
</ul>
<h3 id="formats-autorisés-pour-lintervalle-de-temps">Formats autorisés pour l’intervalle de temps</h3>
<p>La définition de l’intervalle de temps peut prendre différentes formes :</p>
......
......@@ -4,19 +4,21 @@
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>RESIF: RESIFWS: Timeseries Docs: v 1.0</title>
<style type="text/css">
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
<title>RESIF: RESIFWS: Timeseries Docs: v1</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
</style>
<link rel="stylesheet" href="./static/resifws.css"/>
<a href="./local=fr"> <img src="./static/images/fr.png" alt="Anglais - Français"> </a>
</head>
<body>
<p style="text-align:center"> <img src="./static/images/logoresif.png" alt="logoresif" width="341" height="95"></p>
<h1 id="webservice-fdsn-timeseries">Webservice FDSN timeseries</h1>
<p style="text-align:center;"> <img src="./static/images/logoresif.png" alt="logoresif" width="341" height="95"></p>
<h1 id="webservice-timeseries">Webservice timeseries</h1>
<p>This service provides access to the time series data of the RESIF seismic network. Optional signal processing may be applied and data may be returned in several formats. The time span may be up to 31 days. Only unrestricted data are available.</p>
<h2 id="signal-processing-options">Signal processing options</h2>
<ul>
......@@ -63,7 +65,7 @@ default values are uppercase</code></pre>
<p><a href="http://ws.resif.fr/resifws/timeseries/1/query?net=RA&station=PYTO&cha=HN2&loc=02&demean&correct&start=2017-11-02T13:35:00&end=2017-11-02T13:40:00&format=ascii">http://ws.resif.fr/resifws/timeseries/1/query?net=RA&amp;station=PYTO&amp;cha=HN2&amp;loc=02&amp;demean&amp;correct&amp;start=2017-11-02T13:35:00&amp;end=2017-11-02T13:40:00&amp;format=ascii</a></p>
<p><a href="http://ws.resif.fr/resifws/timeseries/1/query?net=RA&station=PYTO&cha=HN2&loc=02&demean&correct&start=2017-11-02T13:35:00&end=2017-11-02T13:40:00&format=plot">http://ws.resif.fr/resifws/timeseries/1/query?net=RA&amp;station=PYTO&amp;cha=HN2&amp;loc=02&amp;demean&amp;correct&amp;start=2017-11-02T13:35:00&amp;end=2017-11-02T13:40:00&amp;format=plot</a></p>
<h2 id="detailed-descriptions-of-each-query-parameter">Detailed descriptions of each query parameter</h2>
<h3 id="station-codes-details">Station code details</h3>
<h3 id="station-code-details">Station code details</h3>
<p>The four parameters (network, station, location, channel) determine channels of interest.</p>
<table>
<thead>
......
......@@ -4,19 +4,21 @@
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>RESIF: RESIFWS: Timeseriesplot Docs: v 1.0</title>
<style type="text/css">
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
<title>RESIF: RESIFWS: Timeseriesplot Docs: v1</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
</style>
<link rel="stylesheet" href="./static/resifws.css"/>
<a href="./local=en"> <img src="./static/images/en.png" alt="Français - Anglais"> </a>
</head>
<body>
<p style="text-align:center"> <img src="./static/images/logoresif.png" alt="logoresif" width="341" height="95"></p>
<h1 id="webservice-fdsn-timeseriesplot">Webservice FDSN timeseriesplot</h1>
<p style="text-align:center;"> <img src="./static/images/logoresif.png" alt="logoresif" width="341" height="95"></p>
<h1 id="webservice-timeseriesplot">Webservice timeseriesplot</h1>
<p>Ce service permet d’obtenir une représentation graphique des signaux temporels des capteurs sismiques du réseau RESIF. La plage demandée ne peut excéder 31 jours. Seules les données non restreintes sont accessibles.</p>
<h2 id="le-service-offre-plusieurs-options">Le service offre plusieurs options</h2>
<ul>
......@@ -105,8 +107,8 @@ les valeurs par défaut sont en majuscules</code></pre>
<ul>
<li>NETWORK : 1 à 2 caractères alphanumériques. Un groupe de points de mesures.</li>
<li>STATION : 1 à 5 caractères alphanumériques. Un site de mesure dans un réseau.</li>
<li>CHANNEL : 3 caractères alphanumériques. Le premier caractère indique la bande de fréquence du capteur, le second le type de l'instrument et le troisième l'orientation physique.</li>
<li>LOCATION : 2 caractères alphanumériques. Ils permettent de distinguer plusieurs flux de données d'un même canal.</li>
<li>CHANNEL : 3 caractères alphanumériques. Le premier caractère indique la bande de fréquence du capteur, le second le type de l’instrument et le troisième l’orientation physique.</li>
<li>LOCATION : 2 caractères alphanumériques. Ils permettent de distinguer plusieurs flux de données dun même canal.</li>
</ul>
<h3 id="formats-autorisés-pour-lintervalle-de-temps">Formats autorisés pour l’intervalle de temps</h3>
<p>La définition de l’intervalle de temps peut prendre différentes formes :</p>
......
......@@ -4,19 +4,21 @@
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>RESIF: RESIFWS: Timeseriesplot Docs: v 1.0</title>
<style type="text/css">
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
<title>RESIF: RESIFWS: Timeseriesplot Docs: v1</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
</style>
<link rel="stylesheet" href="./static/resifws.css"/>
<a href="./local=fr"> <img src="./static/images/fr.png" alt="Anglais - Français"> </a>
</head>
<body>
<p style="text-align:center"> <img src="./static/images/logoresif.png" alt="logoresif" width="341" height="95"></p>
<h1 id="webservice-fdsn-timeseriesplot">Webservice FDSN timeseriesplot</h1>
<p style="text-align:center;"> <img src="./static/images/logoresif.png" alt="logoresif" width="341" height="95"></p>
<h1 id="webservice-timeseriesplot">Webservice timeseriesplot</h1>
<p>The timeseriesplot service returns a graphical representation of time series data of the RESIF seismic network. The time span may be up to 31 days. Only unrestricted data are available.</p>
<h2 id="the-service-offers-several-options">The service offers several options</h2>
<ul>
......@@ -51,7 +53,7 @@ default values are uppercase</code></pre>
<p><a href="http://ws.resif.fr/resifws/timeseriesplot/1/query?net=RA&station=PYTO&cha=HN2&loc=00,02&demean&correct&start=2017-11-02T13:35:00&end=2017-11-02T13:40:00">http://ws.resif.fr/resifws/timeseriesplot/1/query?net=RA&amp;station=PYTO&amp;cha=HN2&amp;loc=00,02&amp;demean&amp;correct&amp;start=2017-11-02T13:35:00&amp;end=2017-11-02T13:40:00</a></p>
<p><a href="http://ws.resif.fr/resifws/timeseriesplot/1/query?net=RA&station=PYTO&cha=HN2&loc=00,02&demean&correct&start=2017-11-02T13:35:00&end=2017-11-02T13:40:00&iplot">http://ws.resif.fr/resifws/timeseriesplot/1/query?net=RA&amp;station=PYTO&amp;cha=HN2&amp;loc=00,02&amp;demean&amp;correct&amp;start=2017-11-02T13:35:00&amp;end=2017-11-02T13:40:00&amp;iplot</a></p>
<h2 id="detailed-descriptions-of-each-query-parameter">Detailed descriptions of each query parameter</h2>
<h3 id="station-codes-details">Station code details</h3>
<h3 id="station-code-details">Station code details</h3>
<p>The four parameters (network, station, location, channel) determine channels of interest.</p>
<table>
<thead>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment