1# http://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
5from __future__ import absolute_import
6import logging
7from pyrocko.guts import StringPattern, StringChoice, String, Float, Int,\
8 Timestamp, Object, List, Union, Bool, Unicode
9from pyrocko.model import event
10from pyrocko.gui import marker
11from pyrocko import moment_tensor
12import numpy as num
14logger = logging.getLogger('pyrocko.io.quakeml')
16guts_prefix = 'quakeml'
17guts_xmlns = 'http://quakeml.org/xmlns/bed/1.2'
18polarity_choices = {'positive': 1, 'negative': -1, 'undecidable': None}
21class QuakeMLError(Exception):
22 pass
25class NoPreferredOriginSet(QuakeMLError):
26 pass
29def one_element_or_none(li):
30 if len(li) == 1:
31 return li[0]
32 elif len(li) == 0:
33 return None
34 else:
35 logger.warning('More than one element in list: {}'.format(li))
36 return None
39class ResourceIdentifier(StringPattern):
40 '''
41 Identifies resource origin.
43 They consist of an authority identifier, a unique resource identifier, and
44 an optional local identifier. The URI schema name smi stands for
45 seismological meta-information, thus indicating a connection to a set of
46 metadata associated with the resource.
47 '''
48 pattern = "^(smi|quakeml):[\\w\\d][\\w\\d\\-\\.\\*\\(\\)_~']{2,}/[\\w" +\
49 "\\d\\-\\.\\*\\(\\)_~'][\\w\\d\\-\\.\\*\\(\\)\\+\\?_~'=,;#/&]*$"
52class WhitespaceOrEmptyStringType(StringPattern):
53 pattern = '^\\s*$'
56class OriginUncertaintyDescription(StringChoice):
57 '''
58 Preferred uncertainty description.
59 '''
60 choices = [
61 'horizontal uncertainty',
62 'uncertainty ellipse',
63 'confidence ellipsoid']
66class AmplitudeCategory(StringChoice):
67 '''
68 This attribute describes the way the waveform trace is evaluated to get an
69 amplitude value.
72 This can be just reading a single value for a given point in time (point),
73 taking a mean value over a time interval (mean), integrating the trace
74 over a time interval (integral), specifying just a time interval
75 (duration), or evaluating a period (period).
76 '''
77 choices = ['point', 'mean', 'duration', 'period', 'integral', 'other']
80class OriginDepthType(StringChoice):
81 '''
82 Type of depth determination.
83 '''
84 choices = [
85 'from location',
86 'from moment tensor inversion',
87 'from modeling of broad-band P waveforms',
88 'constrained by depth phases',
89 'constrained by direct phases',
90 'constrained by depth and direct phases',
91 'operator assigned',
92 'other']
95class OriginType(StringChoice):
96 '''
97 Describes the origin type.
98 '''
99 choices = [
100 'hypocenter',
101 'centroid',
102 'amplitude',
103 'macroseismic',
104 'rupture start',
105 'rupture end']
108class MTInversionType(StringChoice):
109 '''
110 Type of moment tensor inversion. Users should avoid to give contradictory
111 information in :py:class:`MTInversionType` and
112 :py:meth:`MomentTensor.method_id`.
113 '''
114 choices = ['general', 'zero trace', 'double couple']
117class EvaluationMode(StringChoice):
118 '''
119 Mode of an evaluation (used in Pick , Amplitude , Magnitude, Origin,
120 FocalMechanism).
121 '''
122 choices = ['manual', 'automatic']
125class EvaluationStatus(StringChoice):
126 '''
127 Status of of an evaluation (used in Pick , Amplitude, Magnitude, Origin,
128 FocalMechanism).
129 '''
130 choices = ['preliminary', 'confirmed', 'reviewed', 'final', 'rejected']
133class PickOnset(StringChoice):
134 '''
135 Flag that roughly categorizes the sharpness of the onset.
136 '''
137 choices = ['emergent', 'impulsive', 'questionable']
140class EventType(StringChoice):
141 '''
142 Describes the type of an event.
143 '''
144 choices = [
145 'not existing',
146 'not reported',
147 'earthquake',
148 'anthropogenic event',
149 'collapse',
150 'cavity collapse',
151 'mine collapse',
152 'building collapse',
153 'explosion',
154 'accidental explosion',
155 'chemical explosion',
156 'controlled explosion',
157 'experimental explosion',
158 'industrial explosion',
159 'mining explosion',
160 'quarry blast',
161 'road cut',
162 'blasting levee',
163 'nuclear explosion',
164 'induced or triggered event',
165 'rock burst',
166 'reservoir loading',
167 'fluid injection',
168 'fluid extraction',
169 'crash',
170 'plane crash',
171 'train crash',
172 'boat crash',
173 'other event',
174 'atmospheric event',
175 'sonic boom',
176 'sonic blast',
177 'acoustic noise',
178 'thunder',
179 'avalanche',
180 'snow avalanche',
181 'debris avalanche',
182 'hydroacoustic event',
183 'ice quake',
184 'slide',
185 'landslide',
186 'rockslide',
187 'meteorite',
188 'volcanic eruption',
189 'duplicate earthquake',
190 'rockburst']
193class DataUsedWaveType(StringChoice):
194 '''
195 Type of waveform data.
196 '''
197 choices = [
198 'P waves',
199 'body waves',
200 'surface waves',
201 'mantle waves',
202 'combined',
203 'unknown']
206class AmplitudeUnit(StringChoice):
207 '''
208 This attribute provides the most likely measurement units.
210 The measurement units for physical quantity are described in the
211 :py:gattr:`Amplitude.generic_amplitude` attribute. Possible values are
212 specified as combination of SI base units.
213 '''
214 choices = ['m', 's', 'm/s', 'm/(s*s)', 'm*s', 'dimensionless', 'other']
217class EventDescriptionType(StringChoice):
218 '''
219 Category of earthquake description.
220 '''
221 choices = [
222 'felt report',
223 'Flinn-Engdahl region',
224 'local time',
225 'tectonic summary',
226 'nearest cities',
227 'earthquake name',
228 'region name']
231class MomentTensorCategory(StringChoice):
232 '''
233 Category of moment tensor inversion.
234 '''
235 choices = ['teleseismic', 'regional']
238class EventTypeCertainty(StringChoice):
239 '''
240 Denotes how certain the information on event type is.
241 '''
242 choices = ['known', 'suspected']
245class SourceTimeFunctionType(StringChoice):
246 '''
247 Type of source time function.
248 '''
249 choices = ['box car', 'triangle', 'trapezoid', 'unknown']
252class PickPolarity(StringChoice):
253 choices = list(polarity_choices.keys())
256class AgencyID(String):
257 pass
260class Author(Unicode):
261 pass
264class Version(String):
265 pass
268class Phase(Object):
269 value = String.T(xmlstyle='content')
272class GroundTruthLevel(String):
273 pass
276class AnonymousNetworkCode(String):
277 pass
280class AnonymousStationCode(String):
281 pass
284class AnonymousChannelCode(String):
285 pass
288class AnonymousLocationCode(String):
289 pass
292class Type(String):
293 pass
296class MagnitudeHint(String):
297 pass
300class Region(Unicode):
301 pass
304class RealQuantity(Object):
305 value = Float.T()
306 uncertainty = Float.T(optional=True)
307 lower_uncertainty = Float.T(optional=True)
308 upper_uncertainty = Float.T(optional=True)
309 confidence_level = Float.T(optional=True)
312class IntegerQuantity(Object):
313 value = Int.T()
314 uncertainty = Int.T(optional=True)
315 lower_uncertainty = Int.T(optional=True)
316 upper_uncertainty = Int.T(optional=True)
317 confidence_level = Float.T(optional=True)
320class ConfidenceEllipsoid(Object):
321 '''
322 This class represents a description of the location uncertainty as a
323 confidence ellipsoid with arbitrary orientation in space.
324 '''
325 semi_major_axis_length = Float.T()
326 semi_minor_axis_length = Float.T()
327 semi_intermediate_axis_length = Float.T()
328 major_axis_plunge = Float.T()
329 major_axis_azimuth = Float.T()
330 major_axis_rotation = Float.T()
333class TimeQuantity(Object):
334 '''
335 This type describes a point in time.
337 It's given in ISO 8601 format, with optional symmetric or asymmetric
338 uncertainties given in seconds. The time has to be specified in UTC.
339 '''
340 value = Timestamp.T()
341 uncertainty = Float.T(optional=True)
342 lower_uncertainty = Float.T(optional=True)
343 upper_uncertainty = Float.T(optional=True)
344 confidence_level = Float.T(optional=True)
347class TimeWindow(Object):
348 '''
349 Describes a time window for amplitude measurements.
351 Which is given by a central point in time, and points in time before and
352 after this central point. Both points before and after may coincide with
353 the central point.
354 '''
355 begin = Float.T()
356 end = Float.T()
357 reference = Timestamp.T()
360class ResourceReference(ResourceIdentifier):
361 '''
362 This type is used to refer to QuakeML resources as described in Sect. 3.1
363 in the `QuakeML manual <https://quake.ethz.ch/quakeml/docs/REC?action=Att\
364 achFile&do=get&target=QuakeML-BED-20130214b.pdf>`_.
365 '''
366 pass
369class DataUsed(Object):
370 '''
371 The DataUsed class describes the type of data that has been used for a
372 moment-tensor inversion.
373 '''
374 wave_type = DataUsedWaveType.T()
375 station_count = Int.T(optional=True)
376 component_count = Int.T(optional=True)
377 shortest_period = Float.T(optional=True)
378 longest_period = Float.T(optional=True)
381class EventDescription(Object):
382 '''
383 Free-form string with additional event description.
385 This can be a well-known name, like 1906 San Francisco Earthquake.
386 A number of categories can be given in :py:gattr:`type`.
387 '''
388 text = Unicode.T()
389 type = EventDescriptionType.T(optional=True)
392class SourceTimeFunction(Object):
393 '''
394 Source time function used in moment-tensor inversion.
395 '''
396 type = SourceTimeFunctionType.T()
397 duration = Float.T()
398 rise_time = Float.T(optional=True)
399 decay_time = Float.T(optional=True)
402class OriginQuality(Object):
403 '''
404 This type describes the origin quality.
406 It contains various attributes commonly used to describe the
407 quality of an origin, e. g., errors, azimuthal coverage, etc.
408 :py:class:`Origin` objects have an optional attribute of the type
409 :py:gattr:`OriginQuality`.
410 '''
411 associated_phase_count = Int.T(optional=True)
412 used_phase_count = Int.T(optional=True)
413 associated_station_count = Int.T(optional=True)
414 used_station_count = Int.T(optional=True)
415 depth_phase_count = Int.T(optional=True)
416 standard_error = Float.T(optional=True)
417 azimuthal_gap = Float.T(optional=True)
418 secondary_azimuthal_gap = Float.T(optional=True)
419 ground_truth_level = GroundTruthLevel.T(optional=True)
420 maximum_distance = Float.T(optional=True)
421 minimum_distance = Float.T(optional=True)
422 median_distance = Float.T(optional=True)
425class Axis(Object):
426 '''
427 This class describes an eigenvector of a moment tensor.
429 Which is expressed in its principal-axes system and uses the angles
430 :py:gattr:`azimuth`, :py:gattr:`plunge`, and the eigenvalue
431 :py:gattr:`length`.
432 '''
433 azimuth = RealQuantity.T()
434 plunge = RealQuantity.T()
435 length = RealQuantity.T()
438class Tensor(Object):
439 '''
440 The Tensor class represents the six moment-tensor elements Mrr, Mtt, Mpp,
441 Mrt, Mrp, Mtp in the spherical coordinate system defined by local upward
442 vertical (r), North-South (t), and West-East (p) directions.
443 '''
444 mrr = RealQuantity.T(xmltagname='Mrr')
445 mtt = RealQuantity.T(xmltagname='Mtt')
446 mpp = RealQuantity.T(xmltagname='Mpp')
447 mrt = RealQuantity.T(xmltagname='Mrt')
448 mrp = RealQuantity.T(xmltagname='Mrp')
449 mtp = RealQuantity.T(xmltagname='Mtp')
452class NodalPlane(Object):
453 '''
454 This class describes a nodal plane using the attributes :py:gattr:`strike`,
455 :py:gattr:`dip`, and :py:gattr:`rake`.
456 '''
457 strike = RealQuantity.T()
458 dip = RealQuantity.T()
459 rake = RealQuantity.T()
462class CompositeTime(Object):
463 '''
464 The CompositeTime type allows complex descriptions.
466 If the specification is given with no greater accuracy than days (i.e., no
467 time components are given), the date refers to local time. However, if
468 time components are given, they have to refer to UTC.
469 '''
470 year = IntegerQuantity.T(optional=True)
471 month = IntegerQuantity.T(optional=True)
472 day = IntegerQuantity.T(optional=True)
473 hour = IntegerQuantity.T(optional=True)
474 minute = IntegerQuantity.T(optional=True)
475 second = RealQuantity.T(optional=True)
478class OriginUncertainty(Object):
479 '''
480 This class describes the location uncertainties of an origin.
482 The uncertainty can be described either as a simple circular horizontal
483 uncertainty, an uncertainty ellipse according to IMS1.0, or a confidence
484 ellipsoid. If multiple uncertainty models are given, the preferred variant
485 can be specified in the attribute :py:gattr:`preferred_description`.
486 '''
487 horizontal_uncertainty = Float.T(optional=True)
488 min_horizontal_uncertainty = Float.T(optional=True)
489 max_horizontal_uncertainty = Float.T(optional=True)
490 azimuth_max_horizontal_uncertainty = Float.T(optional=True)
491 confidence_ellipsoid = ConfidenceEllipsoid.T(optional=True)
492 preferred_description = OriginUncertaintyDescription.T(optional=True)
493 confidence_level = Float.T(optional=True)
496class ResourceReferenceOptional(Union):
497 members = [ResourceReference.T(), WhitespaceOrEmptyStringType.T()]
500class CreationInfo(Object):
501 '''
502 CreationInfo is used to describe creation metadata (author, version, and
503 creation time) of a resource.
504 '''
505 agency_id = AgencyID.T(optional=True, xmltagname='agencyID')
506 agency_uri = ResourceReference.T(optional=True, xmltagname='agencyURI')
507 author = Author.T(optional=True)
508 author_uri = ResourceReference.T(optional=True, xmltagname='authorURI')
509 creation_time = Timestamp.T(optional=True)
510 version = Version.T(optional=True)
513class StationMagnitudeContribution(Object):
514 '''
515 This class describes the weighting of magnitude values from several
516 :py:class:`StationMagnitude` objects for computing a network magnitude
517 estimation.
518 '''
519 station_magnitude_id = ResourceReference.T(xmltagname='stationMagnitudeID')
520 residual = Float.T(optional=True)
521 weight = Float.T(optional=True)
524class PrincipalAxes(Object):
525 '''
526 This class describes the principal axes of a moment tensor.
527 :py:gattr:`t_axis` and :py:gattr:`p_axis` are required, while
528 :py:gattr:`n_axis` is optional.
529 '''
530 t_axis = Axis.T()
531 p_axis = Axis.T()
532 n_axis = Axis.T(optional=True)
535class NodalPlanes(Object):
536 '''
537 This class describes the nodal planes of a moment tensor. The attribute
538 :py:gattr:`preferred_plane` can be used to define which plane is the
539 preferred one.
540 '''
541 preferred_plane = Int.T(optional=True, xmlstyle='attribute')
542 nodal_plane1 = NodalPlane.T(optional=True)
543 nodal_plane2 = NodalPlane.T(optional=True)
546class WaveformStreamID(Object):
547 '''
548 Reference to a stream description in an inventory.
550 This is mostly equivalent to the combination of networkCode, stationCode,
551 locationCode, and channelCode. However, additional information, e.g.,
552 sampling rate, can be referenced by the resourceURI. It is recommended to
553 use resourceURI as a flexible, abstract, and unique stream ID that allows
554 to describe different processing levels, or resampled/filtered products of
555 the same initial stream, without violating the intrinsic meaning of the
556 legacy identifiers (:py:gattr:`network_code`, :py:gattr:`station_code`,
557 :py:gattr:`channel_code`, and :py:gattr:`location_code`). However, for
558 operation in the context of legacy systems, the classical identifier
559 components are supported.
560 '''
561 value = ResourceReferenceOptional.T(xmlstyle='content')
562 network_code = AnonymousNetworkCode.T(xmlstyle='attribute')
563 station_code = AnonymousStationCode.T(xmlstyle='attribute')
564 channel_code = AnonymousChannelCode.T(optional=True, xmlstyle='attribute')
565 location_code = AnonymousLocationCode.T(
566 optional=True, xmlstyle='attribute')
568 @property
569 def nslc_id(self):
570 return (self.network_code, self.station_code, self.location_code,
571 self.channel_code)
574class Comment(Object):
575 '''
576 Comment holds information on comments to a resource as well as author and
577 creation time information.
578 '''
579 id = ResourceReference.T(optional=True, xmlstyle='attribute')
580 text = Unicode.T()
581 creation_info = CreationInfo.T(optional=True)
584class MomentTensor(Object):
585 '''
586 This class represents a moment tensor solution for an event. It is an
587 optional part of a :py:class:`FocalMechanism` description.
588 '''
589 public_id = ResourceReference.T(
590 xmlstyle='attribute', xmltagname='publicID')
591 data_used_list = List.T(DataUsed.T())
592 comment_list = List.T(Comment.T())
593 derived_origin_id = ResourceReference.T(
594 optional=True, xmltagname='derivedOriginID')
595 moment_magnitude_id = ResourceReference.T(
596 optional=True, xmltagname='momentMagnitudeID')
597 scalar_moment = RealQuantity.T(optional=True)
598 tensor = Tensor.T(optional=True)
599 variance = Float.T(optional=True)
600 variance_reduction = Float.T(optional=True)
601 double_couple = Float.T(optional=True)
602 clvd = Float.T(optional=True)
603 iso = Float.T(optional=True)
604 greens_function_id = ResourceReference.T(
605 optional=True, xmltagname='greensFunctionID')
606 filter_id = ResourceReference.T(optional=True, xmltagname='filterID')
607 source_time_function = SourceTimeFunction.T(optional=True)
608 method_id = ResourceReference.T(optional=True, xmltagname='methodID')
609 category = MomentTensorCategory.T(optional=True)
610 inversion_type = MTInversionType.T(optional=True)
611 creation_info = CreationInfo.T(optional=True)
613 def pyrocko_moment_tensor(self):
614 mrr = self.tensor.mrr.value
615 mtt = self.tensor.mtt.value
616 mpp = self.tensor.mpp.value
617 mrt = self.tensor.mrt.value
618 mrp = self.tensor.mrp.value
619 mtp = self.tensor.mtp.value
620 mt = moment_tensor.MomentTensor(m_up_south_east=num.matrix([
621 [mrr, mrt, mrp], [mrt, mtt, mtp], [mrp, mtp, mpp]]))
623 return mt
626class Amplitude(Object):
627 '''
628 This class represents a quantification of the waveform anomaly.
630 Usually it consists of a single amplitude measurement or a measurement of
631 the visible signal duration for duration magnitudes.
632 '''
633 public_id = ResourceReference.T(
634 xmlstyle='attribute', xmltagname='publicID')
635 comment_list = List.T(Comment.T())
636 generic_amplitude = RealQuantity.T()
637 type = Type.T(optional=True)
638 category = AmplitudeCategory.T(optional=True)
639 unit = AmplitudeUnit.T(optional=True)
640 method_id = ResourceReference.T(optional=True, xmltagname='methodID')
641 period = RealQuantity.T(optional=True)
642 snr = Float.T(optional=True)
643 time_window = TimeWindow.T(optional=True)
644 pick_id = ResourceReference.T(optional=True, xmltagname='pickID')
645 waveform_id = WaveformStreamID.T(optional=True, xmltagname='waveformID')
646 filter_id = ResourceReference.T(optional=True, xmltagname='filterID')
647 scaling_time = TimeQuantity.T(optional=True)
648 magnitude_hint = MagnitudeHint.T(optional=True)
649 evaluation_mode = EvaluationMode.T(optional=True)
650 evaluation_status = EvaluationStatus.T(optional=True)
651 creation_info = CreationInfo.T(optional=True)
654class Magnitude(Object):
655 '''
656 Describes a magnitude.
658 It can, but does not need to be associated with an origin. Association
659 with an origin is expressed with the optional attribute
660 :py:gattr:`origin_id`. It is either a combination of different magnitude
661 estimations, or it represents the reported magnitude for the given event.
662 '''
663 public_id = ResourceReference.T(
664 xmlstyle='attribute', xmltagname='publicID')
665 comment_list = List.T(Comment.T())
666 station_magnitude_contribution_list = List.T(
667 StationMagnitudeContribution.T())
668 mag = RealQuantity.T()
669 type = Type.T(optional=True)
670 origin_id = ResourceReference.T(optional=True, xmltagname='originID')
671 method_id = ResourceReference.T(optional=True, xmltagname='methodID')
672 station_count = Int.T(optional=True)
673 azimuthal_gap = Float.T(optional=True)
674 evaluation_mode = EvaluationMode.T(optional=True)
675 evaluation_status = EvaluationStatus.T(optional=True)
676 creation_info = CreationInfo.T(optional=True)
679class StationMagnitude(Object):
680 '''
681 This class describes the magnitude derived from a single waveform stream.
682 '''
683 public_id = ResourceReference.T(
684 xmlstyle='attribute', xmltagname='publicID')
685 comment_list = List.T(Comment.T())
686 origin_id = ResourceReference.T(optional=True, xmltagname='originID')
687 mag = RealQuantity.T()
688 type = Type.T(optional=True)
689 amplitude_id = ResourceReference.T(optional=True, xmltagname='amplitudeID')
690 method_id = ResourceReference.T(optional=True, xmltagname='methodID')
691 waveform_id = WaveformStreamID.T(optional=True, xmltagname='waveformID')
692 creation_info = CreationInfo.T(optional=True)
695class Arrival(Object):
696 '''
697 Successful association of a pick with an origin qualifies this pick as an
698 arrival.
700 An arrival thus connects a pick with an origin and provides
701 additional attributes that describe this relationship. Usually
702 qualification of a pick as an arrival for a given origin is a hypothesis,
703 which is based on assumptions about the type of arrival (phase) as well as
704 observed and (on the basis of an earth model) computed arrival times, or
705 the residual, respectively.Additional pick attributes like the horizontal
706 slowness and backazimuth of the observed wave—especially if derived from
707 array data—may further constrain the nature of the arrival.
708 '''
709 public_id = ResourceReference.T(
710 xmlstyle='attribute', xmltagname='publicID')
711 comment_list = List.T(Comment.T())
712 pick_id = ResourceReference.T(xmltagname='pickID')
713 phase = Phase.T()
714 time_correction = Float.T(optional=True)
715 azimuth = Float.T(optional=True)
716 distance = Float.T(optional=True)
717 takeoff_angle = RealQuantity.T(optional=True)
718 time_residual = Float.T(optional=True)
719 horizontal_slowness_residual = Float.T(optional=True)
720 backazimuth_residual = Float.T(optional=True)
721 time_weight = Float.T(optional=True)
722 time_used = Int.T(optional=True)
723 horizontal_slowness_weight = Float.T(optional=True)
724 backazimuth_weight = Float.T(optional=True)
725 earth_model_id = ResourceReference.T(
726 optional=True, xmltagname='earthModelID')
727 creation_info = CreationInfo.T(optional=True)
730class Pick(Object):
731 '''
732 A pick is the observation of an amplitude anomaly in a seismogram at a
733 specific point in time. It is not necessarily related to a seismic event.
734 '''
735 public_id = ResourceReference.T(
736 xmlstyle='attribute', xmltagname='publicID')
737 comment_list = List.T(Comment.T())
738 time = TimeQuantity.T()
739 waveform_id = WaveformStreamID.T(xmltagname='waveformID')
740 filter_id = ResourceReference.T(optional=True, xmltagname='filterID')
741 method_id = ResourceReference.T(optional=True, xmltagname='methodID')
742 horizontal_slowness = RealQuantity.T(optional=True)
743 backazimuth = RealQuantity.T(optional=True)
744 slowness_method_id = ResourceReference.T(
745 optional=True, xmltagname='slownessMethodID')
746 onset = PickOnset.T(optional=True)
747 phase_hint = Phase.T(optional=True)
748 polarity = PickPolarity.T(optional=True)
749 evaluation_mode = EvaluationMode.T(optional=True)
750 evaluation_status = EvaluationStatus.T(optional=True)
751 creation_info = CreationInfo.T(optional=True)
753 @property
754 def pyrocko_polarity(self):
755 return polarity_choices.get(self.polarity, None)
757 def get_pyrocko_phase_marker(self, event=None):
758 if not self.phase_hint:
759 logger.warn('Pick %s: phase_hint undefined' % self.public_id)
760 phasename = 'undefined'
761 else:
762 phasename = self.phase_hint.value
764 return marker.PhaseMarker(
765 event=event, nslc_ids=[self.waveform_id.nslc_id],
766 tmin=self.time.value, tmax=self.time.value,
767 phasename=phasename,
768 polarity=self.pyrocko_polarity,
769 automatic=self.evaluation_mode)
772class FocalMechanism(Object):
773 '''
774 This class describes the focal mechanism of an event.
776 It includes different descriptions like nodal planes, principal axes, and
777 a moment tensor. The moment tensor description is provided by objects of
778 the class :py:class:`MomentTensor` which can be specified as
779 child elements of :py:class:`FocalMechanism`.
780 '''
781 public_id = ResourceReference.T(
782 xmlstyle='attribute', xmltagname='publicID')
783 waveform_id_list = List.T(WaveformStreamID.T(xmltagname='waveformID'))
784 comment_list = List.T(Comment.T())
785 moment_tensor_list = List.T(MomentTensor.T())
786 triggering_origin_id = ResourceReference.T(
787 optional=True, xmltagname='triggeringOriginID')
788 nodal_planes = NodalPlanes.T(optional=True)
789 principal_axes = PrincipalAxes.T(optional=True)
790 azimuthal_gap = Float.T(optional=True)
791 station_polarity_count = Int.T(optional=True)
792 misfit = Float.T(optional=True)
793 station_distribution_ratio = Float.T(optional=True)
794 method_id = ResourceReference.T(optional=True, xmltagname='methodID')
795 evaluation_mode = EvaluationMode.T(optional=True)
796 evaluation_status = EvaluationStatus.T(optional=True)
797 creation_info = CreationInfo.T(optional=True)
800class Origin(Object):
801 '''
802 This class represents the focal time and geographical location of an
803 earthquake hypocenter, as well as additional meta-information.
804 :py:class:`Origin` can have objects of type :py:class:`OriginUncertainty`
805 and :py:class:`Arrival` as child elements.
806 '''
807 public_id = ResourceReference.T(
808 xmlstyle='attribute', xmltagname='publicID')
809 composite_time_list = List.T(CompositeTime.T())
810 comment_list = List.T(Comment.T())
811 origin_uncertainty_list = List.T(OriginUncertainty.T())
812 arrival_list = List.T(Arrival.T())
813 time = TimeQuantity.T()
814 longitude = RealQuantity.T()
815 latitude = RealQuantity.T()
816 depth = RealQuantity.T(optional=True)
817 depth_type = OriginDepthType.T(optional=True)
818 time_fixed = Bool.T(optional=True)
819 epicenter_fixed = Bool.T(optional=True)
820 reference_system_id = ResourceReference.T(
821 optional=True, xmltagname='referenceSystemID')
822 method_id = ResourceReference.T(optional=True, xmltagname='methodID')
823 earth_model_id = ResourceReference.T(
824 optional=True, xmltagname='earthModelID')
825 quality = OriginQuality.T(optional=True)
826 type = OriginType.T(optional=True)
827 region = Region.T(optional=True)
828 evaluation_mode = EvaluationMode.T(optional=True)
829 evaluation_status = EvaluationStatus.T(optional=True)
830 creation_info = CreationInfo.T(optional=True)
832 def position_values(self):
833 lat = self.latitude.value
834 lon = self.longitude.value
835 if not self.depth:
836 logger.warn(
837 'Origin %s: Depth is undefined. Set to depth=0.' %
838 self.public_id)
839 depth = 0.
840 else:
841 depth = self.depth.value
843 return lat, lon, depth
845 def get_pyrocko_event(self):
846 lat, lon, depth = self.position_values()
847 otime = self.time.value
848 if self.creation_info:
849 cat = self.creation_info.agency_id
850 else:
851 cat = None
853 return event.Event(
854 name=self.public_id,
855 lat=lat,
856 lon=lon,
857 time=otime,
858 depth=depth,
859 catalog=cat,
860 region=self.region)
863class Event(Object):
864 '''
865 The class Event describes a seismic event.
867 The Event does not necessarily need to be a tectonic earthquake. An event
868 is usually associated with one or more origins, which contain information
869 about focal time and geographical location of the event. Multiple origins
870 can cover automatic and manual locations, a set of location from different
871 agencies, locations generated with different location programs and earth
872 models, etc. Furthermore, an event is usually associated with one or more
873 magnitudes, and with one or more focal mechanism determinations. In
874 standard QuakeML-BED, :py:class:`Origin`, :py:class:`Magnitude`,
875 :py:class:`StationMagnitude`, and :py:class:`FocalMechanism` are child
876 elements of Event. In BED-RT all these elements are on the same hierarchy
877 level as child elements of :py:class:`EventParameters`. The association of
878 origins, magnitudes, and focal mechanisms to a particular event is
879 expressed using references inside :py:class:`Event`.
880 '''
881 public_id = ResourceReference.T(
882 xmlstyle='attribute', xmltagname='publicID')
883 description_list = List.T(EventDescription.T())
884 comment_list = List.T(Comment.T())
885 focal_mechanism_list = List.T(FocalMechanism.T())
886 amplitude_list = List.T(Amplitude.T())
887 magnitude_list = List.T(Magnitude.T())
888 station_magnitude_list = List.T(StationMagnitude.T())
889 origin_list = List.T(Origin.T())
890 pick_list = List.T(Pick.T())
891 preferred_origin_id = ResourceReference.T(
892 optional=True, xmltagname='preferredOriginID')
893 preferred_magnitude_id = ResourceReference.T(
894 optional=True, xmltagname='preferredMagnitudeID')
895 preferred_focal_mechanism_id = ResourceReference.T(
896 optional=True, xmltagname='preferredFocalMechanismID')
897 type = EventType.T(
898 optional=True)
899 type_certainty = EventTypeCertainty.T(
900 optional=True)
901 creation_info = CreationInfo.T(
902 optional=True)
903 region = Region.T(
904 optional=True)
906 def describe(self):
907 return '''%s:
908 origins: %i %s
909 magnitudes: %i %s
910 focal_machanisms: %i %s
911 picks: %i
912 amplitudes: %i
913 station_magnitudes: %i''' % (
914 self.public_id,
915 len(self.origin_list),
916 '@' if self.preferred_origin_id else '-',
917 len(self.magnitude_list),
918 '@' if self.preferred_magnitude_id else '-',
919 len(self.focal_mechanism_list),
920 '@' if self.preferred_focal_mechanism_id else '-',
921 len(self.pick_list),
922 len(self.amplitude_list),
923 len(self.station_magnitude_list))
925 def get_pyrocko_phase_markers(self):
926 event = self.get_pyrocko_event()
927 return [
928 p.get_pyrocko_phase_marker(event=event) for p in self.pick_list]
930 def get_pyrocko_event(self):
931 '''
932 Convert into Pyrocko event object.
934 Uses *preferred* origin, magnitude, and moment tensor. If no preferred
935 item is specified, it picks the first from the list and emits a
936 warning.
937 '''
939 origin = self.preferred_origin
940 if not origin and self.origin_list:
941 origin = self.origin_list[0]
942 if len(self.origin_list) > 1:
943 logger.warn(
944 'Event %s: No preferred origin set, '
945 'more than one available, using first' % self.public_id)
947 if not origin:
948 raise QuakeMLError(
949 'No origin available for event: %s' % self.public_id)
951 ev = origin.get_pyrocko_event()
953 foc_mech = self.preferred_focal_mechanism
954 if not foc_mech and self.focal_mechanism_list:
955 foc_mech = self.focal_mechanism_list[0]
956 if len(self.focal_mechanism_list) > 1:
957 logger.warn(
958 'Event %s: No preferred focal mechanism set, '
959 'more than one available, using first' % ev.name)
961 if foc_mech and foc_mech.moment_tensor_list:
962 ev.moment_tensor = \
963 foc_mech.moment_tensor_list[0].pyrocko_moment_tensor()
965 if len(foc_mech.moment_tensor_list) > 1:
966 logger.warn(
967 'more than one moment tensor available, using first')
969 mag = None
970 pref_mag = self.preferred_magnitude
971 if pref_mag:
972 mag = pref_mag
973 elif self.magnitude_list:
974 mag = self.magnitude_list[0]
975 if len(self.magnitude_list) > 1:
976 logger.warn(
977 'Event %s: No preferred magnitude set, '
978 'more than one available, using first' % ev.name)
980 if mag:
981 ev.magnitude = mag.mag.value
982 ev.magnitude_type = mag.type
984 ev.region = self.get_effective_region()
986 return ev
988 def get_effective_region(self):
989 if self.region:
990 return self.region
992 for desc in self.description_list:
993 if desc.type in ('Flinn-Engdahl region', 'region name'):
994 return desc.text
996 return None
998 @property
999 def preferred_origin(self):
1000 return one_element_or_none(
1001 [x for x in self.origin_list
1002 if x.public_id == self.preferred_origin_id])
1004 @property
1005 def preferred_magnitude(self):
1006 return one_element_or_none(
1007 [x for x in self.magnitude_list
1008 if x.public_id == self.preferred_magnitude_id])
1010 @property
1011 def preferred_focal_mechanism(self):
1012 return one_element_or_none(
1013 [x for x in self.focal_mechanism_list
1014 if x.public_id == self.preferred_focal_mechanism_id])
1017class EventParameters(Object):
1018 '''
1019 In the bulletin-type (non real-time) model, this class serves as a
1020 container for Event objects.
1022 In the real-time version, it can hold objects of type :py:class:`Event`,
1023 :py:class:`Origin`, :py:class:`Magnitude`, :py:class:`StationMagnitude`,
1024 :py:class:`FocalMechanism`, Reading, :py:class:`Amplitude` and
1025 :py:class:`Pick`.
1026 '''
1027 public_id = ResourceReference.T(
1028 xmlstyle='attribute', xmltagname='publicID')
1029 comment_list = List.T(Comment.T())
1030 event_list = List.T(Event.T(xmltagname='event'))
1031 description = Unicode.T(optional=True)
1032 creation_info = CreationInfo.T(optional=True)
1035class QuakeML(Object):
1036 '''
1037 QuakeML data container.
1038 '''
1039 xmltagname = 'quakeml'
1040 xmlns = 'http://quakeml.org/xmlns/quakeml/1.2'
1041 guessable_xmlns = [xmlns, guts_xmlns]
1043 event_parameters = EventParameters.T(optional=True)
1045 def get_events(self):
1046 return self.event_parameters.event_list
1048 def get_pyrocko_events(self):
1049 '''
1050 Get event information in Pyrocko's basic event format.
1052 :rtype:
1053 List of :py:class:`pyrocko.model.event.Event` objects.
1054 '''
1055 events = []
1056 for e in self.event_parameters.event_list:
1057 events.append(e.get_pyrocko_event())
1059 return events
1061 def get_pyrocko_phase_markers(self):
1062 '''
1063 Get pick information in Pyrocko's basic marker format.
1065 :rtype:
1066 List of :py:class:`pyrocko.gui.marker.PhaseMarker` objects.
1067 '''
1068 markers = []
1069 for e in self.event_parameters.event_list:
1070 markers.extend(e.get_pyrocko_phase_markers())
1072 return markers
1074 @classmethod
1075 def load_xml(cls, stream=None, filename=None, string=None):
1076 '''
1077 Load QuakeML data from stream, file or string.
1079 :param stream:
1080 Stream open for reading in binary mode.
1081 :type stream:
1082 file-like object, optional
1084 :param filename:
1085 Path to file to be opened for reading.
1086 :type filename:
1087 str, optional
1089 :param string:
1090 String with QuakeML data to be deserialized.
1091 :type string:
1092 str, optional
1094 The arguments ``stream``, ``filename``, and ``string`` are mutually
1095 exclusive.
1097 :returns:
1098 Parsed QuakeML data structure.
1099 :rtype:
1100 :py:class:`QuakeML` object
1102 '''
1104 return super(QuakeML, cls).load_xml(
1105 stream=stream,
1106 filename=filename,
1107 string=string,
1108 ns_hints=[
1109 'http://quakeml.org/xmlns/quakeml/1.2',
1110 'http://quakeml.org/xmlns/bed/1.2'],
1111 ns_ignore=True)