Files
2025/bin/sh2proc
2025-10-29 12:34:04 +00:00

963 lines
37 KiB
Plaintext
Executable File

#!/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. #
# #
# Author: Alexander Jaeger, Stephan Herrnkind, #
# Lukas Lehmann, Dirk Roessler# #
# Email: herrnkind@gempa.de #
############################################################################
# from time import strptime
import sys
import traceback
import seiscomp.client
import seiscomp.core
import seiscomp.datamodel
import seiscomp.io
import seiscomp.logging
import seiscomp.math
TimeFormats = ["%d-%b-%Y_%H:%M:%S.%f", "%d-%b-%Y_%H:%M:%S"]
# SC3 has more event types available in the datamodel
EventTypes = {
"teleseismic quake": seiscomp.datamodel.EARTHQUAKE,
"local quake": seiscomp.datamodel.EARTHQUAKE,
"regional quake": seiscomp.datamodel.EARTHQUAKE,
"quarry blast": seiscomp.datamodel.QUARRY_BLAST,
"nuclear explosion": seiscomp.datamodel.NUCLEAR_EXPLOSION,
"mining event": seiscomp.datamodel.MINING_EXPLOSION,
}
def wfs2Str(wfsID):
return f"{wfsID.networkCode()}.{wfsID.stationCode()}.{wfsID.locationCode()}.{wfsID.channelCode()}"
###############################################################################
class SH2Proc(seiscomp.client.Application):
###########################################################################
def __init__(self):
seiscomp.client.Application.__init__(self, len(sys.argv), sys.argv)
self.setMessagingEnabled(True)
self.setDatabaseEnabled(True, True)
self.setLoadInventoryEnabled(True)
self.setLoadConfigModuleEnabled(True)
self.setDaemonEnabled(False)
self.inputFile = "-"
self.streams = None
###########################################################################
def initConfiguration(self):
if not seiscomp.client.Application.initConfiguration(self):
return False
# If the database connection is passed via command line or configuration
# file then messaging is disabled. Messaging is only used to get
# the configured database connection URI.
if self.databaseURI() != "":
self.setMessagingEnabled(False)
else:
# A database connection is not required if the inventory is loaded
# from file
if not self.isInventoryDatabaseEnabled():
self.setMessagingEnabled(False)
self.setDatabaseEnabled(False, False)
return True
##########################################################################
def printUsage(self):
print(
"""Usage:
sh2proc [options]
Convert Seismic Handler event data to SeisComP XML format which is sent to stdout."""
)
seiscomp.client.Application.printUsage(self)
print(
"""Examples:
Convert the Seismic Handler file shm.evt to SCML. Receive the database
connection to read inventory and configuration information from messaging
sh2proc shm.evt > event.xml
Read Seismic Handler data from stdin. Provide inventory and configuration in XML
cat shm.evt | sh2proc --inventory-db=inventory.xml --config-db=config.xml > event.xml
"""
)
##########################################################################
def validateParameters(self):
if not seiscomp.client.Application.validateParameters(self):
return False
for opt in self.commandline().unrecognizedOptions():
if len(opt) > 1 and opt.startswith("-"):
continue
self.inputFile = opt
break
return True
###########################################################################
def loadStreams(self):
now = seiscomp.core.Time.GMT()
inv = seiscomp.client.Inventory.Instance()
self.streams = {}
# try to load streams by detecLocid and detecStream
mod = self.configModule()
if mod is not None and mod.configStationCount() > 0:
seiscomp.logging.info("loading streams using detecLocid and detecStream")
for i in range(mod.configStationCount()):
cfg = mod.configStation(i)
net = cfg.networkCode()
sta = cfg.stationCode()
if sta in self.streams:
seiscomp.logging.warning(
f"ambiguous stream id found for station {net}.{sta}"
)
continue
setup = seiscomp.datamodel.findSetup(cfg, self.name(), True)
if not setup:
seiscomp.logging.warning(
f"could not find station setup for {net}.{sta}"
)
continue
params = seiscomp.datamodel.ParameterSet.Find(setup.parameterSetID())
if not params:
seiscomp.logging.warning(
f"could not find station parameters for {net}.{sta}"
)
continue
detecLocid = ""
detecStream = None
for j in range(params.parameterCount()):
param = params.parameter(j)
if param.name() == "detecStream":
detecStream = param.value()
elif param.name() == "detecLocid":
detecLocid = param.value()
if detecStream is None:
seiscomp.logging.warning(
f"could not find detecStream for {net}.{sta}"
)
continue
loc = inv.getSensorLocation(net, sta, detecLocid, now)
if loc is None:
seiscomp.logging.warning(
f"could not find preferred location for {net}.{sta}"
)
continue
components = {}
tc = seiscomp.datamodel.ThreeComponents()
seiscomp.datamodel.getThreeComponents(tc, loc, detecStream[:2], now)
if tc.vertical():
cha = tc.vertical()
wfsID = seiscomp.datamodel.WaveformStreamID(
net, sta, loc.code(), cha.code(), ""
)
components[cha.code()[-1]] = wfsID
seiscomp.logging.debug(f"add stream {wfs2Str(wfsID)} (vertical)")
if tc.firstHorizontal():
cha = tc.firstHorizontal()
wfsID = seiscomp.datamodel.WaveformStreamID(
net, sta, loc.code(), cha.code(), ""
)
components[cha.code()[-1]] = wfsID
seiscomp.logging.debug(
f"add stream {wfs2Str(wfsID)} (first horizontal)"
)
if tc.secondHorizontal():
cha = tc.secondHorizontal()
wfsID = seiscomp.datamodel.WaveformStreamID(
net, sta, loc.code(), cha.code(), ""
)
components[cha.code()[-1]] = wfsID
seiscomp.logging.debug(
f"add stream {wfs2Str(wfsID)} (second horizontal)"
)
if len(components) > 0:
self.streams[sta] = components
return
# fallback loading streams from inventory
seiscomp.logging.warning(
"no configuration module available, loading streams "
"from inventory and selecting first available stream "
"matching epoch"
)
for iNet in range(inv.inventory().networkCount()):
net = inv.inventory().network(iNet)
seiscomp.logging.debug(
f"network {net.code()}: loaded {net.stationCount()} stations"
)
for iSta in range(net.stationCount()):
sta = net.station(iSta)
try:
start = sta.start()
if not start <= now:
continue
except:
continue
try:
end = sta.end()
if not now <= end:
continue
except:
pass
for iLoc in range(sta.sensorLocationCount()):
loc = sta.sensorLocation(iLoc)
for iCha in range(loc.streamCount()):
cha = loc.stream(iCha)
wfsID = seiscomp.datamodel.WaveformStreamID(
net.code(), sta.code(), loc.code(), cha.code(), ""
)
comp = cha.code()[2]
if sta.code() not in self.streams:
components = {}
components[comp] = wfsID
self.streams[sta.code()] = components
else:
# Seismic Handler does not support network,
# location and channel code: make sure network and
# location codes match first item in station
# specific steam list
oldWfsID = list(self.streams[sta.code()].values())[0]
if (
net.code() != oldWfsID.networkCode()
or loc.code() != oldWfsID.locationCode()
or cha.code()[:2] != oldWfsID.channelCode()[:2]
):
seiscomp.logging.warning(
f"ambiguous stream id found for station\
{sta.code()}, ignoring {wfs2Str(wfsID)}"
)
continue
self.streams[sta.code()][comp] = wfsID
seiscomp.logging.debug(f"add stream {wfs2Str(wfsID)}")
###########################################################################
def parseTime(self, timeStr):
time = seiscomp.core.Time()
for fmt in TimeFormats:
if time.fromString(timeStr, fmt):
break
return time
###########################################################################
def parseMagType(self, value):
if value == "m":
return "M"
if value == "ml":
return "ML"
if value == "mb":
return "mb"
if value == "ms":
return "Ms(BB)"
if value == "mw":
return "Mw"
if value == "bb":
return "mB"
return ""
###########################################################################
def sh2proc(self, file):
ep = seiscomp.datamodel.EventParameters()
origin = seiscomp.datamodel.Origin.Create()
event = seiscomp.datamodel.Event.Create()
origin.setCreationInfo(seiscomp.datamodel.CreationInfo())
origin.creationInfo().setCreationTime(seiscomp.core.Time.GMT())
originQuality = None
originCE = None
latFound = False
lonFound = False
depthError = None
originComments = {}
# variables, reset after 'end of phase'
pick = None
stationMag = None
staCode = None
compCode = None
stationMagBB = None
ampPeriod = None
ampBBPeriod = None
amplitudeDisp = None
amplitudeVel = None
amplitudeSNR = None
amplitudeBB = None
magnitudeMB = None
magnitudeML = None
magnitudeMS = None
magnitudeBB = None
# To avoid undefined warning
arrival = None
phase = None
km2degFac = 1.0 / seiscomp.math.deg2km(1.0)
# read file line by line, split key and value at colon
iLine = 0
for line in file:
iLine += 1
a = line.split(":", 1)
key = a[0].strip()
keyLower = key.lower()
value = None
# empty line
if len(keyLower) == 0:
continue
# end of phase
if keyLower == "--- end of phase ---":
if pick is None:
seiscomp.logging.warning(f"Line {iLine}: found empty phase block")
continue
if staCode is None or compCode is None:
seiscomp.logging.warning(
f"Line {iLine}: end of phase, stream code incomplete"
)
continue
if not staCode in self.streams:
seiscomp.logging.warning(
f"Line {iLine}: end of phase, station code {staCode} not found in inventory"
)
continue
if not compCode in self.streams[staCode]:
seiscomp.logging.warning(
f"Line {iLine}: end of phase, component\
{compCode} of station {staCode} not found in inventory"
)
continue
streamID = self.streams[staCode][compCode]
pick.setWaveformID(streamID)
ep.add(pick)
arrival.setPickID(pick.publicID())
arrival.setPhase(phase)
origin.add(arrival)
if amplitudeSNR is not None:
amplitudeSNR.setPickID(pick.publicID())
amplitudeSNR.setWaveformID(streamID)
ep.add(amplitudeSNR)
if amplitudeBB is not None:
amplitudeBB.setPickID(pick.publicID())
amplitudeBB.setWaveformID(streamID)
ep.add(amplitudeBB)
if stationMagBB is not None:
stationMagBB.setWaveformID(streamID)
origin.add(stationMagBB)
stationMagContrib = (
seiscomp.datamodel.StationMagnitudeContribution()
)
stationMagContrib.setStationMagnitudeID(stationMagBB.publicID())
if magnitudeBB is None:
magnitudeBB = seiscomp.datamodel.Magnitude.Create()
magnitudeBB.add(stationMagContrib)
if stationMag is not None:
if stationMag.type() in ["mb", "ML"] and amplitudeDisp is not None:
amplitudeDisp.setPickID(pick.publicID())
amplitudeDisp.setWaveformID(streamID)
amplitudeDisp.setPeriod(
seiscomp.datamodel.RealQuantity(ampPeriod)
)
amplitudeDisp.setType(stationMag.type())
ep.add(amplitudeDisp)
if stationMag.type() in ["Ms(BB)"] and amplitudeVel is not None:
amplitudeVel.setPickID(pick.publicID())
amplitudeVel.setWaveformID(streamID)
amplitudeVel.setPeriod(
seiscomp.datamodel.RealQuantity(ampPeriod)
)
amplitudeVel.setType(stationMag.type())
ep.add(amplitudeVel)
stationMag.setWaveformID(streamID)
origin.add(stationMag)
stationMagContrib = (
seiscomp.datamodel.StationMagnitudeContribution()
)
stationMagContrib.setStationMagnitudeID(stationMag.publicID())
magType = stationMag.type()
if magType == "ML":
if magnitudeML is None:
magnitudeML = seiscomp.datamodel.Magnitude.Create()
magnitudeML.add(stationMagContrib)
elif magType == "Ms(BB)":
if magnitudeMS is None:
magnitudeMS = seiscomp.datamodel.Magnitude.Create()
magnitudeMS.add(stationMagContrib)
elif magType == "mb":
if magnitudeMB is None:
magnitudeMB = seiscomp.datamodel.Magnitude.Create()
magnitudeMB.add(stationMagContrib)
pick = None
staCode = None
compCode = None
stationMag = None
stationMagBB = None
ampPeriod = None
ampBBPeriod = None
amplitudeDisp = None
amplitudeVel = None
amplitudeSNR = None
amplitudeBB = None
continue
# empty key
if len(a) == 1:
seiscomp.logging.warning(f"Line {iLine}: key without value")
continue
value = a[1].strip()
if pick is None:
pick = seiscomp.datamodel.Pick.Create()
arrival = seiscomp.datamodel.Arrival()
try:
##############################################################
# station parameters
# station code
if keyLower == "station code":
staCode = value
# pick time
elif keyLower == "onset time":
pick.setTime(seiscomp.datamodel.TimeQuantity(self.parseTime(value)))
# pick onset type
elif keyLower == "onset type":
found = False
for onset in [
seiscomp.datamodel.EMERGENT,
seiscomp.datamodel.IMPULSIVE,
seiscomp.datamodel.QUESTIONABLE,
]:
if value == seiscomp.datamodel.EPickOnsetNames.name(onset):
pick.setOnset(onset)
found = True
break
if not found:
raise Exception("Unsupported onset value")
# phase code
elif keyLower == "phase name":
phase = seiscomp.datamodel.Phase()
phase.setCode(value)
pick.setPhaseHint(phase)
# event type
elif keyLower == "event type":
evttype = EventTypes[value]
event.setType(evttype)
originComments[key] = value
# filter ID
elif keyLower == "applied filter":
pick.setFilterID(value)
# channel code, prepended by configured Channel prefix if only
# one character is found
elif keyLower == "component":
compCode = value
# pick evaluation mode
elif keyLower == "pick type":
found = False
for mode in [
seiscomp.datamodel.AUTOMATIC,
seiscomp.datamodel.MANUAL,
]:
if value == seiscomp.datamodel.EEvaluationModeNames.name(mode):
pick.setEvaluationMode(mode)
found = True
break
if not found:
raise Exception("Unsupported evaluation mode value")
# pick author
elif keyLower == "analyst":
creationInfo = seiscomp.datamodel.CreationInfo()
creationInfo.setAuthor(value)
pick.setCreationInfo(creationInfo)
# pick polarity
# isn't tested
elif keyLower == "sign":
if value == "positive":
sign = "0" # positive
elif value == "negative":
sign = "1" # negative
else:
sign = "2" # unknown
pick.setPolarity(float(sign))
# arrival weight
elif keyLower == "weight":
arrival.setWeight(float(value))
# arrival azimuth
elif keyLower == "theo. azimuth (deg)":
arrival.setAzimuth(float(value))
# pick theo backazimuth
elif keyLower == "theo. backazimuth (deg)":
if pick.slownessMethodID() == "corrected":
seiscomp.logging.debug(
f"Line {iLine}: ignoring parameter: {key}"
)
else:
pick.setBackazimuth(
seiscomp.datamodel.RealQuantity(float(value))
)
pick.setSlownessMethodID("theoretical")
# pick beam slowness
elif keyLower == "beam-slowness (sec/deg)":
if pick.slownessMethodID() == "corrected":
seiscomp.logging.debug(
f"Line {iLine}: ignoring parameter: {key}"
)
else:
pick.setHorizontalSlowness(
seiscomp.datamodel.RealQuantity(float(value))
)
pick.setSlownessMethodID("Array Beam")
# pick beam backazimuth
elif keyLower == "beam-azimuth (deg)":
if pick.slownessMethodID() == "corrected":
seiscomp.logging.debug(
f"Line {iLine}: ignoring parameter: {key}"
)
else:
pick.setBackazimuth(
seiscomp.datamodel.RealQuantity(float(value))
)
# pick epi slowness
elif keyLower == "epi-slowness (sec/deg)":
pick.setHorizontalSlowness(
seiscomp.datamodel.RealQuantity(float(value))
)
pick.setSlownessMethodID("corrected")
# pick epi backazimuth
elif keyLower == "epi-azimuth (deg)":
pick.setBackazimuth(seiscomp.datamodel.RealQuantity(float(value)))
# arrival distance degree
elif keyLower == "distance (deg)":
arrival.setDistance(float(value))
# arrival distance km, recalculates for degree
elif keyLower == "distance (km)":
if isinstance(arrival.distance(), float):
seiscomp.logging.debug(
f"Line {iLine - 1}: ignoring parameter: distance (deg)"
)
arrival.setDistance(float(value) * km2degFac)
# arrival time residual
elif keyLower == "residual time":
arrival.setTimeResidual(float(value))
# amplitude snr
elif keyLower == "signal/noise":
amplitudeSNR = seiscomp.datamodel.Amplitude.Create()
amplitudeSNR.setType("SNR")
amplitudeSNR.setAmplitude(
seiscomp.datamodel.RealQuantity(float(value))
)
# amplitude period
elif keyLower.startswith("period"):
ampPeriod = float(value)
# amplitude value for displacement
elif keyLower == "amplitude (nm)":
amplitudeDisp = seiscomp.datamodel.Amplitude.Create()
amplitudeDisp.setAmplitude(
seiscomp.datamodel.RealQuantity(float(value))
)
amplitudeDisp.setUnit("nm")
# amplitude value for velocity
elif keyLower.startswith("vel. amplitude"):
amplitudeVel = seiscomp.datamodel.Amplitude.Create()
amplitudeVel.setAmplitude(
seiscomp.datamodel.RealQuantity(float(value))
)
amplitudeVel.setUnit("nm/s")
elif keyLower == "bb amplitude (nm/sec)":
amplitudeBB = seiscomp.datamodel.Amplitude.Create()
amplitudeBB.setAmplitude(
seiscomp.datamodel.RealQuantity(float(value))
)
amplitudeBB.setType("mB")
amplitudeBB.setUnit("nm/s")
amplitudeBB.setPeriod(seiscomp.datamodel.RealQuantity(ampBBPeriod))
elif keyLower == "bb period (sec)":
ampBBPeriod = float(value)
elif keyLower == "broadband magnitude":
magType = self.parseMagType("bb")
stationMagBB = seiscomp.datamodel.StationMagnitude.Create()
stationMagBB.setMagnitude(
seiscomp.datamodel.RealQuantity(float(value))
)
stationMagBB.setType(magType)
stationMagBB.setAmplitudeID(amplitudeBB.publicID())
# ignored
elif keyLower == "quality number":
seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
# station magnitude value and type
elif keyLower.startswith("magnitude "):
magType = self.parseMagType(key[10:])
stationMag = seiscomp.datamodel.StationMagnitude.Create()
stationMag.setMagnitude(
seiscomp.datamodel.RealQuantity(float(value))
)
if len(magType) > 0:
stationMag.setType(magType)
if magType == "mb":
stationMag.setAmplitudeID(amplitudeDisp.publicID())
elif magType == "MS(BB)":
stationMag.setAmplitudeID(amplitudeVel.publicID())
else:
seiscomp.logging.debug(
f"Line {iLine}: Magnitude Type not known {magType}."
)
###############################################################
# origin parameters
# event ID, added as origin comment later on
elif keyLower == "event id":
originComments[key] = value
# magnitude value and type
elif keyLower == "mean bb magnitude":
magType = self.parseMagType("bb")
if magnitudeBB is None:
magnitudeBB = seiscomp.datamodel.Magnitude.Create()
magnitudeBB.setMagnitude(
seiscomp.datamodel.RealQuantity(float(value))
)
magnitudeBB.setType(magType)
elif keyLower.startswith("mean magnitude "):
magType = self.parseMagType(key[15:])
if magType == "ML":
if magnitudeML is None:
magnitudeML = seiscomp.datamodel.Magnitude.Create()
magnitudeML.setMagnitude(
seiscomp.datamodel.RealQuantity(float(value))
)
magnitudeML.setType(magType)
elif magType == "Ms(BB)":
if magnitudeMS is None:
magnitudeMS = seiscomp.datamodel.Magnitude.Create()
magnitudeMS.setMagnitude(
seiscomp.datamodel.RealQuantity(float(value))
)
magnitudeMS.setType(magType)
elif magType == "mb":
if magnitudeMB is None:
magnitudeMB = seiscomp.datamodel.Magnitude.Create()
magnitudeMB.setMagnitude(
seiscomp.datamodel.RealQuantity(float(value))
)
magnitudeMB.setType(magType)
else:
seiscomp.logging.warning(
f"Line {iLine}: Magnitude type {magType} not defined yet."
)
# latitude
elif keyLower == "latitude":
origin.latitude().setValue(float(value))
latFound = True
elif keyLower == "error in latitude (km)":
origin.latitude().setUncertainty(float(value))
# longitude
elif keyLower == "longitude":
origin.longitude().setValue(float(value))
lonFound = True
elif keyLower == "error in longitude (km)":
origin.longitude().setUncertainty(float(value))
# depth
elif keyLower == "depth (km)":
origin.setDepth(seiscomp.datamodel.RealQuantity(float(value)))
if depthError is not None:
origin.depth().setUncertainty(depthError)
elif keyLower == "depth type":
seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
elif keyLower == "error in depth (km)":
depthError = float(value)
try:
origin.depth().setUncertainty(depthError)
except seiscomp.core.ValueException:
pass
# time
elif keyLower == "origin time":
origin.time().setValue(self.parseTime(value))
elif keyLower == "error in origin time":
origin.time().setUncertainty(float(value))
# location method
elif keyLower == "location method":
origin.setMethodID(str(value))
# region table, added as origin comment later on
elif keyLower == "region table":
originComments[key] = value
# region table, added as origin comment later on
elif keyLower == "region id":
originComments[key] = value
# source region, added as origin comment later on
elif keyLower == "source region":
originComments[key] = value
# used station count
elif keyLower == "no. of stations used":
if originQuality is None:
originQuality = seiscomp.datamodel.OriginQuality()
originQuality.setUsedStationCount(int(value))
# ignored
elif keyLower == "reference location name":
seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
# confidence ellipsoid major axis
elif keyLower == "error ellipse major":
if originCE is None:
originCE = seiscomp.datamodel.ConfidenceEllipsoid()
originCE.setSemiMajorAxisLength(float(value))
# confidence ellipsoid minor axis
elif keyLower == "error ellipse minor":
if originCE is None:
originCE = seiscomp.datamodel.ConfidenceEllipsoid()
originCE.setSemiMinorAxisLength(float(value))
# confidence ellipsoid rotation
elif keyLower == "error ellipse strike":
if originCE is None:
originCE = seiscomp.datamodel.ConfidenceEllipsoid()
originCE.setMajorAxisRotation(float(value))
# azimuthal gap
elif keyLower == "max azimuthal gap (deg)":
if originQuality is None:
originQuality = seiscomp.datamodel.OriginQuality()
originQuality.setAzimuthalGap(float(value))
# creation info author
elif keyLower == "author":
origin.creationInfo().setAuthor(value)
# creation info agency
elif keyLower == "source of information":
origin.creationInfo().setAgencyID(value)
# earth model id
elif keyLower == "velocity model":
origin.setEarthModelID(value)
# standard error
elif keyLower == "rms of residuals (sec)":
if originQuality is None:
originQuality = seiscomp.datamodel.OriginQuality()
originQuality.setStandardError(float(value))
# ignored
elif keyLower == "phase flags":
seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
# ignored
elif keyLower == "location input params":
seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
# missing keys
elif keyLower == "ampl&period source":
seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
elif keyLower == "location quality":
seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
elif keyLower == "reference latitude":
seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
elif keyLower == "reference longitude":
seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
elif keyLower.startswith("amplitude time"):
seiscomp.logging.debug(f"Line {iLine}: ignoring parameter: {key}")
# unknown key
else:
seiscomp.logging.warning(
"Line {iLine}: ignoring unknown parameter: {key}"
)
except ValueError:
seiscomp.logging.warning(f"Line {iLine}: can not parse {key} value")
except Exception:
seiscomp.logging.error("Line {iLine}: {str(traceback.format_exc())}")
return None
# check
if not latFound:
seiscomp.logging.warning("could not add origin, missing latitude parameter")
elif not lonFound:
seiscomp.logging.warning(
"could not add origin, missing longitude parameter"
)
elif not origin.time().value().valid():
seiscomp.logging.warning(
"could not add origin, missing origin time parameter"
)
else:
if magnitudeMB is not None:
origin.add(magnitudeMB)
if magnitudeML is not None:
origin.add(magnitudeML)
if magnitudeMS is not None:
origin.add(magnitudeMS)
if magnitudeBB is not None:
origin.add(magnitudeBB)
ep.add(event)
ep.add(origin)
if originQuality is not None:
origin.setQuality(originQuality)
if originCE is not None:
uncertainty = seiscomp.datamodel.OriginUncertainty()
uncertainty.setConfidenceEllipsoid(originCE)
origin.setUncertainty(uncertainty)
for k, v in originComments.items():
comment = seiscomp.datamodel.Comment()
comment.setId(k)
comment.setText(v)
origin.add(comment)
return ep
###########################################################################
def run(self):
self.loadStreams()
try:
if self.inputFile == "-":
f = sys.stdin
else:
f = open(self.inputFile)
except IOError as e:
seiscomp.logging.error(str(e))
return False
ep = self.sh2proc(f)
if ep is None:
return False
ar = seiscomp.io.XMLArchive()
ar.create("-")
ar.setFormattedOutput(True)
ar.writeObject(ep)
ar.close()
return True
###############################################################################
def main():
try:
app = SH2Proc()
return app()
except:
sys.stderr.write(str(traceback.format_exc()))
return 1
if __name__ == "__main__":
sys.exit(main())
# vim: ts=4 et