# http://pyrocko.org - GPLv3
#
# The Pyrocko Developers, 21st Century
# ---|P------/S----------~Lg----------
'''
QuakeML 1.2 input, output, and data model.
This modules provides support to read and write `QuakeML version 1.2
<https://quake.ethz.ch/quakeml>`_. It defines a hierarchy of Python objects,
closely following the QuakeML data model.
QuakeML is a flexible, extensible and modular XML representation of
seismological data which is intended to cover a broad range of fields of
application in modern seismology. It covers basic seismic event description,
including moment tensors.
For convenience and ease of use, this documentation contains excerpts from the
`QuakeML Manual
<https://quake.ethz.ch/quakeml/docs/REC?action=AttachFile&do=get&target=QuakeML-BED-20130214b.pdf>`_.
However, this information is not intended to be complete. Please refer to the
QuakeML Manual for details.
'''
from __future__ import absolute_import
import logging
from pyrocko.guts import StringPattern, StringChoice, String, Float, Int,\
Timestamp, Object, List, Union, Bool, Unicode
from pyrocko.model import event
from pyrocko.gui import marker
from pyrocko import moment_tensor
import numpy as num
logger = logging.getLogger('pyrocko.io.quakeml')
guts_prefix = 'quakeml'
guts_xmlns = 'http://quakeml.org/xmlns/bed/1.2'
polarity_choices = {'positive': 1, 'negative': -1, 'undecidable': None}
[docs]class QuakeMLError(Exception):
pass
[docs]class NoPreferredOriginSet(QuakeMLError):
pass
def one_element_or_none(li):
if len(li) == 1:
return li[0]
elif len(li) == 0:
return None
else:
logger.warning('More than one element in list: {}'.format(li))
return None
[docs]class ResourceIdentifier(StringPattern):
'''
Identifies resource origin.
They consist of an authority identifier, a unique resource identifier, and
an optional local identifier. The URI schema name smi stands for
seismological meta-information, thus indicating a connection to a set of
metadata associated with the resource.
'''
pattern = "^(smi|quakeml):[\\w\\d][\\w\\d\\-\\.\\*\\(\\)_~']{2,}/[\\w" +\
"\\d\\-\\.\\*\\(\\)_~'][\\w\\d\\-\\.\\*\\(\\)\\+\\?_~'=,;#/&]*$"
[docs]class WhitespaceOrEmptyStringType(StringPattern):
pattern = '^\\s*$'
[docs]class OriginUncertaintyDescription(StringChoice):
'''
Preferred uncertainty description.
'''
choices = [
'horizontal uncertainty',
'uncertainty ellipse',
'confidence ellipsoid']
[docs]class AmplitudeCategory(StringChoice):
'''
Description of the way the waveform trace is evaluated to get an amplitude
value.
This can be just reading a single value for a given point in time (point),
taking a mean value over a time interval (mean), integrating the trace
over a time interval (integral), specifying just a time interval
(duration), or evaluating a period (period).
'''
choices = ['point', 'mean', 'duration', 'period', 'integral', 'other']
[docs]class OriginDepthType(StringChoice):
'''
Type of depth determination.
'''
choices = [
'from location',
'from moment tensor inversion',
'from modeling of broad-band P waveforms',
'constrained by depth phases',
'constrained by direct phases',
'constrained by depth and direct phases',
'operator assigned',
'other']
[docs]class OriginType(StringChoice):
'''
Describes the origin type.
'''
choices = [
'hypocenter',
'centroid',
'amplitude',
'macroseismic',
'rupture start',
'rupture end']
[docs]class MTInversionType(StringChoice):
'''
Type of moment tensor inversion.
Users should avoid to give contradictory information in
:py:class:`MTInversionType` and :py:gattr:`MomentTensor.method_id`.
'''
choices = ['general', 'zero trace', 'double couple']
[docs]class EvaluationMode(StringChoice):
'''
Mode of an evaluation.
Used in :py:class:`Pick`, :py:class:`Amplitude`, :py:class:`Magnitude`,
:py:class:`Origin`, :py:class:`FocalMechanism`.
'''
choices = ['manual', 'automatic']
[docs]class EvaluationStatus(StringChoice):
'''
Status of an evaluation.
Used in :py:class:`Pick`, :py:class:`Amplitude`, :py:class:`Magnitude`,
:py:class:`Origin`, :py:class:`FocalMechanism`.
'''
choices = ['preliminary', 'confirmed', 'reviewed', 'final', 'rejected']
[docs]class PickOnset(StringChoice):
'''
Flag that roughly categorizes the sharpness of the onset.
'''
choices = ['emergent', 'impulsive', 'questionable']
[docs]class EventType(StringChoice):
'''
Describes the type of an event.
'''
choices = [
'not existing',
'not reported',
'earthquake',
'anthropogenic event',
'collapse',
'cavity collapse',
'mine collapse',
'building collapse',
'explosion',
'accidental explosion',
'chemical explosion',
'controlled explosion',
'experimental explosion',
'industrial explosion',
'mining explosion',
'quarry blast',
'road cut',
'blasting levee',
'nuclear explosion',
'induced or triggered event',
'rock burst',
'reservoir loading',
'fluid injection',
'fluid extraction',
'crash',
'plane crash',
'train crash',
'boat crash',
'other event',
'atmospheric event',
'sonic boom',
'sonic blast',
'acoustic noise',
'thunder',
'avalanche',
'snow avalanche',
'debris avalanche',
'hydroacoustic event',
'ice quake',
'slide',
'landslide',
'rockslide',
'meteorite',
'volcanic eruption',
'duplicate earthquake',
'rockburst']
[docs]class DataUsedWaveType(StringChoice):
'''
Type of waveform data.
'''
choices = [
'P waves',
'body waves',
'surface waves',
'mantle waves',
'combined',
'unknown']
[docs]class AmplitudeUnit(StringChoice):
'''
Provides the most likely measurement units.
The measurement units for physical quantity are described in the
:py:gattr:`Amplitude.generic_amplitude` attribute. Possible values are
specified as combination of SI base units.
'''
choices = ['m', 's', 'm/s', 'm/(s*s)', 'm*s', 'dimensionless', 'other']
[docs]class EventDescriptionType(StringChoice):
'''
Category of earthquake description.
'''
choices = [
'felt report',
'Flinn-Engdahl region',
'local time',
'tectonic summary',
'nearest cities',
'earthquake name',
'region name']
[docs]class MomentTensorCategory(StringChoice):
'''
Category of moment tensor inversion.
'''
choices = ['teleseismic', 'regional']
[docs]class EventTypeCertainty(StringChoice):
'''
Denotes how certain the information on event type is.
'''
choices = ['known', 'suspected']
[docs]class SourceTimeFunctionType(StringChoice):
'''
Type of source time function.
'''
choices = ['box car', 'triangle', 'trapezoid', 'unknown']
[docs]class PickPolarity(StringChoice):
choices = list(polarity_choices.keys())
[docs]class AgencyID(String):
pass
[docs]class Author(Unicode):
pass
[docs]class Version(String):
pass
[docs]class Phase(Object):
value = String.T(xmlstyle='content')
[docs]class GroundTruthLevel(String):
pass
[docs]class AnonymousNetworkCode(String):
pass
[docs]class AnonymousStationCode(String):
pass
[docs]class AnonymousChannelCode(String):
pass
[docs]class AnonymousLocationCode(String):
pass
[docs]class Type(String):
pass
[docs]class MagnitudeHint(String):
pass
[docs]class Region(Unicode):
pass
[docs]class RealQuantity(Object):
value = Float.T()
uncertainty = Float.T(optional=True)
lower_uncertainty = Float.T(optional=True)
upper_uncertainty = Float.T(optional=True)
confidence_level = Float.T(optional=True)
[docs]class IntegerQuantity(Object):
value = Int.T()
uncertainty = Int.T(optional=True)
lower_uncertainty = Int.T(optional=True)
upper_uncertainty = Int.T(optional=True)
confidence_level = Float.T(optional=True)
[docs]class ConfidenceEllipsoid(Object):
'''
Representation of the location uncertainty as a confidence ellipsoid with
arbitrary orientation in space.
'''
semi_major_axis_length = Float.T()
semi_minor_axis_length = Float.T()
semi_intermediate_axis_length = Float.T()
major_axis_plunge = Float.T()
major_axis_azimuth = Float.T()
major_axis_rotation = Float.T()
[docs]class TimeQuantity(Object):
'''
Representation of a point in time.
It's given in ISO 8601 format, with optional symmetric or asymmetric
uncertainties given in seconds. The time has to be specified in UTC.
'''
value = Timestamp.T()
uncertainty = Float.T(optional=True)
lower_uncertainty = Float.T(optional=True)
upper_uncertainty = Float.T(optional=True)
confidence_level = Float.T(optional=True)
[docs]class TimeWindow(Object):
'''
Representation of a time window for amplitude measurements.
Which is given by a central point in time, and points in time before and
after this central point. Both points before and after may coincide with
the central point.
'''
begin = Float.T()
end = Float.T()
reference = Timestamp.T()
[docs]class ResourceReference(ResourceIdentifier):
'''
This type is used to refer to QuakeML resources as described in Sect. 3.1
in the `QuakeML manual <https://quake.ethz.ch/quakeml/docs/REC?action=Att\
achFile&do=get&target=QuakeML-BED-20130214b.pdf>`_.
'''
pass
[docs]class DataUsed(Object):
'''
Description of the type of data used in a moment-tensor inversion.
'''
wave_type = DataUsedWaveType.T()
station_count = Int.T(optional=True)
component_count = Int.T(optional=True)
shortest_period = Float.T(optional=True)
longest_period = Float.T(optional=True)
[docs]class EventDescription(Object):
'''
Free-form string with additional event description.
This can be a well-known name, like 1906 San Francisco Earthquake.
A number of categories can be given in :py:gattr:`type`.
'''
text = Unicode.T()
type = EventDescriptionType.T(optional=True)
[docs]class SourceTimeFunction(Object):
'''
Source time function used in moment-tensor inversion.
'''
type = SourceTimeFunctionType.T()
duration = Float.T()
rise_time = Float.T(optional=True)
decay_time = Float.T(optional=True)
[docs]class OriginQuality(Object):
'''
Description of an origin's quality.
It contains various attributes commonly used to describe the
quality of an origin, e. g., errors, azimuthal coverage, etc.
:py:class:`Origin` objects have an optional attribute of the type
:py:gattr:`OriginQuality`.
'''
associated_phase_count = Int.T(optional=True)
used_phase_count = Int.T(optional=True)
associated_station_count = Int.T(optional=True)
used_station_count = Int.T(optional=True)
depth_phase_count = Int.T(optional=True)
standard_error = Float.T(optional=True)
azimuthal_gap = Float.T(optional=True)
secondary_azimuthal_gap = Float.T(optional=True)
ground_truth_level = GroundTruthLevel.T(optional=True)
maximum_distance = Float.T(optional=True)
minimum_distance = Float.T(optional=True)
median_distance = Float.T(optional=True)
[docs]class Axis(Object):
'''
Representation of an eigenvector of a moment tensor.
Which is expressed in its principal-axes system and uses the angles
:py:gattr:`azimuth`, :py:gattr:`plunge`, and the eigenvalue
:py:gattr:`length`.
'''
azimuth = RealQuantity.T()
plunge = RealQuantity.T()
length = RealQuantity.T()
[docs]class Tensor(Object):
'''
Representation of the six independent moment-tensor elements in spherical
coordinates.
These are the moment-tensor elements Mrr, Mtt, Mpp, Mrt, Mrp, Mtp in the
spherical coordinate system defined by local upward vertical (r),
North-South (t), and West-East (p) directions.
'''
mrr = RealQuantity.T(xmltagname='Mrr')
mtt = RealQuantity.T(xmltagname='Mtt')
mpp = RealQuantity.T(xmltagname='Mpp')
mrt = RealQuantity.T(xmltagname='Mrt')
mrp = RealQuantity.T(xmltagname='Mrp')
mtp = RealQuantity.T(xmltagname='Mtp')
[docs]class NodalPlane(Object):
'''
Description of a nodal plane of a focal mechanism.
'''
strike = RealQuantity.T()
dip = RealQuantity.T()
rake = RealQuantity.T()
[docs]class CompositeTime(Object):
'''
Representation of a time instant.
If the specification is given with no greater accuracy than days (i.e., no
time components are given), the date refers to local time. However, if
time components are given, they have to refer to UTC.
'''
year = IntegerQuantity.T(optional=True)
month = IntegerQuantity.T(optional=True)
day = IntegerQuantity.T(optional=True)
hour = IntegerQuantity.T(optional=True)
minute = IntegerQuantity.T(optional=True)
second = RealQuantity.T(optional=True)
[docs]class OriginUncertainty(Object):
'''
Description of the location uncertainties of an origin.
The uncertainty can be described either as a simple circular horizontal
uncertainty, an uncertainty ellipse according to IMS1.0, or a confidence
ellipsoid. If multiple uncertainty models are given, the preferred variant
can be specified in the attribute :py:gattr:`preferred_description`.
'''
horizontal_uncertainty = Float.T(optional=True)
min_horizontal_uncertainty = Float.T(optional=True)
max_horizontal_uncertainty = Float.T(optional=True)
azimuth_max_horizontal_uncertainty = Float.T(optional=True)
confidence_ellipsoid = ConfidenceEllipsoid.T(optional=True)
preferred_description = OriginUncertaintyDescription.T(optional=True)
confidence_level = Float.T(optional=True)
[docs]class ResourceReferenceOptional(Union):
members = [ResourceReference.T(), WhitespaceOrEmptyStringType.T()]
[docs]class CreationInfo(Object):
'''
Description of creation metadata (author, version, and creation time) of a
resource.
'''
agency_id = AgencyID.T(optional=True, xmltagname='agencyID')
agency_uri = ResourceReference.T(optional=True, xmltagname='agencyURI')
author = Author.T(optional=True)
author_uri = ResourceReference.T(optional=True, xmltagname='authorURI')
creation_time = Timestamp.T(optional=True)
version = Version.T(optional=True)
[docs]class StationMagnitudeContribution(Object):
'''
Description of the weighting of magnitude values from several
:py:class:`StationMagnitude` objects for computing a network magnitude
estimation.
'''
station_magnitude_id = ResourceReference.T(xmltagname='stationMagnitudeID')
residual = Float.T(optional=True)
weight = Float.T(optional=True)
[docs]class PrincipalAxes(Object):
'''
Representation of the principal axes of a moment tensor.
:py:gattr:`t_axis` and :py:gattr:`p_axis` are required, while
:py:gattr:`n_axis` is optional.
'''
t_axis = Axis.T()
p_axis = Axis.T()
n_axis = Axis.T(optional=True)
[docs]class NodalPlanes(Object):
'''
Representation of the nodal planes of a moment tensor.
The attribute :py:gattr:`preferred_plane` can be used to define which plane
is the preferred one.
'''
preferred_plane = Int.T(optional=True, xmlstyle='attribute')
nodal_plane1 = NodalPlane.T(optional=True)
nodal_plane2 = NodalPlane.T(optional=True)
[docs]class MomentTensor(Object):
'''
Representation of a moment tensor solution for an event.
It is an optional part of a :py:class:`FocalMechanism` description.
'''
public_id = ResourceReference.T(
xmlstyle='attribute', xmltagname='publicID')
data_used_list = List.T(DataUsed.T())
comment_list = List.T(Comment.T())
derived_origin_id = ResourceReference.T(
optional=True, xmltagname='derivedOriginID')
moment_magnitude_id = ResourceReference.T(
optional=True, xmltagname='momentMagnitudeID')
scalar_moment = RealQuantity.T(optional=True)
tensor = Tensor.T(optional=True)
variance = Float.T(optional=True)
variance_reduction = Float.T(optional=True)
double_couple = Float.T(optional=True)
clvd = Float.T(optional=True)
iso = Float.T(optional=True)
greens_function_id = ResourceReference.T(
optional=True, xmltagname='greensFunctionID')
filter_id = ResourceReference.T(optional=True, xmltagname='filterID')
source_time_function = SourceTimeFunction.T(optional=True)
method_id = ResourceReference.T(optional=True, xmltagname='methodID')
category = MomentTensorCategory.T(optional=True)
inversion_type = MTInversionType.T(optional=True)
creation_info = CreationInfo.T(optional=True)
def pyrocko_moment_tensor(self):
mrr = self.tensor.mrr.value
mtt = self.tensor.mtt.value
mpp = self.tensor.mpp.value
mrt = self.tensor.mrt.value
mrp = self.tensor.mrp.value
mtp = self.tensor.mtp.value
mt = moment_tensor.MomentTensor(m_up_south_east=num.array([
[mrr, mrt, mrp], [mrt, mtt, mtp], [mrp, mtp, mpp]]))
return mt
[docs]class Amplitude(Object):
'''
Quantification of the waveform anomaly.
Usually it consists of a single amplitude measurement or a measurement of
the visible signal duration for duration magnitudes.
'''
public_id = ResourceReference.T(
xmlstyle='attribute', xmltagname='publicID')
comment_list = List.T(Comment.T())
generic_amplitude = RealQuantity.T()
type = Type.T(optional=True)
category = AmplitudeCategory.T(optional=True)
unit = AmplitudeUnit.T(optional=True)
method_id = ResourceReference.T(optional=True, xmltagname='methodID')
period = RealQuantity.T(optional=True)
snr = Float.T(optional=True)
time_window = TimeWindow.T(optional=True)
pick_id = ResourceReference.T(optional=True, xmltagname='pickID')
waveform_id = WaveformStreamID.T(optional=True, xmltagname='waveformID')
filter_id = ResourceReference.T(optional=True, xmltagname='filterID')
scaling_time = TimeQuantity.T(optional=True)
magnitude_hint = MagnitudeHint.T(optional=True)
evaluation_mode = EvaluationMode.T(optional=True)
evaluation_status = EvaluationStatus.T(optional=True)
creation_info = CreationInfo.T(optional=True)
[docs]class Magnitude(Object):
'''
Description of a magnitude value.
It can, but does not need to be associated with an origin. Association
with an origin is expressed with the optional attribute
:py:gattr:`origin_id`. It is either a combination of different magnitude
estimations, or it represents the reported magnitude for the given event.
'''
public_id = ResourceReference.T(
xmlstyle='attribute', xmltagname='publicID')
comment_list = List.T(Comment.T())
station_magnitude_contribution_list = List.T(
StationMagnitudeContribution.T())
mag = RealQuantity.T()
type = Type.T(optional=True)
origin_id = ResourceReference.T(optional=True, xmltagname='originID')
method_id = ResourceReference.T(optional=True, xmltagname='methodID')
station_count = Int.T(optional=True)
azimuthal_gap = Float.T(optional=True)
evaluation_mode = EvaluationMode.T(optional=True)
evaluation_status = EvaluationStatus.T(optional=True)
creation_info = CreationInfo.T(optional=True)
[docs]class StationMagnitude(Object):
'''
Description of a magnitude derived from a single waveform stream.
'''
public_id = ResourceReference.T(
xmlstyle='attribute', xmltagname='publicID')
comment_list = List.T(Comment.T())
origin_id = ResourceReference.T(optional=True, xmltagname='originID')
mag = RealQuantity.T()
type = Type.T(optional=True)
amplitude_id = ResourceReference.T(optional=True, xmltagname='amplitudeID')
method_id = ResourceReference.T(optional=True, xmltagname='methodID')
waveform_id = WaveformStreamID.T(optional=True, xmltagname='waveformID')
creation_info = CreationInfo.T(optional=True)
[docs]class Arrival(Object):
'''
Successful association of a pick with an origin qualifies this pick as an
arrival.
An arrival thus connects a pick with an origin and provides
additional attributes that describe this relationship. Usually
qualification of a pick as an arrival for a given origin is a hypothesis,
which is based on assumptions about the type of arrival (phase) as well as
observed and (on the basis of an earth model) computed arrival times, or
the residual, respectively. Additional pick attributes like the horizontal
slowness and backazimuth of the observed wave—especially if derived from
array data—may further constrain the nature of the arrival.
'''
public_id = ResourceReference.T(
xmlstyle='attribute', xmltagname='publicID')
comment_list = List.T(Comment.T())
pick_id = ResourceReference.T(xmltagname='pickID')
phase = Phase.T()
time_correction = Float.T(optional=True)
azimuth = Float.T(optional=True)
distance = Float.T(optional=True)
takeoff_angle = RealQuantity.T(optional=True)
time_residual = Float.T(optional=True)
horizontal_slowness_residual = Float.T(optional=True)
backazimuth_residual = Float.T(optional=True)
time_weight = Float.T(optional=True)
time_used = Int.T(optional=True)
horizontal_slowness_weight = Float.T(optional=True)
backazimuth_weight = Float.T(optional=True)
earth_model_id = ResourceReference.T(
optional=True, xmltagname='earthModelID')
creation_info = CreationInfo.T(optional=True)
[docs]class Pick(Object):
'''
A pick is the observation of an amplitude anomaly in a seismogram at a
specific point in time.
It is not necessarily related to a seismic event.
'''
public_id = ResourceReference.T(
xmlstyle='attribute', xmltagname='publicID')
comment_list = List.T(Comment.T())
time = TimeQuantity.T()
waveform_id = WaveformStreamID.T(xmltagname='waveformID')
filter_id = ResourceReference.T(optional=True, xmltagname='filterID')
method_id = ResourceReference.T(optional=True, xmltagname='methodID')
horizontal_slowness = RealQuantity.T(optional=True)
backazimuth = RealQuantity.T(optional=True)
slowness_method_id = ResourceReference.T(
optional=True, xmltagname='slownessMethodID')
onset = PickOnset.T(optional=True)
phase_hint = Phase.T(optional=True)
polarity = PickPolarity.T(optional=True)
evaluation_mode = EvaluationMode.T(optional=True)
evaluation_status = EvaluationStatus.T(optional=True)
creation_info = CreationInfo.T(optional=True)
@property
def pyrocko_polarity(self):
return polarity_choices.get(self.polarity, None)
def get_pyrocko_phase_marker(self, event=None):
if not self.phase_hint:
logger.warning('Pick %s: phase_hint undefined' % self.public_id)
phasename = 'undefined'
else:
phasename = self.phase_hint.value
return marker.PhaseMarker(
event=event, nslc_ids=[self.waveform_id.nslc_id],
tmin=self.time.value, tmax=self.time.value,
phasename=phasename,
polarity=self.pyrocko_polarity,
automatic=self.evaluation_mode)
[docs]class FocalMechanism(Object):
'''
Description of the focal mechanism of an event.
It includes different descriptions like nodal planes, principal axes, and
a moment tensor. The moment tensor description is provided by objects of
the class :py:class:`MomentTensor` which can be specified as
child elements of :py:class:`FocalMechanism`.
'''
public_id = ResourceReference.T(
xmlstyle='attribute', xmltagname='publicID')
waveform_id_list = List.T(WaveformStreamID.T(xmltagname='waveformID'))
comment_list = List.T(Comment.T())
moment_tensor_list = List.T(MomentTensor.T())
triggering_origin_id = ResourceReference.T(
optional=True, xmltagname='triggeringOriginID')
nodal_planes = NodalPlanes.T(optional=True)
principal_axes = PrincipalAxes.T(optional=True)
azimuthal_gap = Float.T(optional=True)
station_polarity_count = Int.T(optional=True)
misfit = Float.T(optional=True)
station_distribution_ratio = Float.T(optional=True)
method_id = ResourceReference.T(optional=True, xmltagname='methodID')
evaluation_mode = EvaluationMode.T(optional=True)
evaluation_status = EvaluationStatus.T(optional=True)
creation_info = CreationInfo.T(optional=True)
[docs]class Origin(Object):
'''
Representation of the focal time and geographical location of an earthquake
hypocenter, as well as additional meta-information.
:py:class:`Origin` can have objects of type :py:class:`OriginUncertainty`
and :py:class:`Arrival` as child elements.
'''
public_id = ResourceReference.T(
xmlstyle='attribute', xmltagname='publicID')
composite_time_list = List.T(CompositeTime.T())
comment_list = List.T(Comment.T())
origin_uncertainty_list = List.T(OriginUncertainty.T())
arrival_list = List.T(Arrival.T())
time = TimeQuantity.T()
longitude = RealQuantity.T()
latitude = RealQuantity.T()
depth = RealQuantity.T(optional=True)
depth_type = OriginDepthType.T(optional=True)
time_fixed = Bool.T(optional=True)
epicenter_fixed = Bool.T(optional=True)
reference_system_id = ResourceReference.T(
optional=True, xmltagname='referenceSystemID')
method_id = ResourceReference.T(optional=True, xmltagname='methodID')
earth_model_id = ResourceReference.T(
optional=True, xmltagname='earthModelID')
quality = OriginQuality.T(optional=True)
type = OriginType.T(optional=True)
region = Region.T(optional=True)
evaluation_mode = EvaluationMode.T(optional=True)
evaluation_status = EvaluationStatus.T(optional=True)
creation_info = CreationInfo.T(optional=True)
def position_values(self):
lat = self.latitude.value
lon = self.longitude.value
if not self.depth:
logger.warning(
'Origin %s: Depth is undefined. Set to depth=0.' %
self.public_id)
depth = 0.
else:
depth = self.depth.value
return lat, lon, depth
def get_pyrocko_event(self):
lat, lon, depth = self.position_values()
otime = self.time.value
if self.creation_info:
cat = self.creation_info.agency_id
else:
cat = None
return event.Event(
name=self.public_id,
lat=lat,
lon=lon,
time=otime,
depth=depth,
catalog=cat,
region=self.region)
[docs]class Event(Object):
'''
Representation of a seismic event.
The Event does not necessarily need to be a tectonic earthquake. An event
is usually associated with one or more origins, which contain information
about focal time and geographical location of the event. Multiple origins
can cover automatic and manual locations, a set of location from different
agencies, locations generated with different location programs and earth
models, etc. Furthermore, an event is usually associated with one or more
magnitudes, and with one or more focal mechanism determinations. In
standard QuakeML-BED, :py:class:`Origin`, :py:class:`Magnitude`,
:py:class:`StationMagnitude`, and :py:class:`FocalMechanism` are child
elements of Event. In BED-RT all these elements are on the same hierarchy
level as child elements of :py:class:`EventParameters`. The association of
origins, magnitudes, and focal mechanisms to a particular event is
expressed using references inside :py:class:`Event`.
'''
public_id = ResourceReference.T(
xmlstyle='attribute', xmltagname='publicID')
description_list = List.T(EventDescription.T())
comment_list = List.T(Comment.T())
focal_mechanism_list = List.T(FocalMechanism.T())
amplitude_list = List.T(Amplitude.T())
magnitude_list = List.T(Magnitude.T())
station_magnitude_list = List.T(StationMagnitude.T())
origin_list = List.T(Origin.T())
pick_list = List.T(Pick.T())
preferred_origin_id = ResourceReference.T(
optional=True, xmltagname='preferredOriginID')
preferred_magnitude_id = ResourceReference.T(
optional=True, xmltagname='preferredMagnitudeID')
preferred_focal_mechanism_id = ResourceReference.T(
optional=True, xmltagname='preferredFocalMechanismID')
type = EventType.T(
optional=True)
type_certainty = EventTypeCertainty.T(
optional=True)
creation_info = CreationInfo.T(
optional=True)
region = Region.T(
optional=True)
def describe(self):
return '''%s:
origins: %i %s
magnitudes: %i %s
focal_machanisms: %i %s
picks: %i
amplitudes: %i
station_magnitudes: %i''' % (
self.public_id,
len(self.origin_list),
'@' if self.preferred_origin_id else '-',
len(self.magnitude_list),
'@' if self.preferred_magnitude_id else '-',
len(self.focal_mechanism_list),
'@' if self.preferred_focal_mechanism_id else '-',
len(self.pick_list),
len(self.amplitude_list),
len(self.station_magnitude_list))
def get_pyrocko_phase_markers(self):
event = self.get_pyrocko_event()
return [
p.get_pyrocko_phase_marker(event=event) for p in self.pick_list]
[docs] def get_pyrocko_event(self):
'''
Convert into Pyrocko event object.
Uses *preferred* origin, magnitude, and moment tensor. If no preferred
item is specified, it picks the first from the list and emits a
warning.
'''
origin = self.preferred_origin
if not origin and self.origin_list:
origin = self.origin_list[0]
if len(self.origin_list) > 1:
logger.warning(
'Event %s: No preferred origin set, '
'more than one available, using first' % self.public_id)
if not origin:
raise QuakeMLError(
'No origin available for event: %s' % self.public_id)
ev = origin.get_pyrocko_event()
foc_mech = self.preferred_focal_mechanism
if not foc_mech and self.focal_mechanism_list:
foc_mech = self.focal_mechanism_list[0]
if len(self.focal_mechanism_list) > 1:
logger.warning(
'Event %s: No preferred focal mechanism set, '
'more than one available, using first' % ev.name)
if foc_mech and foc_mech.moment_tensor_list:
ev.moment_tensor = \
foc_mech.moment_tensor_list[0].pyrocko_moment_tensor()
if len(foc_mech.moment_tensor_list) > 1:
logger.warning(
'more than one moment tensor available, using first')
mag = None
pref_mag = self.preferred_magnitude
if pref_mag:
mag = pref_mag
elif self.magnitude_list:
mag = self.magnitude_list[0]
if len(self.magnitude_list) > 1:
logger.warning(
'Event %s: No preferred magnitude set, '
'more than one available, using first' % ev.name)
if mag:
ev.magnitude = mag.mag.value
ev.magnitude_type = mag.type
ev.region = self.get_effective_region()
return ev
def get_effective_region(self):
if self.region:
return self.region
for desc in self.description_list:
if desc.type in ('Flinn-Engdahl region', 'region name'):
return desc.text
return None
@property
def preferred_origin(self):
return one_element_or_none(
[x for x in self.origin_list
if x.public_id == self.preferred_origin_id])
@property
def preferred_magnitude(self):
return one_element_or_none(
[x for x in self.magnitude_list
if x.public_id == self.preferred_magnitude_id])
@property
def preferred_focal_mechanism(self):
return one_element_or_none(
[x for x in self.focal_mechanism_list
if x.public_id == self.preferred_focal_mechanism_id])
[docs]class EventParameters(Object):
'''
In the bulletin-type (non real-time) model, this class serves as a
container for Event objects.
In the real-time version, it can hold objects of type :py:class:`Event`,
:py:class:`Origin`, :py:class:`Magnitude`, :py:class:`StationMagnitude`,
:py:class:`FocalMechanism`, Reading, :py:class:`Amplitude` and
:py:class:`Pick` (real-time mode is not covered by this module at the
moment).
'''
public_id = ResourceReference.T(
xmlstyle='attribute', xmltagname='publicID')
comment_list = List.T(Comment.T())
event_list = List.T(Event.T(xmltagname='event'))
description = Unicode.T(optional=True)
creation_info = CreationInfo.T(optional=True)
[docs]class QuakeML(Object):
'''
QuakeML data container.
'''
xmltagname = 'quakeml'
xmlns = 'http://quakeml.org/xmlns/quakeml/1.2'
guessable_xmlns = [xmlns, guts_xmlns]
event_parameters = EventParameters.T(optional=True)
def get_events(self):
return self.event_parameters.event_list
[docs] def get_pyrocko_events(self):
'''
Get event information in Pyrocko's basic event format.
:rtype:
List of :py:class:`pyrocko.model.event.Event` objects.
'''
events = []
for e in self.event_parameters.event_list:
events.append(e.get_pyrocko_event())
return events
[docs] def get_pyrocko_phase_markers(self):
'''
Get pick information in Pyrocko's basic marker format.
:rtype:
List of :py:class:`pyrocko.gui.marker.PhaseMarker` objects.
'''
markers = []
for e in self.event_parameters.event_list:
markers.extend(e.get_pyrocko_phase_markers())
return markers
[docs] @classmethod
def load_xml(cls, stream=None, filename=None, string=None):
'''
Load QuakeML data from stream, file or string.
:param stream:
Stream open for reading in binary mode.
:type stream:
file-like object, optional
:param filename:
Path to file to be opened for reading.
:type filename:
str, optional
:param string:
String with QuakeML data to be deserialized.
:type string:
str, optional
The arguments ``stream``, ``filename``, and ``string`` are mutually
exclusive.
:returns:
Parsed QuakeML data structure.
:rtype:
:py:class:`QuakeML` object
'''
return super(QuakeML, cls).load_xml(
stream=stream,
filename=filename,
string=string,
ns_hints=[
'http://quakeml.org/xmlns/quakeml/1.2',
'http://quakeml.org/xmlns/bed/1.2'],
ns_ignore=True)