You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1218 lines
43 KiB
Python

#!/usr/bin/env seiscomp-python
# -*- coding: utf-8 -*-
############################################################################
# Copyright (C) GFZ Potsdam #
# All rights reserved. #
# #
# GNU Affero General Public License Usage #
# This file may be used under the terms of the GNU Affero #
# Public License version 3.0 as published by the Free Software Foundation #
# and appearing in the file LICENSE included in the packaging of this #
# file. Please review the following information to ensure the GNU Affero #
# Public License version 3.0 requirements will be met: #
# https://www.gnu.org/licenses/agpl-3.0.html. #
############################################################################
from __future__ import print_function
import sys
import seiscomp.client
import seiscomp.io
import seiscomp.math
import seiscomp.datamodel
import seiscomp.logging
import seiscomp.seismology
def time2str(time):
"""
Convert a seiscomp.core.Time to a string
"""
return time.toString("%Y-%m-%d %H:%M:%S.%f000000")[:23]
def lat2str(lat, enhanced=False):
if enhanced:
s = "%.5f " % abs(lat)
else:
s = "%.2f " % abs(lat)
if lat >= 0:
s += "N"
else:
s += "S"
return s
def lon2str(lon, enhanced=False):
if enhanced:
s = "%.5f " % abs(lon)
else:
s = "%.2f " % abs(lon)
if lon >= 0:
s += "E"
else:
s += "W"
return s
def stationCount(org, minArrivalWeight):
count = 0
for i in range(org.arrivalCount()):
arr = org.arrival(i)
# if arr.weight()> 0.5:
if arr.weight() >= minArrivalWeight:
count += 1
return count
def uncertainty(quantity):
# for convenience/readability: get uncertainty from a quantity
try:
err = 0.5*(quantity.lowerUncertainty()+quantity.upperUncertainty())
except ValueError:
try:
err = quantity.uncertainty()
except ValueError:
err = None
return err
class Bulletin(object):
def __init__(self, dbq, long=True):
self._dbq = dbq
self._long = long
self._evt = None
self.format = "autoloc1"
self.enhanced = False
self.polarities = False
self.useEventAgencyID = False
self.distInKM = False
self.minDepthPhaseCount = 3
self.minArrivalWeight = 0.5
self.minStationMagnitudeWeight = 0.5
def _getArrivalsSorted(self, org):
# returns arrival list sorted by distance
arrivals = [ org.arrival(i) for i in range(org.arrivalCount()) ]
return sorted(arrivals, key=lambda t: t.distance())
def _getPicks(self, org):
if self._dbq is None:
return {}
orid = org.publicID()
pick = {}
for obj in self._dbq.getPicks(orid):
p = seiscomp.datamodel.Pick.Cast(obj)
key = p.publicID()
pick[key] = p
return pick
def _getAmplitudes(self, org):
if self._dbq is None:
return {}
orid = org.publicID()
ampl = {}
for obj in self._dbq.getAmplitudesForOrigin(orid):
amp = seiscomp.datamodel.Amplitude.Cast(obj)
key = amp.publicID()
ampl[key] = amp
return ampl
def _printOriginAutoloc3(self, org, extra=False):
orid = org.publicID()
arrivals = self._getArrivalsSorted(org)
pick = self._getPicks(org)
ampl = self._getAmplitudes(org)
try:
depthPhaseCount = org.quality().depthPhaseCount()
except ValueError:
depthPhaseCount = 0
for arr in arrivals:
wt = arr.weight()
pha = arr.phase().code()
# if (pha[0] in ["p","s"] and wt >= 0.5 ):
if (pha[0] in ["p", "s"] and wt >= self.minArrivalWeight):
depthPhaseCount += 1
txt = ""
evt = self._evt
if not evt and self._dbq:
evt = self._dbq.getEvent(orid)
if evt:
txt += "Event:\n"
txt += " Public ID %s\n" % evt.publicID()
if extra:
txt += " Preferred Origin ID %s\n" % evt.preferredOriginID()
txt += " Preferred Magnitude ID %s\n" % evt.preferredMagnitudeID()
try:
evtType = evt.type()
txt += " Type %s\n" % \
seiscomp.datamodel.EEventTypeNames.name(evtType)
except ValueError:
seiscomp.logging.warning("%s: ignoring unknown event type" %
evt.publicID())
txt += " Description\n"
for i in range(evt.eventDescriptionCount()):
evtd = evt.eventDescription(i)
evtdtype = seiscomp.datamodel.EEventDescriptionTypeNames.name(
evtd.type())
txt += " %s: %s" % (evtdtype, evtd.text())
if extra:
try:
txt += "\n Creation time %s\n" % evt.creationInfo().creationTime().toString("%Y-%m-%d %H:%M:%S")
except ValueError:
pass
txt += "\n"
preferredMagnitudeID = evt.preferredMagnitudeID()
else:
preferredMagnitudeID = ""
tim = org.time().value()
lat = org.latitude().value()
lon = org.longitude().value()
dep = org.depth().value()
timerr = uncertainty(org.time())
laterr = uncertainty(org.latitude())
lonerr = uncertainty(org.longitude())
deperr = uncertainty(org.depth())
tstr = time2str(tim)
originHeader = "Origin:\n"
if evt:
if org.publicID() != evt.preferredOriginID():
originHeader = "Origin (NOT the preferred origin of this event):\n"
txt += originHeader
if extra:
txt += " Public ID %s\n" % org.publicID()
txt += " Date %s\n" % tstr[:10]
if timerr:
if self.enhanced:
txt += " Time %s +/- %8.3f s\n" % (
tstr[11:], timerr)
else:
txt += " Time %s +/- %6.1f s\n" % (
tstr[11:-2], timerr)
else:
if self.enhanced:
txt += " Time %s\n" % tstr[11:]
else:
txt += " Time %s\n" % tstr[11:-2]
if laterr:
if self.enhanced:
txt += " Latitude %10.5f deg +/- %8.3f km\n" % (
lat, laterr)
else:
txt += " Latitude %7.2f deg +/- %6.0f km\n" % (
lat, laterr)
else:
if self.enhanced:
txt += " Latitude %10.5f deg\n" % lat
else:
txt += " Latitude %7.2f deg\n" % lat
if lonerr:
if self.enhanced:
txt += " Longitude %10.5f deg +/- %8.3f km\n" % (
lon, lonerr)
else:
txt += " Longitude %7.2f deg +/- %6.0f km\n" % (
lon, lonerr)
else:
if self.enhanced:
txt += " Longitude %10.5f deg\n" % lon
else:
txt += " Longitude %7.2f deg\n" % lon
if self.enhanced:
txt += " Depth %11.3f km" % dep
else:
txt += " Depth %7.0f km" % dep
if deperr is None:
txt += "\n"
elif deperr == 0:
txt += " (fixed)\n"
else:
if depthPhaseCount >= self.minDepthPhaseCount:
if self.enhanced:
txt += " +/- %8.3f km (%d depth phases)\n" % (
deperr, depthPhaseCount)
else:
txt += " +/- %4.0f km (%d depth phases)\n" % (
deperr, depthPhaseCount)
else:
if self.enhanced:
txt += " +/- %8.3f km\n" % deperr
else:
txt += " +/- %4.0f km\n" % deperr
agencyID = ""
if self.useEventAgencyID:
try:
agencyID = evt.creationInfo().agencyID()
except ValueError:
pass
else:
try:
agencyID = org.creationInfo().agencyID()
except ValueError:
pass
txt += " Agency %s\n" % agencyID
if extra:
try:
authorID = org.creationInfo().author()
except ValueError:
authorID = "NOT SET"
txt += " Author %s\n" % authorID
txt += " Mode "
try:
txt += "%s\n" % seiscomp.datamodel.EEvaluationModeNames.name(
org.evaluationMode())
except ValueError:
txt += "NOT SET\n"
txt += " Status "
try:
txt += "%s\n" % seiscomp.datamodel.EEvaluationStatusNames.name(
org.evaluationStatus())
except ValueError:
txt += "NOT SET\n"
if extra:
txt += " Creation time "
try:
txt += "%s\n" % org.creationInfo().creationTime().toString("%Y-%m-%d %H:%M:%S")
except ValueError:
txt += "NOT SET\n"
try:
if self.enhanced:
txt += " Residual RMS %9.3f s\n" % org.quality().standardError()
else:
txt += " Residual RMS %6.2f s\n" % org.quality().standardError()
except ValueError:
pass
try:
if self.enhanced:
txt += " Azimuthal gap %8.1f deg\n" % org.quality().azimuthalGap()
else:
txt += " Azimuthal gap %5.0f deg\n" % org.quality().azimuthalGap()
except ValueError:
pass
txt += "\n"
networkMagnitudeCount = org.magnitudeCount()
networkMagnitudes = {}
# Each station magnitude contributes to the network
# magnitude of the same type.
#
# We save here the StationMagnitudeContribution objects
# by publicID of the corresponding StationMagnitude object.
stationMagnitudeContributions = {}
tmptxt = txt
txt = ""
foundPrefMag = False
for i in range(networkMagnitudeCount):
mag = org.magnitude(i)
val = mag.magnitude().value()
typ = mag.type()
networkMagnitudes[typ] = mag
for k in range(mag.stationMagnitudeContributionCount()):
smc = mag.stationMagnitudeContribution(k)
smid = smc.stationMagnitudeID()
stationMagnitudeContributions[smid] = smc
err = uncertainty(mag.magnitude())
if err is not None:
err = "+/- %.2f" % err
else:
err = ""
if mag.publicID() == preferredMagnitudeID:
preferredMarker = "preferred"
foundPrefMag = True
else:
preferredMarker = " "
if extra:
try:
agencyID = mag.creationInfo().agencyID()
except ValueError:
pass
else:
agencyID = ""
txt += " %-8s %5.2f %8s %3d %s %s\n" % \
(typ, val, err, mag.stationCount(), preferredMarker, agencyID)
if not foundPrefMag and preferredMagnitudeID != "":
mag = seiscomp.datamodel.Magnitude.Find(preferredMagnitudeID)
if mag is None and self._dbq:
o = self._dbq.loadObject(
seiscomp.datamodel.Magnitude.TypeInfo(), preferredMagnitudeID)
mag = seiscomp.datamodel.Magnitude.Cast(o)
if mag:
val = mag.magnitude().value()
typ = mag.type()
networkMagnitudes[typ] = mag
err = uncertainty(mag.magnitude())
if err is not None:
err = "+/- %.2f" % err
else:
err = ""
preferredMarker = "preferred"
if extra:
try:
agencyID = mag.creationInfo().agencyID()
except ValueError:
pass
else:
agencyID = ""
txt += " %-8s %5.2f %8s %3d %s %s\n" % \
(typ, val, err, mag.stationCount(), preferredMarker, agencyID)
txt = tmptxt + "%d Network magnitudes:\n" % networkMagnitudeCount + txt
if not self._long:
return txt
lineFMT = " %-5s %-2s "
if self.enhanced:
lineFMT += "%9.3f" if self.distInKM else "%9.5f"
else:
lineFMT += "%5.0f" if self.distInKM else "%5.1f"
lineFMT += " %s %-7s %s %s %1s%1s %3.1f "
if self.polarities:
lineFMT += "%s "
lineFMT += "%-5s\n"
dist_azi = {}
lines = []
for arr in arrivals:
p = seiscomp.datamodel.Pick.Find(arr.pickID())
if p is None:
lines.append((180, " ## missing pick %s\n" % arr.pickID()))
continue
if self.distInKM:
dist = seiscomp.math.deg2km(arr.distance())
else:
dist = arr.distance()
wfid = p.waveformID()
net = wfid.networkCode()
sta = wfid.stationCode()
if self.enhanced:
try:
azi = "%5.1f" % arr.azimuth()
except ValueError:
azi = " N/A"
tstr = time2str(p.time().value())[11:]
try:
res = "%7.3f" % arr.timeResidual()
except ValueError:
res = " N/A"
else:
try:
azi = "%3.0f" % arr.azimuth()
except ValueError:
azi = "N/A"
tstr = time2str(p.time().value())[11:-2]
try:
res = "%5.1f" % arr.timeResidual()
except ValueError:
res = " N/A"
dist_azi[net+"_"+sta] = (dist, azi)
wt = arr.weight()
pha = arr.phase().code()
flag = "X "[wt > 0.1]
try:
status = seiscomp.datamodel.EEvaluationModeNames.name(p.evaluationMode())[
0].upper()
except ValueError:
status = "-"
if self.polarities:
try:
pol = seiscomp.datamodel.EPickPolarityNames.name(
p.polarity())
except ValueError:
pol = None
if pol:
if pol == "positive":
pol = "u"
elif pol == "negative":
pol = "d"
elif pol == "undecidable":
pol = "x"
else:
pol = "."
else:
pol = "."
line = lineFMT % (sta, net, dist, azi, pha,
tstr, res, status, flag, wt, pol, sta)
else:
line = lineFMT % (sta, net, dist, azi, pha,
tstr, res, status, flag, wt, sta)
lines.append((dist, line))
lines.sort()
txt += "\n"
txt += "%d Phase arrivals:\n" % org.arrivalCount()
if self.enhanced:
txt += " sta net dist azi phase time res wt "
else:
txt += " sta net dist azi phase time res wt "
if self.polarities:
txt += " "
txt += "sta \n"
for dist, line in lines:
txt += line
txt += "\n"
stationMagnitudeCount = org.stationMagnitudeCount()
activeStationMagnitudeCount = 0
stationMagnitudes = {}
for i in range(stationMagnitudeCount):
mag = org.stationMagnitude(i)
typ = mag.type()
if typ not in networkMagnitudes:
continue
if typ not in stationMagnitudes:
stationMagnitudes[typ] = []
# suppress unused station magnitudes
smid = mag.publicID()
if not smid in stationMagnitudeContributions:
continue
try:
w = stationMagnitudeContributions[smid].weight()
except ValueError:
w = self.minStationMagnitudeWeight
if w < self.minStationMagnitudeWeight:
continue
stationMagnitudes[typ].append(mag)
activeStationMagnitudeCount += 1
lineFMT = " %-5s %-2s "
if self.enhanced:
lineFMT += "%9.3f" if self.distInKM else "%9.5f"
else:
lineFMT += "%5.0f" if self.distInKM else "%5.1f"
lineFMT += " %s %-6s %5.2f %5.2f %8s %4s\n"
lines = []
for typ in stationMagnitudes:
for mag in stationMagnitudes[typ]:
key = mag.amplitudeID()
amp = seiscomp.datamodel.Amplitude.Find(key)
if amp is None and self._dbq:
seiscomp.logging.debug(
"missing station amplitude '%s'" % key)
# FIXME really slow!!!
obj = self._dbq.loadObject(
seiscomp.datamodel.Amplitude.TypeInfo(), key)
amp = seiscomp.datamodel.Amplitude.Cast(obj)
p = a = "N/A"
if amp:
try:
a = "%g" % amp.amplitude().value()
except ValueError:
a = "N/A"
if typ in ["mb", "Ms", "Ms(BB)"]:
try:
p = "%.2f" % amp.period().value()
except ValueError:
p = "N/A"
else:
p = ""
wfid = mag.waveformID()
net = wfid.networkCode()
sta = wfid.stationCode()
try:
dist, azi = dist_azi[net+"_"+sta]
except ValueError:
dist, azi = 0, " N/A" if self.enhanced else "N/A"
val = mag.magnitude().value()
res = val - networkMagnitudes[typ].magnitude().value()
line = lineFMT % (sta, net, dist, azi, typ, val, res, a, p)
lines.append((dist, line))
lines.sort()
if activeStationMagnitudeCount:
txt += "%d Station magnitudes:\n" % activeStationMagnitudeCount
if self.enhanced:
txt += " sta net dist azi type value res amp per\n"
else:
txt += " sta net dist azi type value res amp per\n"
for dist, line in lines:
txt += line
else:
txt += "No station magnitudes\n"
return txt
def _printOriginAutoloc1(self, org):
evt = self._evt
if not evt and self._dbq:
evt = self._dbq.getEvent(org.publicID())
if evt:
evid = evt.publicID()
pos = evid.find("#") # XXX Hack!!!
if pos != -1:
evid = evid[:pos]
prefMagID = evt.preferredMagnitudeID()
else:
evid = "..."
prefMagID = ""
txt = ""
if self.enhanced:
depth = org.depth().value()
sTime = org.time().value().toString("%Y/%m/%d %H:%M:%S.%f00")[:24]
else:
depth = int(org.depth().value()+0.5)
sTime = org.time().value().toString("%Y/%m/%d %H:%M:%S.%f")[:22]
tmp = {
"evid": evid,
"nsta": stationCount(org, self.minArrivalWeight),
"time": sTime,
"lat": lat2str(org.latitude().value(), self.enhanced),
"lon": lon2str(org.longitude().value(), self.enhanced),
"dep": depth,
"reg": seiscomp.seismology.Regions.getRegionName(org.latitude().value(), org.longitude().value()),
# changed to properly report location method. (Marco Olivieri 21/06/2010)
"method": org.methodID(),
"model": org.earthModelID(),
# end (MO)
"stat": "A"
}
try:
if org.evaluationMode() == seiscomp.datamodel.MANUAL:
tmp["stat"] = "M"
except ValueError:
pass
# dummy default
tmp["mtyp"] = "M"
tmp["mval"] = 0.
foundMag = False
networkMagnitudeCount = org.magnitudeCount()
for i in range(networkMagnitudeCount):
mag = org.magnitude(i)
if mag.publicID() == prefMagID:
tmp["mtyp"] = mag.type()
tmp["mval"] = mag.magnitude().value()
foundMag = True
break
if not foundMag and prefMagID != "":
mag = seiscomp.datamodel.Magnitude.Find(prefMagID)
if mag is None and self._dbq:
o = self._dbq.loadObject(
seiscomp.datamodel.Magnitude.TypeInfo(), prefMagID)
mag = seiscomp.datamodel.Magnitude.Cast(o)
if mag:
tmp["mtyp"] = mag.type()
tmp["mval"] = mag.magnitude().value()
# changed to properly report location method. (Marco Olivieri 21/06/2010)
# txt += """
# Autoloc alert %(evid)s: determined by %(nsta)d stations, type %(stat)s
#
# LOCSAT solution (with start solution, %(nsta)d stations used, weight %(nsta)d):
#
# %(reg)s %(mtyp)s=%(mval).1f %(time)s %(lat)s %(lon)s %(dep)d km
#
# Stat Net Date Time Amp Per Res Dist Az mb ML mB
#""" % tmp
txtFMT = """
Alert %(evid)s: determined by %(nsta)d stations, type %(stat)s
%(method)s solution with earthmodel %(model)s (with start solution, %(nsta)d stations used, weight %(nsta)d):
%(reg)s %(mtyp)s=%(mval).1f %(time)s %(lat)s %(lon)s %(dep)"""
if self.enhanced:
txtFMT += """.3f km
Stat Net Date Time Amp Per Res Dist Az mb ML mB
"""
else:
txtFMT += """d km
Stat Net Date Time Amp Per Res Dist Az mb ML mB
"""
txt += txtFMT % tmp
# end (MO)
arrivals = self._getArrivalsSorted(org)
pick = self._getPicks(org)
ampl = self._getAmplitudes(org)
stationMagnitudeCount = org.stationMagnitudeCount()
stationMagnitudes = {}
for i in range(stationMagnitudeCount):
mag = org.stationMagnitude(i)
typ = mag.type()
if typ == "MLv" or typ == "MLh":
typ = "ML"
if typ not in stationMagnitudes:
stationMagnitudes[typ] = {}
sta = mag.waveformID().stationCode()
stationMagnitudes[typ][sta] = mag
lineFMT = " %-5s %-2s %s %10.1f %4.1f %s "
if self.enhanced:
lineFMT += "%9.3f" if self.distInKM else "%9.5f"
else:
lineFMT += "%5.0f" if self.distInKM else "%5.1f"
lineFMT += " %s%s\n"
for arr in arrivals:
if arr.weight() < self.minArrivalWeight:
continue
if self.distInKM:
dist = seiscomp.math.deg2km(arr.distance())
else:
dist = arr.distance()
p = seiscomp.datamodel.Pick.Find(arr.pickID())
if p is None:
txt += " ## missing pick %s\n" % arr.pickID()
continue
wfid = p.waveformID()
net = wfid.networkCode()
sta = wfid.stationCode()
if self.enhanced:
tstr = p.time().value().toString(
"%y/%m/%d %H:%M:%S.%f00")[:22]
try:
res = "%7.3f" % arr.timeResidual()
except ValueError:
res = " N/A"
try:
azi = "%5.1f" % arr.azimuth()
except ValueError:
azi = " N/A"
else:
tstr = p.time().value().toString("%y/%m/%d %H:%M:%S.%f")[:20]
try:
res = "%5.1f" % arr.timeResidual()
except ValueError:
res = " N/A"
try:
azi = "%3.0f" % arr.azimuth()
except ValueError:
azi = "N/A"
pha = arr.phase().code()
mstr = ""
amp = per = 0.
for typ in ["mb", "ML", "mB"]:
mag = 0.
try:
m = stationMagnitudes[typ][sta]
mag = m.magnitude().value()
if typ == "mb":
ampid = m.amplitudeID()
a = seiscomp.datamodel.Amplitude.Find(ampid)
if a is None and self._dbq:
obj = self._dbq.loadObject(
seiscomp.datamodel.Amplitude.TypeInfo(), ampid)
a = seiscomp.datamodel.Amplitude.Cast(obj)
if a:
per = a.period().value()
try:
amp = a.amplitude().value()
except ValueError:
amp = -1
except KeyError:
pass
mstr += " %3.1f" % mag
txt += lineFMT % (sta, net, tstr, amp, per, res, dist, azi, mstr)
if self.enhanced:
txt += "\n RMS-ERR: %.3f\n\n" % org.quality().standardError()
else:
txt += "\n RMS-ERR: %.2f\n\n" % org.quality().standardError()
if evt:
try:
if self.enhanced:
tm = evt.creationInfo().creationTime().toString("%Y/%m/%d %H:%M:%S.%f")
else:
tm = evt.creationInfo().creationTime().toString("%Y/%m/%d %H:%M:%S")
txt += " Event created: %s\n" % tm
except ValueError:
pass
try:
if self.enhanced:
tm = org.creationInfo().creationTime().toString("%Y/%m/%d %H:%M:%S.%f")
else:
tm = org.creationInfo().creationTime().toString("%Y/%m/%d %H:%M:%S")
txt += " This origin created: %s\n" % tm
except ValueError:
pass
return txt
def _printOriginFDSN(self, org):
evt = self._evt
if not evt and self._dbq:
evt = self._dbq.getEvent(org.publicID())
author, agencyID, eType, prefMagID = '', '', '', ''
if evt:
evid = evt.publicID()
pos = evid.find("#") # XXX Hack!!!
if pos != -1:
evid = evid[:pos]
try:
prefMagID = evt.preferredMagnitudeID()
except ValueError:
pass
try:
author = org.creationInfo().author()
except ValueError:
pass
if self.useEventAgencyID:
try:
agencyID = evt.creationInfo().agencyID()
except ValueError:
pass
else:
try:
agencyID = org.creationInfo().agencyID()
except ValueError:
pass
try:
eType = evt.type()
except ValueError:
pass
else:
evid = ''
prefMagID = ''
if self.enhanced:
depth = "{:.3f}".format(org.depth().value())
sTime = org.time().value().toString('%FT%T.%f')[:26]
else:
depth = "{:.0f}".format(org.depth().value())
sTime = org.time().value().toString('%FT%T.%f')[:23]
lat = lat2str(org.latitude().value(), self.enhanced)
lon = lon2str(org.longitude().value(), self.enhanced)
try:
region = seiscomp.seismology.Regions.getRegionName(org.latitude().value(), org.longitude().value())
except ValueError:
region = ''
foundMag = False
mType, mVal, mAuthor = '', '', ''
networkMagnitudeCount = org.magnitudeCount()
for i in range(networkMagnitudeCount):
mag = org.magnitude(i)
if mag.publicID() == prefMagID:
mType = mag.type()
mVal = "{:.1f}".format(mag.magnitude().value())
foundMag = True
break
if not foundMag and prefMagID != "":
mag = seiscomp.datamodel.Magnitude.Find(prefMagID)
if mag is None and self._dbq:
o = self._dbq.loadObject(
seiscomp.datamodel.Magnitude.TypeInfo(), prefMagID)
mag = seiscomp.datamodel.Magnitude.Cast(o)
if mag:
mType = mag.type()
mVal = "{:.1f}".format(mag.magnitude().value())
try:
mAuthor = mag.creationInfo().author()
except ValueError:
pass
txt = "%s|%s|%s|%s|%s|%s||%s|%s|%s|%s|%s|%s|%s\n" % (
evid, sTime, lat, lon, depth, author, agencyID, evid, mType,
mVal, mAuthor, region, eType)
return txt
def printOrigin(self, origin):
org = None
if isinstance(origin, seiscomp.datamodel.Origin):
org = origin
elif isinstance(origin, str):
if self._dbq:
org = self._dbq.loadObject(
seiscomp.datamodel.Origin.TypeInfo(), origin)
org = seiscomp.datamodel.Origin.Cast(org)
if not org:
raise KeyError("Unknown origin " + origin)
else:
raise TypeError("illegal type for origin")
if self.format == "fdsnws":
return self._printOriginFDSN(org)
elif self.format == "autoloc1":
return self._printOriginAutoloc1(org)
elif self.format == "autoloc3":
return self._printOriginAutoloc3(org, extra=False)
elif self.format == "autoloc3extra":
return self._printOriginAutoloc3(org, extra=True)
else:
pass
def printEvent(self, event):
try:
evt = None
if isinstance(event, seiscomp.datamodel.Event):
self._evt = event
org = seiscomp.datamodel.Origin.Find(
event.preferredOriginID())
if not org:
org = event.preferredOriginID()
return self.printOrigin(org)
elif isinstance(event, str):
if self._dbq:
evt = self._dbq.loadObject(
seiscomp.datamodel.Event.TypeInfo(), event)
evt = seiscomp.datamodel.Event.Cast(evt)
self._evt = evt
if evt is None:
raise KeyError("unknown event " + event)
return self.printOrigin(evt.preferredOriginID())
else:
raise TypeError("illegal type for event")
finally:
self._evt = None
class BulletinApp(seiscomp.client.Application):
def __init__(self, argc, argv):
seiscomp.client.Application.__init__(self, argc, argv)
self.setMessagingEnabled(False)
self.setDatabaseEnabled(True, True)
self.setDaemonEnabled(False)
self.setLoggingToStdErr(True)
self.setLoadRegionsEnabled(True)
self.format = "autoloc1"
self.inputFile = None
def createCommandLineDescription(self):
try:
self.commandline().addGroup("Input")
self.commandline().addStringOption("Input", "format,f",
"Input format to use (xml "
"[default], zxml (zipped xml), "
"binary).")
self.commandline().addStringOption("Input", "input,i",
"Input file, default: stdin.")
self.commandline().addGroup("Dump")
self.commandline().addStringOption("Dump", "event,E",
"ID of event to dump. Separate "
"multiple IDs by comma.")
self.commandline().addStringOption("Dump", "origin,O",
"ID of origin to dump. Separate"
" multiple IDs by comma.")
self.commandline().addOption("Dump", "autoloc1,1",
"autoloc1 format.")
self.commandline().addOption("Dump", "autoloc3,3",
"autoloc3 format")
self.commandline().addOption("Dump", "enhanced,e",
"Enhanced output precision for local "
"earthquakes.")
self.commandline().addOption("Dump", "event-agency-id",
"Use agency ID information from event"
" instead of preferred origin.")
self.commandline().addOption("Dump", "fdsnws",
"Dump in FDSNWS event text format, "
"e.g., for generating catalogs.")
self.commandline().addOption("Dump", "first-only",
"Dump only the first event/origin. "
"Expects input from file or stdin.")
self.commandline().addOption("Dump", "dist-in-km,k",
"Plot distances in km instead of"
" degree.")
self.commandline().addOption("Dump", "polarities,p",
"Dump onset polarities.")
self.commandline().addStringOption("Dump", "weight,w",
"Weight threshold for printed "
"and counted picks.")
self.commandline().addOption("Dump", "extra,x",
"Extra detailed autoloc3 format.")
except RuntimeError:
seiscomp.logging.warning(
"caught unexpected error %s" % sys.exc_info())
return True
def validateParameters(self):
if not seiscomp.client.Application.validateParameters(self):
return False
try:
self.inputFile = self.commandline().optionString("input")
except RuntimeError:
self.inputFile = None
if self.inputFile:
self.setDatabaseEnabled(False, False)
if not self.commandline().hasOption("event") and not self.commandline().hasOption("origin"):
self.setDatabaseEnabled(False, False)
return True
def printUsage(self):
print('''Usage:
scbulletin [options]
Generate bulletins from events or origins in autoloc1, autoloc3 or fdsnws format.''')
seiscomp.client.Application.printUsage(self)
print('''Examples:
Create a bulletin from one event in the seiscomp database
scbulletin -d mysql://sysop:sysop@localhost/seiscomp -E gempa2012abcd
Create a bulletin from event parameters in XML
scbulletin -i gempa2012abcd.xml
''')
return True
def run(self):
evid = None
orid = None
mw = None
inputFile = None
txt = None
try:
evid = self.commandline().optionString("event")
except RuntimeError:
pass
try:
orid = self.commandline().optionString("origin")
except RuntimeError:
pass
if evid != "" or orid != "" or not self.inputFile:
dbq = seiscomp.datamodel.DatabaseQuery(self.database())
else:
dbq = None
bulletin = Bulletin(dbq)
bulletin.format = "autoloc1"
try:
mw = self.commandline().optionString("weight")
except RuntimeError:
pass
if mw != "" and mw is not None:
bulletin.minArrivalWeight = float(mw)
if self.commandline().hasOption("autoloc1"):
bulletin.format = "autoloc1"
elif self.commandline().hasOption("autoloc3"):
if self.commandline().hasOption("extra"):
bulletin.format = "autoloc3extra"
else:
bulletin.format = "autoloc3"
if self.commandline().hasOption("fdsnws"):
bulletin.format = "fdsnws"
if self.commandline().hasOption("enhanced"):
bulletin.enhanced = True
if self.commandline().hasOption("polarities"):
bulletin.polarities = True
if self.commandline().hasOption("event-agency-id"):
bulletin.useEventAgencyID = True
if self.commandline().hasOption("dist-in-km"):
bulletin.distInKM = True
inputFile = self.inputFile
if not self.inputFile:
txt = ''
try:
if evid:
for ev in evid.split(','):
try:
txt += bulletin.printEvent(ev)
except ValueError:
seiscomp.logging.error("Unknown event '%s'" % ev)
elif orid:
for org in orid.split(','):
try:
txt += bulletin.printOrigin(org)
except ValueError:
seiscomp.logging.error("Unknown origin '%s'" % org)
else:
inputFile = "-"
print("Expecting input in XML from stdin", file=sys.stderr)
except Exception as exc:
print("ERROR: {}".format(exc), file=sys.stderr)
# return False
else:
inputFile = self.inputFile
if inputFile:
inputFormat = "xml"
try:
try:
inputFormat = self.commandline().optionString("format")
except RuntimeError:
pass
if inputFormat == "xml":
ar = seiscomp.io.XMLArchive()
elif inputFormat == "zxml":
ar = seiscomp.io.XMLArchive()
ar.setCompression(True)
elif inputFormat == "binary":
ar = seiscomp.io.BinaryArchive()
else:
raise TypeError("unknown input format: " + inputFormat)
if not ar.open(inputFile):
raise IOError("Unable to open input file " + inputFile)
obj = ar.readObject()
if obj is None:
raise IOError("Invalid format in " + inputFile)
ep = seiscomp.datamodel.EventParameters.Cast(obj)
if ep is None:
raise TypeError("No event parameters found in "
+ inputFile)
if ep.eventCount() <= 0:
if ep.originCount() <= 0:
raise TypeError("No origin and no event in event "
"parameters found in " + inputFile)
if self.commandline().hasOption("first-only"):
org = ep.origin(0)
txt = bulletin.printOrigin(org)
else:
txt = ""
for i in range(ep.originCount()):
org = ep.origin(i)
if orid and org.publicID() not in orid:
seiscomp.logging.error(
"%s: Skipping origin with ID %s" %
(inputFile, org.publicID()))
continue
txt += bulletin.printOrigin(org)
else:
if self.commandline().hasOption("first-only"):
ev = ep.event(0)
if ev is None:
raise TypeError("Invalid event in " + inputFile)
try:
txt = bulletin.printEvent(ev)
except KeyError:
raise TypeError("Unknown event")
elif orid:
txt = ""
for oid in orid.split(','):
org = ep.findOrigin(oid)
if org:
txt += bulletin.printOrigin(org)
else:
seiscomp.logging.error("%s: Skipping origin with ID %s" %
(inputFile, oid))
else:
txt = ""
for i in range(ep.eventCount()):
ev = ep.event(i)
if evid and ev.publicID() not in evid:
seiscomp.logging.error("%s: Skipping event with ID %s" %
(inputFile, ev.publicID()))
continue
try:
txt += bulletin.printEvent(ev)
except KeyError:
raise TypeError("Unknown event")
except Exception as exc:
seiscomp.logging.error("%s" % str(exc))
return False
if txt:
if bulletin.format == "fdsnws":
print("#EventID|Time|Latitude|Longitude|Depth/km|Author|"
"Catalog|Contributor|ContributorID|MagType|Magnitude|"
"MagAuthor|EventLocationName|EventType")
print("{}".format(txt), file=sys.stdout)
return True
def main():
app = BulletinApp(len(sys.argv), sys.argv)
return app()
if __name__ == "__main__":
sys.exit(main())