Exemplo n.º 1
0
def maketime(timestring):
    outtime = None
    try:
        outtime = HistoricTime.strptime(timestring,TIMEFMT1)
    except:
        try:
            outtime = HistoricTime.strptime(timestring,TIMEFMT2)
        except:
            try:
                outtime = HistoricTime.strptime(timestring,DATEFMT)
            except:
                raise Exception('Could not parse time or date from %s' % timestring)
    return outtime
Exemplo n.º 2
0
def maketime(timestring):
    outtime = None
    try:
        outtime = HistoricTime.strptime(timestring, TIMEFMT1)
    except Exception:
        try:
            outtime = HistoricTime.strptime(timestring, TIMEFMT2)
        except Exception:
            try:
                outtime = HistoricTime.strptime(timestring, DATEFMT)
            except Exception:
                raise Exception("Could not parse time or date from %s" % timestring)
    return outtime
Exemplo n.º 3
0
def rupture_from_dict(d):
    """
    Method returns either a Rupture subclass (QuadRupture, EdgeRupture, or
    PointRupture) object based on a GeoJSON dictionary.

    .. seealso::
        :func:`rupture_from_dict_and_origin`

    Args:
        d (dict):
            Rupture GeoJSON dictionary, which must contain origin
            information in the 'metadata' field.

    Returns:
        a Rupture subclass.

    """
    validate_json(d)

    # We don't want to mess with the input just in case it gets used again
    d = copy.deepcopy(d)

    try:
        d['metadata']['time'] = HistoricTime.strptime(d['metadata']['time'],
                                                      constants.TIMEFMT)
    except ValueError:
        d['metadata']['time'] = HistoricTime.strptime(d['metadata']['time'],
                                                      constants.ALT_TIMEFMT)

    origin = Origin(d['metadata'])

    # What type of rupture is this?
    geo_type = d['features'][0]['geometry']['type']
    if geo_type == 'MultiPolygon':
        valid_quads = is_quadrupture_class(d)
        if valid_quads is True:
            rupt = QuadRupture(d, origin)
        elif 'mesh_dx' in d['metadata']:
            # EdgeRupture will have 'mesh_dx' in metadata
            mesh_dx = d['metadata']['mesh_dx']
            rupt = EdgeRupture(d, origin, mesh_dx=mesh_dx)
        else:
            raise ValueError('Invalid rupture dictionary.')
    elif geo_type == 'Point':
        rupt = PointRupture(origin)

    return rupt
Exemplo n.º 4
0
def rupture_from_dict(d):
    """
    Method returns either a Rupture subclass (QuadRupture, EdgeRupture, or
    PointRupture) object based on a GeoJSON dictionary.

    .. seealso::
        :func:`rupture_from_dict_and_origin`

    Args:
        d (dict):
            Rupture GeoJSON dictionary, which must contain origin
            information in the 'metadata' field.

    Returns:
        a Rupture subclass.

    """
    validate_json(d)

    d['metadata']['time'] = HistoricTime.strptime(d['metadata']['time'],
                                                  constants.TIMEFMT)
    origin = Origin(d['metadata'])

    # What type of rupture is this?
    geo_type = d['features'][0]['geometry']['type']
    if geo_type == 'MultiPolygon':
        # EdgeRupture will have 'mesh_dx' in metadata
        if 'mesh_dx' in d['metadata']:
            mesh_dx = d['metadata']['mesh_dx']
            rupt = EdgeRupture(d, origin, mesh_dx=mesh_dx)
        else:
            rupt = QuadRupture(d, origin)
    elif geo_type == 'Point':
        rupt = PointRupture(origin)

    return rupt
Exemplo n.º 5
0
    def __init__(self, event):
        """
        Construct an Origin object.

        Args:
            event (dict): Dictionary of values. See list above for required
            keys.

        Returns:
            Origin object.
        Raises:
            ValueError: When input time is not and cannot be converted to
                HistoricTime object.
        """

        # ---------------------------------------------------------------------
        # Check for missing keys
        # ---------------------------------------------------------------------
        missing = []
        for req in constants.ORIGIN_REQUIRED_KEYS:
            if req not in list(event.keys()):
                missing.append(req)

        if len(missing):
            raise Exception('Input event dictionary is missing the following '
                            'required keys: "%s"' % (','.join(missing)))

        # ---------------------------------------------------------------------
        # Check some types, ranges, and defaults
        # ---------------------------------------------------------------------
        if not type(event['eventsourcecode']) is str:
            raise Exception('eventsourcecode must be a string.')

        if (event['lat'] > 90) or (event['lat'] < -90):
            raise Exception('lat must be between -90 and 90 degrees.')

        if (event['lon'] > 180) or (event['lon'] < -180):
            raise Exception('lat must be between -180 and 180 degrees.')

        # make sure that time is an HistoricTime instance
        if 'time' in event:
            if isinstance(event['time'],str):
                try:
                    event['time'] = HistoricTime.strptime(event['time'],TIMEFMT)
                except ValueError:
                    fmt = 'Input time string %s cannot be converted to datetime.'
                    raise ValueError(fmt % event['time'])

        if 'mech' in event.keys():
            if event['mech'] == '':
                event['mech'] = constants.DEFAULT_MECH
            if not event['mech'] in constants.RAKEDICT.keys():
                raise Exception('mech must be SS, NM, RS, or ALL.')
        elif 'type' in event.keys():
            event['mech'] = event['type']
            if event['mech'] == '':
                event['mech'] = constants.DEFAULT_MECH
            if not event['mech'] in constants.RAKEDICT.keys():
                raise Exception('mech must be SS, NM, RS, or ALL.')
        else:
            event['mech'] = constants.DEFAULT_MECH


        # ---------------------------------------------------------------------
        # Add keys as class attributes
        # ---------------------------------------------------------------------
        for k, v in event.items():
            if k == 'rake':
                setattr(self, k, float(v))
            else:
                setattr(self, k, v)

        # What about rake?
        if not hasattr(self, 'rake'):
            if hasattr(self, 'mech'):
                mech = self.mech
                self.rake = constants.RAKEDICT[mech]
            else:
                self.rake = constants.RAKEDICT['ALL']

        if self.rake is None:
            self.rake = 0.0
Exemplo n.º 6
0
def read_event_file(eventxml):
    """Read event.xml file from disk, returning a dictionary of attributes.
    Input XML format looks like this (all elements are required unless
    explicitly labeled optional):

    .. code-block:: xml

         <earthquake
             id="2008ryan"
             netid="us"
             network="USGS National Network" (required but may be empty string)
             lat="30.9858"
             lon="103.3639"
             mag="7.9"
             depth="19.0"
             time="YYYY-mm-ddTHH:MM:SS.ffffffZ" (omitting fractional seconds
                                                is also supported)
             locstring="EASTERN SICHUAN, CHINA"
             mech='SS' | 'NM' | 'RS' | 'ALL' (optional)
             reference="Smith et al. 2016" (optional)
             productcode='us2008ryan' (optional)
         />

    Args:
        eventxml (str): Path to event XML file OR file-like object.

    Returns:
       dict: Dictionary with keys:
         - id: Origin network and origin code (i.e., us2017abcd).
         - netid: Origin network ("us").
         - network: (A long-form description of the network)
         - lat: Origin latitude
         - lon: Origin longitude
         - mag: Origin magnitude
         - depth: Origin depth
         - time: Origin time as an HistoricTime object.
         - locstring: Location string
         - mech: (optional) Moment mechanism, one of:
           - 'RS' (Reverse)
           - 'SS' (Strike-Slip)
           - 'NM' (Normal)
           - 'ALL' (Undetermined)
         - reference: (optional) A description of the source of the data.
         - productcode: (optional) This product source's unique code for this
                        particular ShakeMap.

    Raises:
        ValueError: If the time string cannot be parsed into a datetime object
        KeyError: If any of the required attributes are missing from event.xml
    """
    if isinstance(eventxml, str):
        tree = dET.parse(eventxml)
        root = tree.getroot()
    else:
        data = eventxml.read()
        root = dET.fromstring(data)

    # Turn XML content into dictionary
    if root.tag == 'earthquake':
        xmldict = dict(root.items())
    else:
        eq = root.find('earthquake')
        xmldict = dict(eq.items())

    eqdict = {}

    #########################################################
    # A Short Primer on PDL-style Identifiers
    # Because Everybody (Including The Author) Forgets It.
    #
    # In PDL, there are 4 identifiers that fully specify a product:
    # - source The network that generated the *product* (us, ci, etc.).
    # - code   The unique ID string that identifies this product,
    #          usually prepended by *source* (us2008abcd).
    # - eventsource The network that created the *origin* (us, ci, etc.)
    # - eventsourcecode The code within that network that uniquely
    #                   identifies the event (2008abcd).
    #
    # For our purposes, we're storing *source* and *code* as
    # *productsource* and *productcode* respectively in the
    # container, in an effort to reduce confusion about their
    # meaning. Time will tell.
    #########################################################

    # read in the id fields
    eqdict['id'] = xmldict['id']
    # This isn't optional, but maybe it isn't in some old files
    if 'network' in xmldict:
        eqdict['network'] = xmldict['network']
    else:
        eqdict['network'] = ""

    eqdict['netid'] = xmldict['netid']

    # look for the productcode attribute in the xml,
    # otherwise use the event id
    if 'productcode' in xmldict:
        eqdict['productcode'] = xmldict['productcode']
    elif isinstance(eventxml, str):
        eqdict['productcode'] = eqdict['id']
    else:
        # It's up to the user of this data how to construct the
        # product code
        pass

    # Support old event file date/times
    if 'time' in xmldict:
        try:
            eqdict['time'] = HistoricTime.strptime(xmldict['time'],
                                                   constants.TIMEFMT)
        except ValueError:
            try:
                eqdict['time'] = HistoricTime.strptime(xmldict['time'],
                                                       constants.ALT_TIMEFMT)
            except ValueError:
                raise ValueError("Couldn't convert %s to HistoricTime" %
                                 xmldict['time'])
    else:
        if 'year' not in xmldict or 'month' not in xmldict or \
           'day' not in xmldict or 'hour' not in xmldict or \
           'minute' not in xmldict or 'second' not in xmldict:
            raise ValueError("Missing date/time elements in event file.")
        eqdict['time'] = HistoricTime.datetime(xmldict['year'],
                                               xmldict['month'],
                                               xmldict['day'], xmldict['hour'],
                                               xmldict['minute'],
                                               xmldict['second'])

    eqdict['lat'] = float(xmldict['lat'])
    eqdict['lon'] = float(xmldict['lon'])
    eqdict['depth'] = float(xmldict['depth'])
    eqdict['mag'] = float(xmldict['mag'])
    eqdict['locstring'] = xmldict['locstring']

    if 'mech' in xmldict:
        eqdict['mech'] = xmldict['mech']
    # Older files may have "type" instead of "mech"
    if 'type' in xmldict:
        eqdict['type'] = xmldict['type']
    if 'reference' in xmldict:
        eqdict['reference'] = xmldict['reference']

    return eqdict
Exemplo n.º 7
0
def test_origin():
    fault_text = """30.979788       103.454422      1
31.691615       104.419160      1
31.723569       104.374760      1
32.532213       105.220821      1
32.641450       105.135050      20
31.846790       104.246202      20
31.942158       104.205286      20
31.290105       103.284388      20
30.979788       103.454422      1"""
    event_text = """<?xml version="1.0" encoding="US-ASCII" standalone="yes"?>
<earthquake id="2008ryan" lat="30.9858" lon="103.3639" mag="7.9"
time="2008-05-12T06:28:01Z"
depth="19.0" netid="us" network=""
locstring="EASTERN SICHUAN, CHINA"
mech="" />"""
    source_text = "mech=RS"
    ffile = io.StringIO(fault_text)  # noqa
    efile = io.StringIO(event_text)
    sfile = io.StringIO(source_text)
    origin = Origin.fromFile(efile, sourcefile=sfile)

    testdict = {
        'mag': 7.9,
        'id': '2008ryan',
        'locstring': 'EASTERN SICHUAN, CHINA',
        'mech': 'RS',
        'lon': 103.3639,
        'lat': 30.9858,
        'depth': 19.0
    }
    for key in testdict.keys():
        value = eval(f'origin.{key}')
        if type(value) is str:
            assert testdict[key] == value
        if type(value) is float:
            np.testing.assert_almost_equal(testdict[key], value)

    assert origin.mech == "RS"
    origin.setMechanism("SS")
    assert origin.mech == "SS"
    origin.setMechanism("SS", rake=10)
    assert origin.mech == "SS"
    assert origin.rake == 10.0
    assert origin.dip == 90.0
    origin.setMechanism("SS", rake=-350)
    assert origin.rake == 10.0
    origin.setMechanism("SS", rake=370)
    assert origin.rake == 10.0
    with pytest.raises(Exception) as a:
        origin.setMechanism("SS", dip=100)
    with pytest.raises(Exception) as a:
        origin.setMechanism("Strike slip")
    # Rake too large
    with pytest.raises(Exception) as a:
        origin.setMechanism("SS", rake=1111)
    # Lat is greater than 90
    with pytest.raises(Exception) as a:
        event_text = """<?xml version="1.0" encoding="US-ASCII"
standalone="yes"?> <earthquake id="2008" lat="91.9858" lon="103.3639"
mag="7.9" time="2008-05-12T06:28:01Z"
timezone="GMT" depth="19.0" locstring="EASTERN SICHUAN, CHINA"
created="1211173621" otime="1210573681" mech="" netid="us" network=""/>"""
        efile = io.StringIO(event_text)
        origin = Origin.fromFile(efile)
    # Lon is greater than 180
    with pytest.raises(Exception) as a:
        event_text = """<?xml version="1.0" encoding="US-ASCII"
standalone="yes"?> <earthquake id="2008" lat="31.9858" lon="183.3639"
mag="7.9" time="2008-05-12T06:28:01Z"
timezone="GMT" depth="19.0" locstring="EASTERN SICHUAN, CHINA"
created="1211173621" otime="1210573681" type="" netid="us" network=""/>"""
        efile = io.StringIO(event_text)
        origin = Origin.fromFile(efile)
    # No event id
    with pytest.raises(Exception) as a:
        event_text = """<?xml version="1.0" encoding="US-ASCII"
standalone="yes"?> <earthquake lat="30.9858" lon="103.3639" mag="7.9"
time="2008-05-12T06:28:01Z"
timezone="GMT" depth="19.0" locstring="EASTERN SICHUAN, CHINA"
created="1211173621" otime="1210573681" type="" netid="us" network=""/>"""
        efile = io.StringIO(event_text)
        origin = Origin.fromFile(efile)
    # Put mech in event keys
    event_text = """<?xml version="1.0" encoding="US-ASCII" standalone="yes"?>
<earthquake id="2008" lat="30.9858" lon="103.3639" mag="7.9"
time="2008-05-12T06:28:01Z"
depth="19.0" locstring="EASTERN SICHUAN, CHINA" created="1211173621"
otime="1210573681" type="" mech="SS" netid="us" network=""/>"""
    efile = io.StringIO(event_text)
    origin = Origin.fromFile(efile)
    assert origin.mech == 'SS'
    # Empty mech
    event_text = """<?xml version="1.0" encoding="US-ASCII" standalone="yes"?>
<earthquake id="2008" lat="30.9858" lon="103.3639" mag="7.9"
time="2008-05-12T06:28:01Z"
depth="19.0" locstring="EASTERN SICHUAN, CHINA" created="1211173621"
otime="1210573681" type="" mech="" netid="us" network=""/>"""
    efile = io.StringIO(event_text)
    origin = Origin.fromFile(efile)
    assert origin.mech == 'ALL'
    # Mech not acceptable value
    with pytest.raises(Exception) as a:  # noqa
        event_text = """<?xml version="1.0" encoding="US-ASCII"
standalone="yes"?> <earthquake id="2008" lat="31.9858" lon="103.3639"
mag="7.9"
time="2008-05-12T06:28:01Z"
depth="19.0" locstring="EASTERN SICHUAN, CHINA"
created="1211173621" otime="1210573681" type="" mech="Strike slip"
netid="us" network=""/>"""
        efile = io.StringIO(event_text)
        origin = Origin.fromFile(efile)

    # Missing keys
    with pytest.raises(KeyError):
        event_text = """<?xml version="1.0" encoding="US-ASCII"
standalone="yes"?> <earthquake id="2008"
mag="7.9"
time="2008-05-12T06:28:01Z"
depth="19.0" locstring="EASTERN SICHUAN, CHINA"
created="1211173621" otime="1210573681" type=""
network=""/>"""
        efile = io.StringIO(event_text)
        origin = Origin.fromFile(efile)

    # Use "type" instead of "mech"
    event_text = """<?xml version="1.0" encoding="US-ASCII" standalone="yes"?>
<earthquake id="2008ryan" lat="30.9858" lon="103.3639" mag="7.9"
time="2008-05-12T06:28:01Z"
depth="19.0" netid="us" network=""
locstring="EASTERN SICHUAN, CHINA"
type="RS" />"""
    efile = io.StringIO(event_text)
    origin = Origin.fromFile(efile)
    assert origin.mech == 'RS'

    # No rake or mech
    event_text = """<?xml version="1.0" encoding="US-ASCII" standalone="yes"?>
<earthquake id="2008ryan" lat="30.9858" lon="103.3639" mag="7.9"
time="2008-05-12T06:28:01Z"
depth="19.0" netid="us" network=""
locstring="EASTERN SICHUAN, CHINA"
reference="Smith, et al. (2019)"
 />"""
    efile = io.StringIO(event_text)
    origin = Origin.fromFile(efile)
    assert origin.rake == 0.0

    hypo = origin.getHypo()
    assert hypo.x == origin.lon
    assert hypo.y == origin.lat
    assert hypo.z == origin.depth

    # Write the origin to a file
    event = {}
    event['id'] = 'us2000ryan'
    event['netid'] = 'us'
    event['network'] = 'USGS Network'
    event['lat'] = 30.9858
    event['lon'] = 103.3639
    event['depth'] = 19.0
    event['mag'] = 7.9
    event['time'] = HistoricTime.strptime('2008-05-12T06:28:01.0Z',
                                          constants.TIMEFMT)
    event['locstring'] = "EASTERN SICHUAN, CHINA"
    event['mech'] = 'RS'
    event['reference'] = "Smith, et al. (2019)"
    event['productcode'] = "us2000ryan"

    tfile = tempfile.NamedTemporaryFile()
    xmlfile = tfile.name
    tfile.close()
    res = write_event_file(event, xmlfile)
    with open(xmlfile, 'r') as f:
        fstr = f.read()
    target_str = (
        '<earthquake id="us2000ryan" netid="us" network="USGS Network" lat="30.9858" lon="103.3639" depth="19.0" mag="7.9" time="2008-05-12T06:28:01Z" locstring="EASTERN SICHUAN, CHINA" mech="RS" reference="Smith, et al. (2019)" productcode="us2000ryan" event_type="ACTUAL"/>'
    )
    assert target_str in fstr
    if res is not None:
        print(res)
        assert False

    origin = Origin.fromFile(xmlfile)
    os.remove(xmlfile)
    assert origin.id == event['id']
    assert origin.netid == event['netid']
    assert origin.network == event['network']
    assert origin.time == event['time']
Exemplo n.º 8
0
    def __init__(self, event):
        """
        Construct an Origin object.

        Args:
            event (dict): Dictionary of values. See list above for required
            keys.

        Returns:
            Origin object.
        Raises:
            ValueError: When input time is not and cannot be converted to
                HistoricTime object.
        """

        # ---------------------------------------------------------------------
        # Check for missing keys
        # ---------------------------------------------------------------------
        missing = []
        for req in constants.ORIGIN_REQUIRED_KEYS:
            if req not in list(event.keys()):
                missing.append(req)

        if len(missing):
            raise Exception('Input event dictionary is missing the following '
                            'required keys: "%s"' % (','.join(missing)))

        # ---------------------------------------------------------------------
        # Check some types, ranges, and defaults
        # ---------------------------------------------------------------------
        if not type(event['eventsourcecode']) is str:
            raise Exception('eventsourcecode must be a string.')

        if (event['lat'] > 90) or (event['lat'] < -90):
            raise Exception('lat must be between -90 and 90 degrees.')

        if (event['lon'] > 180) or (event['lon'] < -180):
            raise Exception('lat must be between -180 and 180 degrees.')

        # make sure that time is an HistoricTime instance
        if 'time' in event:
            if isinstance(event['time'], str):
                try:
                    event['time'] = HistoricTime.strptime(
                        event['time'], TIMEFMT)
                except ValueError:
                    fmt = 'Input time string %s cannot be converted to datetime.'
                    raise ValueError(fmt % event['time'])

        if 'mech' in event.keys():
            if event['mech'] == '':
                event['mech'] = constants.DEFAULT_MECH
            if not event['mech'] in constants.RAKEDICT.keys():
                raise Exception('mech must be SS, NM, RS, or ALL.')
        elif 'type' in event.keys():
            event['mech'] = event['type']
            if event['mech'] == '':
                event['mech'] = constants.DEFAULT_MECH
            if not event['mech'] in constants.RAKEDICT.keys():
                raise Exception('mech must be SS, NM, RS, or ALL.')
        else:
            event['mech'] = constants.DEFAULT_MECH

        # ---------------------------------------------------------------------
        # Add keys as class attributes
        # ---------------------------------------------------------------------
        for k, v in event.items():
            if k == 'rake':
                setattr(self, k, float(v))
            else:
                setattr(self, k, v)

        # What about rake?
        if not hasattr(self, 'rake'):
            if hasattr(self, 'mech'):
                mech = self.mech
                self.rake = constants.RAKEDICT[mech]
            else:
                self.rake = constants.RAKEDICT['ALL']

        if self.rake is None:
            self.rake = 0.0