def get_event_filename(event, prefix): """ Helper function generating a descriptive event filename. :param event: The event object. :param prefix: A prefix for the file, denoting e.g. the event catalog. >>> from obspy import readEvents >>> event = readEvents()[0] >>> print get_event_filename(event, "GCMT") GCMT_event_KYRGYZSTAN-XINJIANG_BORDER_REG._Mag_4.4_2012-4-4-14.xml """ from obspy.core.util.geodetics import FlinnEngdahl mag = event.preferred_magnitude() or event.magnitudes[0] org = event.preferred_origin() or event.origins[0] # Get the flinn_engdahl region for a nice name. fe = FlinnEngdahl() region_name = fe.get_region(org.longitude, org.latitude) region_name = region_name.replace(" ", "_") # Replace commas, as some file systems cannot deal with them. region_name = region_name.replace(",", "") return "%s_event_%s_Mag_%.1f_%s-%s-%s-%s.xml" % \ (prefix, region_name, mag.mag, org.time.year, org.time.month, org.time.day, org.time.hour)
def iris2quakeml(url, output_folder=None): if "/spudservice/" not in url: url = url.replace("/spud/", "/spudservice/") if url.endswith("/"): url += "quakeml" else: url += "/quakeml" print "Downloading %s..." % url r = urllib2.urlopen(url) if r.code != 200: r.close() msg = "Error Downloading file!" raise Exception(msg) # For some reason the quakeml file is escaped HTML. h = HTMLParser.HTMLParser() data = h.unescape(r.read()) r.close() data = StringIO(data) try: cat = readEvents(data) except: msg = "Could not read downloaded event data" raise ValueError(msg) cat.events = cat.events[:1] ev = cat[0] # Parse the event and get the preferred focal mechanism. Then get the # origin and magnitude associated with that focal mechanism. All other # focal mechanisms, origins and magnitudes will be removed. Just makes it # simpler and less error prone. if ev.preferred_focal_mechanism(): ev.focal_mechanisms = [ev.preferred_focal_mechanism()] else: ev.focal_mechanisms = [ev.focal_mechanisms[:1]] # Set the origin and magnitudes of the event. mt = ev.focal_mechanisms[0].moment_tensor ev.magnitudes = [mt.moment_magnitude_id.getReferredObject()] ev.origins = [mt.derived_origin_id.getReferredObject()] # Get the flinn_engdahl region for a nice name. fe = FlinnEngdahl() region_name = fe.get_region(ev.origins[0].longitude, ev.origins[0].latitude) region_name = region_name.replace(" ", "_") event_name = "GCMT_event_%s_Mag_%.1f_%s-%s-%s-%s-%s.xml" % \ (region_name, ev.magnitudes[0].mag, ev.origins[0].time.year, ev.origins[0].time.month, ev.origins[0].time.day, ev.origins[0].time.hour, ev.origins[0].time.minute) if output_folder: event_name = os.path.join(output_folder, event_name) cat.write(event_name, format="quakeml", validate=True) print "Written file", event_name
def get_event_filename(event, prefix): """ Helper function generating a descriptive event filename. :param event: The event object. :param prefix: A prefix for the file, denoting e.g. the event catalog. >>> from obspy import readEvents >>> event = readEvents()[0] >>> print get_event_filename(event, "GCMT") GCMT_event_KYRGYZSTAN-XINJIANG_BORDER_REG._Mag_4.4_2012-4-4-14.xml """ from obspy.core.util.geodetics import FlinnEngdahl mag = event.preferred_magnitude() or event.magnitudes[0] org = event.preferred_origin() or event.origins[0] # Get the flinn_engdahl region for a nice name. fe = FlinnEngdahl() region_name = fe.get_region(org.longitude, org.latitude) region_name = region_name.replace(" ", "_") # Replace commas, as some file systems cannot deal with them. region_name = region_name.replace(",", "") return "%s_event_%s_Mag_%.1f_%s-%s-%s-%s.xml" % \ (prefix, region_name, mag.mag, org.time.year, org.time.month, org.time.day, org.time.hour)
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:] raise StopIteration # 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 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:] raise StopIteration # 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 determinator 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 iris2quakeml(url, output_folder=None): if not "/spudservice/" in url: url = url.replace("/spud/", "/spudservice/") if url.endswith("/"): url += "quakeml" else: url += "/quakeml" print "Downloading %s..." % url r = requests.get(url) if r.status_code != 200: msg = "Error Downloading file!" raise Exception(msg) # For some reason the quakeml file is escaped HTML. h = HTMLParser.HTMLParser() data = h.unescape(r.content) # Replace some XML tags. data = data.replace("long-period body waves", "body waves") data = data.replace("intermediate-period surface waves", "surface waves") data = data.replace("long-period mantle waves", "mantle waves") data = data.replace("<html><body><pre>", "") data = data.replace("</pre></body></html>", "") # Change the resource identifiers. Colons are not allowed in QuakeML. pattern = r"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d{6})" data = re.sub(pattern, r"\1-\2-\3T\4-\5-\6.\7", data) data = StringIO(data) try: cat = readEvents(data) except: msg = "Could not read downloaded event data" raise ValueError(msg) # Parse the event, and use only one origin, magnitude and focal mechanism. # Only the first event is used. Should not be a problem for the chosen # global cmt application. ev = cat[0] if ev.preferred_origin(): ev.origins = [ev.preferred_origin()] else: ev.origins = [ev.origins[0]] if ev.preferred_focal_mechanism(): ev.focal_mechanisms = [ev.preferred_focal_mechanism()] else: ev.focal_mechanisms = [ev.focal_mechanisms[0]] try: mt = ev.focal_mechanisms[0].moment_tensor except: msg = "No moment tensor found in file." raise ValueError seismic_moment_in_dyn_cm = mt.scalar_moment if not seismic_moment_in_dyn_cm: msg = "No scalar moment found in file." raise ValueError(msg) # Create a new magnitude object with the moment magnitude calculated from # the given seismic moment. mag = Magnitude() mag.magnitude_type = "Mw" mag.origin_id = ev.origins[0].resource_id # This is the formula given on the GCMT homepage. mag.mag = (2.0 / 3.0) * (math.log10(seismic_moment_in_dyn_cm) - 16.1) mag.resource_id = ev.origins[0].resource_id.resource_id.replace("Origin", "Magnitude") ev.magnitudes = [mag] ev.preferred_magnitude_id = mag.resource_id # Convert the depth to meters. org = ev.origins[0] org.depth *= 1000.0 if org.depth_errors.uncertainty: org.depth_errors.uncertainty *= 1000.0 # Ugly asserts -- this is just a simple script. assert(len(ev.magnitudes) == 1) assert(len(ev.origins) == 1) assert(len(ev.focal_mechanisms) == 1) # All values given in the QuakeML file are given in dyne * cm. Convert them # to N * m. for key, value in mt.tensor.iteritems(): if key.startswith("m_") and len(key) == 4: mt.tensor[key] /= 1E7 if key.endswith("_errors") and hasattr(value, "uncertainty"): mt.tensor[key].uncertainty /= 1E7 mt.scalar_moment /= 1E7 if mt.scalar_moment_errors.uncertainty: mt.scalar_moment_errors.uncertainty /= 1E7 p_axes = ev.focal_mechanisms[0].principal_axes for ax in [p_axes.t_axis, p_axes.p_axis, p_axes.n_axis]: if ax is None or not ax.length: continue ax.length /= 1E7 # Check if it has a source time function stf = mt.source_time_function if stf: if stf.type != "triangle": msg = ("Source time function type '%s' not yet mapped. Please " "contact the developers.") % stf.type raise NotImplementedError(msg) if not stf.duration: if not stf.decay_time: msg = "Not known how to derive duration without decay time." raise NotImplementedError(msg) # Approximate the duraction for triangular STF. stf.duration = 2 * stf.decay_time # Get the flinn_engdahl region for a nice name. fe = FlinnEngdahl() region_name = fe.get_region(ev.origins[0].longitude, ev.origins[0].latitude) region_name = region_name.replace(" ", "_") event_name = "GCMT_event_%s_Mag_%.1f_%s-%s-%s-%s-%s.xml" % \ (region_name, ev.magnitudes[0].mag, ev.origins[0].time.year, ev.origins[0].time.month, ev.origins[0].time.day, ev.origins[0].time.hour, ev.origins[0].time.minute) # Check if the ids of the magnitude and origin contain the corresponding # tag. Otherwise replace tme. ev.origins[0].resource_id = ev.origins[0].resource_id.resource_id.replace( "quakeml/gcmtid", "quakeml/origin/gcmtid") ev.magnitudes[0].resource_id = \ ev.magnitudes[0].resource_id.resource_id.replace( "quakeml/gcmtid", "quakeml/magnitude/gcmtid") # Fix up the moment tensor resource_ids. mt.derived_origin_id = ev.origins[0].resource_id mt.resource_id = mt.resource_id.resource_id.replace("focalmechanism", "momenttensor") cat = Catalog() cat.resource_id = ev.origins[0].resource_id.resource_id.replace("origin", "event_parameters") cat.append(ev) if output_folder: event_name = os.path.join(output_folder, event_name) cat.write(event_name, format="quakeml", validate=True) print "Written file", event_name
def iris2quakeml(url, output_folder=None): if not "/spudservice/" in url: url = url.replace("/spud/", "/spudservice/") if url.endswith("/"): url += "quakeml" else: url += "/quakeml" print "Downloading %s..." % url r = requests.get(url) if r.status_code != 200: msg = "Error Downloading file!" raise Exception(msg) # For some reason the quakeml file is escaped HTML. h = HTMLParser.HTMLParser() data = h.unescape(r.content) # Replace some XML tags. data = data.replace("long-period body waves", "body waves") data = data.replace("intermediate-period surface waves", "surface waves") data = data.replace("long-period mantle waves", "mantle waves") data = data.replace("<html><body><pre>", "") data = data.replace("</pre></body></html>", "") # Change the resource identifiers. Colons are not allowed in QuakeML. pattern = r"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d{6})" data = re.sub(pattern, r"\1-\2-\3T\4-\5-\6.\7", data) data = StringIO(data) try: cat = readEvents(data) except: msg = "Could not read downloaded event data" raise ValueError(msg) # Parse the event, and use only one origin, magnitude and focal mechanism. # Only the first event is used. Should not be a problem for the chosen # global cmt application. ev = cat[0] if ev.preferred_origin(): ev.origins = [ev.preferred_origin()] else: ev.origins = [ev.origins[0]] if ev.preferred_focal_mechanism(): ev.focal_mechanisms = [ev.preferred_focal_mechanism()] else: ev.focal_mechanisms = [ev.focal_mechanisms[0]] try: mt = ev.focal_mechanisms[0].moment_tensor except: msg = "No moment tensor found in file." raise ValueError seismic_moment_in_dyn_cm = mt.scalar_moment if not seismic_moment_in_dyn_cm: msg = "No scalar moment found in file." raise ValueError(msg) # Create a new magnitude object with the moment magnitude calculated from # the given seismic moment. mag = Magnitude() mag.magnitude_type = "Mw" mag.origin_id = ev.origins[0].resource_id # This is the formula given on the GCMT homepage. mag.mag = (2.0 / 3.0) * (math.log10(seismic_moment_in_dyn_cm) - 16.1) mag.resource_id = ev.origins[0].resource_id.resource_id.replace( "Origin", "Magnitude") ev.magnitudes = [mag] ev.preferred_magnitude_id = mag.resource_id # Convert the depth to meters. org = ev.origins[0] org.depth *= 1000.0 if org.depth_errors.uncertainty: org.depth_errors.uncertainty *= 1000.0 # Ugly asserts -- this is just a simple script. assert (len(ev.magnitudes) == 1) assert (len(ev.origins) == 1) assert (len(ev.focal_mechanisms) == 1) # All values given in the QuakeML file are given in dyne * cm. Convert them # to N * m. for key, value in mt.tensor.iteritems(): if key.startswith("m_") and len(key) == 4: mt.tensor[key] /= 1E7 if key.endswith("_errors") and hasattr(value, "uncertainty"): mt.tensor[key].uncertainty /= 1E7 mt.scalar_moment /= 1E7 if mt.scalar_moment_errors.uncertainty: mt.scalar_moment_errors.uncertainty /= 1E7 p_axes = ev.focal_mechanisms[0].principal_axes for ax in [p_axes.t_axis, p_axes.p_axis, p_axes.n_axis]: if ax is None or not ax.length: continue ax.length /= 1E7 # Check if it has a source time function stf = mt.source_time_function if stf: if stf.type != "triangle": msg = ("Source time function type '%s' not yet mapped. Please " "contact the developers.") % stf.type raise NotImplementedError(msg) if not stf.duration: if not stf.decay_time: msg = "Not known how to derive duration without decay time." raise NotImplementedError(msg) # Approximate the duraction for triangular STF. stf.duration = 2 * stf.decay_time # Get the flinn_engdahl region for a nice name. fe = FlinnEngdahl() region_name = fe.get_region(ev.origins[0].longitude, ev.origins[0].latitude) region_name = region_name.replace(" ", "_") event_name = "GCMT_event_%s_Mag_%.1f_%s-%s-%s-%s-%s.xml" % \ (region_name, ev.magnitudes[0].mag, ev.origins[0].time.year, ev.origins[0].time.month, ev.origins[0].time.day, ev.origins[0].time.hour, ev.origins[0].time.minute) # Check if the ids of the magnitude and origin contain the corresponding # tag. Otherwise replace tme. ev.origins[0].resource_id = ev.origins[0].resource_id.resource_id.replace( "quakeml/gcmtid", "quakeml/origin/gcmtid") ev.magnitudes[0].resource_id = \ ev.magnitudes[0].resource_id.resource_id.replace( "quakeml/gcmtid", "quakeml/magnitude/gcmtid") # Fix up the moment tensor resource_ids. mt.derived_origin_id = ev.origins[0].resource_id mt.resource_id = mt.resource_id.resource_id.replace( "focalmechanism", "momenttensor") cat = Catalog() cat.resource_id = ev.origins[0].resource_id.resource_id.replace( "origin", "event_parameters") cat.append(ev) if output_folder: event_name = os.path.join(output_folder, event_name) cat.write(event_name, format="quakeml", validate=True) print "Written file", event_name