class PlotGroup(Object): name = StringID.T( help='group name') section = StringID.T( optional=True, help='group\'s section path, e.g. results.waveforms') title = Unicode.T( optional=True, help='group\'s title') description = Unicode.T( optional=True, help='group description') formats = List.T( PlotFormat.T(), help='plot format') variant = StringID.T( help='variant of the group') feather_icon = String.T( default='bar-chart-2', help='Feather icon for the HTML report.') size_cm = Tuple.T(2, Float.T()) items = List.T(PlotItem.T()) attributes = Dict.T(StringID.T(), List.T(String.T())) def filename_image(self, item, format): return '%s.%s.%s.%s' % ( self.name, self.variant, item.name, format.extension)
class Station(BaseNode): '''This type represents a Station epoch. It is common to only have a single station epoch with the station's creation and termination dates as the epoch start and end dates.''' latitude = Latitude.T(xmltagname='Latitude') longitude = Longitude.T(xmltagname='Longitude') elevation = Distance.T(xmltagname='Elevation') site = Site.T(optional=True, xmltagname='Site') vault = Unicode.T(optional=True, xmltagname='Vault') geology = Unicode.T(optional=True, xmltagname='Geology') equipment_list = List.T(Equipment.T(xmltagname='Equipment')) operator_list = List.T(Operator.T(xmltagname='Operator')) creation_date = DummyAwareOptionalTimestamp.T(optional=True, xmltagname='CreationDate') termination_date = DummyAwareOptionalTimestamp.T( optional=True, xmltagname='TerminationDate') total_number_channels = Counter.T(optional=True, xmltagname='TotalNumberChannels') selected_number_channels = Counter.T(optional=True, xmltagname='SelectedNumberChannels') external_reference_list = List.T( ExternalReference.T(xmltagname='ExternalReference')) channel_list = List.T(Channel.T(xmltagname='Channel')) @property def position_values(self): lat = self.latitude.value lon = self.longitude.value elevation = value_or_none(self.elevation) return lat, lon, elevation
class Person(Object): '''Representation of a person's contact information. A person can belong to multiple agencies and have multiple email addresses and phone numbers.''' name_list = List.T(Unicode.T(xmltagname='Name')) agency_list = List.T(Unicode.T(xmltagname='Agency')) email_list = List.T(Email.T(xmltagname='Email')) phone_list = List.T(PhoneNumber.T(xmltagname='Phone'))
class PlotItem(Object): name = StringID.T() attributes = Dict.T( StringID.T(), List.T(String.T())) title = Unicode.T( optional=True, help='item\'s description') description = Unicode.T( optional=True, help='item\'s description')
class ReportConfig(HasPaths): report_base_path = Path.T(default='report') entries_sub_path = String.T(default='${event_name}/${problem_name}') title = Unicode.T(default=u'Grond Report', help='Title shown on report overview page.') description = Unicode.T( default=u'This interactive document aggregates earthquake source ' u'inversion results from optimisations performed with Grond.', help='Description shown on report overview page.') plot_config_collection = PlotConfigCollection.T( help='Configurations for plots to be included in the report.')
class Equipment(Object): resource_id = String.T(optional=True, xmlstyle='attribute') type = String.T(optional=True, xmltagname='Type') description = Unicode.T(optional=True, xmltagname='Description') manufacturer = Unicode.T(optional=True, xmltagname='Manufacturer') vendor = Unicode.T(optional=True, xmltagname='Vendor') model = Unicode.T(optional=True, xmltagname='Model') serial_number = String.T(optional=True, xmltagname='SerialNumber') installation_date = DummyAwareOptionalTimestamp.T( optional=True, xmltagname='InstallationDate') removal_date = DummyAwareOptionalTimestamp.T(optional=True, xmltagname='RemovalDate') calibration_date_list = List.T(Timestamp.T(xmltagname='CalibrationDate'))
class ReportConfig(HasPaths): report_base_path = Path.T(default='report') entries_sub_path = String.T(default='${event_name}/${problem_name}') title = Unicode.T(default=u'silvertine Report', help='Title shown on report overview page.') description = Unicode.T( default=u'This interactive document aggregates earthquake source ' u'inversion results from optimisations performed with silvertine.', help='Description shown on report overview page.') plot_config_collection = PlotConfigCollection.T( help='Configurations for plots to be included in the report.') make_archive = Bool.T( default=True, help='Set to `false` to prevent creation of compressed archive.')
class Units(Object): '''A type to document units. Corresponds to SEED blockette 34.''' def __init__(self, name=None, **kwargs): Object.__init__(self, name=name, **kwargs) name = String.T(xmltagname='Name') description = Unicode.T(optional=True, xmltagname='Description')
class EventParameters(Object): public_id = ResourceReference.T(xmlstyle='attribute', xmltagname='publicID') comment_list = List.T(Comment.T()) event_list = List.T(Event.T(xmltagname='event')) description = Unicode.T(optional=True) creation_info = CreationInfo.T(optional=True)
class BaseNode(Object): '''A base node type for derivation from: Network, Station and Channel types.''' code = String.T(xmlstyle='attribute') start_date = DummyAwareOptionalTimestamp.T(optional=True, xmlstyle='attribute') end_date = DummyAwareOptionalTimestamp.T(optional=True, xmlstyle='attribute') restricted_status = RestrictedStatus.T(optional=True, xmlstyle='attribute') alternate_code = String.T(optional=True, xmlstyle='attribute') historical_code = String.T(optional=True, xmlstyle='attribute') description = Unicode.T(optional=True, xmltagname='Description') comment_list = List.T(Comment.T(xmltagname='Comment')) def spans(self, *args): if len(args) == 0: return True elif len(args) == 1: return ((self.start_date is None or self.start_date <= args[0]) and (self.end_date is None or args[0] <= self.end_date)) elif len(args) == 2: return ((self.start_date is None or args[1] >= self.start_date) and (self.end_date is None or self.end_date >= args[0]))
class City(Object): def __init__(self, name, lat, lon, population=None, asciiname=None): name = unicode(name) lat = float(lat) lon = float(lon) if asciiname is None: asciiname = name.encode('ascii', errors='replace') if population is None: population = 0 else: population = int(population) Object.__init__(self, name=name, lat=lat, lon=lon, population=population, asciiname=asciiname) name = Unicode.T() lat = Float.T() lon = Float.T() population = Int.T() asciiname = String.T()
class BaseFilter(Object): '''The BaseFilter is derived by all filters.''' resource_id = String.T(optional=True, xmlstyle='attribute') name = String.T(optional=True, xmlstyle='attribute') description = Unicode.T(optional=True, xmltagname='Description') input_units = Units.T(optional=True, xmltagname='InputUnits') output_units = Units.T(optional=True, xmltagname='OutputUnits')
class Comment(Object): '''Container for a comment or log entry. Corresponds to SEED blockettes 31, 51 and 59.''' id = Counter.T(optional=True, xmlstyle='attribute') value = Unicode.T(xmltagname='Value') begin_effective_time = DummyAwareOptionalTimestamp.T( optional=True, xmltagname='BeginEffectiveTime') end_effective_time = DummyAwareOptionalTimestamp.T( optional=True, xmltagname='EndEffectiveTime') author_list = List.T(Person.T(xmltagname='Author'))
class RunInfo(Object): tags = List.T(Unicode.T(), help='List of user defined labels') def add_tag(self, tag): if tag not in self.tags: self.tags.append(tag) self.tags.sort() else: logger.warn('While adding tag: tag already set: %s' % tag) def remove_tag(self, tag): try: self.tags.remove(tag) except ValueError: logger.warn('While removing tag: tag not set: %s' % tag)
class Site(Object): '''Description of a site location using name and optional geopolitical boundaries (country, city, etc.).''' name = Unicode.T(xmltagname='Name') description = Unicode.T(optional=True, xmltagname='Description') town = Unicode.T(optional=True, xmltagname='Town') county = Unicode.T(optional=True, xmltagname='County') region = Unicode.T(optional=True, xmltagname='Region') country = Unicode.T(optional=True, xmltagname='Country')
class Comment(Object): id = ResourceReference.T(optional=True, xmlstyle='attribute') text = Unicode.T() creation_info = CreationInfo.T(optional=True)
class EventDescription(Object): text = Unicode.T() type = EventDescriptionType.T(optional=True)
class Event(Object): '''Seismic event representation :param lat: latitude of hypocenter (default 0.0) :param lon: longitude of hypocenter (default 0.0) :param time: origin time as float in seconds after '1970-01-01 00:00:00 :param name: event identifier as string (optional) :param depth: source depth (optional) :param magnitude: magnitude of event (optional) :param region: source region (optional) :param catalog: name of catalog that lists this event (optional) :param moment_tensor: moment tensor as :py:class:`moment_tensor.MomentTensor` instance (optional) :param duration: source duration as float (optional) ''' lat = Float.T(default=0.0) lon = Float.T(default=0.0) time = Timestamp.T(default=util.str_to_time('1970-01-01 00:00:00')) name = String.T(default='', optional=True) depth = Float.T(optional=True) magnitude = Float.T(optional=True) magnitude_type = String.T(optional=True) region = Unicode.T(optional=True) catalog = String.T(optional=True) moment_tensor = moment_tensor.MomentTensor.T(optional=True) duration = Float.T(optional=True) def __init__(self, lat=0., lon=0., time=0., name='', depth=None, magnitude=None, magnitude_type=None, region=None, load=None, loadf=None, catalog=None, moment_tensor=None, duration=None): vals = None if load is not None: vals = Event.oldload(load) elif loadf is not None: vals = Event.oldloadf(loadf) if vals: lat, lon, time, name, depth, magnitude, magnitude_type, region, \ catalog, moment_tensor, duration = vals Object.__init__(self, lat=lat, lon=lon, time=time, name=name, depth=depth, magnitude=magnitude, magnitude_type=magnitude_type, region=region, catalog=catalog, moment_tensor=moment_tensor, duration=duration) def time_as_string(self): return util.time_to_str(self.time) def set_name(self, name): self.name = name def olddump(self, filename): file = open(filename, 'w') self.olddumpf(file) file.close() def olddumpf(self, file): file.write('name = %s\n' % self.name) file.write('time = %s\n' % util.time_to_str(self.time)) if self.lat is not None: file.write('latitude = %.12g\n' % self.lat) if self.lon is not None: file.write('longitude = %.12g\n' % self.lon) if self.magnitude is not None: file.write('magnitude = %g\n' % self.magnitude) file.write('moment = %g\n' % moment_tensor.magnitude_to_moment(self.magnitude)) if self.magnitude_type is not None: file.write('magnitude_type = %s\n' % self.magnitude_type) if self.depth is not None: file.write('depth = %.10g\n' % self.depth) if self.region is not None: file.write('region = %s\n' % self.region) if self.catalog is not None: file.write('catalog = %s\n' % self.catalog) if self.moment_tensor is not None: m = self.moment_tensor.m() sdr1, sdr2 = self.moment_tensor.both_strike_dip_rake() file.write( ('mnn = %g\nmee = %g\nmdd = %g\nmne = %g\nmnd = %g\nmed = %g\n' 'strike1 = %g\ndip1 = %g\nrake1 = %g\n' 'strike2 = %g\ndip2 = %g\nrake2 = %g\n') % ((m[0, 0], m[1, 1], m[2, 2], m[0, 1], m[0, 2], m[1, 2]) + sdr1 + sdr2)) if self.duration is not None: file.write('duration = %g\n' % self.duration) @staticmethod def unique(events, deltat=10., group_cmp=(lambda a, b: cmp(a.catalog, b.catalog))): groups = Event.grouped(events, deltat) events = [] for group in groups: if group: group.sort(group_cmp) events.append(group[-1]) return events @staticmethod def grouped(events, deltat=10.): events = list(events) groups = [] for ia, a in enumerate(events): groups.append([]) haveit = False for ib, b in enumerate(events[:ia]): if abs(b.time - a.time) < deltat: groups[ib].append(a) haveit = True break if not haveit: groups[ia].append(a) groups = [g for g in groups if g] groups.sort(key=lambda g: sum(e.time for e in g) // len(g)) return groups @staticmethod def dump_catalog(events, filename=None, stream=None): if filename is not None: file = open(filename, 'w') else: file = stream try: i = 0 for ev in events: ev.olddumpf(file) file.write('--------------------------------------------\n') i += 1 finally: if filename is not None: file.close() @staticmethod def oldload(filename): with open(filename, 'r') as file: return Event.oldloadf(file) @staticmethod def oldloadf(file): d = {} try: for line in file: if line.lstrip().startswith('#'): continue toks = line.split(' = ', 1) if len(toks) == 2: k, v = toks[0].strip(), toks[1].strip() if k in ('name', 'region', 'catalog', 'magnitude_type'): d[k] = v if k in (('latitude longitude magnitude depth duration ' 'mnn mee mdd mne mnd med strike1 dip1 rake1 ' 'strike2 dip2 rake2 duration').split()): d[k] = float(v) if k == 'time': d[k] = util.str_to_time(v) if line.startswith('---'): d['have_separator'] = True break except Exception as e: raise FileParseError(e) if not d: raise EOF() if 'have_separator' in d and len(d) == 1: raise EmptyEvent() mt = None m6 = [d[x] for x in 'mnn mee mdd mne mnd med'.split() if x in d] if len(m6) == 6: mt = moment_tensor.MomentTensor(m=moment_tensor.symmat6(*m6)) else: sdr = [d[x] for x in 'strike1 dip1 rake1'.split() if x in d] if len(sdr) == 3: moment = 1.0 if 'moment' in d: moment = d['moment'] elif 'magnitude' in d: moment = moment_tensor.magnitude_to_moment(d['magnitude']) mt = moment_tensor.MomentTensor(strike=sdr[0], dip=sdr[1], rake=sdr[2], scalar_moment=moment) return (d.get('latitude', 0.0), d.get('longitude', 0.0), d.get('time', 0.0), d.get('name', ''), d.get('depth', None), d.get('magnitude', None), d.get('magnitude_type', None), d.get('region', None), d.get('catalog', None), mt, d.get('duration', None)) @staticmethod def load_catalog(filename): file = open(filename, 'r') try: while True: try: ev = Event(loadf=file) yield ev except EmptyEvent: pass except EOF: pass file.close() def get_hash(self): e = self if isinstance(e.time, util.hpfloat): stime = util.time_to_str(e.time, format='%Y-%m-%d %H:%M:%S.6FRAC') else: stime = util.time_to_str(e.time, format='%Y-%m-%d %H:%M:%S.3FRAC') s = float_or_none_to_str return ehash(', '.join( (stime, s(e.lat), s(e.lon), s(e.depth), s(e.magnitude), str(e.catalog), str(e.name), str(e.region)))) def human_str(self): s = [ 'Latitude [deg]: %g' % self.lat, 'Longitude [deg]: %g' % self.lon, 'Time [UTC]: %s' % util.time_to_str(self.time) ] if self.name: s.append('Name: %s' % self.name) if self.depth is not None: s.append('Depth [km]: %g' % (self.depth / 1000.)) if self.magnitude is not None: s.append('Magnitude [%s]: %3.1f' % (self.magnitude_type or 'M?', self.magnitude)) if self.region: s.append('Region: %s' % self.region) if self.catalog: s.append('Catalog: %s' % self.catalog) if self.moment_tensor: s.append(str(self.moment_tensor)) return '\n'.join(s)
class Operator(Object): agency_list = List.T(Unicode.T(xmltagname='Agency')) contact_list = List.T(Person.T(xmltagname='Contact')) web_site = String.T(optional=True, xmltagname='WebSite')
class ReportInfo(Object): title = Unicode.T(optional=True) description = Unicode.T(optional=True) have_archive = Bool.T(optional=True)
class PhoneNumber(Object): description = Unicode.T(optional=True, xmlstyle='attribute') country_code = Int.T(optional=True, xmltagname='CountryCode') area_code = Int.T(xmltagname='AreaCode') phone_number = PhoneNumber.T(xmltagname='PhoneNumber')
class Parameter(Object): name__ = String.T() unit = Unicode.T(optional=True) scale_factor = Float.T(default=1., optional=True) scale_unit = Unicode.T(optional=True) label = Unicode.T(optional=True) optional = Bool.T(default=True, optional=True) def __init__(self, *args, **kwargs): if len(args) >= 1: kwargs['name'] = args[0] if len(args) >= 2: kwargs['unit'] = newstr(args[1]) self.groups = [None] self._name = None Object.__init__(self, **kwargs) def get_label(self, with_unit=True): lbl = [self.label or self.name] if with_unit: unit = self.get_unit_label() if unit: lbl.append('[%s]' % unit) return ' '.join(lbl) def set_groups(self, groups): if not isinstance(groups, list): raise AttributeError('Groups must be a list of strings.') self.groups = groups def _get_name(self): if None not in self.groups: return '%s.%s' % ('.'.join(self.groups), self._name) return self._name def _set_name(self, value): self._name = value name = property(_get_name, _set_name) @property def name_nogroups(self): return self._name def get_value_label(self, value, format='%(value)g%(unit)s'): value = self.scaled(value) unit = self.get_unit_suffix() return format % dict(value=value, unit=unit) def get_unit_label(self): if self.scale_unit is not None: return self.scale_unit elif self.unit: return self.unit else: return None def get_unit_suffix(self): unit = self.get_unit_label() if not unit: return '' else: return ' %s' % unit def scaled(self, x): if isinstance(x, tuple): return tuple(v / self.scale_factor for v in x) if isinstance(x, list): return list(v / self.scale_factor for v in x) else: return x / self.scale_factor def inv_scaled(self, x): if isinstance(x, tuple): return tuple(v * self.scale_factor for v in x) if isinstance(x, list): return list(v * self.scale_factor for v in x) else: return x * self.scale_factor
class ReportInfo(Object): title = Unicode.T(optional=True) description = Unicode.T(optional=True) version_info = info.VersionInfo.T() have_archive = Bool.T(optional=True)
class ExternalReference(Object): '''This type contains a URI and description for external data that users may want to reference in StationXML.''' uri = String.T(xmltagname='URI') description = Unicode.T(xmltagname='Description')