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 pattern = "^(smi|quakeml):[\\w\\d][\\w\\d\\-\\.\\*\\(\\)_~']{2,}/[\\w" +\ 

41 "\\d\\-\\.\\*\\(\\)_~'][\\w\\d\\-\\.\\*\\(\\)\\+\\?_~'=,;#/&]*$" 

42 

43 

44class WhitespaceOrEmptyStringType(StringPattern): 

45 pattern = '^\\s*$' 

46 

47 

48class OriginUncertaintyDescription(StringChoice): 

49 choices = [ 

50 'horizontal uncertainty', 

51 'uncertainty ellipse', 

52 'confidence ellipsoid'] 

53 

54 

55class AmplitudeCategory(StringChoice): 

56 choices = ['point', 'mean', 'duration', 'period', 'integral', 'other'] 

57 

58 

59class OriginDepthType(StringChoice): 

60 choices = [ 

61 'from location', 

62 'from moment tensor inversion', 

63 'from modeling of broad-band P waveforms', 

64 'constrained by depth phases', 

65 'constrained by direct phases', 

66 'constrained by depth and direct phases', 

67 'operator assigned', 

68 'other'] 

69 

70 

71class OriginType(StringChoice): 

72 choices = [ 

73 'hypocenter', 

74 'centroid', 

75 'amplitude', 

76 'macroseismic', 

77 'rupture start', 

78 'rupture end'] 

79 

80 

81class MTInversionType(StringChoice): 

82 choices = ['general', 'zero trace', 'double couple'] 

83 

84 

85class EvaluationMode(StringChoice): 

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

87 

88 

89class EvaluationStatus(StringChoice): 

90 choices = ['preliminary', 'confirmed', 'reviewed', 'final', 'rejected'] 

91 

92 

93class PickOnset(StringChoice): 

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

95 

96 

97class EventType(StringChoice): 

98 choices = [ 

99 'not existing', 

100 'not reported', 

101 'earthquake', 

102 'anthropogenic event', 

103 'collapse', 

104 'cavity collapse', 

105 'mine collapse', 

106 'building collapse', 

107 'explosion', 

108 'accidental explosion', 

109 'chemical explosion', 

110 'controlled explosion', 

111 'experimental explosion', 

112 'industrial explosion', 

113 'mining explosion', 

114 'quarry blast', 

115 'road cut', 

116 'blasting levee', 

117 'nuclear explosion', 

118 'induced or triggered event', 

119 'rock burst', 

120 'reservoir loading', 

121 'fluid injection', 

122 'fluid extraction', 

123 'crash', 

124 'plane crash', 

125 'train crash', 

126 'boat crash', 

127 'other event', 

128 'atmospheric event', 

129 'sonic boom', 

130 'sonic blast', 

131 'acoustic noise', 

132 'thunder', 

133 'avalanche', 

134 'snow avalanche', 

135 'debris avalanche', 

136 'hydroacoustic event', 

137 'ice quake', 

138 'slide', 

139 'landslide', 

140 'rockslide', 

141 'meteorite', 

142 'volcanic eruption', 

143 'duplicate earthquake', 

144 'rockburst'] 

145 

146 

147class DataUsedWaveType(StringChoice): 

148 choices = [ 

149 'P waves', 

150 'body waves', 

151 'surface waves', 

152 'mantle waves', 

153 'combined', 

154 'unknown'] 

155 

156 

157class AmplitudeUnit(StringChoice): 

158 choices = ['m', 's', 'm/s', 'm/(s*s)', 'm*s', 'dimensionless', 'other'] 

159 

160 

161class EventDescriptionType(StringChoice): 

162 choices = [ 

163 'felt report', 

164 'Flinn-Engdahl region', 

165 'local time', 

166 'tectonic summary', 

167 'nearest cities', 

168 'earthquake name', 

169 'region name'] 

170 

171 

172class MomentTensorCategory(StringChoice): 

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

174 

175 

176class EventTypeCertainty(StringChoice): 

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

178 

179 

180class SourceTimeFunctionType(StringChoice): 

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

182 

183 

184class PickPolarity(StringChoice): 

185 choices = list(polarity_choices.keys()) 

186 

187 

188class AgencyID(String): 

189 pass 

190 

191 

192class Author(Unicode): 

193 pass 

194 

195 

196class Version(String): 

197 pass 

198 

199 

200class Phase(Object): 

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

202 

203 

204class GroundTruthLevel(String): 

205 pass 

206 

207 

208class AnonymousNetworkCode(String): 

209 pass 

210 

211 

212class AnonymousStationCode(String): 

213 pass 

214 

215 

216class AnonymousChannelCode(String): 

217 pass 

218 

219 

220class AnonymousLocationCode(String): 

221 pass 

222 

223 

224class Type(String): 

225 pass 

226 

227 

228class MagnitudeHint(String): 

229 pass 

230 

231 

232class Region(Unicode): 

233 pass 

234 

235 

236class RealQuantity(Object): 

237 value = Float.T() 

238 uncertainty = Float.T(optional=True) 

239 lower_uncertainty = Float.T(optional=True) 

240 upper_uncertainty = Float.T(optional=True) 

241 confidence_level = Float.T(optional=True) 

242 

243 

244class IntegerQuantity(Object): 

245 value = Int.T() 

246 uncertainty = Int.T(optional=True) 

247 lower_uncertainty = Int.T(optional=True) 

248 upper_uncertainty = Int.T(optional=True) 

249 confidence_level = Float.T(optional=True) 

250 

251 

252class ConfidenceEllipsoid(Object): 

253 semi_major_axis_length = Float.T() 

254 semi_minor_axis_length = Float.T() 

255 semi_intermediate_axis_length = Float.T() 

256 major_axis_plunge = Float.T() 

257 major_axis_azimuth = Float.T() 

258 major_axis_rotation = Float.T() 

259 

260 

261class TimeQuantity(Object): 

262 value = Timestamp.T() 

263 uncertainty = Float.T(optional=True) 

264 lower_uncertainty = Float.T(optional=True) 

265 upper_uncertainty = Float.T(optional=True) 

266 confidence_level = Float.T(optional=True) 

267 

268 

269class TimeWindow(Object): 

270 begin = Float.T() 

271 end = Float.T() 

272 reference = Timestamp.T() 

273 

274 

275class ResourceReference(ResourceIdentifier): 

276 pass 

277 

278 

279class DataUsed(Object): 

280 wave_type = DataUsedWaveType.T() 

281 station_count = Int.T(optional=True) 

282 component_count = Int.T(optional=True) 

283 shortest_period = Float.T(optional=True) 

284 longest_period = Float.T(optional=True) 

285 

286 

287class EventDescription(Object): 

288 text = Unicode.T() 

289 type = EventDescriptionType.T(optional=True) 

290 

291 

292class SourceTimeFunction(Object): 

293 type = SourceTimeFunctionType.T() 

294 duration = Float.T() 

295 rise_time = Float.T(optional=True) 

296 decay_time = Float.T(optional=True) 

297 

298 

299class OriginQuality(Object): 

300 associated_phase_count = Int.T(optional=True) 

301 used_phase_count = Int.T(optional=True) 

302 associated_station_count = Int.T(optional=True) 

303 used_station_count = Int.T(optional=True) 

304 depth_phase_count = Int.T(optional=True) 

305 standard_error = Float.T(optional=True) 

306 azimuthal_gap = Float.T(optional=True) 

307 secondary_azimuthal_gap = Float.T(optional=True) 

308 ground_truth_level = GroundTruthLevel.T(optional=True) 

309 maximum_distance = Float.T(optional=True) 

310 minimum_distance = Float.T(optional=True) 

311 median_distance = Float.T(optional=True) 

312 

313 

314class Axis(Object): 

315 azimuth = RealQuantity.T() 

316 plunge = RealQuantity.T() 

317 length = RealQuantity.T() 

318 

319 

320class Tensor(Object): 

321 mrr = RealQuantity.T(xmltagname='Mrr') 

322 mtt = RealQuantity.T(xmltagname='Mtt') 

323 mpp = RealQuantity.T(xmltagname='Mpp') 

324 mrt = RealQuantity.T(xmltagname='Mrt') 

325 mrp = RealQuantity.T(xmltagname='Mrp') 

326 mtp = RealQuantity.T(xmltagname='Mtp') 

327 

328 

329class NodalPlane(Object): 

330 strike = RealQuantity.T() 

331 dip = RealQuantity.T() 

332 rake = RealQuantity.T() 

333 

334 

335class CompositeTime(Object): 

336 year = IntegerQuantity.T(optional=True) 

337 month = IntegerQuantity.T(optional=True) 

338 day = IntegerQuantity.T(optional=True) 

339 hour = IntegerQuantity.T(optional=True) 

340 minute = IntegerQuantity.T(optional=True) 

341 second = RealQuantity.T(optional=True) 

342 

343 

344class OriginUncertainty(Object): 

345 horizontal_uncertainty = Float.T(optional=True) 

346 min_horizontal_uncertainty = Float.T(optional=True) 

347 max_horizontal_uncertainty = Float.T(optional=True) 

348 azimuth_max_horizontal_uncertainty = Float.T(optional=True) 

349 confidence_ellipsoid = ConfidenceEllipsoid.T(optional=True) 

350 preferred_description = OriginUncertaintyDescription.T(optional=True) 

351 confidence_level = Float.T(optional=True) 

352 

353 

354class ResourceReferenceOptional(Union): 

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

356 

357 

358class CreationInfo(Object): 

359 agency_id = AgencyID.T(optional=True, xmltagname='agencyID') 

360 agency_uri = ResourceReference.T(optional=True, xmltagname='agencyURI') 

361 author = Author.T(optional=True) 

362 author_uri = ResourceReference.T(optional=True, xmltagname='authorURI') 

363 creation_time = Timestamp.T(optional=True) 

364 version = Version.T(optional=True) 

365 

366 

367class StationMagnitudeContribution(Object): 

368 station_magnitude_id = ResourceReference.T(xmltagname='stationMagnitudeID') 

369 residual = Float.T(optional=True) 

370 weight = Float.T(optional=True) 

371 

372 

373class PrincipalAxes(Object): 

374 t_axis = Axis.T() 

375 p_axis = Axis.T() 

376 n_axis = Axis.T(optional=True) 

377 

378 

379class NodalPlanes(Object): 

380 preferred_plane = Int.T(optional=True, xmlstyle='attribute') 

381 nodal_plane1 = NodalPlane.T(optional=True) 

382 nodal_plane2 = NodalPlane.T(optional=True) 

383 

384 

385class WaveformStreamID(Object): 

386 value = ResourceReferenceOptional.T(xmlstyle='content') 

387 network_code = AnonymousNetworkCode.T(xmlstyle='attribute') 

388 station_code = AnonymousStationCode.T(xmlstyle='attribute') 

389 channel_code = AnonymousChannelCode.T(optional=True, xmlstyle='attribute') 

390 location_code = AnonymousLocationCode.T( 

391 optional=True, xmlstyle='attribute') 

392 

393 @property 

394 def nslc_id(self): 

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

396 self.channel_code) 

397 

398 

399class Comment(Object): 

400 id = ResourceReference.T(optional=True, xmlstyle='attribute') 

401 text = Unicode.T() 

402 creation_info = CreationInfo.T(optional=True) 

403 

404 

405class MomentTensor(Object): 

406 public_id = ResourceReference.T( 

407 xmlstyle='attribute', xmltagname='publicID') 

408 data_used_list = List.T(DataUsed.T()) 

409 comment_list = List.T(Comment.T()) 

410 derived_origin_id = ResourceReference.T( 

411 optional=True, xmltagname='derivedOriginID') 

412 moment_magnitude_id = ResourceReference.T( 

413 optional=True, xmltagname='momentMagnitudeID') 

414 scalar_moment = RealQuantity.T(optional=True) 

415 tensor = Tensor.T(optional=True) 

416 variance = Float.T(optional=True) 

417 variance_reduction = Float.T(optional=True) 

418 double_couple = Float.T(optional=True) 

419 clvd = Float.T(optional=True) 

420 iso = Float.T(optional=True) 

421 greens_function_id = ResourceReference.T( 

422 optional=True, xmltagname='greensFunctionID') 

423 filter_id = ResourceReference.T(optional=True, xmltagname='filterID') 

424 source_time_function = SourceTimeFunction.T(optional=True) 

425 method_id = ResourceReference.T(optional=True, xmltagname='methodID') 

426 category = MomentTensorCategory.T(optional=True) 

427 inversion_type = MTInversionType.T(optional=True) 

428 creation_info = CreationInfo.T(optional=True) 

429 

430 def pyrocko_moment_tensor(self): 

431 mrr = self.tensor.mrr.value 

432 mtt = self.tensor.mtt.value 

433 mpp = self.tensor.mpp.value 

434 mrt = self.tensor.mrt.value 

435 mrp = self.tensor.mrp.value 

436 mtp = self.tensor.mtp.value 

437 mt = moment_tensor.MomentTensor(m_up_south_east=num.matrix([ 

438 [mrr, mrt, mrp], [mrt, mtt, mtp], [mrp, mtp, mpp]])) 

439 

440 return mt 

441 

442 

443class Amplitude(Object): 

444 public_id = ResourceReference.T( 

445 xmlstyle='attribute', xmltagname='publicID') 

446 comment_list = List.T(Comment.T()) 

447 generic_amplitude = RealQuantity.T() 

448 type = Type.T(optional=True) 

449 category = AmplitudeCategory.T(optional=True) 

450 unit = AmplitudeUnit.T(optional=True) 

451 method_id = ResourceReference.T(optional=True, xmltagname='methodID') 

452 period = RealQuantity.T(optional=True) 

453 snr = Float.T(optional=True) 

454 time_window = TimeWindow.T(optional=True) 

455 pick_id = ResourceReference.T(optional=True, xmltagname='pickID') 

456 waveform_id = WaveformStreamID.T(optional=True, xmltagname='waveformID') 

457 filter_id = ResourceReference.T(optional=True, xmltagname='filterID') 

458 scaling_time = TimeQuantity.T(optional=True) 

459 magnitude_hint = MagnitudeHint.T(optional=True) 

460 evaluation_mode = EvaluationMode.T(optional=True) 

461 evaluation_status = EvaluationStatus.T(optional=True) 

462 creation_info = CreationInfo.T(optional=True) 

463 

464 

465class Magnitude(Object): 

466 public_id = ResourceReference.T( 

467 xmlstyle='attribute', xmltagname='publicID') 

468 comment_list = List.T(Comment.T()) 

469 station_magnitude_contribution_list = List.T( 

470 StationMagnitudeContribution.T()) 

471 mag = RealQuantity.T() 

472 type = Type.T(optional=True) 

473 origin_id = ResourceReference.T(optional=True, xmltagname='originID') 

474 method_id = ResourceReference.T(optional=True, xmltagname='methodID') 

475 station_count = Int.T(optional=True) 

476 azimuthal_gap = Float.T(optional=True) 

477 evaluation_mode = EvaluationMode.T(optional=True) 

478 evaluation_status = EvaluationStatus.T(optional=True) 

479 creation_info = CreationInfo.T(optional=True) 

480 

481 

482class StationMagnitude(Object): 

483 public_id = ResourceReference.T( 

484 xmlstyle='attribute', xmltagname='publicID') 

485 comment_list = List.T(Comment.T()) 

486 origin_id = ResourceReference.T(optional=True, xmltagname='originID') 

487 mag = RealQuantity.T() 

488 type = Type.T(optional=True) 

489 amplitude_id = ResourceReference.T(optional=True, xmltagname='amplitudeID') 

490 method_id = ResourceReference.T(optional=True, xmltagname='methodID') 

491 waveform_id = WaveformStreamID.T(optional=True, xmltagname='waveformID') 

492 creation_info = CreationInfo.T(optional=True) 

493 

494 

495class Arrival(Object): 

496 public_id = ResourceReference.T( 

497 xmlstyle='attribute', xmltagname='publicID') 

498 comment_list = List.T(Comment.T()) 

499 pick_id = ResourceReference.T(xmltagname='pickID') 

500 phase = Phase.T() 

501 time_correction = Float.T(optional=True) 

502 azimuth = Float.T(optional=True) 

503 distance = Float.T(optional=True) 

504 takeoff_angle = RealQuantity.T(optional=True) 

505 time_residual = Float.T(optional=True) 

506 horizontal_slowness_residual = Float.T(optional=True) 

507 backazimuth_residual = Float.T(optional=True) 

508 time_weight = Float.T(optional=True) 

509 time_used = Int.T(optional=True) 

510 horizontal_slowness_weight = Float.T(optional=True) 

511 backazimuth_weight = Float.T(optional=True) 

512 earth_model_id = ResourceReference.T( 

513 optional=True, xmltagname='earthModelID') 

514 creation_info = CreationInfo.T(optional=True) 

515 

516 

517class Pick(Object): 

518 public_id = ResourceReference.T( 

519 xmlstyle='attribute', xmltagname='publicID') 

520 comment_list = List.T(Comment.T()) 

521 time = TimeQuantity.T() 

522 waveform_id = WaveformStreamID.T(xmltagname='waveformID') 

523 filter_id = ResourceReference.T(optional=True, xmltagname='filterID') 

524 method_id = ResourceReference.T(optional=True, xmltagname='methodID') 

525 horizontal_slowness = RealQuantity.T(optional=True) 

526 backazimuth = RealQuantity.T(optional=True) 

527 slowness_method_id = ResourceReference.T( 

528 optional=True, xmltagname='slownessMethodID') 

529 onset = PickOnset.T(optional=True) 

530 phase_hint = Phase.T(optional=True) 

531 polarity = PickPolarity.T(optional=True) 

532 evaluation_mode = EvaluationMode.T(optional=True) 

533 evaluation_status = EvaluationStatus.T(optional=True) 

534 creation_info = CreationInfo.T(optional=True) 

535 

536 @property 

537 def pyrocko_polarity(self): 

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

539 

540 def get_pyrocko_phase_marker(self, event=None): 

541 if not self.phase_hint: 

542 logger.warn('Pick %s: phase_hint undefined' % self.public_id) 

543 phasename = 'undefined' 

544 else: 

545 phasename = self.phase_hint.value 

546 

547 return marker.PhaseMarker( 

548 event=event, nslc_ids=[self.waveform_id.nslc_id], 

549 tmin=self.time.value, tmax=self.time.value, 

550 phasename=phasename, 

551 polarity=self.pyrocko_polarity, 

552 automatic=self.evaluation_mode) 

553 

554 

555class FocalMechanism(Object): 

556 public_id = ResourceReference.T( 

557 xmlstyle='attribute', xmltagname='publicID') 

558 waveform_id_list = List.T(WaveformStreamID.T(xmltagname='waveformID')) 

559 comment_list = List.T(Comment.T()) 

560 moment_tensor_list = List.T(MomentTensor.T()) 

561 triggering_origin_id = ResourceReference.T( 

562 optional=True, xmltagname='triggeringOriginID') 

563 nodal_planes = NodalPlanes.T(optional=True) 

564 principal_axes = PrincipalAxes.T(optional=True) 

565 azimuthal_gap = Float.T(optional=True) 

566 station_polarity_count = Int.T(optional=True) 

567 misfit = Float.T(optional=True) 

568 station_distribution_ratio = Float.T(optional=True) 

569 method_id = ResourceReference.T(optional=True, xmltagname='methodID') 

570 evaluation_mode = EvaluationMode.T(optional=True) 

571 evaluation_status = EvaluationStatus.T(optional=True) 

572 creation_info = CreationInfo.T(optional=True) 

573 

574 

575class Origin(Object): 

576 public_id = ResourceReference.T( 

577 xmlstyle='attribute', xmltagname='publicID') 

578 composite_time_list = List.T(CompositeTime.T()) 

579 comment_list = List.T(Comment.T()) 

580 origin_uncertainty_list = List.T(OriginUncertainty.T()) 

581 arrival_list = List.T(Arrival.T()) 

582 time = TimeQuantity.T() 

583 longitude = RealQuantity.T() 

584 latitude = RealQuantity.T() 

585 depth = RealQuantity.T(optional=True) 

586 depth_type = OriginDepthType.T(optional=True) 

587 time_fixed = Bool.T(optional=True) 

588 epicenter_fixed = Bool.T(optional=True) 

589 reference_system_id = ResourceReference.T( 

590 optional=True, xmltagname='referenceSystemID') 

591 method_id = ResourceReference.T(optional=True, xmltagname='methodID') 

592 earth_model_id = ResourceReference.T( 

593 optional=True, xmltagname='earthModelID') 

594 quality = OriginQuality.T(optional=True) 

595 type = OriginType.T(optional=True) 

596 region = Region.T(optional=True) 

597 evaluation_mode = EvaluationMode.T(optional=True) 

598 evaluation_status = EvaluationStatus.T(optional=True) 

599 creation_info = CreationInfo.T(optional=True) 

600 

601 def position_values(self): 

602 lat = self.latitude.value 

603 lon = self.longitude.value 

604 if not self.depth: 

605 logger.warn( 

606 'Origin %s: Depth is undefined. Set to depth=0.' % 

607 self.public_id) 

608 depth = 0. 

609 else: 

610 depth = self.depth.value 

611 

612 return lat, lon, depth 

613 

614 def get_pyrocko_event(self): 

615 lat, lon, depth = self.position_values() 

616 otime = self.time.value 

617 if self.creation_info: 

618 cat = self.creation_info.agency_id 

619 else: 

620 cat = None 

621 

622 return event.Event( 

623 name=self.public_id, 

624 lat=lat, 

625 lon=lon, 

626 time=otime, 

627 depth=depth, 

628 catalog=cat, 

629 region=self.region) 

630 

631 

632class Event(Object): 

633 public_id = ResourceReference.T( 

634 xmlstyle='attribute', xmltagname='publicID') 

635 description_list = List.T(EventDescription.T()) 

636 comment_list = List.T(Comment.T()) 

637 focal_mechanism_list = List.T(FocalMechanism.T()) 

638 amplitude_list = List.T(Amplitude.T()) 

639 magnitude_list = List.T(Magnitude.T()) 

640 station_magnitude_list = List.T(StationMagnitude.T()) 

641 origin_list = List.T(Origin.T()) 

642 pick_list = List.T(Pick.T()) 

643 preferred_origin_id = ResourceReference.T( 

644 optional=True, xmltagname='preferredOriginID') 

645 preferred_magnitude_id = ResourceReference.T( 

646 optional=True, xmltagname='preferredMagnitudeID') 

647 preferred_focal_mechanism_id = ResourceReference.T( 

648 optional=True, xmltagname='preferredFocalMechanismID') 

649 type = EventType.T( 

650 optional=True) 

651 type_certainty = EventTypeCertainty.T( 

652 optional=True) 

653 creation_info = CreationInfo.T( 

654 optional=True) 

655 region = Region.T( 

656 optional=True) 

657 

658 def describe(self): 

659 return '''%s: 

660 origins: %i %s 

661 magnitudes: %i %s 

662 focal_machanisms: %i %s 

663 picks: %i 

664 amplitudes: %i 

665 station_magnitudes: %i''' % ( 

666 self.public_id, 

667 len(self.origin_list), 

668 '@' if self.preferred_origin_id else '-', 

669 len(self.magnitude_list), 

670 '@' if self.preferred_magnitude_id else '-', 

671 len(self.focal_mechanism_list), 

672 '@' if self.preferred_focal_mechanism_id else '-', 

673 len(self.pick_list), 

674 len(self.amplitude_list), 

675 len(self.station_magnitude_list)) 

676 

677 def get_pyrocko_phase_markers(self): 

678 event = self.get_pyrocko_event() 

679 return [ 

680 p.get_pyrocko_phase_marker(event=event) for p in self.pick_list] 

681 

682 def get_pyrocko_event(self): 

683 ''' 

684 Convert into Pyrocko event object. 

685 

686 Uses *preferred* origin, magnitude, and moment tensor. If no preferred 

687 item is specified, it picks the first from the list and emits a 

688 warning. 

689 ''' 

690 

691 origin = self.preferred_origin 

692 if not origin and self.origin_list: 

693 origin = self.origin_list[0] 

694 if len(self.origin_list) > 1: 

695 logger.warn( 

696 'Event %s: No preferred origin set, ' 

697 'more than one available, using first' % self.public_id) 

698 

699 if not origin: 

700 raise QuakeMLError( 

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

702 

703 ev = origin.get_pyrocko_event() 

704 

705 foc_mech = self.preferred_focal_mechanism 

706 if not foc_mech and self.focal_mechanism_list: 

707 foc_mech = self.focal_mechanism_list[0] 

708 if len(self.focal_mechanism_list) > 1: 

709 logger.warn( 

710 'Event %s: No preferred focal mechanism set, ' 

711 'more than one available, using first' % ev.name) 

712 

713 if foc_mech and foc_mech.moment_tensor_list: 

714 ev.moment_tensor = \ 

715 foc_mech.moment_tensor_list[0].pyrocko_moment_tensor() 

716 

717 if len(foc_mech.moment_tensor_list) > 1: 

718 logger.warn( 

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

720 

721 mag = None 

722 pref_mag = self.preferred_magnitude 

723 if pref_mag: 

724 mag = pref_mag 

725 elif self.magnitude_list: 

726 mag = self.magnitude_list[0] 

727 if len(self.magnitude_list) > 1: 

728 logger.warn( 

729 'Event %s: No preferred magnitude set, ' 

730 'more than one available, using first' % ev.name) 

731 

732 if mag: 

733 ev.magnitude = mag.mag.value 

734 ev.magnitude_type = mag.type 

735 

736 ev.region = self.get_effective_region() 

737 

738 return ev 

739 

740 def get_effective_region(self): 

741 if self.region: 

742 return self.region 

743 

744 for desc in self.description_list: 

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

746 return desc.text 

747 

748 return None 

749 

750 @property 

751 def preferred_origin(self): 

752 return one_element_or_none( 

753 [x for x in self.origin_list 

754 if x.public_id == self.preferred_origin_id]) 

755 

756 @property 

757 def preferred_magnitude(self): 

758 return one_element_or_none( 

759 [x for x in self.magnitude_list 

760 if x.public_id == self.preferred_magnitude_id]) 

761 

762 @property 

763 def preferred_focal_mechanism(self): 

764 return one_element_or_none( 

765 [x for x in self.focal_mechanism_list 

766 if x.public_id == self.preferred_focal_mechanism_id]) 

767 

768 

769class EventParameters(Object): 

770 public_id = ResourceReference.T( 

771 xmlstyle='attribute', xmltagname='publicID') 

772 comment_list = List.T(Comment.T()) 

773 event_list = List.T(Event.T(xmltagname='event')) 

774 description = Unicode.T(optional=True) 

775 creation_info = CreationInfo.T(optional=True) 

776 

777 

778class QuakeML(Object): 

779 ''' 

780 QuakeML data container. 

781 ''' 

782 xmltagname = 'quakeml' 

783 xmlns = 'http://quakeml.org/xmlns/quakeml/1.2' 

784 guessable_xmlns = [xmlns, guts_xmlns] 

785 

786 event_parameters = EventParameters.T(optional=True) 

787 

788 def get_events(self): 

789 return self.event_parameters.event_list 

790 

791 def get_pyrocko_events(self): 

792 ''' 

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

794 

795 :rtype: 

796 List of :py:class:`pyrocko.model.event.Event` objects. 

797 ''' 

798 events = [] 

799 for e in self.event_parameters.event_list: 

800 events.append(e.get_pyrocko_event()) 

801 

802 return events 

803 

804 def get_pyrocko_phase_markers(self): 

805 ''' 

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

807 

808 :rtype: 

809 List of :py:class:`pyrocko.gui.marker.PhaseMarker` objects. 

810 ''' 

811 markers = [] 

812 for e in self.event_parameters.event_list: 

813 markers.extend(e.get_pyrocko_phase_markers()) 

814 

815 return markers 

816 

817 @classmethod 

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

819 ''' 

820 Load QuakeML data from stream, file or string. 

821 

822 :param stream: 

823 Stream open for reading in binary mode. 

824 :type stream: 

825 file-like object, optional 

826 

827 :param filename: 

828 Path to file to be opened for reading. 

829 :type filename: 

830 str, optional 

831 

832 :param string: 

833 String with QuakeML data to be deserialized. 

834 :type string: 

835 str, optional 

836 

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

838 exclusive. 

839 

840 :returns: 

841 Parsed QuakeML data structure. 

842 :rtype: 

843 :py:class:`QuakeML` object 

844 

845 ''' 

846 

847 return super(QuakeML, cls).load_xml( 

848 stream=stream, 

849 filename=filename, 

850 string=string, 

851 ns_hints=[ 

852 'http://quakeml.org/xmlns/quakeml/1.2', 

853 'http://quakeml.org/xmlns/bed/1.2'], 

854 ns_ignore=True)