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 

13 

14logger = logging.getLogger('pyrocko.io.quakeml') 

15 

16guts_prefix = 'quakeml' 

17guts_xmlns = 'http://quakeml.org/xmlns/bed/1.2' 

18polarity_choices = {'positive': 1, 'negative': -1, 'undecidable': None} 

19 

20 

21class QuakeMLError(Exception): 

22 pass 

23 

24 

25class NoPreferredOriginSet(QuakeMLError): 

26 pass 

27 

28 

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 

37 

38 

39class ResourceIdentifier(StringPattern): 

40 ''' 

41 Identifies resource origin. 

42 

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\\-\\.\\*\\(\\)\\+\\?_~'=,;#/&]*$" 

50 

51 

52class WhitespaceOrEmptyStringType(StringPattern): 

53 pattern = '^\\s*$' 

54 

55 

56class OriginUncertaintyDescription(StringChoice): 

57 ''' 

58 Preferred uncertainty description. 

59 ''' 

60 choices = [ 

61 'horizontal uncertainty', 

62 'uncertainty ellipse', 

63 'confidence ellipsoid'] 

64 

65 

66class AmplitudeCategory(StringChoice): 

67 ''' 

68 This attribute describes the way the waveform trace is evaluated to get an 

69 amplitude value. 

70 

71 

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'] 

78 

79 

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'] 

93 

94 

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'] 

106 

107 

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'] 

115 

116 

117class EvaluationMode(StringChoice): 

118 ''' 

119 Mode of an evaluation (used in Pick , Amplitude , Magnitude, Origin, 

120 FocalMechanism). 

121 ''' 

122 choices = ['manual', 'automatic'] 

123 

124 

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'] 

131 

132 

133class PickOnset(StringChoice): 

134 ''' 

135 Flag that roughly categorizes the sharpness of the onset. 

136 ''' 

137 choices = ['emergent', 'impulsive', 'questionable'] 

138 

139 

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'] 

191 

192 

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'] 

204 

205 

206class AmplitudeUnit(StringChoice): 

207 ''' 

208 This attribute provides the most likely measurement units. 

209 

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'] 

215 

216 

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'] 

229 

230 

231class MomentTensorCategory(StringChoice): 

232 ''' 

233 Category of moment tensor inversion. 

234 ''' 

235 choices = ['teleseismic', 'regional'] 

236 

237 

238class EventTypeCertainty(StringChoice): 

239 ''' 

240 Denotes how certain the information on event type is. 

241 ''' 

242 choices = ['known', 'suspected'] 

243 

244 

245class SourceTimeFunctionType(StringChoice): 

246 ''' 

247 Type of source time function. 

248 ''' 

249 choices = ['box car', 'triangle', 'trapezoid', 'unknown'] 

250 

251 

252class PickPolarity(StringChoice): 

253 choices = list(polarity_choices.keys()) 

254 

255 

256class AgencyID(String): 

257 pass 

258 

259 

260class Author(Unicode): 

261 pass 

262 

263 

264class Version(String): 

265 pass 

266 

267 

268class Phase(Object): 

269 value = String.T(xmlstyle='content') 

270 

271 

272class GroundTruthLevel(String): 

273 pass 

274 

275 

276class AnonymousNetworkCode(String): 

277 pass 

278 

279 

280class AnonymousStationCode(String): 

281 pass 

282 

283 

284class AnonymousChannelCode(String): 

285 pass 

286 

287 

288class AnonymousLocationCode(String): 

289 pass 

290 

291 

292class Type(String): 

293 pass 

294 

295 

296class MagnitudeHint(String): 

297 pass 

298 

299 

300class Region(Unicode): 

301 pass 

302 

303 

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) 

310 

311 

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) 

318 

319 

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() 

331 

332 

333class TimeQuantity(Object): 

334 ''' 

335 This type describes a point in time. 

336 

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) 

345 

346 

347class TimeWindow(Object): 

348 ''' 

349 Describes a time window for amplitude measurements. 

350 

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() 

358 

359 

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 

367 

368 

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) 

379 

380 

381class EventDescription(Object): 

382 ''' 

383 Free-form string with additional event description. 

384 

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) 

390 

391 

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) 

400 

401 

402class OriginQuality(Object): 

403 ''' 

404 This type describes the origin quality. 

405 

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) 

423 

424 

425class Axis(Object): 

426 ''' 

427 This class describes an eigenvector of a moment tensor. 

428 

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() 

436 

437 

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') 

450 

451 

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() 

460 

461 

462class CompositeTime(Object): 

463 ''' 

464 The CompositeTime type allows complex descriptions. 

465 

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) 

476 

477 

478class OriginUncertainty(Object): 

479 ''' 

480 This class describes the location uncertainties of an origin. 

481 

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) 

494 

495 

496class ResourceReferenceOptional(Union): 

497 members = [ResourceReference.T(), WhitespaceOrEmptyStringType.T()] 

498 

499 

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) 

511 

512 

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) 

522 

523 

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) 

533 

534 

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) 

544 

545 

546class WaveformStreamID(Object): 

547 ''' 

548 Reference to a stream description in an inventory. 

549 

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') 

567 

568 @property 

569 def nslc_id(self): 

570 return (self.network_code, self.station_code, self.location_code, 

571 self.channel_code) 

572 

573 

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) 

582 

583 

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) 

612 

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]])) 

622 

623 return mt 

624 

625 

626class Amplitude(Object): 

627 ''' 

628 This class represents a quantification of the waveform anomaly. 

629 

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) 

652 

653 

654class Magnitude(Object): 

655 ''' 

656 Describes a magnitude. 

657 

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) 

677 

678 

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) 

693 

694 

695class Arrival(Object): 

696 ''' 

697 Successful association of a pick with an origin qualifies this pick as an 

698 arrival. 

699 

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) 

728 

729 

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) 

752 

753 @property 

754 def pyrocko_polarity(self): 

755 return polarity_choices.get(self.polarity, None) 

756 

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 

763 

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) 

770 

771 

772class FocalMechanism(Object): 

773 ''' 

774 This class describes the focal mechanism of an event. 

775 

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) 

798 

799 

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) 

831 

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 

842 

843 return lat, lon, depth 

844 

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 

852 

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) 

861 

862 

863class Event(Object): 

864 ''' 

865 The class Event describes a seismic event. 

866 

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) 

905 

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)) 

924 

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] 

929 

930 def get_pyrocko_event(self): 

931 ''' 

932 Convert into Pyrocko event object. 

933 

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 ''' 

938 

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) 

946 

947 if not origin: 

948 raise QuakeMLError( 

949 'No origin available for event: %s' % self.public_id) 

950 

951 ev = origin.get_pyrocko_event() 

952 

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) 

960 

961 if foc_mech and foc_mech.moment_tensor_list: 

962 ev.moment_tensor = \ 

963 foc_mech.moment_tensor_list[0].pyrocko_moment_tensor() 

964 

965 if len(foc_mech.moment_tensor_list) > 1: 

966 logger.warn( 

967 'more than one moment tensor available, using first') 

968 

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) 

979 

980 if mag: 

981 ev.magnitude = mag.mag.value 

982 ev.magnitude_type = mag.type 

983 

984 ev.region = self.get_effective_region() 

985 

986 return ev 

987 

988 def get_effective_region(self): 

989 if self.region: 

990 return self.region 

991 

992 for desc in self.description_list: 

993 if desc.type in ('Flinn-Engdahl region', 'region name'): 

994 return desc.text 

995 

996 return None 

997 

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]) 

1003 

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]) 

1009 

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]) 

1015 

1016 

1017class EventParameters(Object): 

1018 ''' 

1019 In the bulletin-type (non real-time) model, this class serves as a 

1020 container for Event objects. 

1021 

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) 

1033 

1034 

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] 

1042 

1043 event_parameters = EventParameters.T(optional=True) 

1044 

1045 def get_events(self): 

1046 return self.event_parameters.event_list 

1047 

1048 def get_pyrocko_events(self): 

1049 ''' 

1050 Get event information in Pyrocko's basic event format. 

1051 

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()) 

1058 

1059 return events 

1060 

1061 def get_pyrocko_phase_markers(self): 

1062 ''' 

1063 Get pick information in Pyrocko's basic marker format. 

1064 

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()) 

1071 

1072 return markers 

1073 

1074 @classmethod 

1075 def load_xml(cls, stream=None, filename=None, string=None): 

1076 ''' 

1077 Load QuakeML data from stream, file or string. 

1078 

1079 :param stream: 

1080 Stream open for reading in binary mode. 

1081 :type stream: 

1082 file-like object, optional 

1083 

1084 :param filename: 

1085 Path to file to be opened for reading. 

1086 :type filename: 

1087 str, optional 

1088 

1089 :param string: 

1090 String with QuakeML data to be deserialized. 

1091 :type string: 

1092 str, optional 

1093 

1094 The arguments ``stream``, ``filename``, and ``string`` are mutually 

1095 exclusive. 

1096 

1097 :returns: 

1098 Parsed QuakeML data structure. 

1099 :rtype: 

1100 :py:class:`QuakeML` object 

1101 

1102 ''' 

1103 

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)