Ejemplo n.º 1
0
    def _parse_record_m(self, line, event, pick):
        """
        Parses the 'surface wave record' M
        """
        # unused: Z_comp = line[7]
        z_period = self._float(line[9:13])
        # note: according to the format documentation,
        # column 20 should be blank. However, it seems that
        # z_amplitude includes that column
        z_amplitude = self._float(line[13:21])  # micrometers
        # TODO: N_comp and E_comp seems to be never there
        msz_mag = line[49:52]
        ms_mag = self._float(line[53:56])
        # unused: Ms_usage_flag = line[56]

        evid = event.resource_id.id.split('/')[-1]
        station_string = \
            pick.waveform_id.get_seed_string()\
            .replace(' ', '-').replace('.', '_').lower()
        amplitude = None
        if z_amplitude is not None:
            amplitude = Amplitude()
            prefix = '/'.join((res_id_prefix, 'amp', evid, station_string))
            amplitude.resource_id = ResourceIdentifier(prefix=prefix)
            amplitude.generic_amplitude = z_amplitude * 1E-6
            amplitude.unit = 'm'
            amplitude.period = z_period
            amplitude.type = 'AS'
            amplitude.magnitude_hint = 'Ms'
            amplitude.pick_id = pick.resource_id
            event.amplitudes.append(amplitude)
        if msz_mag is not None:
            station_magnitude = StationMagnitude()
            prefix = '/'.join((res_id_prefix, 'stationmagntiude',
                               evid, station_string))
            station_magnitude.resource_id = ResourceIdentifier(prefix=prefix)
            station_magnitude.origin_id = event.origins[0].resource_id
            station_magnitude.mag = ms_mag
            station_magnitude.station_magnitude_type = 'Ms'
            if amplitude is not None:
                station_magnitude.amplitude_id = amplitude.resource_id
            event.station_magnitudes.append(station_magnitude)
Ejemplo n.º 2
0
    def _parseRecordM(self, line, event, pick):
        """
        Parses the 'surface wave record' M
        """
        #unused: Z_comp = line[7]
        Z_period = self._float(line[9:13])
        # note: according to the format documentation,
        # column 20 should be blank. However, it seems that
        # Z_amplitude includes that column
        Z_amplitude = self._float(line[13:21])  # micrometers
        #TODO: N_comp and E_comp seems to be never there
        MSZ_mag = line[49:52]
        Ms_mag = self._float(line[53:56])
        #unused: Ms_usage_flag = line[56]

        evid = event.resource_id.id.split('/')[-1]
        station_string =\
            pick.waveform_id.getSEEDString()\
            .replace(' ', '-').replace('.', '_').lower()
        amplitude = None
        if Z_amplitude is not None:
            amplitude = Amplitude()
            prefix = '/'.join((res_id_prefix, 'amp', evid, station_string))
            amplitude.resource_id = ResourceIdentifier(prefix=prefix)
            amplitude.generic_amplitude = Z_amplitude * 1E-6
            amplitude.unit = 'm'
            amplitude.period = Z_period
            amplitude.type = 'AS'
            amplitude.magnitude_hint = 'Ms'
            amplitude.pick_id = pick.resource_id
            event.amplitudes.append(amplitude)
        if MSZ_mag is not None:
            station_magnitude = StationMagnitude()
            prefix = '/'.join(
                (res_id_prefix, 'stationmagntiude', evid, station_string))
            station_magnitude.resource_id = ResourceIdentifier(prefix=prefix)
            station_magnitude.origin_id = event.origins[0].resource_id
            station_magnitude.mag = Ms_mag
            station_magnitude.station_magnitude_type = 'Ms'
            if amplitude is not None:
                station_magnitude.amplitude_id = amplitude.resource_id
            event.station_magnitudes.append(station_magnitude)
Ejemplo n.º 3
0
    def _parseRecordM(self, line, event, pick):
        """
        Parses the 'surface wave record' M
        """
        # unused: Z_comp = line[7]
        Z_period = self._float(line[9:13])
        # note: according to the format documentation,
        # column 20 should be blank. However, it seems that
        # Z_amplitude includes that column
        Z_amplitude = self._float(line[13:21])  # micrometers
        # TODO: N_comp and E_comp seems to be never there
        MSZ_mag = line[49:52]
        Ms_mag = self._float(line[53:56])
        # unused: Ms_usage_flag = line[56]

        evid = event.resource_id.id.split("/")[-1]
        station_string = pick.waveform_id.getSEEDString().replace(" ", "-").replace(".", "_").lower()
        amplitude = None
        if Z_amplitude is not None:
            amplitude = Amplitude()
            prefix = "/".join((res_id_prefix, "amp", evid, station_string))
            amplitude.resource_id = ResourceIdentifier(prefix=prefix)
            amplitude.generic_amplitude = Z_amplitude * 1e-6
            amplitude.unit = "m"
            amplitude.period = Z_period
            amplitude.type = "AS"
            amplitude.magnitude_hint = "Ms"
            amplitude.pick_id = pick.resource_id
            event.amplitudes.append(amplitude)
        if MSZ_mag is not None:
            station_magnitude = StationMagnitude()
            prefix = "/".join((res_id_prefix, "stationmagntiude", evid, station_string))
            station_magnitude.resource_id = ResourceIdentifier(prefix=prefix)
            station_magnitude.origin_id = event.origins[0].resource_id
            station_magnitude.mag = Ms_mag
            station_magnitude.station_magnitude_type = "Ms"
            if amplitude is not None:
                station_magnitude.amplitude_id = amplitude.resource_id
            event.station_magnitudes.append(station_magnitude)
Ejemplo n.º 4
0
Archivo: core.py Proyecto: mbyt/obspy
def _read_picks(f, new_event):
    """
    Internal pick reader. Use read_nordic instead.

    :type f: file
    :param f: File open in read mode
    :type wav_names: list
    :param wav_names: List of waveform files in the sfile
    :type new_event: :class:`~obspy.core.event.event.Event`
    :param new_event: event to associate picks with.

    :returns: :class:`~obspy.core.event.event.Event`
    """
    f.seek(0)
    evtime = new_event.origins[0].time
    pickline = []
    # Set a default, ignored later unless overwritten
    snr = None
    for lineno, line in enumerate(f):
        if line[79] == '7':
            header = line
            break
    for lineno, line in enumerate(f):
        if len(line.rstrip('\n').rstrip('\r')) in [80, 79] and \
           line[79] in ' 4\n':
            pickline += [line]
    for line in pickline:
        if line[18:28].strip() == '':  # If line is empty miss it
            continue
        weight = line[14]
        if weight == '_':
            phase = line[10:17]
            weight = 0
            polarity = ''
        else:
            phase = line[10:14].strip()
            polarity = line[16]
            if weight == ' ':
                weight = 0
        polarity_maps = {"": "undecidable", "C": "positive", "D": "negative"}
        try:
            polarity = polarity_maps[polarity]
        except KeyError:
            polarity = "undecidable"
        # It is valid nordic for the origin to be hour 23 and picks to be hour
        # 00 or 24: this signifies a pick over a day boundary.
        if int(line[18:20]) == 0 and evtime.hour == 23:
            day_add = 86400
            pick_hour = 0
        elif int(line[18:20]) == 24:
            day_add = 86400
            pick_hour = 0
        else:
            day_add = 0
            pick_hour = int(line[18:20])
        try:
            time = UTCDateTime(evtime.year, evtime.month, evtime.day,
                               pick_hour, int(line[20:22]),
                               float(line[23:28])) + day_add
        except ValueError:
            time = UTCDateTime(evtime.year, evtime.month, evtime.day,
                               int(line[18:20]), pick_hour,
                               float("0." + line[23:38].split('.')[1])) +\
                60 + day_add
            # Add 60 seconds on to the time, this copes with s-file
            # preference to write seconds in 1-60 rather than 0-59 which
            # datetime objects accept
        if header[57:60] == 'AIN':
            ain = _float_conv(line[57:60])
            warnings.warn('AIN: %s in header, currently unsupported' % ain)
        elif header[57:60] == 'SNR':
            snr = _float_conv(line[57:60])
        else:
            warnings.warn('%s is not currently supported' % header[57:60])
        # finalweight = _int_conv(line[68:70])
        # Create a new obspy.event.Pick class for this pick
        _waveform_id = WaveformStreamID(station_code=line[1:6].strip(),
                                        channel_code=line[6:8].strip(),
                                        network_code='NA')
        pick = Pick(waveform_id=_waveform_id, phase_hint=phase,
                    polarity=polarity, time=time)
        try:
            pick.onset = onsets[line[9]]
        except KeyError:
            pass
        if line[15] == 'A':
            pick.evaluation_mode = 'automatic'
        else:
            pick.evaluation_mode = 'manual'
        # Note these two are not always filled - velocity conversion not yet
        # implemented, needs to be converted from km/s to s/deg
        # if not velocity == 999.0:
            # new_event.picks[pick_index].horizontal_slowness = 1.0 / velocity
        if _float_conv(line[46:51]) is not None:
            pick.backazimuth = _float_conv(line[46:51])
        # Create new obspy.event.Amplitude class which references above Pick
        # only if there is an amplitude picked.
        if _float_conv(line[33:40]) is not None:
            _amplitude = Amplitude(generic_amplitude=_float_conv(line[33:40]),
                                   period=_float_conv(line[41:45]),
                                   pick_id=pick.resource_id,
                                   waveform_id=pick.waveform_id)
            if pick.phase_hint == 'IAML':
                # Amplitude for local magnitude
                _amplitude.type = 'AML'
                # Set to be evaluating a point in the trace
                _amplitude.category = 'point'
                # Default AML unit in seisan is nm (Page 139 of seisan
                # documentation, version 10.0)
                _amplitude.generic_amplitude /= 1e9
                _amplitude.unit = 'm'
                _amplitude.magnitude_hint = 'ML'
            else:
                # Generic amplitude type
                _amplitude.type = 'A'
            if snr:
                _amplitude.snr = snr
            new_event.amplitudes.append(_amplitude)
        elif _int_conv(line[28:33]) is not None:
            # Create an amplitude instance for code duration also
            _amplitude = Amplitude(generic_amplitude=_int_conv(line[28:33]),
                                   pick_id=pick.resource_id,
                                   waveform_id=pick.waveform_id)
            # Amplitude for coda magnitude
            _amplitude.type = 'END'
            # Set to be evaluating a point in the trace
            _amplitude.category = 'duration'
            _amplitude.unit = 's'
            _amplitude.magnitude_hint = 'Mc'
            if snr is not None:
                _amplitude.snr = snr
            new_event.amplitudes.append(_amplitude)
        # Create new obspy.event.Arrival class referencing above Pick
        if _float_conv(line[33:40]) is None:
            arrival = Arrival(phase=pick.phase_hint, pick_id=pick.resource_id)
            if weight is not None:
                arrival.time_weight = weight
            if _int_conv(line[60:63]) is not None:
                arrival.backazimuth_residual = _int_conv(line[60:63])
            if _float_conv(line[63:68]) is not None:
                arrival.time_residual = _float_conv(line[63:68])
            if _float_conv(line[70:75]) is not None:
                arrival.distance = kilometers2degrees(_float_conv(line[70:75]))
            if _int_conv(line[76:79]) is not None:
                arrival.azimuth = _int_conv(line[76:79])
            new_event.origins[0].arrivals.append(arrival)
        new_event.picks.append(pick)
    return new_event
Ejemplo n.º 5
0
    def _parseRecordP(self, line, event):
        """
        Parses the 'primary phase record' P

        The primary phase is the first phase of the reading,
        regardless its type.
        """
        station = line[2:7].strip()
        phase = line[7:15]
        arrival_time = line[15:24]
        residual = self._float(line[25:30])
        #unused: residual_flag = line[30]
        distance = self._float(line[32:38])  # degrees
        azimuth = self._float(line[39:44])
        backazimuth = round(azimuth % -360 + 180, 1)
        mb_period = self._float(line[44:48])
        mb_amplitude = self._float(line[48:55])  # nanometers
        mb_magnitude = self._float(line[56:59])
        #unused: mb_usage_flag = line[59]

        origin = event.origins[0]
        evid = event.resource_id.id.split('/')[-1]
        waveform_id = WaveformStreamID()
        waveform_id.station_code = station
        #network_code is required for QuakeML validation
        waveform_id.network_code = '  '
        station_string =\
            waveform_id.getSEEDString()\
            .replace(' ', '-').replace('.', '_').lower()
        prefix = '/'.join(
            (res_id_prefix, 'waveformstream', evid, station_string))
        waveform_id.resource_uri = ResourceIdentifier(prefix=prefix)
        pick = Pick()
        prefix = '/'.join((res_id_prefix, 'pick', evid, station_string))
        pick.resource_id = ResourceIdentifier(prefix=prefix)
        date = origin.time.strftime('%Y%m%d')
        pick.time = UTCDateTime(date + arrival_time)
        #Check if pick is on the next day:
        if pick.time < origin.time:
            pick.time += timedelta(days=1)
        pick.waveform_id = waveform_id
        pick.backazimuth = backazimuth
        onset = phase[0]
        if onset == 'e':
            pick.onset = 'emergent'
            phase = phase[1:]
        elif onset == 'i':
            pick.onset = 'impulsive'
            phase = phase[1:]
        elif onset == 'q':
            pick.onset = 'questionable'
            phase = phase[1:]
        pick.phase_hint = phase.strip()
        event.picks.append(pick)
        if mb_amplitude is not None:
            amplitude = Amplitude()
            prefix = '/'.join((res_id_prefix, 'amp', evid, station_string))
            amplitude.resource_id = ResourceIdentifier(prefix=prefix)
            amplitude.generic_amplitude = mb_amplitude * 1E-9
            amplitude.unit = 'm'
            amplitude.period = mb_period
            amplitude.type = 'AB'
            amplitude.magnitude_hint = 'Mb'
            amplitude.pick_id = pick.resource_id
            amplitude.waveform_id = pick.waveform_id
            event.amplitudes.append(amplitude)
            station_magnitude = StationMagnitude()
            prefix = '/'.join(
                (res_id_prefix, 'stationmagntiude', evid, station_string))
            station_magnitude.resource_id = ResourceIdentifier(prefix=prefix)
            station_magnitude.origin_id = origin.resource_id
            station_magnitude.mag = mb_magnitude
            #station_magnitude.mag_errors['uncertainty'] = 0.0
            station_magnitude.station_magnitude_type = 'Mb'
            station_magnitude.amplitude_id = amplitude.resource_id
            station_magnitude.waveform_id = pick.waveform_id
            res_id = '/'.join(
                (res_id_prefix, 'magnitude/generic/body_wave_magnitude'))
            station_magnitude.method_id =\
                ResourceIdentifier(id=res_id)
            event.station_magnitudes.append(station_magnitude)
        arrival = Arrival()
        prefix = '/'.join((res_id_prefix, 'arrival', evid, station_string))
        arrival.resource_id = ResourceIdentifier(prefix=prefix)
        arrival.pick_id = pick.resource_id
        arrival.phase = pick.phase_hint
        arrival.azimuth = azimuth
        arrival.distance = distance
        arrival.time_residual = residual
        res_id = '/'.join((res_id_prefix, 'earthmodel/ak135'))
        arrival.earth_model_id = ResourceIdentifier(id=res_id)
        origin.arrivals.append(arrival)
        origin.quality.minimum_distance = min(
            d for d in (arrival.distance, origin.quality.minimum_distance)
            if d is not None)
        origin.quality.maximum_distance =\
            max(arrival.distance, origin.quality.minimum_distance)
        origin.quality.associated_phase_count += 1
        return pick, arrival
def __toAmplitude(parser, el):
    """
    """
    amp = Amplitude()
    amp.resource_id = ResourceIdentifier(
        prefix="/".join([RESOURCE_ROOT, "amplitude"]))

    if CURRENT_TYPE == "obspyck":
        amp.method_id = "%s/amplitude_method/obspyck/1" % RESOURCE_ROOT
    else:
        msg = "encountered non-obspyck amplitude!"
        raise Exception(msg)

    amp.generic_amplitude, amp.generic_amplitude_errors = \
        __toFloatQuantity(parser, el, "genericAmplitude")

    amp.unit = parser.xpath2obj('unit', el, str)

    waveform = el.xpath("waveform")[0]
    network = waveform.get("networkCode")
    station = fix_station_name(waveform.get("stationCode"))
    # Map some station names.
    if station in STATION_DICT:
        station = STATION_DICT[station]
    if not network:
        network = NETWORK_DICT[station]

    location = waveform.get("locationCode") or ""
    channel = waveform.get("channelCode") or ""
    amp.waveform_id = WaveformStreamID(
        network_code=network,
        station_code=station,
        channel_code=channel,
        location_code=location)

    # Amplitudes without generic_amplitude are not quakeml conform
    if amp.generic_amplitude is None:
        print ("Amplitude has no generic_amplitude value and is "
               "ignored: %s" % station)
        return None

    # generate time_window
    amp.time_window = TimeWindow()
    t_min = parser.xpath2obj('timeWindow/reference', el, UTCDateTime)
    t_max = t_min + parser.xpath2obj('timeWindow/end', el, float)
    dt_abs = abs(t_max - t_min)
    amp.time_window.reference = t_min
    if t_min < t_max:
        amp.time_window.begin = 0.0
        amp.time_window.end = dt_abs
    else:
        amp.time_window.begin = dt_abs
        amp.time_window.end = 0.0

    # we have so far saved frequency in Hz as "period" tag
    # use two times dt instead
    ##amp.period = 1.0 / parser.xpath2obj('period', el, float)
    amp.period = 2.0 * dt_abs

    t = ("PGV; reference time is time of minimum amplitude. if minimum comes "
         "before maximum, start is 0 and end is relative time of maximum. "
         "otherwise end is 0, start is relative time of minimum.")
    comment = Comment(force_resource_id=False, resource_id=None, text=t)
    amp.comments.append(comment)

    return amp
Ejemplo n.º 7
0
    def _parse_record_p(self, line, event):
        """
        Parses the 'primary phase record' P

        The primary phase is the first phase of the reading,
        regardless its type.
        """
        station = line[2:7].strip()
        phase = line[7:15]
        arrival_time = line[15:24]
        residual = self._float(line[25:30])
        # unused: residual_flag = line[30]
        distance = self._float(line[32:38])  # degrees
        azimuth = self._float(line[39:44])
        backazimuth = round(azimuth % -360 + 180, 1)
        mb_period = self._float(line[44:48])
        mb_amplitude = self._float(line[48:55])  # nanometers
        mb_magnitude = self._float(line[56:59])
        # unused: mb_usage_flag = line[59]

        origin = event.origins[0]
        evid = event.resource_id.id.split('/')[-1]
        waveform_id = WaveformStreamID()
        waveform_id.station_code = station
        # network_code is required for QuakeML validation
        waveform_id.network_code = '  '
        station_string = \
            waveform_id.get_seed_string()\
            .replace(' ', '-').replace('.', '_').lower()
        prefix = '/'.join((res_id_prefix, 'waveformstream',
                           evid, station_string))
        waveform_id.resource_uri = ResourceIdentifier(prefix=prefix)
        pick = Pick()
        prefix = '/'.join((res_id_prefix, 'pick', evid, station_string))
        pick.resource_id = ResourceIdentifier(prefix=prefix)
        date = origin.time.strftime('%Y%m%d')
        pick.time = UTCDateTime(date + arrival_time)
        # Check if pick is on the next day:
        if pick.time < origin.time:
            pick.time += timedelta(days=1)
        pick.waveform_id = waveform_id
        pick.backazimuth = backazimuth
        onset = phase[0]
        if onset == 'e':
            pick.onset = 'emergent'
            phase = phase[1:]
        elif onset == 'i':
            pick.onset = 'impulsive'
            phase = phase[1:]
        elif onset == 'q':
            pick.onset = 'questionable'
            phase = phase[1:]
        pick.phase_hint = phase.strip()
        event.picks.append(pick)
        if mb_amplitude is not None:
            amplitude = Amplitude()
            prefix = '/'.join((res_id_prefix, 'amp', evid, station_string))
            amplitude.resource_id = ResourceIdentifier(prefix=prefix)
            amplitude.generic_amplitude = mb_amplitude * 1E-9
            amplitude.unit = 'm'
            amplitude.period = mb_period
            amplitude.type = 'AB'
            amplitude.magnitude_hint = 'Mb'
            amplitude.pick_id = pick.resource_id
            amplitude.waveform_id = pick.waveform_id
            event.amplitudes.append(amplitude)
            station_magnitude = StationMagnitude()
            prefix = '/'.join((res_id_prefix, 'stationmagntiude',
                               evid, station_string))
            station_magnitude.resource_id = ResourceIdentifier(prefix=prefix)
            station_magnitude.origin_id = origin.resource_id
            station_magnitude.mag = mb_magnitude
            # station_magnitude.mag_errors['uncertainty'] = 0.0
            station_magnitude.station_magnitude_type = 'Mb'
            station_magnitude.amplitude_id = amplitude.resource_id
            station_magnitude.waveform_id = pick.waveform_id
            res_id = '/'.join(
                (res_id_prefix, 'magnitude/generic/body_wave_magnitude'))
            station_magnitude.method_id = \
                ResourceIdentifier(id=res_id)
            event.station_magnitudes.append(station_magnitude)
        arrival = Arrival()
        prefix = '/'.join((res_id_prefix, 'arrival', evid, station_string))
        arrival.resource_id = ResourceIdentifier(prefix=prefix)
        arrival.pick_id = pick.resource_id
        arrival.phase = pick.phase_hint
        arrival.azimuth = azimuth
        arrival.distance = distance
        arrival.time_residual = residual
        res_id = '/'.join((res_id_prefix, 'earthmodel/ak135'))
        arrival.earth_model_id = ResourceIdentifier(id=res_id)
        origin.arrivals.append(arrival)
        origin.quality.minimum_distance = min(
            d for d in (arrival.distance, origin.quality.minimum_distance)
            if d is not None)
        origin.quality.maximum_distance = \
            max(arrival.distance, origin.quality.minimum_distance)
        origin.quality.associated_phase_count += 1
        return pick, arrival
Ejemplo n.º 8
0
def _read_picks(f, new_event):
    """
    Internal pick reader. Use read_nordic instead.

    :type f: file
    :param f: File open in read mode
    :type wav_names: list
    :param wav_names: List of waveform files in the sfile
    :type new_event: :class:`~obspy.core.event.event.Event`
    :param new_event: event to associate picks with.

    :returns: :class:`~obspy.core.event.event.Event`
    """
    f.seek(0)
    evtime = new_event.origins[0].time
    pickline = []
    # Set a default, ignored later unless overwritten
    snr = None
    for line in f:
        if line[79] == '7':
            header = line
            break
    for line in f:
        if len(line.rstrip('\n').rstrip('\r')) in [80, 79] and \
           line[79] in ' 4\n':
            pickline += [line]
    for line in pickline:
        if line[18:28].strip() == '':  # If line is empty miss it
            continue
        weight = line[14]
        if weight == '_':
            phase = line[10:17]
            weight = 0
            polarity = ''
        else:
            phase = line[10:14].strip()
            polarity = line[16]
            if weight == ' ':
                weight = 0
        polarity_maps = {"": "undecidable", "C": "positive", "D": "negative"}
        try:
            polarity = polarity_maps[polarity]
        except KeyError:
            polarity = "undecidable"
        # It is valid nordic for the origin to be hour 23 and picks to be hour
        # 00 or 24: this signifies a pick over a day boundary.
        if int(line[18:20]) == 0 and evtime.hour == 23:
            day_add = 86400
            pick_hour = 0
        elif int(line[18:20]) == 24:
            day_add = 86400
            pick_hour = 0
        else:
            day_add = 0
            pick_hour = int(line[18:20])
        try:
            time = UTCDateTime(evtime.year, evtime.month,
                               evtime.day, pick_hour, int(line[20:22]),
                               float(line[23:28])) + day_add
        except ValueError:
            time = UTCDateTime(evtime.year, evtime.month, evtime.day,
                               int(line[18:20]), pick_hour,
                               float("0." + line[23:38].split('.')[1])) +\
                60 + day_add
            # Add 60 seconds on to the time, this copes with s-file
            # preference to write seconds in 1-60 rather than 0-59 which
            # datetime objects accept
        if header[57:60] == 'AIN':
            ain = _float_conv(line[57:60])
            warnings.warn('AIN: %s in header, currently unsupported' % ain)
        elif header[57:60] == 'SNR':
            snr = _float_conv(line[57:60])
        else:
            warnings.warn('%s is not currently supported' % header[57:60])
        # finalweight = _int_conv(line[68:70])
        # Create a new obspy.event.Pick class for this pick
        _waveform_id = WaveformStreamID(station_code=line[1:6].strip(),
                                        channel_code=line[6:8].strip(),
                                        network_code='NA')
        pick = Pick(waveform_id=_waveform_id,
                    phase_hint=phase,
                    polarity=polarity,
                    time=time)
        try:
            pick.onset = onsets[line[9]]
        except KeyError:
            pass
        if line[15] == 'A':
            pick.evaluation_mode = 'automatic'
        else:
            pick.evaluation_mode = 'manual'
        # Note these two are not always filled - velocity conversion not yet
        # implemented, needs to be converted from km/s to s/deg
        # if not velocity == 999.0:
        # new_event.picks[pick_index].horizontal_slowness = 1.0 / velocity
        if _float_conv(line[46:51]) is not None:
            pick.backazimuth = _float_conv(line[46:51])
        # Create new obspy.event.Amplitude class which references above Pick
        # only if there is an amplitude picked.
        if _float_conv(line[33:40]) is not None:
            _amplitude = Amplitude(generic_amplitude=_float_conv(line[33:40]),
                                   period=_float_conv(line[41:45]),
                                   pick_id=pick.resource_id,
                                   waveform_id=pick.waveform_id)
            if pick.phase_hint == 'IAML':
                # Amplitude for local magnitude
                _amplitude.type = 'AML'
                # Set to be evaluating a point in the trace
                _amplitude.category = 'point'
                # Default AML unit in seisan is nm (Page 139 of seisan
                # documentation, version 10.0)
                _amplitude.generic_amplitude /= 1e9
                _amplitude.unit = 'm'
                _amplitude.magnitude_hint = 'ML'
            else:
                # Generic amplitude type
                _amplitude.type = 'A'
            if snr:
                _amplitude.snr = snr
            new_event.amplitudes.append(_amplitude)
        elif _int_conv(line[28:33]) is not None:
            # Create an amplitude instance for code duration also
            _amplitude = Amplitude(generic_amplitude=_int_conv(line[28:33]),
                                   pick_id=pick.resource_id,
                                   waveform_id=pick.waveform_id)
            # Amplitude for coda magnitude
            _amplitude.type = 'END'
            # Set to be evaluating a point in the trace
            _amplitude.category = 'duration'
            _amplitude.unit = 's'
            _amplitude.magnitude_hint = 'Mc'
            if snr is not None:
                _amplitude.snr = snr
            new_event.amplitudes.append(_amplitude)
        # Create new obspy.event.Arrival class referencing above Pick
        if _float_conv(line[33:40]) is None:
            arrival = Arrival(phase=pick.phase_hint, pick_id=pick.resource_id)
            if weight is not None:
                arrival.time_weight = weight
            if _int_conv(line[60:63]) is not None:
                arrival.backazimuth_residual = _int_conv(line[60:63])
            if _float_conv(line[63:68]) is not None:
                arrival.time_residual = _float_conv(line[63:68])
            if _float_conv(line[70:75]) is not None:
                arrival.distance = kilometers2degrees(_float_conv(line[70:75]))
            if _int_conv(line[76:79]) is not None:
                arrival.azimuth = _int_conv(line[76:79])
            new_event.origins[0].arrivals.append(arrival)
        new_event.picks.append(pick)
    return new_event
Ejemplo n.º 9
0
def __toAmplitude(parser, el):
    """
    """
    amp = Amplitude()
    amp.resource_id = ResourceIdentifier(
        prefix="/".join([RESOURCE_ROOT, "amplitude"]))

    if CURRENT_TYPE == "obspyck":
        amp.method_id = "%s/amplitude_method/obspyck/1" % RESOURCE_ROOT
    else:
        msg = "encountered non-obspyck amplitude!"
        raise Exception(msg)

    amp.generic_amplitude, amp.generic_amplitude_errors = \
        __toFloatQuantity(parser, el, "genericAmplitude")

    amp.unit = parser.xpath2obj('unit', el, str)

    waveform = el.xpath("waveform")[0]
    network = waveform.get("networkCode")
    station = fix_station_name(waveform.get("stationCode"))
    # Map some station names.
    if station in STATION_DICT:
        station = STATION_DICT[station]
    if not network:
        network = NETWORK_DICT[station]

    location = waveform.get("locationCode") or ""
    channel = waveform.get("channelCode") or ""
    amp.waveform_id = WaveformStreamID(network_code=network,
                                       station_code=station,
                                       channel_code=channel,
                                       location_code=location)

    # Amplitudes without generic_amplitude are not quakeml conform
    if amp.generic_amplitude is None:
        print(
            "Amplitude has no generic_amplitude value and is "
            "ignored: %s" % station)
        return None

    # generate time_window
    amp.time_window = TimeWindow()
    t_min = parser.xpath2obj('timeWindow/reference', el, UTCDateTime)
    t_max = t_min + parser.xpath2obj('timeWindow/end', el, float)
    dt_abs = abs(t_max - t_min)
    amp.time_window.reference = t_min
    if t_min < t_max:
        amp.time_window.begin = 0.0
        amp.time_window.end = dt_abs
    else:
        amp.time_window.begin = dt_abs
        amp.time_window.end = 0.0

    # we have so far saved frequency in Hz as "period" tag
    # use two times dt instead
    ##amp.period = 1.0 / parser.xpath2obj('period', el, float)
    amp.period = 2.0 * dt_abs

    t = ("PGV; reference time is time of minimum amplitude. if minimum comes "
         "before maximum, start is 0 and end is relative time of maximum. "
         "otherwise end is 0, start is relative time of minimum.")
    comment = Comment(force_resource_id=False, resource_id=None, text=t)
    amp.comments.append(comment)

    return amp
Ejemplo n.º 10
0
def _read_single_event(event_file, locate_dir, units, local_mag_ph):
    """
    Parse an event file from QuakeMigrate into an obspy Event object.

    Parameters
    ----------
    event_file : `pathlib.Path` object
        Path to .event file to read.
    locate_dir : `pathlib.Path` object
        Path to locate directory (contains "events", "picks" etc. directories).
    units : {"km", "m"}
        Grid projection coordinates for QM LUT (determines units of depths and
        uncertainties in the .event files).
    local_mag_ph : {"S", "P"}
        Amplitude measurement used to calculate local magnitudes.

    Returns
    -------
    event : `obspy.Event` object
        Event object populated with all available information output by
        :class:`~quakemigrate.signal.scan.locate()`, including event locations
        and uncertainties, picks, and amplitudes and magnitudes if available.

    """

    # Parse information from event file
    event_info = pd.read_csv(event_file).iloc[0]
    event_uid = str(event_info["EventID"])

    # Set distance conversion factor (from units of QM LUT projection units).
    if units == "km":
        factor = 1e3
    elif units == "m":
        factor = 1
    else:
        raise AttributeError(f"units must be 'km' or 'm'; not {units}")

    # Create event object to store origin and pick information
    event = Event()
    event.extra = AttribDict()
    event.resource_id = str(event_info["EventID"])
    event.creation_info = CreationInfo(author="QuakeMigrate",
                                       version=quakemigrate.__version__)

    # Add COA info to extra
    event.extra.coa = {"value": event_info["COA"], "namespace": ns}
    event.extra.coa_norm = {"value": event_info["COA_NORM"], "namespace": ns}
    event.extra.trig_coa = {"value": event_info["TRIG_COA"], "namespace": ns}
    event.extra.dec_coa = {"value": event_info["DEC_COA"], "namespace": ns}
    event.extra.dec_coa_norm = {
        "value": event_info["DEC_COA_NORM"],
        "namespace": ns
    }

    # Determine location of cut waveform data - add to event object as a
    # custom extra attribute.
    mseed = locate_dir / "raw_cut_waveforms" / event_uid
    event.extra.cut_waveforms_file = {
        "value": str(mseed.with_suffix(".m").resolve()),
        "namespace": ns
    }
    if (locate_dir / "real_cut_waveforms").exists():
        mseed = locate_dir / "real_cut_waveforms" / event_uid
        event.extra.real_cut_waveforms_file = {
            "value": str(mseed.with_suffix(".m").resolve()),
            "namespace": ns
        }
    if (locate_dir / "wa_cut_waveforms").exists():
        mseed = locate_dir / "wa_cut_waveforms" / event_uid
        event.extra.wa_cut_waveforms_file = {
            "value": str(mseed.with_suffix(".m").resolve()),
            "namespace": ns
        }

    # Create origin with spline location and set to preferred event origin.
    origin = Origin()
    origin.method_id = "spline"
    origin.longitude = event_info["X"]
    origin.latitude = event_info["Y"]
    origin.depth = event_info["Z"] * factor
    origin.time = UTCDateTime(event_info["DT"])
    event.origins = [origin]
    event.preferred_origin_id = origin.resource_id

    # Create origin with gaussian location and associate with event
    origin = Origin()
    origin.method_id = "gaussian"
    origin.longitude = event_info["GAU_X"]
    origin.latitude = event_info["GAU_Y"]
    origin.depth = event_info["GAU_Z"] * factor
    origin.time = UTCDateTime(event_info["DT"])
    event.origins.append(origin)

    ouc = OriginUncertainty()
    ce = ConfidenceEllipsoid()
    ce.semi_major_axis_length = event_info["COV_ErrY"] * factor
    ce.semi_intermediate_axis_length = event_info["COV_ErrX"] * factor
    ce.semi_minor_axis_length = event_info["COV_ErrZ"] * factor
    ce.major_axis_plunge = 0
    ce.major_axis_azimuth = 0
    ce.major_axis_rotation = 0
    ouc.confidence_ellipsoid = ce
    ouc.preferred_description = "confidence ellipsoid"

    # Set uncertainties for both as the gaussian uncertainties
    for origin in event.origins:
        origin.longitude_errors.uncertainty = kilometer2degrees(
            event_info["GAU_ErrX"] * factor / 1e3)
        origin.latitude_errors.uncertainty = kilometer2degrees(
            event_info["GAU_ErrY"] * factor / 1e3)
        origin.depth_errors.uncertainty = event_info["GAU_ErrZ"] * factor
        origin.origin_uncertainty = ouc

    # Add OriginQuality info to each origin?
    for origin in event.origins:
        origin.origin_type = "hypocenter"
        origin.evaluation_mode = "automatic"

    # --- Handle picks file ---
    pick_file = locate_dir / "picks" / event_uid
    if pick_file.with_suffix(".picks").is_file():
        picks = pd.read_csv(pick_file.with_suffix(".picks"))
    else:
        return None

    for _, pickline in picks.iterrows():
        station = str(pickline["Station"])
        phase = str(pickline["Phase"])
        wid = WaveformStreamID(network_code="", station_code=station)

        for method in ["modelled", "autopick"]:
            pick = Pick()
            pick.extra = AttribDict()
            pick.waveform_id = wid
            pick.method_id = method
            pick.phase_hint = phase
            if method == "autopick" and str(pickline["PickTime"]) != "-1":
                pick.time = UTCDateTime(pickline["PickTime"])
                pick.time_errors.uncertainty = float(pickline["PickError"])
                pick.extra.snr = {
                    "value": float(pickline["SNR"]),
                    "namespace": ns
                }
            elif method == "modelled":
                pick.time = UTCDateTime(pickline["ModelledTime"])
            else:
                continue
            event.picks.append(pick)

    # --- Handle amplitudes file ---
    amps_file = locate_dir / "amplitudes" / event_uid
    if amps_file.with_suffix(".amps").is_file():
        amps = pd.read_csv(amps_file.with_suffix(".amps"))

        i = 0
        for _, ampsline in amps.iterrows():
            wid = WaveformStreamID(seed_string=ampsline["id"])
            noise_amp = ampsline["Noise_amp"] / 1000  # mm to m
            for phase in ["P_amp", "S_amp"]:
                amp = Amplitude()
                if pd.isna(ampsline[phase]):
                    continue
                amp.generic_amplitude = ampsline[phase] / 1000  # mm to m
                amp.generic_amplitude_errors.uncertainty = noise_amp
                amp.unit = "m"
                amp.type = "AML"
                amp.method_id = phase
                amp.period = 1 / ampsline[f"{phase[0]}_freq"]
                amp.time_window = TimeWindow(
                    reference=UTCDateTime(ampsline[f"{phase[0]}_time"]))
                # amp.pick_id = ?
                amp.waveform_id = wid
                # amp.filter_id = ?
                amp.magnitude_hint = "ML"
                amp.evaluation_mode = "automatic"
                amp.extra = AttribDict()
                try:
                    amp.extra.filter_gain = {
                        "value": ampsline[f"{phase[0]}_filter_gain"],
                        "namespace": ns
                    }
                    amp.extra.avg_amp = {
                        "value": ampsline[f"{phase[0]}_avg_amp"] / 1000,  # m
                        "namespace": ns
                    }
                except KeyError:
                    pass

                if phase[0] == local_mag_ph and not pd.isna(ampsline["ML"]):
                    i += 1
                    stat_mag = StationMagnitude()
                    stat_mag.extra = AttribDict()
                    # stat_mag.origin_id = ? local_mag_loc
                    stat_mag.mag = ampsline["ML"]
                    stat_mag.mag_errors.uncertainty = ampsline["ML_Err"]
                    stat_mag.station_magnitude_type = "ML"
                    stat_mag.amplitude_id = amp.resource_id
                    stat_mag.extra.picked = {
                        "value": ampsline["is_picked"],
                        "namespace": ns
                    }
                    stat_mag.extra.epi_dist = {
                        "value": ampsline["epi_dist"],
                        "namespace": ns
                    }
                    stat_mag.extra.z_dist = {
                        "value": ampsline["z_dist"],
                        "namespace": ns
                    }

                    event.station_magnitudes.append(stat_mag)

                event.amplitudes.append(amp)

        mag = Magnitude()
        mag.extra = AttribDict()
        mag.mag = event_info["ML"]
        mag.mag_errors.uncertainty = event_info["ML_Err"]
        mag.magnitude_type = "ML"
        # mag.origin_id = ?
        mag.station_count = i
        mag.evaluation_mode = "automatic"
        mag.extra.r2 = {"value": event_info["ML_r2"], "namespace": ns}

        event.magnitudes = [mag]
        event.preferred_magnitude_id = mag.resource_id

    return event