Example #1
0
 def _validate(self):
     """
     Ensure that all segments are closed.
     :raises ShakeMapException:
         if unclosed segments exist.
     """
     # TODO - implement ccw algorithm...
     # http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
     if len(self._lon) != len(self._lat) or len(self._lon) != len(
             self._depth):
         raise ShakeMapException("Fault coordinates don't match")
     inan = np.where(np.isnan(self._lon))[0]
     if not len(inan):
         return
     if not np.isnan(self._lon[inan[-1]]):
         inan = list(inan).append(len(self._lon))
     istart = 0
     for i in range(0, len(inan)):
         iend = inan[i] - 1
         x1 = self._lon[istart]
         x2 = self._lon[iend]
         y1 = self._lat[istart]
         y2 = self._lat[iend]
         z1 = self._depth[istart]
         z2 = self._depth[iend]
         if x1 != x2 or y1 != y2 or z1 != z2:
             raise ShakeMapException(
                 'Unclosed segments exist in fault file.')
         istart = inan[i] + 1
Example #2
0
    def __init__(self,shakemap,topofile,stations,fault,layerdict,source,cities=None):
        req_keys = set(['coast','ocean','lake','country','state','roads'])
        if len(set(layerdict.keys()).intersection(req_keys)) != len(req_keys):
            raise ShakeMapException('layerdict input must have all keys from %s' % str(req_keys))
        self.shakemap = shakemap
        self.topofile = topofile
        self.layerdict = layerdict
        self.cities = cities
        self.city_cols = CITY_COLS
        self.city_rows = CITY_ROWS
        self.cities_per_grid = CITIES_PER_GRID
        self.imt_layer = None
        self.contour_layer = None
        self.intensity_colormap = None
        self.contour_colormap = None
        self.stations = stations
        self.fault = fault
        self.source = source
        self.fig_width = FIG_WIDTH
        self.fig_height = FIG_HEIGHT

        #clip all the vector data now so that map rendering will be fast
        t1 = time.time()
        self._clipBounds()
        t2 = time.time()
        print('%.1f seconds to clip vectors.' % (t2-t1))
Example #3
0
def _load(vs30File,
          samplegeodict=None,
          resample=False,
          method='linear',
          doPadding=False,
          padValue=np.nan):
    try:
        vs30grid = GMTGrid.load(vs30File,
                                samplegeodict=samplegeodict,
                                resample=resample,
                                method=method,
                                doPadding=doPadding,
                                padValue=padValue)
    except Exception as msg1:
        try:
            vs30grid = GDALGrid.load(vs30File,
                                     samplegeodict=samplegeodict,
                                     resample=resample,
                                     method=method,
                                     doPadding=doPadding,
                                     padValue=padValue)
        except Exception as msg2:
            msg = 'Load failure of %s - error messages: "%s"\n "%s"' % (
                vs30File, str(msg1), str(msg2))
            raise ShakeMapException(msg)

    if vs30grid.getData().dtype != np.float64:
        vs30grid.setData(vs30grid.getData().astype(np.float64))

    return vs30grid
Example #4
0
    def send(self):
        # we can really only support sending of one file and/or one directory, so error out
        # if someone has specified more than one of either.
        if len(self.files) > 1:
            raise ShakeMapException(
                'For PDL, you may only send one file at a time.')
        if len(self.directories) > 1:
            raise ShakeMapException(
                'For PDL, you may only send one directory at a time.')

        # make sure we have all the required properties
        for prop in self.required_properties:
            if prop not in list(self.properties.keys()):
                raise ShakeMapException(
                    '"%s" property must be supplied to send via PDL')

        # build pdl command line from properties
        self.properties['command'] = 'send'
        self.properties['status'] = 'UPDATE'
        if self.files:
            self.properties['file'] = self.files[0]
        else:
            self.properties['file'] = ''
        if self.directories:
            self.properties['directory'] = self.directories[0]
        else:
            self.properties['directory'] = ''
        cmd = self.pdlcmd
        for propkey, propvalue in self.properties.items():
            cmd = cmd.replace('[' + propkey.upper() + ']', propvalue)

        # call PDL on the command line
        retcode, stdout, stderr = get_command_output(cmd)
        if not retcode:
            fmt = 'Could not send product "%s" due to error "%s"'
            tpl = (code, stdout + stderr)
            raise ShakeMapException(fmt % tpl)

        # return the number of files we just sent
        nfiles = 0
        if self.properties['file']:
            nfiles += 1
        if self.properties['directory']:
            nfiles += len(os.listdir(self.properties['directory']))

        return nfiles
Example #5
0
    def readFaultFile(cls, faultfile):
        """
        Read fault file format as defined in ShakeMap Software Guide.

        :param faultfile:
            Path to fault file OR file-like object in GMT psxy format, where
            * Fault vertices are space separated lat,lon,depth triplets on a
              single line.
            * Fault segments are separated by lines containing ">"
            * Fault segments must be closed.
            * Fault segments must be all clockwise or all counter-clockwise.
        :returns:
           Fault object.
        :raises ShakeMapException:
            when any of above conditions are not met.
        """
        x = []
        y = []
        z = []
        isFile = False
        if isinstance(faultfile, str):
            isFile = True
            faultfile = open(faultfile, 'rt')
            faultlines = faultfile.readlines()
        else:
            faultlines = faultfile.readlines()
        reference = ''
        for line in faultlines:
            sline = line.strip()
            if sline.startswith('#'):
                reference += sline
                continue
            if sline.startswith('>'):
                if len(x):  # start of new line segment
                    x.append(np.nan)
                    y.append(np.nan)
                    z.append(np.nan)
                    continue
                else:  # start of file
                    continue
            if not len(sline.strip()):
                continue
            parts = sline.split()
            if len(parts) < 3:
                raise ShakeMapException(
                    'Finite fault file %s has no depth values.' % faultfile)
            y.append(float(parts[0]))
            x.append(float(parts[1]))
            z.append(float(parts[2]))
        if isFile:
            faultfile.close()
        if np.isnan(x[-1]):
            x = x[0:-1]
            y = y[0:-1]
            z = z[0:-1]

        return cls(x, y, z, reference)
Example #6
0
    def connect(self):
        """Initiate an ssh connection with properties passed to constructor.
        :returns:
          Instance of the paramiko SSHClient class.
        """
        usePrivateKey = True
        for prop in self.required_props1:
            if prop not in list(self.properties.keys()):
                usePrivateKey = False
                break
        usePassword = True
        for prop in self.required_props2:
            if prop not in list(self.properties.keys()):
                usePassword = False
                break
        if not usePrivateKey and not usePassword:
            raise ShakeMapException(
                'Either username/password must be specified, or the name of an SSH private key file.'
            )

        ssh = SSHClient()
        #load hosts found in ~/.ssh/known_hosts
        ssh.load_system_host_keys(
        )  #should we not assume that the user has these configured already?
        if usePrivateKey:
            try:
                ssh.connect(self.properties['remotehost'],
                            key_filename=self.properties['privatekey'],
                            compress=True)
            except Exception as obj:
                raise ShakeMapException(
                    'Could not connect with private key file %s' %
                    self.properties['privatekey'])
        else:
            try:
                ssh.connect(self.properties['remotehost'],
                            username=self.properties['username'],
                            password=self.properties['password'],
                            compress=True)
            except Exception as obj:
                raise ShakeMapException(
                    'Could not connect with private key file %s' %
                    self.properties['privatekey'])
        return ssh
Example #7
0
    def delete(self):
        for prop in self.required_properties:
            if prop not in list(self.properties.keys()):
                raise ShakeMapException(
                    '"%s" property must be supplied to send via PDL')

        # build pdl command line from properties
        self.properties['status'] = 'DELETE'
        self.properties['files'] = ''
        self.properties['directories'] = ''
        cmd = self.pdlcmd
        for propkey, propvalue in self.properties.items():
            cmd = cmd.replace('[' + propkey.upper() + ']', propvalue)

        retcode, stdout, stderr = get_command_output(cmd)
        if not retcode:
            fmt = 'Could not delete product "%s" due to error "%s"'
            tpl = (code, stdout + stderr)
            raise ShakeMapException(fmt % tpl)
Example #8
0
 def _reverseQuad(self, P0, P1, P2, P3):
     newP0 = copy.deepcopy(P1)
     newP1 = copy.deepcopy(P0)
     newP2 = copy.deepcopy(P3)
     newP3 = copy.deepcopy(P2)
     if not self._isPointToRight(newP0, newP1, newP2):
         raise ShakeMapException(
             'Third vertex of quadrilateral must be to the right of the second vertex'
         )
     return (newP0, newP1, newP2, newP3)
Example #9
0
 def setup(self):
     """
     Initiate an ftp connection with properties passed to constructor.
     :returns:
       Instance of the ftplib.FTP class.
     """
     if 'host' not in list(self.properties.keys()):
         raise NameError('"host" keyword must be supplied to send via FTP')
     if 'directory' not in list(self.properties.keys()):
         raise NameError(
             '"directory" keyword must be supplied to send via FTP')
     host = self.properties['host']
     folder = self.properties['directory']
     try:
         dirparts = folder.strip().split('/')
         ftp = FTP(host)
         if 'user' in self.properties:
             user = self.properties['user']
         else:
             user = ''
         if 'password' in self.properties:
             password = self.properties['password']
         else:
             password = ''
         if user == '':
             ftp.login()
         else:
             ftp.login(user, password)
         for d in dirparts:
             if d == '':
                 continue
             try:
                 ftp.cwd(d)
             except ftplib.error_perm as msg:
                 raise ShakeMapException(
                     'Could not login to host "%s" and navigate to directory "%s"'
                     % (host, folder))
     except Exception as obj:
         raise ShakeMapException('Could not send to %s.  Error "%s"' %
                                 (host, str(obj)))
     return ftp
Example #10
0
    def delete(self):
        '''Delete any files and folders that have been passed to constructor.
        :returns:
          The number of files deleted from local directory.
        '''
        if 'directory' not in self.properties:
            raise ShakeMapException('Property "directory" not specified.')

        if not os.path.isdir(self.properties['directory']):
            raise ShakeMapException('Output directory "%s" does not exist.' %
                                    self.properties['directory'])

        for filename in self.files:
            fbase, fname = os.path.split(filename)
            dfile = os.path.join(self.properties['directory'], fname)
            os.remove(dfile)

        for folder in self.directories:
            fbase, dirname = os.path.split(folder)
            dfolder = os.path.join(self.properties['directory'], dirname)
            shutil.rmtree(dfolder)
Example #11
0
def _getFileGeoDict(fname):
    geodict = None
    try:
        geodict = GMTGrid.getFileGeoDict(fname)
    except Exception as msg1:
        try:
            geodict = GDALGrid.getFileGeoDict(fname)
        except Exception as msg2:
            msg = 'File geodict failure with %s - error messages: "%s"\n "%s"' % (
                fname, str(msg1), str(msg2))
            raise ShakeMapException(msg)
    return geodict
Example #12
0
 def __init__(self, properties=None, files=None, directory=None):
     self.properties = properties
     if files is not None:
         if not isinstance(files, list):
             raise ShakeMapException('Input files must be a list')
         for f in files:
             if not os.path.isfile(f):
                 raise ShakeMapException(
                     'Input file %s could not be found' % f)
     if directory is not None:
         if not os.path.isdir(directory):
             raise ShakeMapException(
                 'Input directory %s could not be found' % directory)
     if files is not None:
         self.files = files
     else:
         self.files = []
     if directory is not None:
         self.directories = [directory]
     else:
         self.directories = []
Example #13
0
    def getRuptureContext(self, gmpelist):
        """
        Return an Openquake 
        `RuptureContext <http://docs.openquake.org/oq-hazardlib/master/gsim/index.html#openquake.hazardlib.gsim.base.RuptureContext>`__
        suitable for any GMPE (except for those requiring hypo_loc).

        If Source does not contain a Fault, strike, dip, ztor, and width will be
        filled with default values. Rake may not be known, or may be estimated
        from a focal mechanism.

        :param gmpelist:
            Sequence of hazardlib GMPE objects.
        :raises:
            ShakeMapException when a GMPE requiring 'hypo_loc' is passed in.
        :returns:
            RuptureContext object with all known parameters filled in.
        """
        # rupturecontext constructor inputs:
        # 'mag', 'strike', 'dip', 'rake', 'ztor', 'hypo_lon', 'hypo_lat',
        # 'hypo_depth', 'width', 'hypo_loc'
        for gmpe in gmpelist:
            reqset = gmpe.REQUIRES_RUPTURE_PARAMETERS
            if 'hypo_loc' in reqset:
                raise ShakeMapException(
                    'Rupture parameter "hypo_loc" is not supported!')

        rup = base.RuptureContext()
        rup.mag = self.getEventParam('mag')
        if self._fault is not None:
            rup.strike = self._fault.getStrike()
            rup.dip = self._fault.getDip()
            rup.ztor = self._fault.getTopOfRupture()
            rup.width = self._fault.getWidth()
        else:
            rup.strike = DEFAULT_STRIKE
            rup.dip = self.getEventParam('dip')
            rup.ztor = DEFAULT_ZTOR
            rup.width = DEFAULT_WIDTH

        if 'rake' in self._event_dict:
            rup.rake = self.getEventParam('rake')
        elif 'mech' in self._event_dict:
            mech = self._event_dict['mech']
            rup.rake = RAKEDICT[mech]
        else:
            rup.rake = RAKEDICT['ALL']

        rup.hypo_lat = self.getEventParam('lat')
        rup.hypo_lon = self.getEventParam('lon')
        rup.hypo_depth = self.getEventParam('depth')
        return rup
Example #14
0
    def send(self):
        '''Send any files or folders that have been passed to constructor.
        :returns:
          Number of files sent to local directory.
        '''
        nfiles = 0
        if 'directory' not in self.properties:
            raise ShakeMapException('Property "directory" not specified.')

        if not os.path.isdir(self.properties['directory']):
            raise ShakeMapException('Output directory "%s" does not exist.' %
                                    self.properties['directory'])

        for filename in self.files:
            shutil.copy(filename, self.properties['directory'])

        nfiles += len(self.files)

        for folder in self.directories:
            shutil.copytree(folder, self.properties['directory'])
            nfiles += len(os.walk(folder).next()[2])

        return nfiles
Example #15
0
def _testSendFile(properties):
    print('Testing sending single file...')
    thisfile = os.path.abspath(__file__)
    thispath, thisfilename = os.path.split(thisfile)
    try:
        sender = FTPSender(properties=properties, files=[thisfile])
        sender.send()
        url = 'ftp://%s%s%s' % (properties['host'], properties['directory'],
                                thisfilename)
        fh = urllib.request.urlopen(url)
        fh.close()
        sender.delete()
    except Exception as obj:
        fmt = 'Test failed - you may have a file called %s on host %s and directory %s'
        tpl = (thisfile, properties['host'], ['directory'])
        raise ShakeMapException(fmt % tpl)
    print('Passed sending single file.')
Example #16
0
    def send(self):
        '''Send any files or folders that have been passed to constructor.
        :returns:
          Number of files sent to remote SSH server.
        '''
        if 'host' not in list(self.properties.keys()):
            raise NameError('"host" keyword must be supplied to send via FTP')
        if 'directory' not in list(self.properties.keys()):
            raise NameError(
                '"directory" keyword must be supplied to send via FTP')
        try:
            host = self.properties['host']
            folder = self.properties['directory']
            ftp = self.setup()
            # ftp.cwd(self.properties['directory'])
            nfiles = 0
            if self.files is not None:
                for f in self.files:
                    self.__sendfile(f, ftp)
                    nfiles += 1
            if self.directories is not None:
                for directory in self.directories:
                    # root is the top level local directory
                    root, thisfolder = os.path.split(directory)
                    for path, subdirs, files in os.walk(directory):
                        # mpath is the relative path on the ftp server
                        mpath = path.replace(root, '').lstrip(os.sep)
                        allfiles = ftp.nlst()
                        if mpath not in allfiles:
                            ftp.mkd(mpath)
                        # full path to the folder on ftp server
                        ftpfolder = os.path.join(folder, mpath)
                        ftp.cwd(ftpfolder)
                        for f in files:
                            # f is the file name within the current folder
                            # the full path to the local file
                            fpath = os.path.join(path, f)
                            self.__sendfile(fpath, ftp)
                            nfiles += 1
                        ftp.cwd(folder)  # go back to the root
            ftp.quit()
            return nfiles

        except Exception as obj:
            raise ShakeMapException('Could not send to %s.  Error "%s"' %
                                    (host, str(obj)))
Example #17
0
def _testSendFolder(properties):
    #modify this to create a temporary folder and send that - I think __pycache__ is screwing up the deletes...
    #although maybe I should test deleting directories with directories in them...
    print('Testing sending folder...')
    thisfile = os.path.abspath(__file__)
    thispath, thisfilename = os.path.split(thisfile)
    try:
        sender = FTPSender(properties=properties, directory=thispath)
        sender.send()
        url = 'ftp://%s%s' % (properties['host'], properties['directory'])
        fh = urllib.request.urlopen(url)
        fh.close()
        sender.delete()
    except Exception as obj:
        fmt = 'Test failed - you may have a file called %s on host %s and directory %s'
        tpl = (thisfile, properties['host'], ['directory'])
        raise ShakeMapException(fmt % tpl)
    print('Passed sending folder.')
Example #18
0
 def plot(self, ax=None):
     if ax is None:
         fig = plt.figure()
         ax = fig.add_subplot(111, projection='3d')
     else:
         if 'xlim3d' not in list(ax.properties.keys()):
             raise ShakeMapException(
                 'Non-3d axes object passed to plot() method.')
     for quad in self._quadrilaterals:
         P0, P1, P2, P3 = quad
         ax.plot([P0.longitude], [P0.latitude], [-P0.depth], 'B.')
         ax.text([P0.longitude], [P0.latitude], [-P0.depth], 'P0')
         ax.plot([P1.longitude], [P1.latitude], [-P1.depth], 'b.')
         ax.text([P1.longitude], [P1.latitude], [-P1.depth], 'P1')
         ax.plot([P2.longitude], [P2.latitude], [-P2.depth], 'b.')
         ax.text([P2.longitude], [P2.latitude], [-P2.depth], 'P2')
         ax.plot([P3.longitude], [P3.latitude], [-P3.depth], 'b.')
         ax.text([P3.longitude], [P3.latitude], [-P3.depth], 'P3')
Example #19
0
    def sampleFromSites(self, lats, lons, vs30measured_grid=None):
        """
        Create a SitesContext object by sampling the current Sites object.

        :param lats:
            Sequence of latitudes.
        :param lons:
            Sequence of longitudes.
        :param vs30measured_grid:
            Sequence of booleans of the same shape as lats/lons indicating 
            whether the vs30 values are measured or inferred.
        :returns:
            SitesContext object where data are sampled from the current Sites
            object.
        :raises ShakeMapException:
             When lat/lon input sequences do not share dimensionality.
        """
        lats = np.array(lats)
        lons = np.array(lons)
        latshape = lats.shape
        lonshape = lons.shape
        if latshape != lonshape:
            msg = 'Input lat/lon arrays must have the same dimensions'
            raise ShakeMapException(msg)

        site = SitesContext()
        # use default vs30 if outside grid
        site.vs30 = self._Vs30.getValue(lats, lons, default=self._defaultVs30)
        site.lats = lats
        site.lons = lons
        site.z1pt0 = _calculate_z1p0(site.vs30)
        site.z2pt5 = _calculate_z2p5(site.z1pt0)
        if vs30measured_grid is None:  # If we don't know, then use false
            site.vs30measured = np.zeros_like(lons, dtype=bool)
        else:
            site.vs30measured = vs30measured_grid
        site.backarc = self._backarc

        return site
Example #20
0
 def addDirectory(self, directory):
     if not os.path.isdir(directory):
         raise ShakeMapException('Input directory %s could not be found' %
                                 directory)
     self.directories += directory
Example #21
0
    def drawContourMap(self,outfolder,cmin=None,cmax=None):
        if self.contour_colormap is None:
            raise ShakeMapException('MapMaker.setGMTColormap() has not been called.')
        t0 = time.time()
        #resample shakemap to topogrid
        #get the geodict for the topo file
        topodict = GMTGrid.getFileGeoDict(self.topofile)
        #get the geodict for the ShakeMap
        smdict = self.shakemap.getGeoDict()
        #get a geodict that is aligned with topo, but inside shakemap
        sampledict = topodict.getBoundsWithin(smdict)

        self.shakemap = self.shakemap.interpolateToGrid(sampledict)

        gd = self.shakemap.getGeoDict()

        #establish the basemap object
        m = self._setMap(gd)

        #get topo layer and project it
        topogrid = GMTGrid.load(self.topofile,samplegeodict=sampledict,resample=False)
        topodata = topogrid.getData().copy()
        ptopo = self._projectGrid(topodata,m,gd)

        #get contour layer and project it1
        imtdata = self.shakemap.getLayer(self.contour_layer).getData().copy()
        pimt = self._projectGrid(imtdata,m,gd)

        #get the draped intensity data
        hillshade = self._getShaded(ptopo)

        #draw the draped intensity data
        m.imshow(hillshade, interpolation='none',zorder=IMG_ZORDER);

        #draw the contours of imt data
        xmin = gd.xmin
        if gd.xmax < gd.xmin:
            xmin -= 360
        lons = np.linspace(xmin, gd.xmax, gd.nx)
        lats = np.linspace(gd.ymax, gd.ymin, gd.ny)  # backwards so it plots right side up
        x, y = m(*np.meshgrid(lons,lats))
        pimt = gaussian_filter(pimt,5.0)
        dmin = pimt.min()
        dmax = pimt.max()
        levels = self.getContourLevels(dmin,dmax,self.contour_layer)
        cs = m.contour(x,y,np.flipud(pimt),colors='w',cmap=None,levels=levels,zorder=CONTOUR_ZORDER)
        clabels = plt.clabel(cs,colors='k',fmt='%.1f',fontsize=8.0,zorder=CONTOUR_ZORDER)
        for cl in clabels:
            bbox = dict(boxstyle="round",facecolor='white',edgecolor='w')
            cl.set_bbox(bbox)
            cl.set_zorder(CONTOUR_ZORDER)

        #draw country/state boundaries
        self._drawBoundaries(m)

        #draw lakes
        self._drawLakes(m,gd)

        #draw oceans (pre-processed with islands taken out)
        t1 = time.time()
        self._drawOceans(m,gd)
        t2 = time.time()
        print('%.1f seconds to render oceans.' % (t2-t1))

        #draw coastlines
        self._drawCoastlines(m,gd)

        #draw meridians, parallels, labels, ticks
        self._drawGraticules(m,gd)

        #draw filled symbols for MMI and instrumented measures
        self._drawStations(m,fill=True,imt=self.contour_layer)
        
        #draw map scale
        scalex = gd.xmin + (gd.xmax-gd.xmin)/5.0
        scaley = gd.ymin + (gd.ymax-gd.ymin)/10.0
        yoff = (0.007*(m.ymax-m.ymin))
        clon = (gd.xmin + gd.xmax)/2.0
        clat = (gd.ymin + gd.ymax)/2.0
        m.drawmapscale(scalex,scaley,clon,clat,length=100,barstyle='fancy',yoffset=yoff,zorder=SCALE_ZORDER)

        #draw fault polygon, if present
        self._drawFault(m) #get the fault loaded

        #draw epicenter
        hlon = self.shakemap.getEventDict()['lon']
        hlat = self.shakemap.getEventDict()['lat']
        m.plot(hlon,hlat,'k*',latlon=True,fillstyle='none',markersize=22,mew=1.2,zorder=EPICENTER_ZORDER);

        #draw cities
        #reduce the number of cities to those whose labels don't collide
        #set up cities
        if self.city_cols is not None:
            self.cities = self.cities.limitByBounds((gd.xmin,gd.xmax,gd.ymin,gd.ymax))
            self.cities = self.cities.limitByGrid(nx=self.city_cols,ny=self.city_rows,
                                                  cities_per_grid=self.cities_per_grid)
            self.cities = self.cities.limitByMapCollision(m)
        self.cities.renderToMap(m.ax,zorder=CITIES_ZORDER)

        #draw title and supertitle
        eventid = self._drawTitle(isContour=True)

        #draw whatever road data is available
        #self._drawRoads(m)
        
        #save plot to file
        plt.draw()
        outfile = os.path.join(outfolder,'contour_%s_%s.pdf' % (self.contour_layer,eventid))
        plt.savefig(outfile)
        tn = time.time()
        print('%.1f seconds to render entire map.' % (tn-t0))
        return outfile
Example #22
0
    def drawIntensityMap(self,outfolder):
        if self.intensity_colormap is None:
            raise ShakeMapException('MapMaker.setGMTColormap() has not been called.')
        t0 = time.time()
        #resample shakemap to topogrid
        #get the geodict for the topo file
        topodict = GMTGrid.getFileGeoDict(self.topofile)
        #get the geodict for the ShakeMap
        smdict = self.shakemap.getGeoDict()
        #get a geodict that is aligned with topo, but inside shakemap
        sampledict = topodict.getBoundsWithin(smdict)

        self.shakemap = self.shakemap.interpolateToGrid(sampledict)

        gd = self.shakemap.getGeoDict()

        #establish the basemap object
        m = self._setMap(gd)

        #get topo layer and project it
        topogrid = GMTGrid.load(self.topofile,samplegeodict=sampledict,resample=False)
        topodata = topogrid.getData().copy()
        ptopo = self._projectGrid(topodata,m,gd)

        #get intensity layer and project it
        imtdata = self.shakemap.getLayer(self.imt_layer).getData().copy()
        pimt = self._projectGrid(imtdata,m,gd)

        #get the draped intensity data
        draped_hsv = self._getDraped(pimt,ptopo) #where will 10.0 come from

        #draw the draped intensity data
        m.imshow(draped_hsv, interpolation='none',zorder=IMG_ZORDER);

        #draw country/state boundaries
        self._drawBoundaries(m)

        #draw whatever road data is available
        self._drawRoads(m)
        
        #draw lakes
        self._drawLakes(m,gd)

        #draw oceans (pre-processed with islands taken out)
        t1 = time.time()
        self._drawOceans(m,gd)
        t2 = time.time()
        print('%.1f seconds to render oceans.' % (t2-t1))

        #draw coastlines
        self._drawCoastlines(m,gd)

        #draw meridians, parallels, labels, ticks
        self._drawGraticules(m,gd)

        #draw map scale
        scalex = gd.xmin + (gd.xmax-gd.xmin)/5.0
        scaley = gd.ymin + (gd.ymax-gd.ymin)/10.0
        yoff = (0.007*(m.ymax-m.ymin))
        clon = (gd.xmin + gd.xmax)/2.0
        clat = (gd.ymin + gd.ymax)/2.0
        m.drawmapscale(scalex,scaley,clon,clat,length=100,barstyle='fancy',yoffset=yoff,zorder=SCALE_ZORDER)

        #draw fault polygon, if present
        self._drawFault(m) #get the fault loaded

        #draw epicenter
        hlon = self.shakemap.getEventDict()['lon']
        hlat = self.shakemap.getEventDict()['lat']
        m.plot(hlon,hlat,'k*',latlon=True,fillstyle='none',markersize=22,mew=1.2,zorder=EPICENTER_ZORDER);

        #draw cities
        #reduce the number of cities to those whose labels don't collide
        #set up cities
        if self.city_cols is not None:
            self.cities = self.cities.limitByBounds((gd.xmin,gd.xmax,gd.ymin,gd.ymax))
            self.cities = self.cities.limitByGrid(nx=self.city_cols,ny=self.city_rows,
                                                  cities_per_grid=self.cities_per_grid)
            self.cities = self.cities.limitByMapCollision(m)
        self.cities.renderToMap(m.ax,zorder=CITIES_ZORDER)

        #draw title and supertitle
        eventid = self._drawTitle()

        #draw station and macroseismic locations
        self._drawStations(m) #need stationlist object

        #save plot to file
        plt.draw()
        outfile = os.path.join(outfolder,'intensity_%s.pdf' % eventid)
        plt.savefig(outfile)
        tn = time.time()
        print('%.1f seconds to render entire map.' % (tn-t0))
        return outfile
Example #23
0
    def fromVertices(cls,
                     xp0,
                     yp0,
                     zp0,
                     xp1,
                     yp1,
                     zp1,
                     xp2,
                     yp2,
                     zp2,
                     xp3,
                     yp3,
                     zp3,
                     reference=None):
        """
        Create a fault object from the vector of vertices that fully define the
        quadrilaterals. The points p0, ..., p3 are labeled below for a trapezoid:

        ::

              p0--------p1
             /          |
            /           |
           p3-----------p2

        All of the following vector arguments must have the same length.

        :param xp0:
            Vector of longitudes of p0.
        :param yp0:
            Vector of latitudes of p0.
        :param zp0:
            Vector of depths of p0 (positive down).
        :param xp1:
            Vector of longitudes of p1.
        :param yp1:
            Vector of latitudes of p1.
        :param zp1:
            Vector of depths of p1 (positive down).
        :param xp2:
            Vector of longitudes of p2.
        :param yp2:
            Vector of latitudes of p2.
        :param zp2:
            Vector of depths of p2 (positive down).
        :param xp3:
            Vector of longitudes of p3.
        :param yp3:
            Vector of latitudes of p3.
        :param zp3:
            Vector of depths of p3 (positive down).
        :param reference:
            String explaining where the fault definition came from (publication
            style reference, etc.)
        :returns:
            Fault object, where the fault is modeled as a series of trapezoids.
        """
        if len(xp0) == len(yp0) == len(zp0) == len(xp1) == len(yp1) == len(zp1) == \
           len(xp2) == len(yp2) == len(zp2) == len(xp3) == len(yp3) == len(zp3):
            pass
        else:
            raise ShakeMapException('All vectors specifying quadrilateral '
                                    'vertices must have the same length.')

        nq = len(xp0)

        xp0 = np.array(xp0, dtype='d')
        yp0 = np.array(yp0, dtype='d')
        zp0 = np.array(zp0, dtype='d')
        xp1 = np.array(xp1, dtype='d')
        yp1 = np.array(yp1, dtype='d')
        zp1 = np.array(zp1, dtype='d')
        xp2 = np.array(xp2, dtype='d')
        yp2 = np.array(yp2, dtype='d')
        zp2 = np.array(zp2, dtype='d')
        xp3 = np.array(xp3, dtype='d')
        yp3 = np.array(yp3, dtype='d')
        zp3 = np.array(zp3, dtype='d')

        # assemble the vertices as the Fault constructor needs them...
        # which is: for each rectangle, there should be the four corners, the
        # first corner repeated, and then a nan.
        anan = np.ones_like(xp0) * np.nan
        lon = np.array(list(zip(xp0, xp1, xp2, xp3, xp0, anan))).reshape(
            (nq, 6)).flatten(order='C')
        lat = np.array(list(zip(yp0, yp1, yp2, yp3, yp0, anan))).reshape(
            (nq, 6)).flatten(order='C')
        dep = np.array(list(zip(zp0, zp1, zp2, zp3, zp0, anan))).reshape(
            (nq, 6)).flatten(order='C')

        return cls(lon, lat, dep, reference)
Example #24
0
def get_distance(methods, lat, lon, dep, source, use_median_distance=True):
    """
    Calculate distance using any one of a number of distance measures.
    One of quadlist OR hypo must be specified. The following table gives
    the allowed distance strings and a description of each. 

    +--------+----------------------------------------------------------+
    | String | Description                                              |
    +========+==========================================================+
    | repi   | Distance to epicenter.                                   |
    +--------+----------------------------------------------------------+
    | rhypo  | Distance to hypocenter.                                  |
    +--------+----------------------------------------------------------+
    | rjb    | Joyner-Boore distance; this is closest distance to the   |
    |        | surface projection of the rupture plane.                 |
    +--------+----------------------------------------------------------+
    | rrup   | Rupture distance; closest distance to the rupture plane. |
    +--------+----------------------------------------------------------+
    | rx     | Strike-normal distance; same as GC2 coordiante T.        |
    +--------+----------------------------------------------------------+
    | ry     | Strike-parallel distance; same as GC2 coordiante U, but  |
    |        | with a shift in origin definition. See Spudich and Chiou |
    |        | (2015) http://dx.doi.org/10.3133/ofr20151028.            |
    +--------+----------------------------------------------------------+
    | ry0    | Horizontal distance off the end of the rupture measured  |
    |        | parallel to strike. Can only be zero or positive. We     |
    |        | compute this as a function of GC2 coordinate U.          |
    +--------+----------------------------------------------------------+
    | U      | GC2 coordinate U.                                        |
    +--------+----------------------------------------------------------+
    | T      | GC2 coordinate T.                                        |
    +--------+----------------------------------------------------------+

    :param methods:
        List of strings (or just a string) of distances to compute.
    :param lat:
       A numpy array of latitudes.
    :param lon:
       A numpy array of longidues.
    :param dep:
       A numpy array of depths (km).
    :param source:
       source instance.
    :param use_median_distance:
        Boolean; only used if GMPE requests fault distances and not fault is
        availalbe. Default is True, meaning that point-source distances are
        adjusted based on magnitude to get the median fault distance.
    :returns:
       dictionary of numpy array of distances, size of lon.shape
    """
    fault = source.getFault()
    hypo = source.getHypo()
    if fault is not None:
        quadlist = fault.getQuadrilaterals()
    else:
        quadlist = None

    # Dictionary for holding the distances
    distdict = dict()

    if not isinstance(methods, list):
        methods = [methods]

    methods_available = set(
        ['repi', 'rhypo', 'rjb', 'rrup', 'rx', 'ry', 'ry0', 'U', 'T'])
    if not set(methods).issubset(methods_available):
        raise NotImplementedError(
            'One or more requested distance method is not '
            'valid or is not implemented yet')

    if (lat.shape == lon.shape) and (lat.shape == dep.shape):
        pass
    else:
        raise ShakeMapException('lat, lon, and dep must have the same shape.')

    oldshape = lon.shape

    if len(oldshape) == 2:
        newshape = (oldshape[0] * oldshape[1], 1)
    else:
        newshape = (oldshape[0], 1)

    if ('rrup' in methods) or ('rjb' in methods):
        x, y, z = latlon2ecef(lat, lon, dep)
        x.shape = newshape
        y.shape = newshape
        z.shape = newshape
        sites_ecef = np.hstack((x, y, z))

    # Define a projection that spands sites and fault
    if fault is None:
        all_lat = lat
        all_lon = lon
    else:
        all_lat = np.append(lat, fault.getLats())
        all_lon = np.append(lon, fault.getLons())

    west = np.nanmin(all_lon)
    east = np.nanmax(all_lon)
    south = np.nanmin(all_lat)
    north = np.nanmax(all_lat)
    proj = get_orthographic_projection(west, east, north, south)

    # ---------------------------------------------
    # Distances that do not require loop over quads
    # ---------------------------------------------

    if ('repi' in methods) or \
       (('rjb' in methods) and (quadlist is None)) or \
       (('rrup' in methods) and (quadlist is None)) or \
       (('ry0' in methods) and (quadlist is None)) or \
       (('rx' in methods) and (quadlist is None)) or \
       (('T' in methods) and (quadlist is None)) or \
       (('U' in methods) and (quadlist is None)):
        if hypo is None:
            raise ShakeMapException('Cannot calculate epicentral distance '
                                    'without a point object')
        repidist = geodetic.distance(hypo.longitude, hypo.latitude, 0.0, lon,
                                     lat, dep)
        repidist = repidist.reshape(oldshape)
        distdict['repi'] = repidist

    if ('rhypo' in methods) or \
       (('rrup' in methods) and (quadlist is None)):
        if hypo is None:
            raise ShakeMapException('Cannot calculate epicentral distance '
                                    'without a point object')
        rhypodist = geodetic.distance(hypo.longitude, hypo.latitude,
                                      hypo.depth, lon, lat, dep)
        rhypodist = rhypodist.reshape(oldshape)
        distdict['rhypo'] = rhypodist

    # --------------------------------------------------------
    # Loop over quadlist for those distances that require loop
    # --------------------------------------------------------
    if 'rrup' in methods:
        minrrup = np.ones(newshape, dtype=lon.dtype) * 1e16
    if 'rjb' in methods:
        minrjb = np.ones(newshape, dtype=lon.dtype) * 1e16
    if ('rx' in methods) or ('ry' in methods) or \
       ('ry0' in methods) or ('U' in methods) or ('T' in methods):
        totweight = np.zeros(newshape, dtype=lon.dtype)
        GC2T = np.zeros(newshape, dtype=lon.dtype)
        GC2U = np.zeros(newshape, dtype=lon.dtype)

        if quadlist is not None:
            #-----------------------------------------------------------------
            # For these distances, we need to sort out strike discordance and
            # nominal strike prior to starting the loop if there is more than
            # one segment.
            #-----------------------------------------------------------------

            segind = fault._getSegmentIndex()
            segindnp = np.array(segind)
            uind = np.unique(segind)
            nseg = len(uind)

            #-------------------------------------------------------------------
            # The first thing we need to worry about is finding the coordinate
            # shift. U's origin is " selected from the two endpoints most
            # distant from each other."
            #-------------------------------------------------------------------

            if nseg > 1:
                # Need to get index of first and last quad
                # for each segment
                iq0 = np.zeros(nseg, dtype='int16')
                iq1 = np.zeros(nseg, dtype='int16')
                for k in uind:
                    ii = [i for i, j in enumerate(segind) if j == uind[k]]
                    iq0[k] = int(np.min(ii))
                    iq1[k] = int(np.max(ii))

                #---------------------------------------------------------------
                # This is an iterator for each possible combination of segments
                # including segment orientations (i.e., flipped).
                #---------------------------------------------------------------

                it_seg = it.product(it.combinations(uind, 2),
                                    it.product([0, 1], [0, 1]))

                # Placeholder for the segment pair/orientation that gives the
                # largest distance.
                dist_save = 0

                for k in it_seg:
                    s0ind = k[0][0]
                    s1ind = k[0][1]
                    p0ind = k[1][0]
                    p1ind = k[1][1]
                    if p0ind == 0:
                        P0 = quadlist[iq0[s0ind]][0]
                    else:
                        P0 = quadlist[iq1[s0ind]][1]
                    if p1ind == 0:
                        P1 = quadlist[iq1[s1ind]][0]
                    else:
                        P1 = quadlist[iq0[s1ind]][1]

                    dist = geodetic.distance(P0.longitude, P0.latitude, 0.0,
                                             P1.longitude, P1.latitude, 0.0)
                    if dist > dist_save:
                        dist_save = dist
                        A0 = P0
                        A1 = P1

                #---------------------------------------------------------------
                # A0 and A1 are the furthest two segment endpoints, but we still
                # need to sort out which one is the "origin".
                #---------------------------------------------------------------

                # Primate fixes the trend of the trial a vector.
                primate = -1
                while primate < 0:
                    A0.depth = 0
                    A1.depth = 0
                    p_origin = Vector.fromPoint(A0)
                    a0 = Vector.fromPoint(A0)
                    a1 = Vector.fromPoint(A1)
                    ahat = (a1 - a0).norm()

                    # Loop over traces
                    e_j = np.zeros(nseg)
                    b_prime = [None] * nseg
                    for j in range(nseg):
                        P0 = quadlist[iq0[j]][0]
                        P1 = quadlist[iq1[j]][1]
                        P0.depth = 0
                        P1.depth = 0
                        p0 = Vector.fromPoint(P0)
                        p1 = Vector.fromPoint(P1)
                        b_prime[j] = p1 - p0
                        e_j[j] = ahat.dot(b_prime[j])
                    E = np.sum(e_j)

                    # List of discordancy
                    dc = [np.sign(a) * np.sign(E) for a in e_j]
                    b = Vector(0, 0, 0)
                    for j in range(nseg):
                        b.x = b.x + b_prime[j].x * dc[j]
                        b.y = b.y + b_prime[j].y * dc[j]
                        b.z = b.z + b_prime[j].z * dc[j]
                    bhat = b.norm()
                    primate = bhat.dot(ahat)
                    if primate < 0:
                        tmpA0 = copy.copy(A0)
                        tmpA1 = copy.copy(A1)
                        A0 = tmpA1
                        A1 = tmpA0

    if quadlist is not None:
        # Length of prior segments
        s_i = 0.0
        l_i = np.zeros(len(quadlist))
        for i in range(len(quadlist)):
            P0, P1, P2, P3 = quadlist[i]

            if 'rrup' in methods:
                rrupdist = _calc_rupture_distance(P0, P1, P2, P3, sites_ecef)
                minrrup = np.minimum(minrrup, rrupdist)

            if 'rjb' in methods:
                S0 = copy.deepcopy(P0)
                S1 = copy.deepcopy(P1)
                S2 = copy.deepcopy(P2)
                S3 = copy.deepcopy(P3)
                S0.depth = 0.0
                S1.depth = 0.0
                S2.depth = 0.0
                S3.depth = 0.0
                rjbdist = _calc_rupture_distance(S0, S1, S2, S3, sites_ecef)
                minrjb = np.minimum(minrjb, rjbdist)

            if ('rx' in methods) or ('ry' in methods) or \
               ('ry0' in methods) or ('U' in methods) or ('T' in methods):
                # Rx, Ry, and Ry0 are all computed if one is requested since
                # they all require similar information for the weights. This
                # isn't necessary for a single segment fault though.
                # Note, we are basing these calculations on GC2 coordinates U
                # and T as described in:
                # Spudich and Chiou (2015)
                # http://dx.doi.org/10.3133/ofr20151028.

                # Compute u_i and t_i for this segment
                t_i = __calc_t_i(P0, P1, lat, lon, proj)
                u_i = __calc_u_i(P0, P1, lat, lon, proj)

                # Quad length
                l_i[i] = get_quad_length(quadlist[i])

                # Weight of segment, three cases
                # Case 3: t_i == 0 and 0 <= u_i <= l_i
                w_i = np.zeros_like(t_i)
                # Case 1:
                ix = t_i != 0
                w_i[ix] = (1.0 / t_i[ix]) * (np.arctan(
                    (l_i[i] - u_i[ix]) / t_i[ix]) -
                                             np.arctan(-u_i[ix] / t_i[ix]))
                # Case 2:
                ix = (t_i == 0) & ((u_i < 0) | (u_i > l_i[i]))
                w_i[ix] = 1 / (u_i[ix] - l_i[i]) - 1 / u_i[ix]

                totweight = totweight + w_i
                GC2T = GC2T + w_i * t_i
                if nseg == 1:
                    GC2U = GC2U + w_i * (u_i + s_i)
                else:
                    if i == 0:
                        qind = np.array(range(len(quadlist)))
                        l_kj = 0
                        s_ij_1 = 0
                    else:
                        l_kj = l_i[(segindnp == segindnp[i]) & (qind < i)]
                        s_ij_1 = np.sum(l_kj)

                    p1 = Vector.fromPoint(quadlist[iq0[segind[i]]][0])
                    s_ij_2 = (
                        (p1 - p_origin) * dc[segind[i]]).dot(ahat) / 1000.0
                    # This is implemented with GC2N, for GC2T use:
                    #                    s_ij_2 = (p1 - p_origin).dot(bhat) / 1000.0

                    s_ij = s_ij_1 + s_ij_2
                    GC2U = GC2U + w_i * (u_i + s_ij)
                s_i = s_i + l_i[i]

        # Collect distances from loop into the distance dict
        if 'rjb' in methods:
            minrjb = minrjb.reshape(oldshape)
            distdict['rjb'] = minrjb

        if ('rx' in methods) or ('ry' in methods) or \
           ('ry0' in methods) or ('U' in methods) or ('T' in methods):
            # Normalize by sum of quad weights
            GC2T = GC2T / totweight
            GC2U = GC2U / totweight
            distdict['T'] = copy.deepcopy(GC2T).reshape(oldshape)
            distdict['U'] = copy.deepcopy(GC2U).reshape(oldshape)

            # Take care of Rx
            Rx = copy.deepcopy(GC2T)  # preserve sign (no absolute value)
            Rx = Rx.reshape(oldshape)
            distdict['rx'] = Rx

            # Ry
            Ry = GC2U - s_i / 2.0
            Ry = Ry.reshape(oldshape)
            distdict['ry'] = Ry

            # Ry0
            Ry0 = np.zeros_like(GC2U)
            ix = GC2U < 0
            Ry0[ix] = np.abs(GC2U[ix])
            if nseg > 1:
                s_i = s_ij + l_i[-1]
            ix = GC2U > s_i
            Ry0[ix] = GC2U[ix] - s_i
            Ry0 = Ry0.reshape(oldshape)
            distdict['ry0'] = Ry0

        if 'rrup' in methods:
            minrrup = minrrup.reshape(oldshape)
            distdict['rrup'] = minrrup

    else:
        if 'rjb' in methods:
            if use_median_distance:
                warnings.warn(
                    'No fault; Replacing rjb with median rjb given M and repi.'
                )
                cdir, tmp = os.path.split(__file__)

                # -------------------
                # Sort out file names
                # -------------------
                mech = source.getEventParam('mech')
                if not hasattr(source, '_tectonic_region'):
                    rf = os.path.join(
                        cdir, "data", "ps2ff",
                        "Rjb_WC94_mechA_ar1p0_seis0_20_Ratios.csv")
                    vf = os.path.join(cdir, "data", "ps2ff",
                                      "Rjb_WC94_mechA_ar1p0_seis0_20_Var.csv")
                elif source._tectonic_region == 'Active Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechA_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechA_ar1p7_seis0_20_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechR_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechR_ar1p7_seis0_20_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechN_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechN_ar1p7_seis0_20_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechSS_ar1p7_seis0_20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_WC94_mechSS_ar1p7_seis0_20_Var.csv")
                elif source._tectonic_region == 'Stable Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechA_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechA_ar1p0_seis0_15_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechR_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechR_ar1p0_seis0_15_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechN_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechN_ar1p0_seis0_15_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechSS_ar1p0_seis0_15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rjb_S14_mechSS_ar1p0_seis0_15_Var.csv")
                else:
                    warnings.warn(
                        'Unsupported tectonic region; using coefficients for unknown'
                        'tectonic region.')
                    rf = os.path.join(
                        cdir, "data", "ps2ff",
                        "Rjb_WC94_mechA_ar1p0_seis0_20_Ratios.csv")
                    vf = os.path.join(cdir, "data", "ps2ff",
                                      "Rjb_WC94_mechA_ar1p0_seis0_20_Var.csv")

                # -----------------
                # Start with ratios
                # -----------------
                repi2rjb_ratios_tbl = pd.read_csv(rf, comment='#')
                r2rrt_cols = repi2rjb_ratios_tbl.columns[1:]
                mag_list = []
                for column in (r2rrt_cols):
                    if re.search('R\d+\.*\d*', column):
                        magnitude = float(
                            re.findall('R(\d+\.*\d*)', column)[0])
                        mag_list.append(magnitude)
                mag_list = np.array(mag_list)
                dist_list = np.log(np.array(repi2rjb_ratios_tbl['Repi_km']))
                repi2rjb_grid = repi2rjb_ratios_tbl.values[:, 1:]
                repi2rjb_obj = spint.RectBivariateSpline(dist_list,
                                                         mag_list,
                                                         repi2rjb_grid,
                                                         kx=1,
                                                         ky=1)

                def repi2rjb_tbl(repi, M):
                    ratio = repi2rjb_obj.ev(np.log(repi), M)
                    rjb = repi * ratio
                    return rjb

                repis = distdict['repi']
                mags = np.ones_like(repis) * source.getEventParam('mag')
                rjb_hat = repi2rjb_tbl(repis, mags)
                distdict['rjb'] = rjb_hat
                # -------------------
                # Additional Variance
                # -------------------
                repi2rjbvar_ratios_tbl = pd.read_csv(vf, comment='#')
                repi2rjbvar_grid = repi2rjbvar_ratios_tbl.values[:, 1:]
                repi2rjbvar_obj = spint.RectBivariateSpline(dist_list,
                                                            mag_list,
                                                            repi2rjbvar_grid,
                                                            kx=1,
                                                            ky=1)
                rjbvar = repi2rjbvar_obj.ev(np.log(repis), mags)
                distdict['rjbvar'] = rjbvar
            else:
                warnings.warn('No fault; Replacing rjb with repi')
                distdict['rjb'] = distdict['repi']
        if 'rrup' in methods:
            if use_median_distance:
                warnings.warn(
                    'No fault; Replacing rrup with median rrup given M and repi.'
                )
                cdir, tmp = os.path.split(__file__)

                # -------------------
                # Sort out file names
                # -------------------
                rake = source._event_dict.get('rake')
                mech = rake_to_mech(rake)
                if not hasattr(source, '_tectonic_region'):
                    rf = os.path.join(
                        cdir, "data", "ps2ff",
                        "Rrup_WC94_mechA_ar1p0_seis0-20_Ratios.csv")
                    vf = os.path.join(
                        cdir, "data", "ps2ff",
                        "Rrup_WC94_mechA_ar1p0_seis0-20_Var.csv")
                elif source._tectonic_region == 'Active Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechA_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechA_ar1p7_seis0-20_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechR_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechR_ar1p7_seis0-20_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechN_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechN_ar1p7_seis0-20_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechSS_ar1p7_seis0-20_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_WC94_mechSS_ar1p7_seis0-20_Var.csv")
                elif source._tectonic_region == 'Stable Shallow Crust':
                    if mech == 'ALL':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechA_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechA_ar1p0_seis0-15_Var.csv")
                    elif mech == 'RS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechR_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechR_ar1p0_seis0-15_Var.csv")
                    elif mech == 'NM':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechN_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechN_ar1p0_seis0-15_Var.csv")
                    elif mech == 'SS':
                        rf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechSS_ar1p0_seis0-15_Ratios.csv")
                        vf = os.path.join(
                            cdir, "data", "ps2ff",
                            "Rrup_S14_mechSS_ar1p0_seis0-15_Var.csv")
                else:
                    warnings.warn(
                        'Unsupported tectonic region; using coefficients for unknown'
                        'tectonic region.')
                    rf = os.path.join(
                        cdir, "data", "ps2ff",
                        "Rrup_WC94_mechA_ar1p0_seis0-20_Ratios.csv")
                    vf = os.path.join(
                        cdir, "data", "ps2ff",
                        "Rrup_WC94_mechA_ar1p0_seis0-20_Var.csv")

                # -----------------
                # Start with ratios
                # -----------------
                repi2rrup_ratios_tbl = pd.read_csv(rf, comment='#')
                r2rrt_cols = repi2rrup_ratios_tbl.columns[1:]
                mag_list = []
                for column in (r2rrt_cols):
                    if re.search('R\d+\.*\d*', column):
                        magnitude = float(
                            re.findall('R(\d+\.*\d*)', column)[0])
                        mag_list.append(magnitude)
                mag_list = np.array(mag_list)
                dist_list = np.log(np.array(repi2rrup_ratios_tbl['Repi_km']))
                repi2rrup_grid = repi2rrup_ratios_tbl.values[:, 1:]
                repi2rrup_obj = spint.RectBivariateSpline(dist_list,
                                                          mag_list,
                                                          repi2rrup_grid,
                                                          kx=1,
                                                          ky=1)

                def repi2rrup_tbl(repi, M):
                    ratio = repi2rrup_obj.ev(np.log(repi), M)
                    rrup = repi * ratio
                    return rrup

                repis = distdict['repi']
                mags = np.ones_like(repis) * source.getEventParam('mag')
                rrup_hat = repi2rrup_tbl(repis, mags)
                distdict['rrup'] = rrup_hat

                # -------------------
                # Additional Variance
                # -------------------
                repi2rrupvar_ratios_tbl = pd.read_csv(vf, comment='#')
                repi2rrupvar_grid = repi2rrupvar_ratios_tbl.values[:, 1:]
                repi2rrupvar_obj = spint.RectBivariateSpline(dist_list,
                                                             mag_list,
                                                             repi2rrupvar_grid,
                                                             kx=1,
                                                             ky=1)
                rrupvar = repi2rrupvar_obj.ev(np.log(repis), mags)
                distdict['rrupvar'] = rrupvar
            else:
                warnings.warn('No fault; Replacing rrup with rhypo')
                distdict['rrup'] = distdict['rhypo']
        if 'rx' in methods:
            warnings.warn('No fault; Setting Rx to zero.')
            distdict['rx'] = np.zeros_like(distdict['repi'])
        if 'ry0' in methods:
            warnings.warn('No fault; Replacing ry0 with repi')
            distdict['ry0'] = distdict['repi']
        if 'ry' in methods:
            warnings.warn('No fault; Replacing ry with repi')
            distdict['ry'] = distdict['repi']

    return distdict
Example #25
0
    def _validateQuad(self, P0, P1, P2, P3):
        """
        Validate and fix* a given quadrilateral (*currently "fix" means check
        third vertex for co-planarity with other three points, and force it to
        be co-planar if it's not wildly out of the plane.()

        :param P0:
            First vertex https://github.com/gem/oq-hazardlib/blob/master/openquake/hazardlib/geo/point.py
        :param P1:
            Second vertex https://github.com/gem/oq-hazardlib/blob/master/openquake/hazardlib/geo/point.py
        :param P2:
            Third vertex https://github.com/gem/oq-hazardlib/blob/master/openquake/hazardlib/geo/point.py
        :param P3:
            Fourth vertex https://github.com/gem/oq-hazardlib/blob/master/openquake/hazardlib/geo/point.py
        :returns:
           Tuple of (potentially) modified vertices.
        :raises ShakeMapException:
           * if top and bottom edges are not parallel to surface
           * if dip angle is not dipping to the right relative to strike 
             (defined by first two vertices)
           * if all 4 points are not reasonably co-planar (P2 is more than 5% of
             mean length of trapezoid out of plane)
        """
        # TODO: Someday fix the rule about dip angle being clockwise and 0-90 degrees
        # In theory, you could flip the quadrilateral by 180 degrees and it
        # would be ok.

        # Are the top and bottom edges both parallel to the surface?
        topDepthsEqual = np.allclose(P0.depth, P1.depth, atol=2e-3)
        bottomDepthsEqual = np.allclose(P2.depth, P3.depth, atol=2e-3)
        if not topDepthsEqual or not bottomDepthsEqual:
            raise ShakeMapException(
                'Top and bottom edges of fault quadrilateral must be parallel to the surface'
            )
        # Is top edge defined by first two vertices?
        if P1.depth > P2.depth:
            raise ShakeMapException(
                'Top edge of a quadrilateral must be defined by the first two vertices'
            )
        # Is dip angle clockwise and btw 0-90 degrees?
        if not self._isPointToRight(P0, P1, P2):
            P0, P1, P2, P3 = self._reverseQuad(P0, P1, P2, P3)
            print('Reversing quad where dip not between 0 and 90 degrees.')
        # Are all 4 points (reasonably) co-planar?
        # Translate vertices to ECEF
        p0 = Vector.fromPoint(P0)
        p1 = Vector.fromPoint(P1)
        p2 = Vector.fromPoint(P2)
        p3 = Vector.fromPoint(P3)
        # Calculate normalized vector along top edge
        v0 = (p1 - p0).norm()
        # Calculate distance btw p3 and p2
        d = (p3 - p2).mag()
        # get the new P2 value
        v1 = v0 * d
        newp2 = p3 + v1
        planepoints = [p0, p1, p2]
        dnormal = self.getDistanceToPlane(planepoints, p2)
        geometricMean = self._getTrapMeanLength(p0, p1, newp2, p3)
        if dnormal / geometricMean > OFFPLANE_TOLERANCE:
            raise ShakeMapException(
                'Points in quadrilateral are not co-planar')
        newP0 = p0.toPoint()
        newP1 = p1.toPoint()
        newP2 = newp2.toPoint()
        newP3 = p3.toPoint()
        return [newP0, newP1, newP2, newP3]
Example #26
0
 def addFiles(self, files):
     for f in files:
         if not os.path.isfile(f):
             raise ShakeMapException('Input file %s could not be found' % f)
     self.files += files
Example #27
0
    def fromTrace(cls,
                  xp0,
                  yp0,
                  xp1,
                  yp1,
                  zp,
                  widths,
                  dips,
                  strike=None,
                  reference=None):
        """
        Create a fault object from a set of vertices that define the top of the
        fault, and an array of widths/dips.

        These top of rupture points are defined by specifying the x and y
        coordinates of each of the two vertices, and then specifying an array of
        depths,widths, and dips for each rectangle.

        :param xp0:
            Array of longitude coordinates for the first (top of rupture) vertex
            of each rectangle (decimal degrees).
        :param yp0:
            Array of latitude coordinates for the first (top of rupture) vertex
            of each rectangle (decimal degrees).
        :param xp1:
            Array of longitude coordinates for the second (top of rupture) vertex
            of each rectangle (decimal degrees).
        :param yp1:
            Array of latitude coordinates for the second (top of rupture) vertex
            of each rectangle (decimal degrees).
        :param zp:
            Array of depths for each of the top of rupture rectangles (km).
        :param widths:
            Array of widths for each of rectangle (km).
        :param dips:
            Array of dips for each of rectangle (degrees).
        :param strike:
            If None then strike is computed from verticies of top edge of each
            quadrilateral. If a scalar, then all quadrilaterals are constructed
            assuming this strike direction. If a vector with the same length as
            the trace coordinates then it specifies the strike for each 
            quadrilateral.
        :param reference:
            String explaining where the fault definition came from (publication
            style reference, etc.)
        :returns:
            Fault object, where the fault is modeled as a series of rectangles.
        """
        if len(xp0) == len(yp0) == len(xp1) == len(yp1) == len(zp) == len(
                dips) == len(widths):
            pass
        else:
            raise ShakeMapException(
                'Number of xp0,yp0,xp1,yp1,zp,widths,dips points must be equal.'
            )
        if strike is None:
            pass
        else:
            if (len(xp0) == len(strike)) | (len(strike) == 1):
                pass
            else:
                raise ShakeMapException(
                    'Strike must be None, scalar, or same length as trace coordinates.'
                )

        # convert dips to radians
        dips = np.radians(dips)

        # ensure that all input sequences are numpy arrays
        xp0 = np.array(xp0, dtype='d')
        xp1 = np.array(xp1, dtype='d')
        yp0 = np.array(yp0, dtype='d')
        yp1 = np.array(yp1, dtype='d')
        zp = np.array(zp, dtype='d')
        widths = np.array(widths, dtype='d')
        dips = np.array(dips, dtype='d')

        # get a projection object
        west = np.min((xp0.min(), xp1.min()))
        east = np.max((xp0.max(), xp1.max()))
        south = np.min((yp0.min(), yp1.min()))
        north = np.max((yp0.max(), yp1.max()))

        # projected coordinates are in km
        proj = get_orthographic_projection(west, east, north, south)
        surfaces = []
        xp2 = np.zeros_like(xp0)
        xp3 = np.zeros_like(xp0)
        yp2 = np.zeros_like(xp0)
        yp3 = np.zeros_like(xp0)
        zpdown = np.zeros_like(zp)
        for i in range(0, len(xp0)):
            # Project the top edge coordinates
            p0x, p0y = proj(xp0[i], yp0[i])
            p1x, p1y = proj(xp1[i], yp1[i])

            # Get the rotation angle defined by these two points
            if strike is None:
                dx = p1x - p0x
                dy = p1y - p0y
                theta = np.arctan2(dx, dy)  # theta is angle from north
            elif len(strike) == 1:
                theta = np.radians(strike)
            else:
                theta = np.radians(strike[i])

            R = np.array([[np.cos(theta), -np.sin(theta)],
                          [np.sin(theta), np.cos(theta)]])

            # Rotate the top edge points into a new coordinate system (vertical
            # line)
            p0 = np.array([p0x, p0y])
            p1 = np.array([p1x, p1y])
            p0p = np.dot(R, p0)
            p1p = np.dot(R, p1)

            # Get right side coordinates in project,rotated system
            dz = np.sin(dips[i]) * widths[i]
            dx = np.cos(dips[i]) * widths[i]
            p3xp = p0p[0] + dx
            p3yp = p0p[1]
            p2xp = p1p[0] + dx
            p2yp = p1p[1]

            # Get right side coordinates in un-rotated projected system
            p3p = np.array([p3xp, p3yp])
            p2p = np.array([p2xp, p2yp])
            Rback = np.array([[np.cos(-theta), -np.sin(-theta)],
                              [np.sin(-theta), np.cos(-theta)]])
            p3 = np.dot(Rback, p3p)
            p2 = np.dot(Rback, p2p)
            p3x = np.array([p3[0]])
            p3y = np.array([p3[1]])
            p2x = np.array([p2[0]])
            p2y = np.array([p2[1]])

            # project lower edge points back to lat/lon coordinates
            lon3, lat3 = proj(p3x, p3y, reverse=True)
            lon2, lat2 = proj(p2x, p2y, reverse=True)

            xp2[i] = lon2
            xp3[i] = lon3
            yp2[i] = lat2
            yp3[i] = lat3
            zpdown[i] = zp[i] + dz

        # assemble the vertices as the Fault constructor needs them...
        # which is: for each rectangle, there should be the four corners, the
        # first corner repeated, and then a nan.
        nrects = len(zp)
        anan = np.ones_like(xp0) * np.nan
        lon = np.array(list(zip(xp0, xp1, xp2, xp3, xp0, anan))).reshape(
            (nrects, 6)).flatten(order='C')
        lat = np.array(list(zip(yp0, yp1, yp2, yp3, yp0, anan))).reshape(
            (nrects, 6)).flatten(order='C')

        # we need an array of depths, but we need to double each zp and zpdown
        # element we have
        dep = []
        for i in range(0, nrects):
            dep += [zp[i], zp[i], zpdown[i], zpdown[i], zp[i], np.nan]
        dep = np.array(dep)

        # take the nans off the end of each array
        lon = lon[0:-1]
        lat = lat[0:-1]
        dep = dep[0:-1]

        return cls(lon, lat, dep, reference)
Example #28
0
def _calc_rupture_distance(P0, P1, P2, P3, points):
    """
    Calculate the shortest distance from a set of points to a rupture surface.

    :param P0:
        Point object, representing the first top-edge vertex of a fault quadrilateral.
    :param P1:
        Point object, representing the second top-edge vertex of a fault quadrilateral.
    :param P2:
        Point object, representing the first bottom-edge vertex of a fault quadrilateral.
    :param P3:
        Point object, representing the second bottom-edge vertex of a fault quadrilateral.
    :param points:
        Numpy array Nx3 of points (ECEF) to calculate distance from.
    :returns:
        Array of size N of distances (in km) from input points to rupture surface.
    """
    # Convert to ecef
    p0 = Vector.fromPoint(P0)
    p1 = Vector.fromPoint(P1)
    p2 = Vector.fromPoint(P2)
    p3 = Vector.fromPoint(P3)

    # Make a unit vector normal to the plane
    normalVector = (p1 - p0).cross(p2 - p0).norm()

    dist = np.ones_like(points[:, 0]) * np.nan

    p0d = p0.getArray() - points
    p1d = p1.getArray() - points
    p2d = p2.getArray() - points
    p3d = p3.getArray() - points

    # Create 4 planes with normals pointing outside rectangle
    n0 = (p1 - p0).cross(normalVector).getArray()
    n1 = (p2 - p1).cross(normalVector).getArray()
    n2 = (p3 - p2).cross(normalVector).getArray()
    n3 = (p0 - p3).cross(normalVector).getArray()

    sgn0 = np.signbit(np.sum(n0 * p0d, axis=1))
    sgn1 = np.signbit(np.sum(n1 * p1d, axis=1))
    sgn2 = np.signbit(np.sum(n2 * p2d, axis=1))
    sgn3 = np.signbit(np.sum(n3 * p3d, axis=1))

    inside_idx = (sgn0 == sgn1) & (sgn1 == sgn2) & (sgn2 == sgn3)
    dist[inside_idx] = np.power(
        np.abs(np.sum(p0d[inside_idx, :] * normalVector.getArray(), axis=1)),
        2)

    outside_idx = np.logical_not(inside_idx)
    s0 = _distance_sq_to_segment(p0d, p1d)
    s1 = _distance_sq_to_segment(p1d, p2d)
    s2 = _distance_sq_to_segment(p2d, p3d)
    s3 = _distance_sq_to_segment(p3d, p0d)

    smin = np.minimum(np.minimum(s0, s1), np.minimum(s2, s3))
    dist[outside_idx] = smin[outside_idx]
    dist = np.sqrt(dist) / 1000.0
    shp = dist.shape
    if len(shp) == 1:
        dist.shape = (shp[0], 1)
    if np.any(np.isnan(dist)):
        raise ShakeMapException("Could not calculate some distances!")
    dist = np.fliplr(dist)
    return dist
Example #29
0
    def setMechanism(self, mech, rake=None, dip=None):
        """
        Set the earthquake mechanism manually (overriding any values read 
        in from event.xml or source.txt. If rake and dip are not specified, 
        they will be assigned by mechanism as follows:

        +-------+--------+-----+
        | Mech  |  Rake  | Dip |
        +=======+========+=====+
        | RS    |    90  |  40 |
        +-------+--------+-----+
        | NM    |   -90  |  50 |
        +-------+--------+-----+
        | SS    |     0  |  90 |
        +-------+--------+-----+
        | ALL   |    45  |  90 |
        +-------+--------+-----+

        :param mech:
            String - one of 'RS' (reverse), 'NM' (normal), 'SS' (strike slip), 
            or 'ALL' (unknown).
        :param rake:
            Value between -360 and 360 degrees. If set, will override default
            value for mechanism (see table above).
        :param dip:
            Value betweeen 0 and 90 degrees. If set, will override default value
            for mechanism (see table above). Value will be converted to range 
            between -180 and 180 degrees.
        """
        mechs = {
            'RS': {
                'rake': 90.0,
                'dip': 40.0
            },
            'NM': {
                'rake': -90.0,
                'dip': 50.0
            },
            'SS': {
                'rake': 0.0,
                'dip': 90.0
            },
            'ALL': {
                'rake': 45.0,
                'dip': 90.0
            }
        }
        if mech not in list(mechs.keys()):
            raise ShakeMapException('Mechanism must be one of: %s' %
                                    str(list(mechs.keys())))
        if dip is not None:
            if dip < 0 or dip > 90:
                raise ShakeMapException('Dip must be in range 0-90 degrees.')
            if dip < 0 or dip > 90:
                raise ShakeMapException('Dip must be in range 0-90 degrees.')
        else:
            dip = mechs[mech]['dip']

        if rake is not None:
            if rake < -180:
                rake += 360
            if rake > 180:
                rake -= 360
            if rake < -180 or rake > 180:
                raise ShakeMapException(
                    'Rake must be transformable to be in range -180 to 180 degrees.'
                )
        else:
            rake = mechs[mech]['rake']

        self.setEventParam('dip', dip)
        self.setEventParam('rake', rake)
        self.setEventParam('mech', mech)