def _parse_record_ah(self, line, event): """ Parses the 'additional hypocenter' record AH """ date = line[2:10] time = line[11:20] # unused: hypocenter_quality = line[20] latitude = self._float(line[21:27]) lat_type = line[27] longitude = self._float(line[29:36]) lon_type = line[36] # unused: preliminary_flag = line[37] depth = self._float(line[38:43]) # unused: depth_quality = line[43] standard_dev = self._float_unused(line[44:48]) station_number = self._int_unused(line[48:51]) phase_number = self._int_unused(line[51:55]) source_code = line[56:60].strip() evid = event.resource_id.id.split('/')[-1] origin = Origin() res_id = '/'.join((res_id_prefix, 'origin', evid, source_code.lower())) origin.resource_id = ResourceIdentifier(id=res_id) origin.creation_info = CreationInfo(agency_id=source_code) origin.time = UTCDateTime(date + time) origin.latitude = latitude * self._coordinate_sign(lat_type) origin.longitude = longitude * self._coordinate_sign(lon_type) origin.depth = depth * 1000 origin.depth_type = 'from location' origin.quality = OriginQuality() origin.quality.standard_error = standard_dev origin.quality.used_station_count = station_number origin.quality.used_phase_count = phase_number origin.origin_type = 'hypocenter' event.origins.append(origin)
def test_setting_nans_or_inf_fails(self): """ Tests that settings NaNs or infs as floating point values fails. """ o = Origin() with self.assertRaises(ValueError) as e: o.latitude = float('nan') self.assertEqual( e.exception.args[0], "On Origin object: Value 'nan' for 'latitude' is not a finite " "floating point value.") with self.assertRaises(ValueError) as e: o.latitude = float('inf') self.assertEqual( e.exception.args[0], "On Origin object: Value 'inf' for 'latitude' is not a finite " "floating point value.") with self.assertRaises(ValueError) as e: o.latitude = float('-inf') self.assertEqual( e.exception.args[0], "On Origin object: Value '-inf' for 'latitude' is " "not a finite floating point value.")
def attach_new_origin( old_event: Event, new_event: Event, new_origin: Origin, preferred: bool, index: Optional[int] = None, ) -> Catalog: """ Attach a new origin to an existing events object. Parameters ---------- old_event : obspy.core.event.Event The old event that will receive the new origin new_event : obspy.core.event.Event The new event that contains the origin, needed for merging picks that may not exist in old_event new_origin : obspy.core.event.Origin The new origin that will be attached to old_event preferred : bool If True mark the new origin as the preferred_origin index : int or None The origin index of old_cat that new_origin will overwrite, if None append the new_origin to old_cat.origins Returns ------- obspy.Catalog modifies old_cat in-place, returns old_catalog """ # make sure all the picks/amplitudes in new_event are also in old_event merge_events(old_event, new_event, delete_old=False) # point the arrivals in the new origin at the old picks _associate_picks(old_event, new_event, new_origin) # append the origin if index is not None: # if this origin is to replace another try: old_ori = old_event.origins[index] except IndexError: msg = ("%d is not valid for an origin list of length %d") % ( index, len(old_event.origins), ) msg += " appending new origin to end of list" warnings.warn(msg) old_event.origins.append(new_origin) else: # set resource id and creation info new_origin.resource_id = old_ori.resource_id new_origin.creation_info = old_ori.creation_info old_event.origins[index] = new_origin else: old_event.origins.append(new_origin) # bump origin creation info bump_creation_version(new_origin) # set preferred if preferred: old_event.preferred_origin_id = new_origin.resource_id validate_catalog(old_event) return old_event
def test_multiple_origins(self): """ Parameters of multiple origins should not interfere with each other. """ origin = Origin() origin.resource_id = 'smi:ch.ethz.sed/origin/37465' origin.time = UTCDateTime(0) origin.latitude = 12 origin.latitude_errors.confidence_level = 95 origin.longitude = 42 origin.depth_type = 'from location' self.assertEqual( origin.resource_id, ResourceIdentifier(id='smi:ch.ethz.sed/origin/37465')) self.assertEqual(origin.latitude, 12) self.assertEqual(origin.latitude_errors.confidence_level, 95) self.assertEqual(origin.latitude_errors.uncertainty, None) self.assertEqual(origin.longitude, 42) origin2 = Origin(force_resource_id=False) origin2.latitude = 13.4 self.assertEqual(origin2.depth_type, None) self.assertEqual(origin2.resource_id, None) self.assertEqual(origin2.latitude, 13.4) self.assertEqual(origin2.latitude_errors.confidence_level, None) self.assertEqual(origin2.longitude, None)
def ORNL_events_to_cat(ornl_file): """Make Catalog from ORNL locations""" cat = Catalog() loc_df = pd.read_csv(ornl_file, infer_datetime_format=True) loc_df = loc_df.set_index('event_datetime') eid = 0 for dt, row in loc_df.iterrows(): ot = UTCDateTime(dt) hmc_east = row['x(m)'] hmc_north = row['y(m)'] hmc_elev = row['z(m)'] errX = row['error_x (m)'] errY = row['error_y (m)'] errZ = row['error_z (m)'] rms = row['rms (millisecond)'] converter = SURF_converter() lon, lat, elev = converter.to_lonlat((hmc_east, hmc_north, hmc_elev)) o = Origin(time=ot, latitude=lat, longitude=lon, depth=130 - elev) o.origin_uncertainty = OriginUncertainty() o.quality = OriginQuality() ou = o.origin_uncertainty oq = o.quality ou.max_horizontal_uncertainty = np.max([errX, errY]) ou.min_horizontal_uncertainty = np.min([errX, errY]) o.depth_errors.uncertainty = errZ oq.standard_error = rms * 1e3 extra = AttribDict({ 'hmc_east': { 'value': hmc_east, 'namespace': 'smi:local/hmc' }, 'hmc_north': { 'value': hmc_north, 'namespace': 'smi:local/hmc' }, 'hmc_elev': { 'value': hmc_elev, 'namespace': 'smi:local/hmc' }, 'hmc_eid': { 'value': eid, 'namespace': 'smi:local/hmc' } }) o.extra = extra rid = ResourceIdentifier(id=ot.strftime('%Y%m%d%H%M%S%f')) # Dummy magnitude of 1. for all events until further notice mag = Magnitude(mag=1., mag_errors=QuantityError(uncertainty=1.)) ev = Event(origins=[o], magnitudes=[mag], resource_id=rid) ev.preferred_origin_id = o.resource_id.id cat.events.append(ev) eid += 1 return cat
def test_setting_nans_or_inf_fails(self): """ Tests that settings NaNs or infs as floating point values fails. """ o = Origin() with pytest.raises(ValueError, match='is not a finite'): o.latitude = float('nan') with pytest.raises(ValueError, match='is not a finite'): o.latitude = float('inf') with pytest.raises(ValueError, match='is not a finite'): o.latitude = float('-inf')
def test_multipleOrigins(self): """ Parameters of multiple origins should not interfere with each other. """ origin = Origin() origin.resource_id = 'smi:ch.ethz.sed/origin/37465' origin.time = UTCDateTime(0) origin.latitude = 12 origin.latitude_errors.confidence_level = 95 origin.longitude = 42 origin.depth_type = 'from location' self.assertEqual( origin.resource_id, ResourceIdentifier(id='smi:ch.ethz.sed/origin/37465')) self.assertEqual(origin.latitude, 12) self.assertEqual(origin.latitude_errors.confidence_level, 95) self.assertEqual(origin.latitude_errors.uncertainty, None) self.assertEqual(origin.longitude, 42) origin2 = Origin(force_resource_id=False) origin2.latitude = 13.4 self.assertEqual(origin2.depth_type, None) self.assertEqual(origin2.resource_id, None) self.assertEqual(origin2.latitude, 13.4) self.assertEqual(origin2.latitude_errors.confidence_level, None) self.assertEqual(origin2.longitude, None)
def plot_some_events(): from obspy.core.event import Catalog, Event, Origin, Magnitude from obspy.core import UTCDateTime as UTC eqs = """2008-09-10T16:12:03 6.0 -20.40 -69.40 40 2008-03-24T20:39:06 5.9 -20.10 -69.20 85 2008-03-01T19:51:59 5.7 -20.10 -69.60 15 2008-02-15T16:54:04 5.5 -23.00 -70.20 32 2008-02-04T17:01:30 6.6 -20.20 -70.00 36 2007-12-16T08:09:16 7.1 -22.80 -70.00 14 2007-11-14T15:40:51 7.8 -22.34 -70.06 37""" #GEOFON:-22.30 -69.80 events = [] for eq in eqs.split('\n'): time, mag, lat, lon, depth = eq.split() ev = Event(event_type='earthquake', creation_info='GEOFON', origins=[Origin(time=UTC(time), latitude=float(lat), longitude=float(lon), depth=float(depth))], magnitudes=[Magnitude(mag=float(mag), magnitude_type='M')]) events.append(ev) cat = Catalog(events[::-1]) #print cat #cat.plot(projection='local') lons = [ev.origins[0].longitude for ev in cat] lats = [ev.origins[0].latitude for ev in cat] dates = [ev.origins[0].time for ev in cat] mags = [ev.magnitudes[0].mag for ev in cat]
def _block2event(block, seed_map, id_default, ph2comp, eventid_map): """ Read HypoDD event block """ lines = block.strip().splitlines() yr, mo, dy, hr, mn, sc, la, lo, dp, mg, eh, ez, rms, id_ = lines[0].split() if eventid_map is not None and id_ in eventid_map: id_ = eventid_map[id_] time = UTCDateTime(int(yr), int(mo), int(dy), int(hr), int(mn), float(sc), strict=False) laterr = None if float(eh) == 0 else float(eh) / DEG2KM lonerr = (None if laterr is None or float(la) > 89 else laterr / cos(deg2rad(float(la)))) ez = None if float(ez) == 0 else float(ez) * 1000 rms = None if float(rms) == 0 else float(rms) picks = [] arrivals = [] for line in lines[1:]: sta, reltime, weight, phase = line.split() comp = ph2comp.get(phase, '') wid = seed_map.get(sta, id_default) _waveform_id = WaveformStreamID(seed_string=wid.format(sta, comp)) pick = Pick(waveform_id=_waveform_id, phase_hint=phase, time=time + float(reltime)) arrival = Arrival(phase=phase, pick_id=pick.resource_id, time_weight=float(weight)) picks.append(pick) arrivals.append(arrival) qu = OriginQuality(associated_phase_count=len(picks), standard_error=rms) origin = Origin(arrivals=arrivals, resource_id="smi:local/origin/" + id_, quality=qu, latitude=float(la), longitude=float(lo), depth=1000 * float(dp), latitude_errors=laterr, longitude_errors=lonerr, depth_errors=ez, time=time) if mg.lower() == 'nan': magnitudes = [] preferred_magnitude_id = None else: magnitude = Magnitude(mag=mg, resource_id="smi:local/magnitude/" + id_) magnitudes = [magnitude] preferred_magnitude_id = magnitude.resource_id event = Event(resource_id="smi:local/event/" + id_, picks=picks, origins=[origin], magnitudes=magnitudes, preferred_origin_id=origin.resource_id, preferred_magnitude_id=preferred_magnitude_id) return event
def test_write_empty(self): """ Function to check that writing a blank event works as it should. """ test_event = Event() with self.assertRaises(NordicParsingError): _write_nordic(test_event, filename=None, userid='TEST', evtype='L', outdir='.', wavefiles='test') test_event.origins.append(Origin()) with self.assertRaises(NordicParsingError): _write_nordic(test_event, filename=None, userid='TEST', evtype='L', outdir='.', wavefiles='test') test_event.origins[0].time = UTCDateTime() with TemporaryWorkingDirectory(): test_sfile = _write_nordic(test_event, filename=None, userid='TEST', evtype='L', outdir='.', wavefiles='test') self.assertTrue(os.path.isfile(test_sfile))
def setUpClass(cls) -> None: cls.event = Event(origins=[Origin(time=UTCDateTime(2001, 3, 26))], picks=[ Pick(time=UTCDateTime(2001, 3, 26, 1, 1, 1)), Pick(time=UTCDateTime(2001, 3, 26, 1, 1, 5)) ]) cls.event.preferred_origin_id = cls.event.origins[0].resource_id
def test_get_peak_time(): datafiles, _ = read_data_dir("geonet", "us1000778i", "20161113_110259_WTMC_20.V2A") datafile = datafiles[0] stream1 = read_geonet(datafile)[0] max_cls = Max(stream1).result assert len(max_cls) == 2 max_cls = Max({"chan": [0, 1, 2, 3]}).result assert len(max_cls) == 1 stream2 = read_geonet(datafile)[0] origin = Origin(latitude=42.6925, longitude=173.021944) stream_summary = StationSummary.from_stream(stream2, ["channels"], ["pgv", "pga"], origin) assert stream2[0].stats.pga_time == UTCDateTime( "2016-11-13T11:03:08.880001Z") assert stream2[0].stats.pgv_time == UTCDateTime( "2016-11-13T11:03:10.580001Z") assert stream2[1].stats.pga_time == UTCDateTime( "2016-11-13T11:03:09.960001Z") assert stream2[1].stats.pgv_time == UTCDateTime( "2016-11-13T11:03:08.860001Z") assert stream2[2].stats.pga_time == UTCDateTime( "2016-11-13T11:03:08.140001Z") assert stream2[2].stats.pgv_time == UTCDateTime( "2016-11-13T11:03:09.560001Z")
def reloc2cat(reloc_file, cat): from obspy import UTCDateTime from obspy.core.event import Origin, ResourceIdentifier with open(reloc_file, 'rb') as f: for row in f: ev_id = int(row.split()[0]) row_lst = row.split() # Catch stupid 60 entries for seconds if row_lst[15].split('.')[0] == '60': row_lst[14] = int(row_lst[14]) + 1 row_lst[15] = '00.000' cat[ev_id].origins.append( Origin(latitude=float(row_lst[1]), longitude=float(row_lst[2]), depth=float(row_lst[3]) * 1000, time=UTCDateTime(year=int(row_lst[10]), month=int(row_lst[11]), day=int(row_lst[12]), hour=int(row_lst[13]), minute=int(row_lst[14]), second=int(row_lst[15].split('.')[0]), microsecond=(int( row_lst[15].split('.')[1]))), method_id=ResourceIdentifier(id='HypoDD'))) cat[ev_id].preferred_origin_id = str( cat[ev_id].origins[-1].resource_id) return cat
def test_separated_dist_longlong(self): peaks = np.array([100, 65, 20, 120, 300]) index = np.array([2000, 5000, 10, 70, 500]) index = index * int(1e10) trig_int = 100 * int(1e10) hypocentral_separation = 10.0 catalog = Catalog([ Event(origins=[Origin(latitude=0.0, longitude=90.0, depth=1000.)]), Event(origins=[Origin(latitude=0.0, longitude=90.0, depth=1000.)]), Event(origins=[Origin(latitude=0.0, longitude=80.0, depth=1000.)]), Event(origins=[Origin(latitude=0.0, longitude=90.0, depth=1000.)]), Event(origins=[Origin(latitude=0.0, longitude=90.0, depth=1000.)]), ]) peaks_out = decluster_distance_time( peaks, index, trig_int, catalog, hypocentral_separation, threshold=0) assert len(peaks) == len(peaks_out)
def _deserialize(self, zmap_str): catalog = Catalog() for row in zmap_str.split('\n'): if len(row) == 0: continue origin = Origin() event = Event(origins=[origin]) event.preferred_origin_id = origin.resource_id.id # Begin value extraction columns = row.split('\t', 13)[:13] # ignore extra columns values = dict(zip(_STD_ZMAP_COLUMNS + _EXT_ZMAP_COLUMNS, columns)) # Extract origin origin.longitude = self._str2num(values.get('lon')) origin.latitude = self._str2num(values.get('lat')) depth = self._str2num(values.get('depth')) if depth is not None: origin.depth = depth * 1000.0 z_err = self._str2num(values.get('z_err')) if z_err is not None: origin.depth_errors.uncertainty = z_err * 1000.0 h_err = self._str2num(values.get('h_err')) if h_err is not None: ou = OriginUncertainty() ou.horizontal_uncertainty = h_err ou.preferred_description = 'horizontal uncertainty' origin.origin_uncertainty = ou year = self._str2num(values.get('year')) if year is not None: t_fields = ['year', 'month', 'day', 'hour', 'minute', 'second'] comps = [self._str2num(values.get(f)) for f in t_fields] if year % 1 != 0: origin.time = self._decyear2utc(year) elif any(v > 0 for v in comps[1:]): # no seconds involved if len(comps) < 6: utc_args = [int(v) for v in comps if v is not None] # we also have to handle seconds else: utc_args = [ int(v) if v is not None else 0 for v in comps[:-1] ] # just leave float seconds as is utc_args.append(comps[-1]) origin.time = UTCDateTime(*utc_args) mag = self._str2num(values.get('mag')) # Extract magnitude if mag is not None: magnitude = Magnitude(mag=mag) m_err = self._str2num(values.get('m_err')) magnitude.mag_errors.uncertainty = m_err event.magnitudes.append(magnitude) event.preferred_magnitude_id = magnitude.resource_id.id event.scope_resource_ids() catalog.append(event) return catalog
def test_clustered_dist_time(self): peaks = np.array([100, 65, 20, 120, 300]) index = np.array([2000, 5000, 10, 70, 500]) trig_int = 100 hypocentral_separation = 10.0 catalog = Catalog([ Event(origins=[Origin(latitude=0.0, longitude=90.0, depth=1000.)]), Event(origins=[Origin(latitude=0.0, longitude=90.0, depth=1000.)]), Event(origins=[Origin(latitude=0.0, longitude=90.0, depth=1000.)]), Event(origins=[Origin(latitude=0.0, longitude=90.0, depth=1000.)]), Event(origins=[Origin(latitude=0.0, longitude=90.0, depth=1000.)]), ]) peaks_out = decluster_distance_time( peaks, index, trig_int, catalog, hypocentral_separation, threshold=0) assert len(peaks) > len(peaks_out) assert peaks_out == [(300.0, 500), (120.0, 70), (100.0, 2000), (65.0, 5000)]
def add_origin(self): """ Return the origin found by iLoc """ if self._iloc_origin is None: # run iloc and return iloc determined origin return self.origins.append(Origin()) else: return self.origins
def test_str_empty_origin(self): """ Ensure an event with an empty origin returns a str without raising a TypeError (#2119). """ event = Event(origins=[Origin()]) out = event.short_str() self.assertIsInstance(out, str) self.assertEqual(out, 'None | None, None')
def catalog(self, bingham_dataset, new_time): """ Assemble a events to test yield_event_waveforms with an event that was not initiated from the start. """ # get first event, create new origin to slightly add some time. ori = Origin(time=new_time, latitude=47.1, longitude=-100.22) event = Event(origins=[ori]) return obspy.Catalog(events=[event])
def setUpClass(cls) -> None: cls.event = Event(origins=[ Origin(time=UTCDateTime(2019, 1, 1), latitude=-45., longitude=90.0, depth=10000.) ], magnitudes=[Magnitude(mag=7.4)]) cls.event.preferred_origin_id = cls.event.origins[0].resource_id
def test_creation_info(self): # 1 - empty Origin class will set creation_info to None orig = Origin() assert orig.creation_info is None # 2 - preset via dict or existing CreationInfo object orig = Origin(creation_info={}) assert isinstance(orig.creation_info, CreationInfo) orig = Origin(creation_info=CreationInfo(author='test2')) assert isinstance(orig.creation_info, CreationInfo) assert orig.creation_info.author == 'test2' # 3 - check set values orig = Origin(creation_info={'author': 'test'}) assert orig.creation_info == orig['creation_info'] assert orig.creation_info.author == 'test' assert orig['creation_info']['author'] == 'test' orig.creation_info.agency_id = "muh" assert orig.creation_info == orig['creation_info'] assert orig.creation_info.agency_id == 'muh' assert orig['creation_info']['agency_id'] == 'muh'
def test_creationInfo(self): # 1 - empty Origin class will set creation_info to None orig = Origin() self.assertEqual(orig.creation_info, None) # 2 - preset via dict or existing CreationInfo object orig = Origin(creation_info={}) self.assertTrue(isinstance(orig.creation_info, CreationInfo)) orig = Origin(creation_info=CreationInfo(author='test2')) self.assertTrue(isinstance(orig.creation_info, CreationInfo)) self.assertEqual(orig.creation_info.author, 'test2') # 3 - check set values orig = Origin(creation_info={'author': 'test'}) self.assertEqual(orig.creation_info, orig['creation_info']) self.assertEqual(orig.creation_info.author, 'test') self.assertEqual(orig['creation_info']['author'], 'test') orig.creation_info.agency_id = "muh" self.assertEqual(orig.creation_info, orig['creation_info']) self.assertEqual(orig.creation_info.agency_id, 'muh') self.assertEqual(orig['creation_info']['agency_id'], 'muh')
def test_seishub(self): """Test the seishub method, use obspy default seishub client.""" import sys if sys.version_info.major == 2: from future.backports.urllib.request import URLError else: from urllib.request import URLError t = UTCDateTime(2009, 9, 3) test_cat = Catalog() test_cat.append(Event()) test_cat[0].origins.append(Origin()) test_cat[0].origins[0].time = t test_cat[0].origins[0].latitude = 45 test_cat[0].origins[0].longitude = 45 test_cat[0].origins[0].depth = 5000 test_cat[0].picks.append( Pick(waveform_id=WaveformStreamID(station_code='MANZ', channel_code='EHZ', network_code='BW'), phase_hint='PG', time=t + 2000)) test_cat[0].picks.append( Pick(waveform_id=WaveformStreamID(station_code='MANZ', channel_code='EHN', network_code='BW'), phase_hint='SG', time=t + 2005)) test_cat[0].picks.append( Pick(waveform_id=WaveformStreamID(station_code='MANZ', channel_code='EHE', network_code='BW'), phase_hint='SG', time=t + 2005.5)) test_url = "http://teide.geophysik.uni-muenchen.de:8080" if sys.version_info.major == 3: try: template = template_gen(method="from_seishub", catalog=test_cat, url=test_url, lowcut=1.0, highcut=5.0, samp_rate=20, filt_order=4, length=3, prepick=0.5, swin='all', process_len=300) except URLError: pass else: pass if 'template' in locals(): self.assertEqual(len(template), 3)
def read_cat_ref(cat_file): """ Parses a given refrence catalogue (in ascii format,see the header for details) output is Obspy catalogue object """ cat_ref = np.loadtxt(cat_file,delimiter=',',skiprows=1) cat = Catalog() for i,e in enumerate(cat_ref): event = Event(resource_id='smi:local/='+str(i),creation_info='HG') origin = Origin() origin.time = UTCDateTime(int(e[2]),int(e[3]),int(e[4]), int(e[7]),int(e[8]),e[9]) origin.longitude = e[0] origin.latitude = e[1] origin.depth = e[6] * 1000. #in meters event.origins.append(origin) if ~(np.isnan(e[10])): mag = Magnitude(creation_info='HER') mag.mag = e[10] mag.magnitude_type = 'Mw' event.magnitudes.append(mag) if ~(np.isnan(e[11])): mag = Magnitude(creation_info='MAR') mag.mag = e[11] mag.magnitude_type = 'Mw' event.magnitudes.append(mag) if ~(np.isnan(e[12])): mag = Magnitude(creation_info='SIP') mag.mag = e[12] mag.magnitude_type = 'Mw' event.magnitudes.append(mag) cat.append(event) return cat
def get_event_object(self): ''' update events otime,lat,lon and mag with IRIS (or any other clients) catalog ''' # get parameters from the cataog if self.use_catalog == 1: print("WARNING using event data from the IRIS catalog") cat = self.client.get_events( starttime=self.otime - self.sec_before_after_event, endtime=self.otime + self.sec_before_after_event) self.ev = cat[0] # use catalog parameters self.otime = self.ev.origins[0].time self.elat = self.ev.origins[0].latitude self.elon = self.ev.origins[0].longitude self.edep = self.ev.origins[0].depth self.emag = self.ev.magnitudes[0].mag # use parameters from the input file else: print("WARNING using event data from user-defined catalog") #self.ev = Event() org = Origin() org.latitude = self.elat org.longitude = self.elon org.depth = self.edep org.time = self.otime mag = Magnitude() mag.mag = self.emag mag.magnitude_type = "Mw" self.ev.origins.append(org) self.ev.magnitudes.append(mag)
def _deserialize(self, zmap_str): catalog = Catalog() for row in zmap_str.split('\n'): if len(row) == 0: continue origin = Origin() event = Event(origins=[origin]) event.preferred_origin_id = origin.resource_id.id # Begin value extraction columns = row.split('\t', 13)[:13] # ignore extra columns values = dict(zip(_STD_ZMAP_COLUMNS + _EXT_ZMAP_COLUMNS, columns)) # Extract origin origin.longitude = self._str2num(values.get('lon')) origin.latitude = self._str2num(values.get('lat')) depth = self._str2num(values.get('depth')) if depth is not None: origin.depth = depth * 1000.0 z_err = self._str2num(values.get('z_err')) if z_err is not None: origin.depth_errors.uncertainty = z_err * 1000.0 h_err = self._str2num(values.get('h_err')) if h_err is not None: ou = OriginUncertainty() ou.horizontal_uncertainty = h_err ou.preferred_description = 'horizontal uncertainty' origin.origin_uncertainty = ou year = self._str2num(values.get('year')) if year is not None: t_fields = ['year', 'month', 'day', 'hour', 'minute', 'second'] comps = [self._str2num(values.get(f)) for f in t_fields] if year % 1 != 0: origin.time = self._decyear2utc(year) elif any(v > 0 for v in comps[1:]): # no seconds involved if len(comps) < 6: utc_args = [int(v) for v in comps if v is not None] # we also have to handle seconds else: utc_args = [int(v) if v is not None else 0 for v in comps[:-1]] # just leave float seconds as is utc_args.append(comps[-1]) origin.time = UTCDateTime(*utc_args) mag = self._str2num(values.get('mag')) # Extract magnitude if mag is not None: magnitude = Magnitude(mag=mag) m_err = self._str2num(values.get('m_err')) magnitude.mag_errors.uncertainty = m_err event.magnitudes.append(magnitude) event.preferred_magnitude_id = magnitude.resource_id.id event.scope_resource_ids() catalog.append(event) return catalog
def setUpClass(cls) -> None: cls.client = Client("GEONET") cls.tribe = read_tribe( os.path.join( os.path.abspath(os.path.dirname(os.path.dirname(__file__))), "test_data", "test_tribe.tgz")) cls.trigger_event = Event(origins=[ Origin(time=UTCDateTime(2019, 1, 1), latitude=-45., longitude=178.0, depth=10000.) ], magnitudes=[Magnitude(mag=7.4)])
def get_default_catalog(self): """Get a catalog with picks from the default stream.""" pick1 = Pick(time=UTCDateTime(2009, 8, 24, 0, 20, 7, 696381), waveform_id=WaveformStreamID(seed_string='BW.RJOB..EHZ'), phase_hint='P') origin = Origin( time=UTCDateTime(2009, 8, 24, 0, 20, 6, 410034), longitude=0, latitude=0, depth=0, ) event = Event(picks=[pick1], origins=[origin]) return Catalog(events=[event])
def event_to_quakeml(event, filename): """ Write one of those events to QuakeML. """ # Create all objects. cat = Catalog() ev = Event() org = Origin() mag = Magnitude() fm = FocalMechanism() mt = MomentTensor() t = Tensor() # Link them together. cat.append(ev) ev.origins.append(org) ev.magnitudes.append(mag) ev.focal_mechanisms.append(fm) fm.moment_tensor = mt mt.tensor = t # Fill values ev.resource_id = "smi:inversion/%s" % str(event["identifier"]) org.time = event["time"] org.longitude = event["longitude"] org.latitude = event["latitude"] org.depth = event["depth_in_km"] * 1000 mag.mag = event["Mw"] mag.magnitude_type = "Mw" t.m_rr = event["Mrr"] t.m_tt = event["Mpp"] t.m_pp = event["Mtt"] t.m_rt = event["Mrt"] t.m_rp = event["Mrp"] t.m_tp = event["Mtp"] cat.write(filename, format="quakeml")
def _block2event(block, seed_map, id_default, ph2comp): """ Read HypoDD event block """ lines = block.strip().splitlines() yr, mo, dy, hr, mn, sc, la, lo, dp, mg, eh, ez, rms, id_ = lines[0].split() time = UTCDateTime(int(yr), int(mo), int(dy), int(hr), int(mn), float(sc), strict=False) picks = [] arrivals = [] for line in lines[1:]: sta, reltime, weight, phase = line.split() comp = ph2comp.get(phase, '') wid = seed_map.get(sta, id_default) _waveform_id = WaveformStreamID(seed_string=wid.format(sta, comp)) pick = Pick(waveform_id=_waveform_id, phase_hint=phase, time=time + float(reltime)) arrival = Arrival(phase=phase, pick_id=pick.resource_id, time_weight=float(weight)) picks.append(pick) arrivals.append(arrival) qu = None if rms == '0.0' else OriginQuality(standard_error=float(rms)) origin = Origin(arrivals=arrivals, resource_id="smi:local/origin/" + id_, quality=qu, latitude=float(la), longitude=float(lo), depth=1000 * float(dp), time=time) if mg.lower() == 'nan': magnitudes = [] preferred_magnitude_id = None else: magnitude = Magnitude(mag=mg, resource_id="smi:local/magnitude/" + id_) magnitudes = [magnitude] preferred_magnitude_id = magnitude.resource_id event = Event(resource_id="smi:local/event/" + id_, picks=picks, origins=[origin], magnitudes=magnitudes, preferred_origin_id=origin.resource_id, preferred_magnitude_id=preferred_magnitude_id) return event
def test_issue_2173(self): """ Ensure events with empty origins are equal after round-trip to disk. See #2173. """ # create event and save to disk origin = Origin(time=UTCDateTime('2016-01-01')) event1 = Event(origins=[origin]) bio = io.BytesIO() event1.write(bio, 'quakeml') # read from disk event2 = read_events(bio)[0] # saved and loaded event should be equal self.assertEqual(event1, event2)
def get_origin(self, id=None, event_name=None): """ Extracts event metadata from SAC headers """ # What stream in the dataset should we extract origin metadata from? # If stream id is not provided, then use the most recently-added stream if id: index = self._get_index(id) else: index = -1 data = self.__list__[index] sac_headers = data[0].meta.sac # if hypocenter is included as an inversion parameter, then we # cannot rely on any of the following metadata, which are likely based # on catalog locations or other preliminary information try: latitude = sac_headers.evla longitude = sac_headers.evlo except (TypeError, ValueError): warn("Could not determine event location from sac headers. " "Setting location to nan...") latitude = np.nan longitudue = np.nan try: depth = sac_headers.evdp except (TypeError, ValueError): warn("Could not determine event depth from sac headers. " "Setting depth to nan...") depth = 0. try: origin_time = obspy.UTCDateTime(year=sac_headers.nzyear, julday=sac_headers.nzjday, hour=sac_headers.nzhour, minute=sac_headers.nzmin, second=sac_headers.nzsec) except (TypeError, ValueError): warn("Could not determine origin time from sac headers. " "Setting origin time to zero...") origin_time = obspy.UTCDateTime(0) return Origin( time=origin_time, longitude=sac_headers.evlo, latitude=sac_headers.evla, depth=depth * 1000.0, )
def test_multiple_origins(self): """ Parameters of multiple origins should not interfere with each other. """ origin = Origin() origin.resource_id = 'smi:ch.ethz.sed/origin/37465' origin.time = UTCDateTime(0) origin.latitude = 12 origin.latitude_errors.confidence_level = 95 origin.longitude = 42 origin.depth_type = 'from location' assert origin.resource_id == \ ResourceIdentifier(id='smi:ch.ethz.sed/origin/37465') assert origin.latitude == 12 assert origin.latitude_errors.confidence_level == 95 assert origin.latitude_errors.uncertainty is None assert origin.longitude == 42 origin2 = Origin(force_resource_id=False) origin2.latitude = 13.4 assert origin2.depth_type is None assert origin2.resource_id is None assert origin2.latitude == 13.4 assert origin2.latitude_errors.confidence_level is None assert origin2.longitude is None
def test_write_pha_minimal(self): ori = Origin(time=UTC(0), latitude=42, longitude=43, depth=10000) pick = Pick(time=UTC(10), phase_hint='S', waveform_id=WaveformStreamID(station_code='STA')) del ori.latitude_errors del ori.longitude_errors del ori.depth_errors cat = Catalog([Event(origins=[ori], picks=[pick])]) with NamedTemporaryFile() as tf: tempfile = tf.name with self.assertWarnsRegex(UserWarning, 'Missing mag'): cat.write(tempfile, 'HYPODDPHA') cat2 = read_events(tempfile) self.assertEqual(len(cat2), 1) self.assertEqual(len(cat2[0].picks), 1)
def _deserialize(self, zmap_str): catalog = Catalog() for row in zmap_str.split("\n"): if len(row) == 0: continue origin = Origin() event = Event(origins=[origin]) event.preferred_origin_id = origin.resource_id.id # Begin value extraction columns = row.split("\t", 13)[:13] # ignore extra columns values = dict(zip(_STD_ZMAP_COLUMNS + _EXT_ZMAP_COLUMNS, columns)) # Extract origin origin.longitude = self._str2num(values.get("lon")) origin.latitude = self._str2num(values.get("lat")) depth = self._str2num(values.get("depth")) if depth is not None: origin.depth = depth * 1000.0 z_err = self._str2num(values.get("z_err")) if z_err is not None: origin.depth_errors.uncertainty = z_err * 1000.0 h_err = self._str2num(values.get("h_err")) if h_err is not None: ou = OriginUncertainty() ou.horizontal_uncertainty = h_err ou.preferred_description = "horizontal uncertainty" origin.origin_uncertainty = ou year = self._str2num(values.get("year")) if year is not None: t_fields = ["year", "month", "day", "hour", "minute", "second"] comps = [self._str2num(values.get(f)) for f in t_fields] if year % 1 != 0: origin.time = self._decyear2utc(year) elif any(v > 0 for v in comps[1:]): utc_args = [int(v) for v in comps if v is not None] origin.time = UTCDateTime(*utc_args) mag = self._str2num(values.get("mag")) # Extract magnitude if mag is not None: magnitude = Magnitude(mag=mag) m_err = self._str2num(values.get("m_err")) magnitude.mag_errors.uncertainty = m_err event.magnitudes.append(magnitude) event.preferred_magnitude_id = magnitude.resource_id.id catalog.append(event) return catalog
def __toOrigin(parser, origin_el): """ Parses a given origin etree element. :type parser: :class:`~obspy.core.util.xmlwrapper.XMLParser` :param parser: Open XMLParser object. :type origin_el: etree.element :param origin_el: origin element to be parsed. :return: A ObsPy :class:`~obspy.core.event.Origin` object. """ origin = Origin() # I guess setting the program used as the method id is fine. origin.method_id = parser.xpath2obj('program', origin_el) # Standard parameters. origin.time, origin.time_errors = \ __toTimeQuantity(parser, origin_el, "time") origin.latitude, origin.latitude_errors = \ __toFloatQuantity(parser, origin_el, "latitude") origin.longitude, origin.longitude_errors = \ __toFloatQuantity(parser, origin_el, "longitude") origin.depth, origin.depth_errors = \ __toFloatQuantity(parser, origin_el, "depth") # Figure out the depth type. depth_type = parser.xpath2obj("depth_type", origin_el, str) # Map Seishub specific depth type to the QuakeML depth type. if depth_type == "from location program": depth_type == "from location" origin.depth_type = "from location" # Earth model. origin.earth_model_id = parser.xpath2obj("earth_mod", origin_el, str) # Parse th origin uncertainty. Rather verbose but should cover all cases. pref_desc = parser.xpath2obj("originUncertainty/preferredDescription", origin_el, str) hor_uncert = parser.xpath2obj("originUncertainty/horizontalUncertainty", origin_el, float) min_hor_uncert = parser.xpath2obj(\ "originUncertainty/minHorizontalUncertainty", origin_el, float) max_hor_uncert = parser.xpath2obj(\ "originUncertainty/maxHorizontalUncertainty", origin_el, float) azi_max_hor_uncert = parser.xpath2obj(\ "originUncertainty/azimuthMaxHorizontalUncertainty", origin_el, float) origin_uncert = {} if pref_desc: origin_uncert["preferred_description"] = pref_desc if hor_uncert: origin_uncert["horizontal_uncertainty"] = hor_uncert if min_hor_uncert: origin_uncert["min_horizontal_uncertainty"] = min_hor_uncert if max_hor_uncert: origin_uncert["max_horizontal_uncertainty"] = max_hor_uncert if azi_max_hor_uncert: origin_uncert["azimuth_max_horizontal_uncertainty"] = \ azi_max_hor_uncert if origin_uncert: origin.origin_uncertainty = origin_uncert # Parse the OriginQuality if applicable. if not origin_el.xpath("originQuality"): return origin origin_quality_el = origin_el.xpath("originQuality")[0] origin.quality = OriginQuality() origin.quality.associated_phase_count = \ parser.xpath2obj("associatedPhaseCount", origin_quality_el, int) # QuakeML does apparently not distinguish between P and S wave phase # count. Some Seishub event files do. p_phase_count = parser.xpath2obj("P_usedPhaseCount", origin_quality_el, int) s_phase_count = parser.xpath2obj("S_usedPhaseCount", origin_quality_el, int) # Use both in case they are set. if p_phase_count and s_phase_count: phase_count = p_phase_count + s_phase_count # Also add two Seishub element file specific elements. origin.quality.p_used_phase_count = p_phase_count origin.quality.s_used_phase_count = s_phase_count # Otherwise the total usedPhaseCount should be specified. else: phase_count = parser.xpath2obj("usedPhaseCount", origin_quality_el, int) origin.quality.used_phase_count = phase_count origin.quality.associated_station_count = \ parser.xpath2obj("associatedStationCount", origin_quality_el, int) origin.quality.used_station_count = \ parser.xpath2obj("usedStationCount", origin_quality_el, int) origin.quality.depth_phase_count = \ parser.xpath2obj("depthPhaseCount", origin_quality_el, int) origin.quality.standard_error = \ parser.xpath2obj("standardError", origin_quality_el, float) origin.quality.azimuthal_gap = \ parser.xpath2obj("azimuthalGap", origin_quality_el, float) origin.quality.secondary_azimuthal_gap = \ parser.xpath2obj("secondaryAzimuthalGap", origin_quality_el, float) origin.quality.ground_truth_level = \ parser.xpath2obj("groundTruthLevel", origin_quality_el, float) origin.quality.minimum_distance = \ parser.xpath2obj("minimumDistance", origin_quality_el, float) origin.quality.maximum_distance = \ parser.xpath2obj("maximumDistance", origin_quality_el, float) origin.quality.median_distance = \ parser.xpath2obj("medianDistance", origin_quality_el, float) return origin
def __read_single_fnetmt_entry(line, **kwargs): """ Reads a single F-net moment tensor solution to a :class:`~obspy.core.event.Event` object. :param line: String containing moment tensor information. :type line: str. """ a = line.split() try: ot = UTCDateTime().strptime(a[0], '%Y/%m/%d,%H:%M:%S.%f') except ValueError: ot = UTCDateTime().strptime(a[0], '%Y/%m/%d,%H:%M:%S') lat, lon, depjma, magjma = map(float, a[1:5]) depjma *= 1000 region = a[5] strike = tuple(map(int, a[6].split(';'))) dip = tuple(map(int, a[7].split(';'))) rake = tuple(map(int, a[8].split(';'))) mo = float(a[9]) depmt = float(a[10]) * 1000 magmt = float(a[11]) var_red = float(a[12]) mxx, mxy, mxz, myy, myz, mzz, unit = map(float, a[13:20]) event_name = util.gen_sc3_id(ot) e = Event(event_type="earthquake") e.resource_id = _get_resource_id(event_name, 'event') # Standard JMA solution o_jma = Origin(time=ot, latitude=lat, longitude=lon, depth=depjma, depth_type="from location", region=region) o_jma.resource_id = _get_resource_id(event_name, 'origin', 'JMA') m_jma = Magnitude(mag=magjma, magnitude_type='ML', origin_id=o_jma.resource_id) m_jma.resource_id = _get_resource_id(event_name, 'magnitude', 'JMA') # MT solution o_mt = Origin(time=ot, latitude=lat, longitude=lon, depth=depmt, region=region, depth_type="from moment tensor inversion") o_mt.resource_id = _get_resource_id(event_name, 'origin', 'MT') m_mt = Magnitude(mag=magmt, magnitude_type='Mw', origin_id=o_mt.resource_id) m_mt.resource_id = _get_resource_id(event_name, 'magnitude', 'MT') foc_mec = FocalMechanism(triggering_origin_id=o_jma.resource_id) foc_mec.resource_id = _get_resource_id(event_name, "focal_mechanism") nod1 = NodalPlane(strike=strike[0], dip=dip[0], rake=rake[0]) nod2 = NodalPlane(strike=strike[1], dip=dip[1], rake=rake[1]) nod = NodalPlanes(nodal_plane_1=nod1, nodal_plane_2=nod2) foc_mec.nodal_planes = nod tensor = Tensor(m_rr=mxx, m_tt=myy, m_pp=mzz, m_rt=mxy, m_rp=mxz, m_tp=myz) cm = Comment(text="Basis system: North,East,Down (Jost and \ Herrmann 1989") cm.resource_id = _get_resource_id(event_name, 'comment', 'mt') mt = MomentTensor(derived_origin_id=o_mt.resource_id, moment_magnitude_id=m_mt.resource_id, scalar_moment=mo, comments=[cm], tensor=tensor, variance_reduction=var_red) mt.resource_id = _get_resource_id(event_name, 'moment_tensor') foc_mec.moment_tensor = mt e.origins = [o_jma, o_mt] e.magnitudes = [m_jma, m_mt] e.focal_mechanisms = [foc_mec] e.preferred_magnitude_id = m_mt.resource_id.id e.preferred_origin_id = o_mt.resource_id.id e.preferred_focal_mechanism_id = foc_mec.resource_id.id return e
def read_nlloc_hyp(filename, coordinate_converter=None, picks=None, **kwargs): """ Reads a NonLinLoc Hypocenter-Phase file to a :class:`~obspy.core.event.Catalog` object. .. note:: Coordinate conversion from coordinate frame of NonLinLoc model files / location run to WGS84 has to be specified explicitly by the user if necessary. .. note:: An example can be found on the :mod:`~obspy.nlloc` submodule front page in the documentation pages. :param filename: File or file-like object in text mode. :type coordinate_converter: func :param coordinate_converter: Function to convert (x, y, z) coordinates of NonLinLoc output to geographical coordinates and depth in meters (longitude, latitude, depth in kilometers). If left `None` NonLinLoc (x, y, z) output is left unchanged (e.g. if it is in geographical coordinates already like for NonLinLoc in global mode). The function should accept three arguments x, y, z and return a tuple of three values (lon, lat, depth in kilometers). :type picks: list of :class:`~obspy.core.event.Pick` :param picks: Original picks used to generate the NonLinLoc location. If provided, the output event will include the original picks and the arrivals in the output origin will link to them correctly (with their `pick_id` attribute). If not provided, the output event will include (the rather basic) pick information that can be reconstructed from the NonLinLoc hypocenter-phase file. :rtype: :class:`~obspy.core.event.Catalog` """ if not hasattr(filename, "read"): # Check if it exists, otherwise assume its a string. try: with open(filename, "rt") as fh: data = fh.read() except: try: data = filename.decode() except: data = str(filename) data = data.strip() else: data = filename.read() if hasattr(data, "decode"): data = data.decode() lines = data.splitlines() # remember picks originally used in location, if provided original_picks = picks if original_picks is None: original_picks = [] # determine indices of block start/end of the NLLOC output file indices_hyp = [None, None] indices_phases = [None, None] for i, line in enumerate(lines): if line.startswith("NLLOC "): indices_hyp[0] = i elif line.startswith("END_NLLOC"): indices_hyp[1] = i elif line.startswith("PHASE "): indices_phases[0] = i elif line.startswith("END_PHASE"): indices_phases[1] = i if any([i is None for i in indices_hyp]): msg = ("NLLOC HYP file seems corrupt," " could not detect 'NLLOC' and 'END_NLLOC' lines.") raise RuntimeError(msg) # strip any other lines around NLLOC block lines = lines[indices_hyp[0]:indices_hyp[1]] # extract PHASES lines (if any) if any(indices_phases): if not all(indices_phases): msg = ("NLLOC HYP file seems corrupt, 'PHASE' block is corrupt.") raise RuntimeError(msg) i1, i2 = indices_phases lines, phases_lines = lines[:i1] + lines[i2 + 1:], lines[i1 + 1:i2] else: phases_lines = [] lines = dict([line.split(None, 1) for line in lines]) line = lines["SIGNATURE"] line = line.rstrip().split('"')[1] signature, version, date, time = line.rsplit(" ", 3) creation_time = UTCDateTime().strptime(date + time, str("%d%b%Y%Hh%Mm%S")) # maximum likelihood origin location info line line = lines["HYPOCENTER"] x, y, z = map(float, line.split()[1:7:2]) if coordinate_converter: x, y, z = coordinate_converter(x, y, z) # origin time info line line = lines["GEOGRAPHIC"] year, month, day, hour, minute = map(int, line.split()[1:6]) seconds = float(line.split()[6]) time = UTCDateTime(year, month, day, hour, minute, seconds) # distribution statistics line line = lines["STATISTICS"] covariance_XX = float(line.split()[7]) covariance_YY = float(line.split()[13]) covariance_ZZ = float(line.split()[17]) stats_info_string = str( "Note: Depth/Latitude/Longitude errors are calculated from covariance " "matrix as 1D marginal (Lon/Lat errors as great circle degrees) " "while OriginUncertainty min/max horizontal errors are calculated " "from 2D error ellipsoid and are therefore seemingly higher compared " "to 1D errors. Error estimates can be reconstructed from the " "following original NonLinLoc error statistics line:\nSTATISTICS " + lines["STATISTICS"]) # goto location quality info line line = lines["QML_OriginQuality"].split() (assoc_phase_count, used_phase_count, assoc_station_count, used_station_count, depth_phase_count) = map(int, line[1:11:2]) stderr, az_gap, sec_az_gap = map(float, line[11:17:2]) gt_level = line[17] min_dist, max_dist, med_dist = map(float, line[19:25:2]) # goto location quality info line line = lines["QML_OriginUncertainty"] hor_unc, min_hor_unc, max_hor_unc, hor_unc_azim = \ map(float, line.split()[1:9:2]) # assign origin info event = Event() cat = Catalog(events=[event]) o = Origin() event.origins = [o] o.origin_uncertainty = OriginUncertainty() o.quality = OriginQuality() ou = o.origin_uncertainty oq = o.quality o.comments.append(Comment(text=stats_info_string)) cat.creation_info.creation_time = UTCDateTime() cat.creation_info.version = "ObsPy %s" % __version__ event.creation_info = CreationInfo(creation_time=creation_time, version=version) event.creation_info.version = version o.creation_info = CreationInfo(creation_time=creation_time, version=version) # negative values can appear on diagonal of covariance matrix due to a # precision problem in NLLoc implementation when location coordinates are # large compared to the covariances. o.longitude = x try: o.longitude_errors.uncertainty = kilometer2degrees(sqrt(covariance_XX)) except ValueError: if covariance_XX < 0: msg = ("Negative value in XX value of covariance matrix, not " "setting longitude error (epicentral uncertainties will " "still be set in origin uncertainty).") warnings.warn(msg) else: raise o.latitude = y try: o.latitude_errors.uncertainty = kilometer2degrees(sqrt(covariance_YY)) except ValueError: if covariance_YY < 0: msg = ("Negative value in YY value of covariance matrix, not " "setting longitude error (epicentral uncertainties will " "still be set in origin uncertainty).") warnings.warn(msg) else: raise o.depth = z * 1e3 # meters! o.depth_errors.uncertainty = sqrt(covariance_ZZ) * 1e3 # meters! o.depth_errors.confidence_level = 68 o.depth_type = str("from location") o.time = time ou.horizontal_uncertainty = hor_unc ou.min_horizontal_uncertainty = min_hor_unc ou.max_horizontal_uncertainty = max_hor_unc # values of -1 seem to be used for unset values, set to None for field in ("horizontal_uncertainty", "min_horizontal_uncertainty", "max_horizontal_uncertainty"): if ou.get(field, -1) == -1: ou[field] = None else: ou[field] *= 1e3 # meters! ou.azimuth_max_horizontal_uncertainty = hor_unc_azim ou.preferred_description = str("uncertainty ellipse") ou.confidence_level = 68 # NonLinLoc in general uses 1-sigma (68%) level oq.standard_error = stderr oq.azimuthal_gap = az_gap oq.secondary_azimuthal_gap = sec_az_gap oq.used_phase_count = used_phase_count oq.used_station_count = used_station_count oq.associated_phase_count = assoc_phase_count oq.associated_station_count = assoc_station_count oq.depth_phase_count = depth_phase_count oq.ground_truth_level = gt_level oq.minimum_distance = kilometer2degrees(min_dist) oq.maximum_distance = kilometer2degrees(max_dist) oq.median_distance = kilometer2degrees(med_dist) # go through all phase info lines for line in phases_lines: line = line.split() arrival = Arrival() o.arrivals.append(arrival) station = str(line[0]) phase = str(line[4]) arrival.phase = phase arrival.distance = kilometer2degrees(float(line[21])) arrival.azimuth = float(line[23]) arrival.takeoff_angle = float(line[24]) arrival.time_residual = float(line[16]) arrival.time_weight = float(line[17]) pick = Pick() wid = WaveformStreamID(station_code=station) date, hourmin, sec = map(str, line[6:9]) t = UTCDateTime().strptime(date + hourmin, "%Y%m%d%H%M") + float(sec) pick.waveform_id = wid pick.time = t pick.time_errors.uncertainty = float(line[10]) pick.phase_hint = phase pick.onset = ONSETS.get(line[3].lower(), None) pick.polarity = POLARITIES.get(line[5].lower(), None) # try to determine original pick for each arrival for pick_ in original_picks: wid = pick_.waveform_id if station == wid.station_code and phase == pick_.phase_hint: pick = pick_ break else: # warn if original picks were specified and we could not associate # the arrival correctly if original_picks: msg = ("Could not determine corresponding original pick for " "arrival. " "Falling back to pick information in NonLinLoc " "hypocenter-phase file.") warnings.warn(msg) event.picks.append(pick) arrival.pick_id = pick.resource_id return cat
def build(self): """ Build an obspy moment tensor focal mech event This makes the tensor output into an Event containing: 1) a FocalMechanism with a MomentTensor, NodalPlanes, and PrincipalAxes 2) a Magnitude of the Mw from the Tensor Which is what we want for outputting QuakeML using the (slightly modified) obspy code. Input ----- filehandle => open file OR str from filehandle.read() Output ------ event => instance of Event() class as described above """ p = self.parser event = Event(event_type='earthquake') origin = Origin() focal_mech = FocalMechanism() nodal_planes = NodalPlanes() moment_tensor = MomentTensor() principal_ax = PrincipalAxes() magnitude = Magnitude() data_used = DataUsed() creation_info = CreationInfo(agency_id='NN') ev_mode = 'automatic' ev_stat = 'preliminary' evid = None orid = None # Parse the entire file line by line. for n,l in enumerate(p.line): if 'REVIEWED BY NSL STAFF' in l: ev_mode = 'manual' ev_stat = 'reviewed' if 'Event ID' in l: evid = p._id(n) if 'Origin ID' in l: orid = p._id(n) if 'Ichinose' in l: moment_tensor.category = 'regional' if re.match(r'^\d{4}\/\d{2}\/\d{2}', l): ev = p._event_info(n) if 'Depth' in l: derived_depth = p._depth(n) if 'Mw' in l: magnitude.mag = p._mw(n) magnitude.magnitude_type = 'Mw' if 'Mo' in l and 'dyne' in l: moment_tensor.scalar_moment = p._mo(n) if 'Percent Double Couple' in l: moment_tensor.double_couple = p._percent(n) if 'Percent CLVD' in l: moment_tensor.clvd = p._percent(n) if 'Epsilon' in l: moment_tensor.variance = p._epsilon(n) if 'Percent Variance Reduction' in l: moment_tensor.variance_reduction = p._percent(n) if 'Major Double Couple' in l and 'strike' in p.line[n+1]: np = p._double_couple(n) nodal_planes.nodal_plane_1 = NodalPlane(*np[0]) nodal_planes.nodal_plane_2 = NodalPlane(*np[1]) nodal_planes.preferred_plane = 1 if 'Spherical Coordinates' in l: mt = p._mt_sphere(n) moment_tensor.tensor = Tensor( m_rr = mt['Mrr'], m_tt = mt['Mtt'], m_pp = mt['Mff'], m_rt = mt['Mrt'], m_rp = mt['Mrf'], m_tp = mt['Mtf'], ) if 'Eigenvalues and eigenvectors of the Major Double Couple' in l: ax = p._vectors(n) principal_ax.t_axis = Axis(ax['T']['trend'], ax['T']['plunge'], ax['T']['ev']) principal_ax.p_axis = Axis(ax['P']['trend'], ax['P']['plunge'], ax['P']['ev']) principal_ax.n_axis = Axis(ax['N']['trend'], ax['N']['plunge'], ax['N']['ev']) if 'Number of Stations' in l: data_used.station_count = p._number_of_stations(n) if 'Maximum' in l and 'Gap' in l: focal_mech.azimuthal_gap = p._gap(n) if re.match(r'^Date', l): creation_info.creation_time = p._creation_time(n) # Creation Time creation_info.version = orid # Fill in magnitude values magnitude.evaluation_mode = ev_mode magnitude.evaluation_status = ev_stat magnitude.creation_info = creation_info.copy() magnitude.resource_id = self._rid(magnitude) # Stub origin origin.time = ev.get('time') origin.latitude = ev.get('lat') origin.longitude = ev.get('lon') origin.depth = derived_depth * 1000. origin.depth_type = "from moment tensor inversion" origin.creation_info = creation_info.copy() # Unique from true origin ID _oid = self._rid(origin) origin.resource_id = ResourceIdentifier(str(_oid) + '/mt') del _oid # Make an id for the MT that references this origin ogid = str(origin.resource_id) doid = ResourceIdentifier(ogid, referred_object=origin) # Make an id for the moment tensor mag which references this mag mrid = str(magnitude.resource_id) mmid = ResourceIdentifier(mrid, referred_object=magnitude) # MT todo: could check/use URL for RID if parsing the php file moment_tensor.evaluation_mode = ev_mode moment_tensor.evaluation_status = ev_stat moment_tensor.data_used = data_used moment_tensor.moment_magnitude_id = mmid moment_tensor.derived_origin_id = doid moment_tensor.creation_info = creation_info.copy() moment_tensor.resource_id = self._rid(moment_tensor) # Fill in focal_mech values focal_mech.nodal_planes = nodal_planes focal_mech.moment_tensor = moment_tensor focal_mech.principal_axes = principal_ax focal_mech.creation_info = creation_info.copy() focal_mech.resource_id = self._rid(focal_mech) # add mech and new magnitude to event event.focal_mechanisms = [focal_mech] event.magnitudes = [magnitude] event.origins = [origin] event.creation_info = creation_info.copy() # If an MT was done, that's the preferred mag/mech event.preferred_magnitude_id = str(magnitude.resource_id) event.preferred_focal_mechanism_id = str(focal_mech.resource_id) if evid: event.creation_info.version = evid event.resource_id = self._rid(event) self.event = event
def _parse_record_dp(self, line, event): """ Parses the 'source parameter data - primary' record Dp """ source_contributor = line[2:6].strip() computation_type = line[6] exponent = self._int_zero(line[7]) scale = math.pow(10, exponent) centroid_origin_time = line[8:14] + '.' + line[14] orig_time_stderr = line[15:17] if orig_time_stderr == 'FX': orig_time_stderr = 'Fixed' else: orig_time_stderr = \ self._float_with_format(orig_time_stderr, '2.1', scale) centroid_latitude = self._float_with_format(line[17:21], '4.2') lat_type = line[21] if centroid_latitude is not None: centroid_latitude *= self._coordinate_sign(lat_type) lat_stderr = line[22:25] if lat_stderr == 'FX': lat_stderr = 'Fixed' else: lat_stderr = self._float_with_format(lat_stderr, '3.2', scale) centroid_longitude = self._float_with_format(line[25:30], '5.2') lon_type = line[30] if centroid_longitude is not None: centroid_longitude *= self._coordinate_sign(lon_type) lon_stderr = line[31:34] if lon_stderr == 'FX': lon_stderr = 'Fixed' else: lon_stderr = self._float_with_format(lon_stderr, '3.2', scale) centroid_depth = self._float_with_format(line[34:38], '4.1') depth_stderr = line[38:40] if depth_stderr == 'FX' or depth_stderr == 'BD': depth_stderr = 'Fixed' else: depth_stderr = self._float_with_format(depth_stderr, '2.1', scale) station_number = self._int_zero(line[40:43]) component_number = self._int_zero(line[43:46]) station_number2 = self._int_zero(line[46:48]) component_number2 = self._int_zero(line[48:51]) # unused: half_duration = self._float_with_format(line[51:54], '3.1') moment = self._float_with_format(line[54:56], '2.1') moment_stderr = self._float_with_format(line[56:58], '2.1') moment_exponent = self._int(line[58:60]) if (moment is not None) and (moment_exponent is not None): moment *= math.pow(10, moment_exponent) if (moment_stderr is not None) and (moment_exponent is not None): moment_stderr *= math.pow(10, moment_exponent) evid = event.resource_id.id.split('/')[-1] # Create a new origin only if centroid time is defined: origin = None if centroid_origin_time.strip() != '.': origin = Origin() res_id = '/'.join((res_id_prefix, 'origin', evid, source_contributor.lower(), 'mw' + computation_type.lower())) origin.resource_id = ResourceIdentifier(id=res_id) origin.creation_info = \ CreationInfo(agency_id=source_contributor) date = event.origins[0].time.strftime('%Y%m%d') origin.time = UTCDateTime(date + centroid_origin_time) # Check if centroid time is on the next day: if origin.time < event.origins[0].time: origin.time += timedelta(days=1) self._store_uncertainty(origin.time_errors, orig_time_stderr) origin.latitude = centroid_latitude origin.longitude = centroid_longitude origin.depth = centroid_depth * 1000 if lat_stderr == 'Fixed' and lon_stderr == 'Fixed': origin.epicenter_fixed = True else: self._store_uncertainty(origin.latitude_errors, self._lat_err_to_deg(lat_stderr)) self._store_uncertainty(origin.longitude_errors, self._lon_err_to_deg(lon_stderr, origin.latitude)) if depth_stderr == 'Fixed': origin.depth_type = 'operator assigned' else: origin.depth_type = 'from location' self._store_uncertainty(origin.depth_errors, depth_stderr, scale=1000) quality = OriginQuality() quality.used_station_count = \ station_number + station_number2 quality.used_phase_count = \ component_number + component_number2 origin.quality = quality origin.origin_type = 'centroid' event.origins.append(origin) focal_mechanism = FocalMechanism() res_id = '/'.join((res_id_prefix, 'focalmechanism', evid, source_contributor.lower(), 'mw' + computation_type.lower())) focal_mechanism.resource_id = ResourceIdentifier(id=res_id) focal_mechanism.creation_info = \ CreationInfo(agency_id=source_contributor) moment_tensor = MomentTensor() if origin is not None: moment_tensor.derived_origin_id = origin.resource_id else: # this is required for QuakeML validation: res_id = '/'.join((res_id_prefix, 'no-origin')) moment_tensor.derived_origin_id = \ ResourceIdentifier(id=res_id) for mag in event.magnitudes: if mag.creation_info.agency_id == source_contributor: moment_tensor.moment_magnitude_id = mag.resource_id res_id = '/'.join((res_id_prefix, 'momenttensor', evid, source_contributor.lower(), 'mw' + computation_type.lower())) moment_tensor.resource_id = ResourceIdentifier(id=res_id) moment_tensor.scalar_moment = moment self._store_uncertainty(moment_tensor.scalar_moment_errors, moment_stderr) data_used = DataUsed() data_used.station_count = station_number + station_number2 data_used.component_count = component_number + component_number2 if computation_type == 'C': res_id = '/'.join((res_id_prefix, 'methodID=CMT')) focal_mechanism.method_id = ResourceIdentifier(id=res_id) # CMT algorithm uses long-period body waves, # very-long-period surface waves and # intermediate period surface waves (since 2004 # for shallow and intermediate-depth earthquakes # --Ekstrom et al., 2012) data_used.wave_type = 'combined' if computation_type == 'M': res_id = '/'.join((res_id_prefix, 'methodID=moment_tensor')) focal_mechanism.method_id = ResourceIdentifier(id=res_id) # FIXME: not sure which kind of data is used by # "moment tensor" algorithm. data_used.wave_type = 'unknown' elif computation_type == 'B': res_id = '/'.join((res_id_prefix, 'methodID=broadband_data')) focal_mechanism.method_id = ResourceIdentifier(id=res_id) # FIXME: is 'combined' correct here? data_used.wave_type = 'combined' elif computation_type == 'F': res_id = '/'.join((res_id_prefix, 'methodID=P-wave_first_motion')) focal_mechanism.method_id = ResourceIdentifier(id=res_id) data_used.wave_type = 'P waves' elif computation_type == 'S': res_id = '/'.join((res_id_prefix, 'methodID=scalar_moment')) focal_mechanism.method_id = ResourceIdentifier(id=res_id) # FIXME: not sure which kind of data is used # for scalar moment determination. data_used.wave_type = 'unknown' moment_tensor.data_used = [data_used] focal_mechanism.moment_tensor = moment_tensor event.focal_mechanisms.append(focal_mechanism) return focal_mechanism
def _parse_record_hy(self, line): """ Parses the 'hypocenter' record HY """ date = line[2:10] time = line[11:20] # unused: location_quality = line[20] latitude = self._float(line[21:27]) lat_type = line[27] longitude = self._float(line[29:36]) lon_type = line[36] depth = self._float(line[38:43]) # unused: depth_quality = line[43] standard_dev = self._float(line[44:48]) station_number = self._int(line[48:51]) # unused: version_flag = line[51] fe_region_number = line[52:55] fe_region_name = self._decode_fe_region_number(fe_region_number) source_code = line[55:60].strip() event = Event() # FIXME: a smarter way to define evid? evid = date + time res_id = '/'.join((res_id_prefix, 'event', evid)) event.resource_id = ResourceIdentifier(id=res_id) description = EventDescription( type='region name', text=fe_region_name) event.event_descriptions.append(description) description = EventDescription( type='Flinn-Engdahl region', text=fe_region_number) event.event_descriptions.append(description) origin = Origin() res_id = '/'.join((res_id_prefix, 'origin', evid)) origin.resource_id = ResourceIdentifier(id=res_id) origin.creation_info = CreationInfo() if source_code: origin.creation_info.agency_id = source_code else: origin.creation_info.agency_id = 'USGS-NEIC' res_id = '/'.join((res_id_prefix, 'earthmodel/ak135')) origin.earth_model_id = ResourceIdentifier(id=res_id) origin.time = UTCDateTime(date + time) origin.latitude = latitude * self._coordinate_sign(lat_type) origin.longitude = longitude * self._coordinate_sign(lon_type) origin.depth = depth * 1000 origin.depth_type = 'from location' origin.quality = OriginQuality() origin.quality.associated_station_count = station_number origin.quality.standard_error = standard_dev # associated_phase_count can be incremented in records 'P ' and 'S ' origin.quality.associated_phase_count = 0 # depth_phase_count can be incremented in record 'S ' origin.quality.depth_phase_count = 0 origin.origin_type = 'hypocenter' origin.region = fe_region_name event.origins.append(origin) return event
def _read_ndk(filename, *args, **kwargs): # @UnusedVariable """ Reads an NDK file to a :class:`~obspy.core.event.Catalog` object. :param filename: File or file-like object in text mode. """ # Read the whole file at once. While an iterator would be more efficient # the largest NDK file out in the wild is 13.7 MB so it does not matter # much. if not hasattr(filename, "read"): # Check if it exists, otherwise assume its a string. try: with open(filename, "rt") as fh: data = fh.read() except: try: data = filename.decode() except: data = str(filename) data = data.strip() else: data = filename.read() if hasattr(data, "decode"): data = data.decode() # Create iterator that yields lines. def lines_iter(): prev_line = -1 while True: next_line = data.find("\n", prev_line + 1) if next_line < 0: break yield data[prev_line + 1: next_line] prev_line = next_line if len(data) > prev_line + 1: yield data[prev_line + 1:] # Use one Flinn Engdahl object for all region determinations. fe = FlinnEngdahl() cat = Catalog(resource_id=_get_resource_id("catalog", str(uuid.uuid4()))) # Loop over 5 lines at once. for _i, lines in enumerate(itertools.zip_longest(*[lines_iter()] * 5)): if None in lines: msg = "Skipped last %i lines. Not a multiple of 5 lines." % ( lines.count(None)) warnings.warn(msg, ObsPyNDKWarning) continue # Parse the lines to a human readable dictionary. try: record = _read_lines(*lines) except (ValueError, ObsPyNDKException): exc = traceback.format_exc() msg = ( "Could not parse event %i (faulty file?). Will be " "skipped. Lines of the event:\n" "\t%s\n" "%s") % (_i + 1, "\n\t".join(lines), exc) warnings.warn(msg, ObsPyNDKWarning) continue # Use one creation info for essentially every item. creation_info = CreationInfo( agency_id="GCMT", version=record["version_code"] ) # Use the ObsPy Flinn Engdahl region determiner as the region in the # NDK files is oftentimes trimmed. region = fe.get_region(record["centroid_longitude"], record["centroid_latitude"]) # Create an event object. event = Event( force_resource_id=False, event_type="earthquake", event_type_certainty="known", event_descriptions=[ EventDescription(text=region, type="Flinn-Engdahl region"), EventDescription(text=record["cmt_event_name"], type="earthquake name") ] ) # Assemble the time for the reference origin. try: time = _parse_date_time(record["date"], record["time"]) except ObsPyNDKException: msg = ("Invalid time in event %i. '%s' and '%s' cannot be " "assembled to a valid time. Event will be skipped.") % \ (_i + 1, record["date"], record["time"]) warnings.warn(msg, ObsPyNDKWarning) continue # Create two origins, one with the reference latitude/longitude and # one with the centroidal values. ref_origin = Origin( force_resource_id=False, time=time, longitude=record["hypo_lng"], latitude=record["hypo_lat"], # Convert to m. depth=record["hypo_depth_in_km"] * 1000.0, origin_type="hypocenter", comments=[Comment(text="Hypocenter catalog: %s" % record["hypocenter_reference_catalog"], force_resource_id=False)] ) ref_origin.comments[0].resource_id = _get_resource_id( record["cmt_event_name"], "comment", tag="ref_origin") ref_origin.resource_id = _get_resource_id(record["cmt_event_name"], "origin", tag="reforigin") cmt_origin = Origin( force_resource_id=False, longitude=record["centroid_longitude"], longitude_errors={ "uncertainty": record["centroid_longitude_error"]}, latitude=record["centroid_latitude"], latitude_errors={ "uncertainty": record["centroid_latitude_error"]}, # Convert to m. depth=record["centroid_depth_in_km"] * 1000.0, depth_errors={ "uncertainty": record["centroid_depth_in_km_error"] * 1000}, time=ref_origin["time"] + record["centroid_time"], time_errors={"uncertainty": record["centroid_time_error"]}, depth_type=record["type_of_centroid_depth"], origin_type="centroid", time_fixed=False, epicenter_fixed=False, creation_info=creation_info.copy() ) cmt_origin.resource_id = _get_resource_id(record["cmt_event_name"], "origin", tag="cmtorigin") event.origins = [ref_origin, cmt_origin] event.preferred_origin_id = cmt_origin.resource_id.id # Create the magnitude object. mag = Magnitude( force_resource_id=False, mag=round(record["Mw"], 2), magnitude_type="Mwc", origin_id=cmt_origin.resource_id, creation_info=creation_info.copy() ) mag.resource_id = _get_resource_id(record["cmt_event_name"], "magnitude", tag="moment_mag") event.magnitudes = [mag] event.preferred_magnitude_id = mag.resource_id.id # Add the reported mb, MS magnitudes as additional magnitude objects. event.magnitudes.append(Magnitude( force_resource_id=False, mag=record["mb"], magnitude_type="mb", comments=[Comment( force_resource_id=False, text="Reported magnitude in NDK file. Most likely 'mb'." )] )) event.magnitudes[-1].comments[-1].resource_id = _get_resource_id( record["cmt_event_name"], "comment", tag="mb_magnitude") event.magnitudes[-1].resource_id = _get_resource_id( record["cmt_event_name"], "magnitude", tag="mb") event.magnitudes.append(Magnitude( force_resource_id=False, mag=record["MS"], magnitude_type="MS", comments=[Comment( force_resource_id=False, text="Reported magnitude in NDK file. Most likely 'MS'." )] )) event.magnitudes[-1].comments[-1].resource_id = _get_resource_id( record["cmt_event_name"], "comment", tag="MS_magnitude") event.magnitudes[-1].resource_id = _get_resource_id( record["cmt_event_name"], "magnitude", tag="MS") # Take care of the moment tensor. tensor = Tensor( m_rr=record["m_rr"], m_rr_errors={"uncertainty": record["m_rr_error"]}, m_pp=record["m_pp"], m_pp_errors={"uncertainty": record["m_pp_error"]}, m_tt=record["m_tt"], m_tt_errors={"uncertainty": record["m_tt_error"]}, m_rt=record["m_rt"], m_rt_errors={"uncertainty": record["m_rt_error"]}, m_rp=record["m_rp"], m_rp_errors={"uncertainty": record["m_rp_error"]}, m_tp=record["m_tp"], m_tp_errors={"uncertainty": record["m_tp_error"]}, creation_info=creation_info.copy() ) mt = MomentTensor( force_resource_id=False, scalar_moment=record["scalar_moment"], tensor=tensor, data_used=[DataUsed(**i) for i in record["data_used"]], inversion_type=record["source_type"], source_time_function=SourceTimeFunction( type=record["moment_rate_type"], duration=record["moment_rate_duration"] ), derived_origin_id=cmt_origin.resource_id, creation_info=creation_info.copy() ) mt.resource_id = _get_resource_id(record["cmt_event_name"], "momenttensor") axis = [Axis(**i) for i in record["principal_axis"]] focmec = FocalMechanism( force_resource_id=False, moment_tensor=mt, principal_axes=PrincipalAxes( # The ordering is the same as for the IRIS SPUD service and # from a website of the Saint Louis University Earthquake # center so it should be correct. t_axis=axis[0], p_axis=axis[2], n_axis=axis[1] ), nodal_planes=NodalPlanes( nodal_plane_1=NodalPlane(**record["nodal_plane_1"]), nodal_plane_2=NodalPlane(**record["nodal_plane_2"]) ), comments=[ Comment(force_resource_id=False, text="CMT Analysis Type: %s" % record["cmt_type"].capitalize()), Comment(force_resource_id=False, text="CMT Timestamp: %s" % record["cmt_timestamp"])], creation_info=creation_info.copy() ) focmec.comments[0].resource_id = _get_resource_id( record["cmt_event_name"], "comment", tag="cmt_type") focmec.comments[1].resource_id = _get_resource_id( record["cmt_event_name"], "comment", tag="cmt_timestamp") focmec.resource_id = _get_resource_id(record["cmt_event_name"], "focal_mechanism") event.focal_mechanisms = [focmec] event.preferred_focal_mechanism_id = focmec.resource_id.id # Set at end to avoid duplicate resource id warning. event.resource_id = _get_resource_id(record["cmt_event_name"], "event") cat.append(event) if len(cat) == 0: msg = "No valid events found in NDK file." raise ObsPyNDKException(msg) return cat
def par2quakeml(Par_filename, QuakeML_filename, rotation_axis=[0.0, 1.0, 0.0], rotation_angle=-57.5, origin_time="2000-01-01 00:00:00.0", event_type="other event"): # initialise event ev = Event() # open and read Par file fid = open(Par_filename, 'r') fid.readline() fid.readline() fid.readline() fid.readline() lat_old = 90.0 - float(fid.readline().strip().split()[0]) lon_old = float(fid.readline().strip().split()[0]) depth = float(fid.readline().strip().split()[0]) fid.readline() Mtt_old = float(fid.readline().strip().split()[0]) Mpp_old = float(fid.readline().strip().split()[0]) Mrr_old = float(fid.readline().strip().split()[0]) Mtp_old = float(fid.readline().strip().split()[0]) Mtr_old = float(fid.readline().strip().split()[0]) Mpr_old = float(fid.readline().strip().split()[0]) # rotate event into physical domain lat, lon = rot.rotate_lat_lon(lat_old, lon_old, rotation_axis, rotation_angle) Mrr, Mtt, Mpp, Mtr, Mpr, Mtp = rot.rotate_moment_tensor( Mrr_old, Mtt_old, Mpp_old, Mtr_old, Mpr_old, Mtp_old, lat_old, lon_old, rotation_axis, rotation_angle) # populate event origin data ev.event_type = event_type ev_origin = Origin() ev_origin.time = UTCDateTime(origin_time) ev_origin.latitude = lat ev_origin.longitude = lon ev_origin.depth = depth ev.origins.append(ev_origin) # populte event moment tensor ev_tensor = Tensor() ev_tensor.m_rr = Mrr ev_tensor.m_tt = Mtt ev_tensor.m_pp = Mpp ev_tensor.m_rt = Mtr ev_tensor.m_rp = Mpr ev_tensor.m_tp = Mtp ev_momenttensor = MomentTensor() ev_momenttensor.tensor = ev_tensor ev_momenttensor.scalar_moment = np.sqrt(Mrr ** 2 + Mtt ** 2 + Mpp ** 2 + Mtr ** 2 + Mpr ** 2 + Mtp ** 2) ev_focalmechanism = FocalMechanism() ev_focalmechanism.moment_tensor = ev_momenttensor ev_focalmechanism.nodal_planes = NodalPlanes().setdefault(0, 0) ev.focal_mechanisms.append(ev_focalmechanism) # populate event magnitude ev_magnitude = Magnitude() ev_magnitude.mag = 0.667 * (np.log10(ev_momenttensor.scalar_moment) - 9.1) ev_magnitude.magnitude_type = 'Mw' ev.magnitudes.append(ev_magnitude) # write QuakeML file cat = Catalog() cat.append(ev) cat.write(QuakeML_filename, format="quakeml") # clean up fid.close()
def __toOrigin(parser, origin_el): """ Parses a given origin etree element. :type parser: :class:`~obspy.core.util.xmlwrapper.XMLParser` :param parser: Open XMLParser object. :type origin_el: etree.element :param origin_el: origin element to be parsed. :return: A ObsPy :class:`~obspy.core.event.Origin` object. """ global CURRENT_TYPE origin = Origin() origin.resource_id = ResourceIdentifier(prefix="/".join([RESOURCE_ROOT, "origin"])) # I guess setting the program used as the method id is fine. origin.method_id = "%s/location_method/%s/1" % (RESOURCE_ROOT, parser.xpath2obj('program', origin_el)) if str(origin.method_id).lower().endswith("none"): origin.method_id = None # Standard parameters. origin.time, origin.time_errors = \ __toTimeQuantity(parser, origin_el, "time") origin.latitude, origin_latitude_error = \ __toFloatQuantity(parser, origin_el, "latitude") origin.longitude, origin_longitude_error = \ __toFloatQuantity(parser, origin_el, "longitude") origin.depth, origin.depth_errors = \ __toFloatQuantity(parser, origin_el, "depth") if origin_longitude_error: origin_longitude_error = origin_longitude_error["uncertainty"] if origin_latitude_error: origin_latitude_error = origin_latitude_error["uncertainty"] # Figure out the depth type. depth_type = parser.xpath2obj("depth_type", origin_el) # Map Seishub specific depth type to the QuakeML depth type. if depth_type == "from location program": depth_type = "from location" if depth_type is not None: origin.depth_type = depth_type # XXX: CHECK DEPTH ORIENTATION!! if CURRENT_TYPE == "seiscomp3": origin.depth *= 1000 if origin.depth_errors.uncertainty: origin.depth_errors.uncertainty *= 1000 else: # Convert to m. origin.depth *= -1000 if origin.depth_errors.uncertainty: origin.depth_errors.uncertainty *= 1000 # Earth model. earth_mod = parser.xpath2obj('earth_mod', origin_el, str) if earth_mod: earth_mod = earth_mod.split() earth_mod = ",".join(earth_mod) origin.earth_model_id = "%s/earth_model/%s/1" % (RESOURCE_ROOT, earth_mod) if (origin_latitude_error is None or origin_longitude_error is None) and \ CURRENT_TYPE not in ["seiscomp3", "toni"]: print "AAAAAAAAAAAAA" raise Exception if origin_latitude_error and origin_latitude_error: if CURRENT_TYPE in ["baynet", "obspyck"]: uncert = OriginUncertainty() if origin_latitude_error > origin_longitude_error: uncert.azimuth_max_horizontal_uncertainty = 0 else: uncert.azimuth_max_horizontal_uncertainty = 90 uncert.min_horizontal_uncertainty, \ uncert.max_horizontal_uncertainty = \ sorted([origin_longitude_error, origin_latitude_error]) uncert.min_horizontal_uncertainty *= 1000.0 uncert.max_horizontal_uncertainty *= 1000.0 uncert.preferred_description = "uncertainty ellipse" origin.origin_uncertainty = uncert elif CURRENT_TYPE == "earthworm": uncert = OriginUncertainty() uncert.horizontal_uncertainty = origin_latitude_error uncert.horizontal_uncertainty *= 1000.0 uncert.preferred_description = "horizontal uncertainty" origin.origin_uncertainty = uncert elif CURRENT_TYPE in ["seiscomp3", "toni"]: pass else: raise Exception # Parse the OriginQuality if applicable. if not origin_el.xpath("originQuality"): return origin origin_quality_el = origin_el.xpath("originQuality")[0] origin.quality = OriginQuality() origin.quality.associated_phase_count = \ parser.xpath2obj("associatedPhaseCount", origin_quality_el, int) # QuakeML does apparently not distinguish between P and S wave phase # count. Some Seishub event files do. p_phase_count = parser.xpath2obj("P_usedPhaseCount", origin_quality_el, int) s_phase_count = parser.xpath2obj("S_usedPhaseCount", origin_quality_el, int) # Use both in case they are set. if p_phase_count is not None and s_phase_count is not None: phase_count = p_phase_count + s_phase_count # Also add two Seishub element file specific elements. origin.quality.p_used_phase_count = p_phase_count origin.quality.s_used_phase_count = s_phase_count # Otherwise the total usedPhaseCount should be specified. else: phase_count = parser.xpath2obj("usedPhaseCount", origin_quality_el, int) if p_phase_count is not None: origin.quality.setdefault("extra", AttribDict()) origin.quality.extra.usedPhaseCountP = {'value': p_phase_count, 'namespace': NAMESPACE} if s_phase_count is not None: origin.quality.setdefault("extra", AttribDict()) origin.quality.extra.usedPhaseCountS = {'value': s_phase_count, 'namespace': NAMESPACE} origin.quality.used_phase_count = phase_count associated_station_count = \ parser.xpath2obj("associatedStationCount", origin_quality_el, int) used_station_count = parser.xpath2obj("usedStationCount", origin_quality_el, int) depth_phase_count = parser.xpath2obj("depthPhaseCount", origin_quality_el, int) standard_error = parser.xpath2obj("standardError", origin_quality_el, float) azimuthal_gap = parser.xpath2obj("azimuthalGap", origin_quality_el, float) secondary_azimuthal_gap = \ parser.xpath2obj("secondaryAzimuthalGap", origin_quality_el, float) ground_truth_level = parser.xpath2obj("groundTruthLevel", origin_quality_el, str) minimum_distance = parser.xpath2obj("minimumDistance", origin_quality_el, float) maximum_distance = parser.xpath2obj("maximumDistance", origin_quality_el, float) median_distance = parser.xpath2obj("medianDistance", origin_quality_el, float) if minimum_distance is not None: minimum_distance = kilometer2degrees(minimum_distance) if maximum_distance is not None: maximum_distance = kilometer2degrees(maximum_distance) if median_distance is not None: median_distance = kilometer2degrees(median_distance) if associated_station_count is not None: origin.quality.associated_station_count = associated_station_count if used_station_count is not None: origin.quality.used_station_count = used_station_count if depth_phase_count is not None: origin.quality.depth_phase_count = depth_phase_count if standard_error is not None and not math.isnan(standard_error): origin.quality.standard_error = standard_error if azimuthal_gap is not None: origin.quality.azimuthal_gap = azimuthal_gap if secondary_azimuthal_gap is not None: origin.quality.secondary_azimuthal_gap = secondary_azimuthal_gap if ground_truth_level is not None: origin.quality.ground_truth_level = ground_truth_level if minimum_distance is not None: origin.quality.minimum_distance = minimum_distance if maximum_distance is not None: origin.quality.maximum_distance = maximum_distance if median_distance is not None and not math.isnan(median_distance): origin.quality.median_distance = median_distance return origin
def _parse_first_line_origin(self, line, event, magnitudes): """ Parse the first line of origin data. :type line: str :param line: Line to parse. :type event: :class:`~obspy.core.event.event.Event` :param event: Event of the origin. :type magnitudes: list of :class:`~obspy.core.event.magnitude.Magnitude` :param magnitudes: Store magnitudes in a list to keep their positions. :rtype: :class:`~obspy.core.event.origin.Origin`, :class:`~obspy.core.event.resourceid.ResourceIdentifier` :returns: Parsed origin or None, resource identifier of the origin. """ magnitude_types = [] magnitude_values = [] magnitude_station_counts = [] fields = self.fields['line_1'] time_origin = line[fields['time']].strip() time_fixed_flag = line[fields['time_fixf']].strip() latitude = line[fields['lat']].strip() longitude = line[fields['lon']].strip() epicenter_fixed_flag = line[fields['epicenter_fixf']].strip() depth = line[fields['depth']].strip() depth_fixed_flag = line[fields['depth_fixf']].strip() phase_count = line[fields['n_def']].strip() station_count = line[fields['n_sta']].strip() azimuthal_gap = line[fields['gap']].strip() magnitude_types.append(line[fields['mag_type_1']].strip()) magnitude_values.append(line[fields['mag_1']].strip()) magnitude_station_counts.append(line[fields['mag_n_sta_1']].strip()) magnitude_types.append(line[fields['mag_type_2']].strip()) magnitude_values.append(line[fields['mag_2']].strip()) magnitude_station_counts.append(line[fields['mag_n_sta_2']].strip()) magnitude_types.append(line[fields['mag_type_3']].strip()) magnitude_values.append(line[fields['mag_3']].strip()) magnitude_station_counts.append(line[fields['mag_n_sta_3']].strip()) author = line[fields['author']].strip() origin_id = line[fields['id']].strip() origin = Origin() origin.quality = OriginQuality() try: origin.time = UTCDateTime(time_origin.replace('/', '-')) origin.latitude = float(latitude) origin.longitude = float(longitude) except (TypeError, ValueError): self._warn('Missing origin data, skipping event') return None, None origin.time_fixed = time_fixed_flag.lower() == 'f' origin.epicenter_fixed = epicenter_fixed_flag.lower() == 'f' try: # Convert value from km to m origin.depth = float(depth) * 1000 except ValueError: pass try: origin.depth_type = DEPTH_TYPES[depth_fixed_flag] except KeyError: origin.depth_type = OriginDepthType('from location') try: origin.quality.used_phase_count = int(phase_count) origin.quality.associated_phase_count = int(phase_count) except ValueError: pass try: origin.quality.used_station_count = int(station_count) origin.quality.associated_station_count = int(station_count) except ValueError: pass try: origin.quality.azimuthal_gap = float(azimuthal_gap) except ValueError: pass self.author = author origin.creation_info = self._get_creation_info() public_id = "origin/%s" % origin_id origin_res_id = self._get_res_id(public_id) for i in range(3): try: magnitude = Magnitude() magnitude.creation_info = self._get_creation_info() magnitude.magnitude_type = magnitude_types[i] magnitude.mag = float(magnitude_values[i]) magnitude.station_count = int(magnitude_station_counts[i]) magnitude.origin_id = origin_res_id magnitudes.append(magnitude) event.magnitudes.append(magnitude) except ValueError: # Magnitude can be empty but we need to keep the # position between mag1, mag2 or mag3. magnitudes.append(None) return origin, origin_res_id
def get_results(self): cids = [] clusters = [] results_file = "{}/{}".format(self.hypoDD_control.control_directory, self.hypoDD_control.relocated_hypocenters_output ) residuals_file = "{}/{}".format(self.hypoDD_control.control_directory, self.hypoDD_control.data_residual_output ) with open(results_file, "r") as f: for line in f: num = line.split() evid = num[0] lat = float(num[1]) lon = float(num[2]) dep = 1000 * float(num[3]) # km to m errx = num[7] erry = num[8] errz = num[9] yr = int(num[10]) mo = int(num[11]) dy = int(num[12]) hr = int(num[13]) mi = int(num[14]) sc = float(num[15]) mag = num[16] nccp = num[17] nccs = num[18] nctp = num[19] ncts = num[20] rcc = num[21] rct = num[22] cid = num[23] if cid not in cids: cids.append(cid) clusters.append(Cluster()) clusters[-1].hypoDD_id=cid clusters[-1].successful_relocation=True clusters[-1].catalog=Catalog() clusters[-1].event_ids=[] origin=Origin() isec = int ( math.floor( sc )) micsec = int ( ( sc - isec) * 1000000 ) origin.time = UTCDateTime(yr, mo, dy, hr, mi, isec, micsec) origin.longitude = lon origin.latitude = lat origin.depth = dep origin.method_id = "hypoDD" # TODO (@ogalanis): Add time/location errors (when # appropriate. Add quality and origin_uncertainty. Add arrivals. event=Event() event.creation_info=CreationInfo() event.creation_info.author = __package__ event.creation_info.version = info.__version__ event.origins=[origin] event.magnitude=Magnitude() event.magnitude.mag=mag idx=cids.index(cid) clusters[idx].catalog.events.append(event) clusters[idx].event_ids.append(evid) if self.hypoDD_control.cid != 0 : my_list = [] clusters[0].connectedness = Connectedness() with open(residuals_file, "r") as f: for line in f: num = line.split() evid_1 = num[2] evid_2 = num[3] obs_type = num[4] if obs_type == "1": my_list = clusters[0].connectedness.cross_corr_P elif obs_type == "2": my_list = clusters[0].connectedness.cross_corr_S elif obs_type == "3": my_list = clusters[0].connectedness.catalog_P elif obs_type == "4": my_list = clusters[0].connectedness.catalog_S else: continue in_list = [x for x in my_list if (( x[0] == evid_1 and x[1] == evid_2 ) or ( x[0] == evid_2 and x[1] == evid_1 ))] if in_list: for x in my_list: if (( x[0] == evid_1 and x[1] == evid_2 ) or ( x[0] == evid_2 and x[1] == evid_1 )): x[2] += 1 else: my_list.append([evid_1,evid_2,1]) return clusters
def outputOBSPY(hp, event=None, only_fm_picks=False): """ Make an Event which includes the current focal mechanism information from HASH Use the 'only_fm_picks' flag to only include the picks HASH used for the FocalMechanism. This flag will replace the 'picks' and 'arrivals' lists of existing events with new ones. Inputs ------- hp : hashpy.HashPype instance event : obspy.core.event.Event only_fm_picks : bool of whether to overwrite the picks/arrivals lists Returns ------- obspy.core.event.Event Event will be new if no event was input, FocalMech added to existing event """ # Returns new (or updates existing) Event with HASH solution n = hp.npol if event is None: event = Event(focal_mechanisms=[], picks=[], origins=[]) origin = Origin(arrivals=[]) origin.time = UTCDateTime(hp.tstamp) origin.latitude = hp.qlat origin.longitude = hp.qlon origin.depth = hp.qdep origin.creation_info = CreationInfo(version=hp.icusp) origin.resource_id = ResourceIdentifier('smi:hash/Origin/{0}'.format(hp.icusp)) for _i in range(n): p = Pick() p.creation_info = CreationInfo(version=hp.arid[_i]) p.resource_id = ResourceIdentifier('smi:hash/Pick/{0}'.format(p.creation_info.version)) p.waveform_id = WaveformStreamID(network_code=hp.snet[_i], station_code=hp.sname[_i], channel_code=hp.scomp[_i]) if hp.p_pol[_i] > 0: p.polarity = 'positive' else: p.polarity = 'negative' a = Arrival() a.creation_info = CreationInfo(version=hp.arid[_i]) a.resource_id = ResourceIdentifier('smi:hash/Arrival/{0}'.format(p.creation_info.version)) a.azimuth = hp.p_azi_mc[_i,0] a.takeoff_angle = 180. - hp.p_the_mc[_i,0] a.pick_id = p.resource_id origin.arrivals.append(a) event.picks.append(p) event.origins.append(origin) event.preferred_origin_id = str(origin.resource_id) else: # just update the changes origin = event.preferred_origin() picks = [] arrivals = [] for _i in range(n): ind = hp.p_index[_i] a = origin.arrivals[ind] p = a.pick_id.getReferredObject() a.takeoff_angle = hp.p_the_mc[_i,0] picks.append(p) arrivals.append(a) if only_fm_picks: origin.arrivals = arrivals event.picks = picks # Use me double couple calculator and populate planes/axes etc x = hp._best_quality_index # Put all the mechanisms into the 'focal_mechanisms' list, mark "best" as preferred for s in range(hp.nmult): dc = DoubleCouple([hp.str_avg[s], hp.dip_avg[s], hp.rak_avg[s]]) ax = dc.axis focal_mech = FocalMechanism() focal_mech.creation_info = CreationInfo(creation_time=UTCDateTime(), author=hp.author) focal_mech.triggering_origin_id = origin.resource_id focal_mech.resource_id = ResourceIdentifier('smi:hash/FocalMechanism/{0}/{1}'.format(hp.icusp, s+1)) focal_mech.method_id = ResourceIdentifier('HASH') focal_mech.nodal_planes = NodalPlanes() focal_mech.nodal_planes.nodal_plane_1 = NodalPlane(*dc.plane1) focal_mech.nodal_planes.nodal_plane_2 = NodalPlane(*dc.plane2) focal_mech.principal_axes = PrincipalAxes() focal_mech.principal_axes.t_axis = Axis(azimuth=ax['T']['azimuth'], plunge=ax['T']['dip']) focal_mech.principal_axes.p_axis = Axis(azimuth=ax['P']['azimuth'], plunge=ax['P']['dip']) focal_mech.station_polarity_count = n focal_mech.azimuthal_gap = hp.magap focal_mech.misfit = hp.mfrac[s] focal_mech.station_distribution_ratio = hp.stdr[s] focal_mech.comments.append( Comment(hp.qual[s], resource_id=ResourceIdentifier(str(focal_mech.resource_id) + '/comment/quality')) ) #---------------------------------------- event.focal_mechanisms.append(focal_mech) if s == x: event.preferred_focal_mechanism_id = str(focal_mech.resource_id) return event
def _read_single_hypocenter(lines, coordinate_converter, original_picks): """ Given a list of lines (starting with a 'NLLOC' line and ending with a 'END_NLLOC' line), parse them into an Event. """ try: # some paranoid checks.. assert lines[0].startswith("NLLOC ") assert lines[-1].startswith("END_NLLOC") for line in lines[1:-1]: assert not line.startswith("NLLOC ") assert not line.startswith("END_NLLOC") except Exception: msg = ("This should not have happened, please report this as a bug at " "https://github.com/obspy/obspy/issues.") raise Exception(msg) indices_phases = [None, None] for i, line in enumerate(lines): if line.startswith("PHASE "): indices_phases[0] = i elif line.startswith("END_PHASE"): indices_phases[1] = i # extract PHASES lines (if any) if any(indices_phases): if not all(indices_phases): msg = ("NLLOC HYP file seems corrupt, 'PHASE' block is corrupt.") raise RuntimeError(msg) i1, i2 = indices_phases lines, phases_lines = lines[:i1] + lines[i2 + 1:], lines[i1 + 1:i2] else: phases_lines = [] lines = dict([line.split(None, 1) for line in lines[:-1]]) line = lines["SIGNATURE"] line = line.rstrip().split('"')[1] signature, version, date, time = line.rsplit(" ", 3) # new NLLoc > 6.0 seems to add prefix 'run:' before date if date.startswith('run:'): date = date[4:] signature = signature.strip() creation_time = UTCDateTime.strptime(date + time, str("%d%b%Y%Hh%Mm%S")) if coordinate_converter: # maximum likelihood origin location in km info line line = lines["HYPOCENTER"] x, y, z = coordinate_converter(*map(float, line.split()[1:7:2])) else: # maximum likelihood origin location lon lat info line line = lines["GEOGRAPHIC"] y, x, z = map(float, line.split()[8:13:2]) # maximum likelihood origin time info line line = lines["GEOGRAPHIC"] year, mon, day, hour, min = map(int, line.split()[1:6]) seconds = float(line.split()[6]) time = UTCDateTime(year, mon, day, hour, min, seconds, strict=False) # distribution statistics line line = lines["STATISTICS"] covariance_xx = float(line.split()[7]) covariance_yy = float(line.split()[13]) covariance_zz = float(line.split()[17]) stats_info_string = str( "Note: Depth/Latitude/Longitude errors are calculated from covariance " "matrix as 1D marginal (Lon/Lat errors as great circle degrees) " "while OriginUncertainty min/max horizontal errors are calculated " "from 2D error ellipsoid and are therefore seemingly higher compared " "to 1D errors. Error estimates can be reconstructed from the " "following original NonLinLoc error statistics line:\nSTATISTICS " + lines["STATISTICS"]) # goto location quality info line line = lines["QML_OriginQuality"].split() (assoc_phase_count, used_phase_count, assoc_station_count, used_station_count, depth_phase_count) = map(int, line[1:11:2]) stderr, az_gap, sec_az_gap = map(float, line[11:17:2]) gt_level = line[17] min_dist, max_dist, med_dist = map(float, line[19:25:2]) # goto location quality info line line = lines["QML_OriginUncertainty"] if "COMMENT" in lines: comment = lines["COMMENT"].strip() comment = comment.strip('\'"') comment = comment.strip() hor_unc, min_hor_unc, max_hor_unc, hor_unc_azim = \ map(float, line.split()[1:9:2]) # assign origin info event = Event() o = Origin() event.origins = [o] event.preferred_origin_id = o.resource_id o.origin_uncertainty = OriginUncertainty() o.quality = OriginQuality() ou = o.origin_uncertainty oq = o.quality o.comments.append(Comment(text=stats_info_string, force_resource_id=False)) event.comments.append(Comment(text=comment, force_resource_id=False)) # SIGNATURE field's first item is LOCSIG, which is supposed to be # 'Identification of an individual, institiution or other entity' # according to # http://alomax.free.fr/nlloc/soft6.00/control.html#_NLLoc_locsig_ # so use it as author in creation info event.creation_info = CreationInfo(creation_time=creation_time, version=version, author=signature) o.creation_info = CreationInfo(creation_time=creation_time, version=version, author=signature) # negative values can appear on diagonal of covariance matrix due to a # precision problem in NLLoc implementation when location coordinates are # large compared to the covariances. o.longitude = x try: o.longitude_errors.uncertainty = kilometer2degrees(sqrt(covariance_xx)) except ValueError: if covariance_xx < 0: msg = ("Negative value in XX value of covariance matrix, not " "setting longitude error (epicentral uncertainties will " "still be set in origin uncertainty).") warnings.warn(msg) else: raise o.latitude = y try: o.latitude_errors.uncertainty = kilometer2degrees(sqrt(covariance_yy)) except ValueError: if covariance_yy < 0: msg = ("Negative value in YY value of covariance matrix, not " "setting longitude error (epicentral uncertainties will " "still be set in origin uncertainty).") warnings.warn(msg) else: raise o.depth = z * 1e3 # meters! o.depth_errors.uncertainty = sqrt(covariance_zz) * 1e3 # meters! o.depth_errors.confidence_level = 68 o.depth_type = str("from location") o.time = time ou.horizontal_uncertainty = hor_unc ou.min_horizontal_uncertainty = min_hor_unc ou.max_horizontal_uncertainty = max_hor_unc # values of -1 seem to be used for unset values, set to None for field in ("horizontal_uncertainty", "min_horizontal_uncertainty", "max_horizontal_uncertainty"): if ou.get(field, -1) == -1: ou[field] = None else: ou[field] *= 1e3 # meters! ou.azimuth_max_horizontal_uncertainty = hor_unc_azim ou.preferred_description = str("uncertainty ellipse") ou.confidence_level = 68 # NonLinLoc in general uses 1-sigma (68%) level oq.standard_error = stderr oq.azimuthal_gap = az_gap oq.secondary_azimuthal_gap = sec_az_gap oq.used_phase_count = used_phase_count oq.used_station_count = used_station_count oq.associated_phase_count = assoc_phase_count oq.associated_station_count = assoc_station_count oq.depth_phase_count = depth_phase_count oq.ground_truth_level = gt_level oq.minimum_distance = kilometer2degrees(min_dist) oq.maximum_distance = kilometer2degrees(max_dist) oq.median_distance = kilometer2degrees(med_dist) # go through all phase info lines for line in phases_lines: line = line.split() arrival = Arrival() o.arrivals.append(arrival) station = str(line[0]) phase = str(line[4]) arrival.phase = phase arrival.distance = kilometer2degrees(float(line[21])) arrival.azimuth = float(line[23]) arrival.takeoff_angle = float(line[24]) arrival.time_residual = float(line[16]) arrival.time_weight = float(line[17]) pick = Pick() # network codes are not used by NonLinLoc, so they can not be known # when reading the .hyp file.. to conform with QuakeML standard set an # empty network code wid = WaveformStreamID(network_code="", station_code=station) # have to split this into ints for overflow to work correctly date, hourmin, sec = map(str, line[6:9]) ymd = [int(date[:4]), int(date[4:6]), int(date[6:8])] hm = [int(hourmin[:2]), int(hourmin[2:4])] t = UTCDateTime(*(ymd + hm), strict=False) + float(sec) pick.waveform_id = wid pick.time = t pick.time_errors.uncertainty = float(line[10]) pick.phase_hint = phase pick.onset = ONSETS.get(line[3].lower(), None) pick.polarity = POLARITIES.get(line[5].lower(), None) # try to determine original pick for each arrival for pick_ in original_picks: wid = pick_.waveform_id if station == wid.station_code and phase == pick_.phase_hint: pick = pick_ break else: # warn if original picks were specified and we could not associate # the arrival correctly if original_picks: msg = ("Could not determine corresponding original pick for " "arrival. " "Falling back to pick information in NonLinLoc " "hypocenter-phase file.") warnings.warn(msg) event.picks.append(pick) arrival.pick_id = pick.resource_id event.scope_resource_ids() return event
def _parseRecordDp(self, line, event): """ Parses the 'source parameter data - primary' record Dp """ source_contributor = line[2:6].strip() computation_type = line[6] exponent = self._intZero(line[7]) scale = math.pow(10, exponent) centroid_origin_time = line[8:14] + "." + line[14] orig_time_stderr = line[15:17] if orig_time_stderr == "FX": orig_time_stderr = "Fixed" else: orig_time_stderr = self._floatWithFormat(orig_time_stderr, "2.1", scale) centroid_latitude = self._floatWithFormat(line[17:21], "4.2") lat_type = line[21] if centroid_latitude is not None: centroid_latitude *= self._coordinateSign(lat_type) lat_stderr = line[22:25] if lat_stderr == "FX": lat_stderr = "Fixed" else: lat_stderr = self._floatWithFormat(lat_stderr, "3.2", scale) centroid_longitude = self._floatWithFormat(line[25:30], "5.2") lon_type = line[30] if centroid_longitude is not None: centroid_longitude *= self._coordinateSign(lon_type) lon_stderr = line[31:34] if lon_stderr == "FX": lon_stderr = "Fixed" else: lon_stderr = self._floatWithFormat(lon_stderr, "3.2", scale) centroid_depth = self._floatWithFormat(line[34:38], "4.1") depth_stderr = line[38:40] if depth_stderr == "FX" or depth_stderr == "BD": depth_stderr = "Fixed" else: depth_stderr = self._floatWithFormat(depth_stderr, "2.1", scale) station_number = self._intZero(line[40:43]) component_number = self._intZero(line[43:46]) station_number2 = self._intZero(line[46:48]) component_number2 = self._intZero(line[48:51]) # unused: half_duration = self._floatWithFormat(line[51:54], '3.1') moment = self._floatWithFormat(line[54:56], "2.1") moment_stderr = self._floatWithFormat(line[56:58], "2.1") moment_exponent = self._int(line[58:60]) if (moment is not None) and (moment_exponent is not None): moment *= math.pow(10, moment_exponent) if (moment_stderr is not None) and (moment_exponent is not None): moment_stderr *= math.pow(10, moment_exponent) evid = event.resource_id.id.split("/")[-1] # Create a new origin only if centroid time is defined: origin = None if centroid_origin_time.strip() != ".": origin = Origin() res_id = "/".join( (res_id_prefix, "origin", evid, source_contributor.lower(), "mw" + computation_type.lower()) ) origin.resource_id = ResourceIdentifier(id=res_id) origin.creation_info = CreationInfo(agency_id=source_contributor) date = event.origins[0].time.strftime("%Y%m%d") origin.time = UTCDateTime(date + centroid_origin_time) # Check if centroid time is on the next day: if origin.time < event.origins[0].time: origin.time += timedelta(days=1) self._storeUncertainty(origin.time_errors, orig_time_stderr) origin.latitude = centroid_latitude origin.longitude = centroid_longitude origin.depth = centroid_depth * 1000 if lat_stderr == "Fixed" and lon_stderr == "Fixed": origin.epicenter_fixed = True else: self._storeUncertainty(origin.latitude_errors, self._latErrToDeg(lat_stderr)) self._storeUncertainty(origin.longitude_errors, self._lonErrToDeg(lon_stderr, origin.latitude)) if depth_stderr == "Fixed": origin.depth_type = "operator assigned" else: origin.depth_type = "from location" self._storeUncertainty(origin.depth_errors, depth_stderr, scale=1000) quality = OriginQuality() quality.used_station_count = station_number + station_number2 quality.used_phase_count = component_number + component_number2 origin.quality = quality origin.type = "centroid" event.origins.append(origin) focal_mechanism = FocalMechanism() res_id = "/".join( (res_id_prefix, "focalmechanism", evid, source_contributor.lower(), "mw" + computation_type.lower()) ) focal_mechanism.resource_id = ResourceIdentifier(id=res_id) focal_mechanism.creation_info = CreationInfo(agency_id=source_contributor) moment_tensor = MomentTensor() if origin is not None: moment_tensor.derived_origin_id = origin.resource_id else: # this is required for QuakeML validation: res_id = "/".join((res_id_prefix, "no-origin")) moment_tensor.derived_origin_id = ResourceIdentifier(id=res_id) for mag in event.magnitudes: if mag.creation_info.agency_id == source_contributor: moment_tensor.moment_magnitude_id = mag.resource_id res_id = "/".join( (res_id_prefix, "momenttensor", evid, source_contributor.lower(), "mw" + computation_type.lower()) ) moment_tensor.resource_id = ResourceIdentifier(id=res_id) moment_tensor.scalar_moment = moment self._storeUncertainty(moment_tensor.scalar_moment_errors, moment_stderr) data_used = DataUsed() data_used.station_count = station_number + station_number2 data_used.component_count = component_number + component_number2 if computation_type == "C": res_id = "/".join((res_id_prefix, "methodID=CMT")) focal_mechanism.method_id = ResourceIdentifier(id=res_id) # CMT algorithm uses long-period body waves, # very-long-period surface waves and # intermediate period surface waves (since 2004 # for shallow and intermediate-depth earthquakes # --Ekstrom et al., 2012) data_used.wave_type = "combined" if computation_type == "M": res_id = "/".join((res_id_prefix, "methodID=moment_tensor")) focal_mechanism.method_id = ResourceIdentifier(id=res_id) # FIXME: not sure which kind of data is used by # "moment tensor" algorithm. data_used.wave_type = "unknown" elif computation_type == "B": res_id = "/".join((res_id_prefix, "methodID=broadband_data")) focal_mechanism.method_id = ResourceIdentifier(id=res_id) # FIXME: is 'combined' correct here? data_used.wave_type = "combined" elif computation_type == "F": res_id = "/".join((res_id_prefix, "methodID=P-wave_first_motion")) focal_mechanism.method_id = ResourceIdentifier(id=res_id) data_used.wave_type = "P waves" elif computation_type == "S": res_id = "/".join((res_id_prefix, "methodID=scalar_moment")) focal_mechanism.method_id = ResourceIdentifier(id=res_id) # FIXME: not sure which kind of data is used # for scalar moment determination. data_used.wave_type = "unknown" moment_tensor.data_used = data_used focal_mechanism.moment_tensor = moment_tensor event.focal_mechanisms.append(focal_mechanism) return focal_mechanism
def request_gcmt(starttime, endtime, minmagnitude=None, mindepth=None, maxdepth=None, minlatitude=None, maxlatitude=None, minlongitude=None, maxlongitude=None): import mechanize from mechanize import Browser import re """ Description I am using mechanize. My attempt is just preliminary, for the current globalcmt.org site. """ #Split numbers and text r = re.compile("([a-zA-Z]+)([0-9]+)") br = Browser() br.open('http://www.globalcmt.org/CMTsearch.html') #Site has just one form br.select_form(nr=0) br.form['yr'] = str(starttime.year) br.form['mo'] = str(starttime.month) br.form['day'] = str(starttime.day) br.form['oyr'] = str(endtime.year) br.form['omo'] = str(endtime.month) br.form['oday'] = str(endtime.day) br.form['list'] = ['4'] br.form['itype'] = ['ymd'] br.form['otype'] = ['ymd'] if minmagnitude: br.form['lmw'] = str(minmagnitude) if minlatitude : br.form['llat'] = str(minlatitude) if maxlatitude : br.form['ulat'] = str(maxlatitude) if minlongitude: br.form['llon'] = str(minlongitude) if maxlongitude: br.form['ulon'] = str(maxlongitude) if mindepth : br.form['lhd'] = str(mindepth) if maxdepth : br.form['uhd'] = str(maxdepth) print("Submitting parameters to globalcmt.") req = br.submit() print("Retrieving data, creating catalog.") data = [] for line in req: data.append(line) data_chunked = _chunking_list(keyword='\n', list=data) origins = [] magnitudes = [] tensor = [] for line in data_chunked: for element in line: if 'event name' in element: for content in element: org = line[1].split() year = int(r.match(org[0]).groups()[1]) mon = int(org[1]) day = int(org[2]) hour = int(org[3]) minute = int(org[4]) sec_temp = int(org[5].split('.')[0]) msec_temp = int(org[5].split('.')[1]) origins_temp = UTCDateTime(year, mon, day, hour, minute, sec_temp, msec_temp) #adding time shift located in line[3] origin = origins_temp + float(line[3].split()[2]) magnitude = float(line[1].split()[10]) latitude = float(line[5].split()[1]) longitude = float(line[6].split()[1]) depth = 1000. * float(line[7].split()[1]) m_rr = float(line[8].split()[1]) m_tt = float(line[9].split()[1]) m_pp = float(line[10].split()[1]) m_rt = float(line[11].split()[1]) m_rp = float(line[12].split()[1]) m_tp = float(line[13].split()[1]) magnitudes.append( ("Mw", magnitude) ) origins.append( (latitude, longitude, depth, origin) ) tensor.append( (m_rr, m_tt, m_pp, m_rt, m_rp, m_tp) ) cat = Catalog() for mag, org, ten in zip(magnitudes, origins, tensor): # Create magnitude object. magnitude = Magnitude() magnitude.magnitude_type = mag[0] magnitude.mag = mag[1] # Write origin object. origin = Origin() origin.latitude = org[0] origin.longitude = org[1] origin.depth = org[2] origin.time = org[3] # Create event object and append to catalog object. event = Event() event.magnitudes.append(magnitude) event.origins.append(origin) event.MomentTensor = MomentTensor() event.MomentTensor.m_rr = ten[0] event.MomentTensor.m_tt = ten[1] event.MomentTensor.m_pp = ten[2] event.MomentTensor.m_rt = ten[3] event.MomentTensor.m_rp = ten[4] event.MomentTensor.m_tp = ten[5] cat.append(event) return cat
def _map_join2origin(self, db): """ Return an Origin instance from an dict of CSS key/values Inputs ====== db : dict of key/values of CSS fields related to the origin (see Join) Returns ======= obspy.core.event.Origin Notes ===== Any object that supports the dict 'get' method can be passed as input, e.g. OrderedDict, custom classes, etc. Join ---- origin <- origerr (outer) """ #-- Basic location ------------------------------------------ origin = Origin() origin.latitude = db.get('lat') origin.longitude = db.get('lon') origin.depth = _km2m(db.get('depth')) origin.time = _utc(db.get('time')) origin.extra = {} #-- Quality ------------------------------------------------- quality = OriginQuality( associated_phase_count = db.get('nass'), used_phase_count = db.get('ndef'), standard_error = db.get('sdobs'), ) origin.quality = quality #-- Solution Uncertainties ---------------------------------- # in CSS the ellipse is projected onto the horizontal plane # using the covariance matrix uncertainty = OriginUncertainty() a = _km2m(db.get('smajax')) b = _km2m(db.get('sminax')) s = db.get('strike') dep_u = _km2m(db.get('sdepth')) time_u = db.get('stime') uncertainty.max_horizontal_uncertainty = a uncertainty.min_horizontal_uncertainty = b uncertainty.azimuth_max_horizontal_uncertainty = s uncertainty.horizontal_uncertainty = a uncertainty.preferred_description = "horizontal uncertainty" if db.get('conf') is not None: uncertainty.confidence_level = db.get('conf') * 100. if uncertainty.horizontal_uncertainty is not None: origin.origin_uncertainty = uncertainty #-- Parameter Uncertainties --------------------------------- if all([a, b, s]): n, e = _get_NE_on_ellipse(a, b, s) lat_u = _m2deg_lat(n) lon_u = _m2deg_lon(e, lat=origin.latitude) origin.latitude_errors = {'uncertainty': lat_u} origin.longitude_errors = {'uncertainty': lon_u} if dep_u: origin.depth_errors = {'uncertainty': dep_u} if time_u: origin.time_errors = {'uncertainty': time_u} #-- Analyst-determined Status ------------------------------- posted_author = _str(db.get('auth')) mode, status = self.get_event_status(posted_author) origin.evaluation_mode = mode origin.evaluation_status = status # Save etype per origin due to schema differences... css_etype = _str(db.get('etype')) # Compatible with future patch rename "_namespace" -> "namespace" origin.extra['etype'] = { 'value': css_etype, 'namespace': CSS_NAMESPACE } origin.creation_info = CreationInfo( creation_time = _utc(db.get('lddate')), agency_id = self.agency, version = db.get('orid'), author = posted_author, ) origin.resource_id = self._rid(origin) return origin