1# http://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

4# ---|P------/S----------~Lg---------- 

5from __future__ import absolute_import 

6 

7import calendar 

8import math 

9import time 

10import copy 

11import logging 

12import sys 

13 

14import numpy as num 

15 

16from pyrocko import util, plot, model, trace 

17from pyrocko.util import TableWriter, TableReader, gmtime_x, mystrftime 

18 

19 

20logger = logging.getLogger('pyrocko.gui.marker') 

21 

22 

23if sys.version_info[0] >= 3: 

24 polarity_symbols = {1: u'\u2191', -1: u'\u2193', None: u'', 0: u'\u2195'} 

25else: 

26 polarity_symbols = {1: '+', -1: '-', None: '', 0: '0'} 

27 

28 

29def str_to_float_or_none(s): 

30 if s == 'None': 

31 return None 

32 return float(s) 

33 

34 

35def str_to_str_or_none(s): 

36 if s == 'None': 

37 return None 

38 return s 

39 

40 

41def str_to_int_or_none(s): 

42 if s == 'None': 

43 return None 

44 return int(s) 

45 

46 

47def str_to_bool(s): 

48 return s.lower() in ('true', 't', '1') 

49 

50 

51def myctime(timestamp): 

52 tt, ms = gmtime_x(timestamp) 

53 return mystrftime(None, tt, ms) 

54 

55 

56g_color_b = [plot.color(x) for x in ( 

57 'scarletred1', 'scarletred2', 'scarletred3', 

58 'chameleon1', 'chameleon2', 'chameleon3', 

59 'skyblue1', 'skyblue2', 'skyblue3', 

60 'orange1', 'orange2', 'orange3', 

61 'plum1', 'plum2', 'plum3', 

62 'chocolate1', 'chocolate2', 'chocolate3')] 

63 

64 

65class MarkerParseError(Exception): 

66 pass 

67 

68 

69class MarkerOneNSLCRequired(Exception): 

70 pass 

71 

72 

73class Marker(object): 

74 ''' 

75 General purpose marker GUI element and base class for 

76 :py:class:`EventMarker` and :py:class:`PhaseMarker`. 

77 

78 :param nslc_ids: list of (network, station, location, channel) tuples 

79 (may contain wildcards) 

80 :param tmin: start time 

81 :param tmax: end time 

82 :param kind: (optional) integer to distinguish groups of markers 

83 (color-coded) 

84 ''' 

85 

86 @staticmethod 

87 def from_string(line): 

88 

89 def fail(): 

90 raise MarkerParseError( 

91 'Unable to create marker from string: "%s"' % line) 

92 

93 def parsedate(ymd, hms, sfs): 

94 return util.to_time_str(calendar.timegm( 

95 time.strptime(ymd+' '+hms, '%Y-%m-%d %H:%M:%S'))) + float(sfs) 

96 

97 try: 

98 toks = line.split() 

99 if len(toks) in (4, 5): 

100 tmin = parsedate(*toks[:3]) 

101 tmax = tmin 

102 

103 elif len(toks) in (8, 9): 

104 tmin = parsedate(*toks[:3]) 

105 tmax = parsedate(*toks[3:6]) 

106 

107 else: 

108 fail() 

109 

110 if len(toks) in (5, 9): 

111 kind = int(toks[-2]) 

112 else: 

113 kind = int(toks[-1]) 

114 

115 nslc_ids = [] 

116 if len(toks) in (5, 9): 

117 for snslc in toks[-1].split(','): 

118 nslc = snslc.split('.') 

119 if len(nslc) != 4: 

120 fail() 

121 

122 nslc_ids.append(tuple(nslc)) 

123 

124 except MarkerParseError: 

125 fail() 

126 

127 return Marker(nslc_ids, tmin, tmax, kind=kind) 

128 

129 @staticmethod 

130 def save_markers(markers, fn, fdigits=3): 

131 ''' 

132 Static method to write marker objects to file. 

133 

134 :param markers: list of :py:class:`Marker` objects 

135 :param fn: filename as string 

136 :param fdigits: number of decimal digits to use for sub-second time 

137 strings (default 3) 

138 ''' 

139 f = open(fn, 'w') 

140 f.write('# Snuffler Markers File Version 0.2\n') 

141 writer = TableWriter(f) 

142 for marker in markers: 

143 a = marker.get_attributes(fdigits=fdigits) 

144 w = marker.get_attribute_widths(fdigits=fdigits) 

145 row = [] 

146 for x in a: 

147 if x is None or x == '': 

148 row.append('None') 

149 else: 

150 row.append(x) 

151 

152 writer.writerow(row, w) 

153 

154 f.close() 

155 

156 @staticmethod 

157 def load_markers(fn): 

158 ''' 

159 Static method to load markers from file. 

160 

161 :param filename: filename as string 

162 :returns: list of :py:class:`Marker`, :py:class:`EventMarker` or 

163 :py:class:`PhaseMarker` objects 

164 ''' 

165 markers = [] 

166 with open(fn, 'r') as f: 

167 line = f.readline() 

168 if not line.startswith('# Snuffler Markers File Version'): 

169 f.seek(0) 

170 for iline, line in enumerate(f): 

171 line = str(line.decode('ascii')) 

172 sline = line.strip() 

173 if not sline or sline.startswith('#'): 

174 continue 

175 try: 

176 m = Marker.from_string(sline) 

177 markers.append(m) 

178 

179 except MarkerParseError: 

180 logger.warning( 

181 'Invalid marker definition in line %i of file "%s"' 

182 % (iline+1, fn)) 

183 

184 f.close() 

185 

186 elif line.startswith('# Snuffler Markers File Version 0.2'): 

187 reader = TableReader(f) 

188 while not reader.eof: 

189 row = reader.readrow() 

190 if not row: 

191 continue 

192 if row[0] == 'event:': 

193 marker = EventMarker.from_attributes(row) 

194 elif row[0] == 'phase:': 

195 marker = PhaseMarker.from_attributes(row) 

196 else: 

197 marker = Marker.from_attributes(row) 

198 

199 markers.append(marker) 

200 else: 

201 logger.warning('Unsupported Markers File Version') 

202 

203 return markers 

204 

205 def __init__(self, nslc_ids, tmin, tmax, kind=0): 

206 self.set(nslc_ids, tmin, tmax) 

207 self.alerted = False 

208 self.selected = False 

209 self.kind = kind 

210 self.active = False 

211 

212 def set(self, nslc_ids, tmin, tmax): 

213 ''' 

214 Set ``nslc_ids``, start time and end time of :py:class:`Marker`. 

215 

216 :param nslc_ids: list or set of (network, station, location, channel) 

217 tuples 

218 :param tmin: start time 

219 :param tmax: end time 

220 ''' 

221 self.nslc_ids = nslc_ids 

222 self.tmin = util.to_time_float(tmin) 

223 self.tmax = util.to_time_float(tmax) 

224 

225 def set_kind(self, kind): 

226 ''' 

227 Set kind of :py:class:`Marker`. 

228 

229 :param kind: (optional) integer to distinguish groups of markers 

230 (color-coded) 

231 ''' 

232 self.kind = kind 

233 

234 def get_tmin(self): 

235 ''' 

236 Get *start time* of :py:class:`Marker`. 

237 ''' 

238 return self.tmin 

239 

240 def get_tmax(self): 

241 ''' 

242 Get *end time* of :py:class:`Marker`. 

243 ''' 

244 return self.tmax 

245 

246 def get_nslc_ids(self): 

247 ''' 

248 Get marker's network-station-location-channel pattern. 

249 

250 :returns: list or set of (network, station, location, channel) tuples 

251 

252 The network, station, location, or channel strings may contain wildcard 

253 expressions. 

254 ''' 

255 return self.nslc_ids 

256 

257 def is_alerted(self): 

258 return self.alerted 

259 

260 def is_selected(self): 

261 return self.selected 

262 

263 def set_alerted(self, state): 

264 self.alerted = state 

265 

266 def match_nsl(self, nsl): 

267 ''' 

268 See documentation of :py:func:`pyrocko.util.match_nslc`. 

269 ''' 

270 patterns = ['.'.join(x[:3]) for x in self.nslc_ids] 

271 return util.match_nslc(patterns, nsl) 

272 

273 def match_nslc(self, nslc): 

274 ''' 

275 See documentation of :py:func:`pyrocko.util.match_nslc`. 

276 ''' 

277 patterns = ['.'.join(x) for x in self.nslc_ids] 

278 return util.match_nslc(patterns, nslc) 

279 

280 def one_nslc(self): 

281 ''' 

282 If one *nslc_id* defines this marker return this id. 

283 If more than one *nslc_id* is defined in the :py:class:`Marker`s 

284 *nslc_ids* raise :py:exc:`MarkerOneNSLCRequired`. 

285 ''' 

286 if len(self.nslc_ids) != 1: 

287 raise MarkerOneNSLCRequired() 

288 

289 return list(self.nslc_ids)[0] 

290 

291 def hoover_message(self): 

292 return '' 

293 

294 def copy(self): 

295 ''' 

296 Get a copy of this marker. 

297 ''' 

298 return copy.deepcopy(self) 

299 

300 def __str__(self): 

301 traces = ','.join(['.'.join(nslc_id) for nslc_id in self.nslc_ids]) 

302 st = myctime 

303 if self.tmin == self.tmax: 

304 return '%s %i %s' % (st(self.tmin), self.kind, traces) 

305 else: 

306 return '%s %s %g %i %s' % ( 

307 st(self.tmin), st(self.tmax), self.tmax-self.tmin, self.kind, 

308 traces) 

309 

310 def get_attributes(self, fdigits=3): 

311 traces = ','.join(['.'.join(nslc_id) for nslc_id in self.nslc_ids]) 

312 

313 def st(t): 

314 return util.time_to_str( 

315 t, format='%Y-%m-%d %H:%M:%S.'+'%iFRAC' % fdigits) 

316 

317 vals = [] 

318 vals.extend(st(self.tmin).split()) 

319 if self.tmin != self.tmax: 

320 vals.extend(st(self.tmax).split()) 

321 vals.append(self.tmax-self.tmin) 

322 

323 vals.append(self.kind) 

324 vals.append(traces) 

325 return vals 

326 

327 def get_attribute_widths(self, fdigits=3): 

328 ws = [10, 9+fdigits] 

329 if self.tmin != self.tmax: 

330 ws.extend([10, 9+fdigits, 12]) 

331 ws.extend([2, 15]) 

332 return ws 

333 

334 @staticmethod 

335 def parse_attributes(vals): 

336 tmin = util.str_to_time(vals[0] + ' ' + vals[1]) 

337 i = 2 

338 tmax = tmin 

339 if len(vals) == 7: 

340 tmax = util.str_to_time(vals[2] + ' ' + vals[3]) 

341 i = 5 

342 

343 kind = int(vals[i]) 

344 traces = vals[i+1] 

345 if traces == 'None': 

346 nslc_ids = [] 

347 else: 

348 nslc_ids = tuple( 

349 [tuple(nslc_id.split('.')) for nslc_id in traces.split(',')]) 

350 

351 return nslc_ids, tmin, tmax, kind 

352 

353 @staticmethod 

354 def from_attributes(vals): 

355 return Marker(*Marker.parse_attributes(vals)) 

356 

357 def select_color(self, colorlist): 

358 

359 def cl(x): 

360 return colorlist[(self.kind*3+x) % len(colorlist)] 

361 

362 if self.selected: 

363 return cl(1) 

364 

365 if self.alerted: 

366 return cl(1) 

367 

368 return cl(2) 

369 

370 def draw( 

371 self, p, time_projection, y_projection, 

372 draw_line=True, 

373 draw_triangle=False, 

374 **kwargs): 

375 

376 from .qt_compat import qc, qg 

377 from . import util as gui_util 

378 

379 color = self.select_color(g_color_b) 

380 pen = qg.QPen(qg.QColor(*color)) 

381 pen.setWidth(2) 

382 p.setPen(pen) 

383 

384 umin = time_projection(self.tmin) 

385 umax = time_projection(self.tmax) 

386 v0, v1 = y_projection.get_out_range() 

387 line = qc.QLineF(umin-1, v0, umax+1, v0) 

388 p.drawLine(line) 

389 

390 if self.selected or self.alerted or not self.nslc_ids: 

391 linepen = qg.QPen(pen) 

392 if self.selected or self.alerted: 

393 linepen.setStyle(qc.Qt.CustomDashLine) 

394 pat = [5., 3.] 

395 linepen.setDashPattern(pat) 

396 if self.alerted and not self.selected: 

397 linepen.setColor(qg.QColor(150, 150, 150)) 

398 

399 s = 9. 

400 utriangle = gui_util.make_QPolygonF( 

401 [-0.577*s, 0., 0.577*s], [0., 1.*s, 0.]) 

402 ltriangle = gui_util.make_QPolygonF( 

403 [-0.577*s, 0., 0.577*s], [0., -1.*s, 0.]) 

404 

405 def drawline(t): 

406 u = time_projection(t) 

407 line = qc.QLineF(u, v0, u, v1) 

408 p.drawLine(line) 

409 

410 def drawtriangles(t): 

411 u = time_projection(t) 

412 t = qg.QPolygonF(utriangle) 

413 t.translate(u, v0) 

414 p.drawConvexPolygon(t) 

415 t = qg.QPolygonF(ltriangle) 

416 t.translate(u, v1) 

417 p.drawConvexPolygon(t) 

418 

419 if draw_line or self.selected or self.alerted: 

420 p.setPen(linepen) 

421 drawline(self.tmin) 

422 drawline(self.tmax) 

423 

424 if draw_triangle: 

425 pen.setStyle(qc.Qt.SolidLine) 

426 pen.setJoinStyle(qc.Qt.MiterJoin) 

427 pen.setWidth(2) 

428 p.setPen(pen) 

429 p.setBrush(qg.QColor(*color)) 

430 drawtriangles(self.tmin) 

431 

432 def draw_trace( 

433 self, viewer, p, tr, time_projection, track_projection, gain, 

434 outline_label=False): 

435 

436 from .qt_compat import qc, qg 

437 from . import util as gui_util 

438 

439 if self.nslc_ids and not self.match_nslc(tr.nslc_id): 

440 return 

441 

442 color = self.select_color(g_color_b) 

443 pen = qg.QPen(qg.QColor(*color)) 

444 pen.setWidth(2) 

445 p.setPen(pen) 

446 p.setBrush(qc.Qt.NoBrush) 

447 

448 def drawpoint(t, y): 

449 u = time_projection(t) 

450 v = track_projection(y) 

451 rect = qc.QRectF(u-2, v-2, 4, 4) 

452 p.drawRect(rect) 

453 

454 def drawline(t): 

455 u = time_projection(t) 

456 v0, v1 = track_projection.get_out_range() 

457 line = qc.QLineF(u, v0, u, v1) 

458 p.drawLine(line) 

459 

460 try: 

461 snippet = tr.chop( 

462 self.tmin, self.tmax, 

463 inplace=False, 

464 include_last=True, 

465 snap=(math.ceil, math.floor)) 

466 

467 vdata = track_projection(gain*snippet.get_ydata()) 

468 udata_min = float( 

469 time_projection(snippet.tmin)) 

470 udata_max = float( 

471 time_projection(snippet.tmin+snippet.deltat*(vdata.size-1))) 

472 udata = num.linspace(udata_min, udata_max, vdata.size) 

473 qpoints = gui_util.make_QPolygonF(udata, vdata) 

474 pen.setWidth(1) 

475 p.setPen(pen) 

476 p.drawPolyline(qpoints) 

477 pen.setWidth(2) 

478 p.setPen(pen) 

479 drawpoint(*tr(self.tmin, clip=True, snap=math.ceil)) 

480 drawpoint(*tr(self.tmax, clip=True, snap=math.floor)) 

481 

482 except trace.NoData: 

483 pass 

484 

485 color = self.select_color(g_color_b) 

486 pen = qg.QPen(qg.QColor(*color)) 

487 pen.setWidth(2) 

488 p.setPen(pen) 

489 

490 drawline(self.tmin) 

491 drawline(self.tmax) 

492 

493 label = self.get_label() 

494 if label: 

495 label_bg = qg.QBrush(qg.QColor(255, 255, 255)) 

496 

497 u = time_projection(self.tmin) 

498 v0, v1 = track_projection.get_out_range() 

499 if outline_label: 

500 du = -7 

501 else: 

502 du = -5 

503 gui_util.draw_label( 

504 p, u+du, v0, label, label_bg, 'TR', 

505 outline=outline_label) 

506 

507 if self.tmin == self.tmax: 

508 try: 

509 drawpoint(self.tmin, tr.interpolate(self.tmin)) 

510 

511 except IndexError: 

512 pass 

513 

514 def get_label(self): 

515 return None 

516 

517 def convert_to_phase_marker( 

518 self, 

519 event=None, 

520 phasename=None, 

521 polarity=None, 

522 automatic=None, 

523 incidence_angle=None, 

524 takeoff_angle=None): 

525 

526 if isinstance(self, PhaseMarker): 

527 return 

528 

529 self.__class__ = PhaseMarker 

530 self._event = event 

531 self._phasename = phasename 

532 self._polarity = polarity 

533 self._automatic = automatic 

534 self._incidence_angle = incidence_angle 

535 self._takeoff_angle = takeoff_angle 

536 if self._event: 

537 self._event_hash = event.get_hash() 

538 self._event_time = event.time 

539 else: 

540 self._event_hash = None 

541 self._event_time = None 

542 self.active = False 

543 

544 def convert_to_event_marker(self, lat=0., lon=0.): 

545 if isinstance(self, EventMarker): 

546 return 

547 

548 if isinstance(self, PhaseMarker): 

549 self.convert_to_marker() 

550 

551 self.__class__ = EventMarker 

552 self._event = model.Event(lat, lon, time=self.tmin, name='Event') 

553 self._event_hash = self._event.get_hash() 

554 self.active = False 

555 self.tmax = self.tmin 

556 self.nslc_ids = [] 

557 

558 

559class EventMarker(Marker): 

560 ''' 

561 GUI element representing a seismological event. 

562 

563 :param event: A :py:class:`pyrocko.model.Event` object containing meta 

564 information of a seismological event 

565 :param kind: (optional) integer to distinguish groups of markers 

566 :param event_hash: (optional) hash code of event (see: 

567 :py:meth:`pyrocko.model.Event.get_hash`) 

568 ''' 

569 

570 def __init__(self, event, kind=0, event_hash=None): 

571 Marker.__init__(self, [], event.time, event.time, kind) 

572 self._event = event 

573 self.active = False 

574 self._event_hash = event_hash 

575 

576 def get_event_hash(self): 

577 if self._event_hash is not None: 

578 return self._event_hash 

579 else: 

580 return self._event.get_hash() 

581 

582 def label(self): 

583 t = [] 

584 mag = self._event.magnitude 

585 if mag is not None: 

586 t.append('M%3.1f' % mag) 

587 

588 reg = self._event.region 

589 if reg is not None: 

590 t.append(reg) 

591 

592 nam = self._event.name 

593 if nam is not None: 

594 t.append(nam) 

595 

596 s = ' '.join(t) 

597 if not s: 

598 s = '(Event)' 

599 return s 

600 

601 def draw(self, p, time_projection, y_projection, with_label=False): 

602 Marker.draw( 

603 self, p, time_projection, y_projection, 

604 draw_line=False, 

605 draw_triangle=True) 

606 

607 if with_label: 

608 self.draw_label(p, time_projection, y_projection) 

609 

610 def draw_label(self, p, time_projection, y_projection): 

611 from .qt_compat import qg 

612 from . import util as gui_util 

613 

614 u = time_projection(self.tmin) 

615 v0, v1 = y_projection.get_out_range() 

616 label_bg = qg.QBrush(qg.QColor(255, 255, 255)) 

617 gui_util.draw_label( 

618 p, u, v0-10., self.label(), label_bg, 'CB', 

619 outline=self.active) 

620 

621 def get_event(self): 

622 ''' 

623 Return an instance of the :py:class:`pyrocko.model.Event` associated 

624 to this :py:class:`EventMarker` 

625 ''' 

626 return self._event 

627 

628 def draw_trace(self, viewer, p, tr, time_projection, track_projection, 

629 gain): 

630 pass 

631 

632 def hoover_message(self): 

633 ev = self.get_event() 

634 evs = [] 

635 for k in 'magnitude lat lon depth name region catalog'.split(): 

636 if ev.__dict__[k] is not None and ev.__dict__[k] != '': 

637 if k == 'depth': 

638 sv = '%g km' % (ev.depth * 0.001) 

639 else: 

640 sv = '%s' % ev.__dict__[k] 

641 evs.append('%s = %s' % (k, sv)) 

642 

643 return ', '.join(evs) 

644 

645 def get_attributes(self, fdigits=3): 

646 attributes = ['event:'] 

647 attributes.extend(Marker.get_attributes(self, fdigits=fdigits)) 

648 del attributes[-1] 

649 e = self._event 

650 attributes.extend([ 

651 e.get_hash(), e.lat, e.lon, e.depth, e.magnitude, e.catalog, 

652 e.name, e.region]) 

653 

654 return attributes 

655 

656 def get_attribute_widths(self, fdigits=3): 

657 ws = [6] 

658 ws.extend(Marker.get_attribute_widths(self, fdigits=fdigits)) 

659 del ws[-1] 

660 ws.extend([14, 12, 12, 12, 4, 5, 0, 0]) 

661 return ws 

662 

663 @staticmethod 

664 def from_attributes(vals): 

665 

666 nslc_ids, tmin, tmax, kind = Marker.parse_attributes( 

667 vals[1:] + ['None']) 

668 lat, lon, depth, magnitude = [ 

669 str_to_float_or_none(x) for x in vals[5:9]] 

670 catalog, name, region = [ 

671 str_to_str_or_none(x) for x in vals[9:]] 

672 e = model.Event( 

673 lat, lon, time=tmin, name=name, depth=depth, magnitude=magnitude, 

674 region=region, catalog=catalog) 

675 marker = EventMarker( 

676 e, kind, event_hash=str_to_str_or_none(vals[4])) 

677 return marker 

678 

679 

680class PhaseMarker(Marker): 

681 ''' 

682 A PhaseMarker is a GUI-element representing a seismological phase arrival 

683 

684 :param nslc_ids: list of (network, station, location, channel) tuples (may 

685 contain wildcards) 

686 :param tmin: start time 

687 :param tmax: end time 

688 :param kind: (optional) integer to distinguish groups of markers 

689 (color-coded) 

690 :param event: a :py:class:`pyrocko.model.Event` object containing meta 

691 information of a seismological event 

692 :param event_hash: (optional) hash code of event (see: 

693 :py:meth:`pyrocko.model.Event.get_hash`) 

694 :param event_time: (optional) time of the associated event 

695 :param phasename: (optional) name of the phase associated with the marker 

696 :param polarity: (optional) polarity of arriving phase 

697 :param automatic: (optional) 

698 :param incident_angle: (optional) incident angle of phase 

699 :param takeoff_angle: (optional) take off angle of phase 

700 ''' 

701 def __init__( 

702 self, nslc_ids, tmin, tmax, 

703 kind=0, 

704 event=None, 

705 event_hash=None, 

706 event_time=None, 

707 phasename=None, 

708 polarity=None, 

709 automatic=None, 

710 incidence_angle=None, 

711 takeoff_angle=None): 

712 

713 Marker.__init__(self, nslc_ids, tmin, tmax, kind) 

714 self._event = event 

715 self._event_hash = event_hash 

716 self._event_time = event_time 

717 self._phasename = phasename 

718 self._automatic = automatic 

719 self._incidence_angle = incidence_angle 

720 self._takeoff_angle = takeoff_angle 

721 

722 self.set_polarity(polarity) 

723 

724 def draw_trace(self, viewer, p, tr, time_projection, track_projection, 

725 gain): 

726 

727 Marker.draw_trace( 

728 self, viewer, p, tr, time_projection, track_projection, gain, 

729 outline_label=( 

730 self._event is not None and 

731 self._event == viewer.get_active_event())) 

732 

733 def get_label(self): 

734 t = [] 

735 if self._phasename is not None: 

736 t.append(self._phasename) 

737 if self._polarity is not None: 

738 t.append(self.get_polarity_symbol()) 

739 

740 if self._automatic: 

741 t.append('@') 

742 

743 return ''.join(t) 

744 

745 def get_event(self): 

746 ''' 

747 Return an instance of the :py:class:`pyrocko.model.Event` associated 

748 to this :py:class:`EventMarker` 

749 ''' 

750 return self._event 

751 

752 def get_event_hash(self): 

753 if self._event_hash is not None: 

754 return self._event_hash 

755 else: 

756 if self._event is None: 

757 return None 

758 else: 

759 return self._event.get_hash() 

760 

761 def get_event_time(self): 

762 if self._event is not None: 

763 return self._event.time 

764 else: 

765 return self._event_time 

766 

767 def set_event_hash(self, event_hash): 

768 self._event_hash = event_hash 

769 

770 def set_event(self, event): 

771 self._event = event 

772 if event is not None: 

773 self.set_event_hash(event.get_hash()) 

774 

775 def get_phasename(self): 

776 return self._phasename 

777 

778 def set_phasename(self, phasename): 

779 self._phasename = phasename 

780 

781 def set_polarity(self, polarity): 

782 if polarity not in [1, -1, 0, None]: 

783 raise ValueError('polarity has to be 1, -1, 0 or None') 

784 self._polarity = polarity 

785 

786 def get_polarity_symbol(self): 

787 return polarity_symbols.get(self._polarity, '') 

788 

789 def get_polarity(self): 

790 return self._polarity 

791 

792 def convert_to_marker(self): 

793 del self._event 

794 del self._event_hash 

795 del self._phasename 

796 del self._polarity 

797 del self._automatic 

798 del self._incidence_angle 

799 del self._takeoff_angle 

800 self.active = False 

801 self.__class__ = Marker 

802 

803 def hoover_message(self): 

804 toks = [] 

805 for k in 'incidence_angle takeoff_angle polarity'.split(): 

806 v = getattr(self, '_' + k) 

807 if v is not None: 

808 toks.append('%s = %s' % (k, v)) 

809 

810 return ', '.join(toks) 

811 

812 def get_attributes(self, fdigits=3): 

813 attributes = ['phase:'] 

814 attributes.extend(Marker.get_attributes(self, fdigits=fdigits)) 

815 

816 et = None, None 

817 if self._event: 

818 et = self._st(self._event.time, fdigits).split() 

819 elif self._event_time: 

820 et = self._st(self._event_time, fdigits).split() 

821 

822 attributes.extend([ 

823 self.get_event_hash(), et[0], et[1], self._phasename, 

824 self._polarity, self._automatic]) 

825 

826 return attributes 

827 

828 def _st(self, t, fdigits): 

829 return util.time_to_str( 

830 t, format='%Y-%m-%d %H:%M:%S.'+'%iFRAC' % fdigits) 

831 

832 def get_attribute_widths(self, fdigits=3): 

833 ws = [6] 

834 ws.extend(Marker.get_attribute_widths(self, fdigits=fdigits)) 

835 ws.extend([14, 12, 12, 8, 4, 5]) 

836 return ws 

837 

838 @staticmethod 

839 def from_attributes(vals): 

840 if len(vals) == 14: 

841 nbasicvals = 7 

842 else: 

843 nbasicvals = 4 

844 nslc_ids, tmin, tmax, kind = Marker.parse_attributes( 

845 vals[1:1+nbasicvals]) 

846 

847 i = 8 

848 if len(vals) == 14: 

849 i = 11 

850 

851 event_hash = str_to_str_or_none(vals[i-3]) 

852 event_sdate = str_to_str_or_none(vals[i-2]) 

853 event_stime = str_to_str_or_none(vals[i-1]) 

854 

855 if event_sdate is not None and event_stime is not None: 

856 event_time = util.str_to_time(event_sdate + ' ' + event_stime) 

857 else: 

858 event_time = None 

859 

860 phasename = str_to_str_or_none(vals[i]) 

861 polarity = str_to_int_or_none(vals[i+1]) 

862 automatic = str_to_bool(vals[i+2]) 

863 marker = PhaseMarker(nslc_ids, tmin, tmax, kind, event=None, 

864 event_hash=event_hash, event_time=event_time, 

865 phasename=phasename, polarity=polarity, 

866 automatic=automatic) 

867 return marker 

868 

869 

870def load_markers(filename): 

871 ''' 

872 Load markers from file. 

873 

874 :param filename: filename as string 

875 :returns: list of :py:class:`Marker` Objects 

876 ''' 

877 

878 return Marker.load_markers(filename) 

879 

880 

881def save_markers(markers, filename, fdigits=3): 

882 ''' 

883 Save markers to file. 

884 

885 :param markers: list of :py:class:`Marker` Objects 

886 :param filename: filename as string 

887 :param fdigits: number of decimal digits to use for sub-second time strings 

888 ''' 

889 

890 return Marker.save_markers(markers, filename, fdigits=fdigits) 

891 

892 

893def associate_phases_to_events(markers): 

894 ''' 

895 Reassociate phases to events after import from markers file. 

896 ''' 

897 

898 hash_to_events = {} 

899 time_to_events = {} 

900 for marker in markers: 

901 if isinstance(marker, EventMarker): 

902 ev = marker.get_event() 

903 hash_to_events[marker.get_event_hash()] = ev 

904 time_to_events[ev.time] = ev 

905 

906 for marker in markers: 

907 if isinstance(marker, PhaseMarker): 

908 h = marker.get_event_hash() 

909 t = marker.get_event_time() 

910 if marker.get_event() is None: 

911 if h is not None and h in hash_to_events: 

912 marker.set_event(hash_to_events[h]) 

913 marker.set_event_hash(None) 

914 elif t is not None and t in time_to_events: 

915 marker.set_event(time_to_events[t]) 

916 marker.set_event_hash(None)