Beispiel #1
0
 def _getFileGeoDict(fname):
     geodict = None
     try:
         geodict = get_file_geodict(fname)
     except Exception as msg1:
         msg = 'File geodict failure with %s - error messages: '\
             '"%s"' % (fname, str(msg1))
         raise ShakeLibException(msg)
     return geodict
Beispiel #2
0
 def _create(cls, geodict, defaultVs30, vs30File, padding, resample):
     if vs30File is not None:
         fgeodict = get_file_geodict(vs30File)
         if not resample:
             if not padding:
                 # we want something that is within and aligned
                 geodict = fgeodict.getBoundsWithin(geodict)
             else:
                 # we want something that is just aligned, since we're
                 # padding edges
                 geodict = fgeodict.getAligned(geodict)
         vs30grid = read(vs30File, samplegeodict=geodict,
                         resample=resample, method='linear',
                         doPadding=padding, padValue=defaultVs30)
     return vs30grid
Beispiel #3
0
def read_user_file_test(fname, xmin, xmax, ymin, ymax):
    gd = get_file_geodict(fname)
    sample = GeoDict.createDictFromBox(xmin, xmax, ymin, ymax, gd.dx, gd.dy)
    t1 = time.time()
    grid = read(fname, samplegeodict=sample)
    t2 = time.time()
    nrows, ncols = grid._data.shape
    npixels = nrows*ncols
    print('%.2f seconds to read %i pixels using h5py' % (t2-t1, npixels))

    west, east, south, north = (-105.00416666665,
                                -102.98750000804999,
                                34.98750000805,
                                37.00416666665)
    src = rasterio.open(fname, 'r')
    window = src.window(west, south, east, north)
    t1 = time.time()
    data = src.read(window=window)
    t2 = time.time()
    print('%.2f seconds to read %i pixels using rasterio' % (t2-t1, npixels))
    ratio = grid._data.sum()/data.sum()
    print('Ratio of h5py data to rasterio data is %.4f' % ratio)
    src.close()
Beispiel #4
0
    def getLosses(self, shakefile):
        """Calculate number of fatalities using semi-empirical approach.

        :param shakefile:
          Path to a ShakeMap grid.xml file.
        :returns:
          Tuple of:
            1) Total number of fatalities
            2) Dictionary of residential fatalities per building type, per country.
            3) Dictionary of non-residential fatalities per building type, per country.
        """
        # get shakemap geodict
        shakedict = ShakeGrid.getFileGeoDict(shakefile, adjust='res')
        # get population geodict
        popdict = get_file_geodict(self._popfile)

        # get country code geodict
        isodict = get_file_geodict(self._isofile)

        # get urban grid geodict
        urbdict = get_file_geodict(self._urbanfile)

        # load all of the grids we need
        if popdict == shakedict == isodict == urbdict:
            # special case, probably for testing...
            shakegrid = ShakeGrid.load(shakefile, adjust='res')
            popgrid = read(self._popfile)
            isogrid = read(self._isofile)
            urbgrid = read(self._urbanfile)
        else:
            sampledict = popdict.getBoundsWithin(shakedict)
            shakegrid = ShakeGrid.load(shakefile,
                                       samplegeodict=sampledict,
                                       resample=True,
                                       method='linear',
                                       adjust='res')
            popgrid = read(self._popfile,
                           samplegeodict=sampledict,
                           resample=False)
            isogrid = read(self._isofile,
                           samplegeodict=sampledict,
                           resample=True,
                           method='nearest',
                           doPadding=True,
                           padValue=0)
            urbgrid = read(self._urbanfile,
                           samplegeodict=sampledict,
                           resample=True,
                           method='nearest',
                           doPadding=True,
                           padValue=RURAL)

        # determine the local apparent time of day (based on longitude)
        edict = shakegrid.getEventDict()
        etime = edict['event_timestamp']
        elon = edict['lon']
        time_of_day, event_year, event_hour = get_time_of_day(etime, elon)

        # round off our MMI data to nearest 0.5 (5.5 should stay 5.5, 5.4
        # should become 5.5, 5.24 should become 5.0, etc.)
        # TODO:  Someday, make this more general to include perhaps grids of all IMT values, or
        # at least the ones we have collapse data for.
        mmidata = np.round(shakegrid.getLayer('mmi').getData() / 0.5) * 0.5

        # get arrays from our other grids
        popdata = popgrid.getData()
        isodata = isogrid.getData()
        urbdata = urbgrid.getData()

        # modify the population values for growth rate by country
        ucodes = np.unique(isodata[~np.isnan(isodata)])
        for ccode in ucodes:
            cidx = (isodata == ccode)
            popdata[cidx] = self._popgrowth.adjustPopulation(
                popdata[cidx], ccode, self._popyear, event_year)

        # create a dictionary containing indoor populations by building type (in cells where MMI >= 6)
        #popbystruct = get_indoor_pop(mmidata,popdata,urbdata,isodata,time_of_day)

        # find all mmi values greater than 9, set them to 9
        mmidata[mmidata > 9.0] = 9.0

        # dictionary containers for sums of fatalities (res/nonres) by building type
        res_fatal_by_ccode = {}
        nonres_fatal_by_ccode = {}

        # fatality sum
        ntotal = 0

        # loop over countries
        ucodes = np.unique(isodata[~np.isnan(isodata)])
        for ucode in ucodes:
            if ucode == 0:
                continue
            res_fatal_by_btype = {}
            nonres_fatal_by_btype = {}

            cdict = self._country.getCountry(int(ucode))
            ccode = cdict['ISO2']
            # get the workforce Series data for the current country
            wforce = self.getWorkforce(ccode)
            if wforce is None:
                logging.info('No workforce data for %s.  Skipping.' %
                             (cdict['Name']))
                continue

            # loop over MMI values 6-9
            for mmi in np.arange(6, 9.5, 0.5):
                c1 = (mmidata == mmi)
                c2 = (isodata == ucode)
                if ucode > 900 and ucode != CALIFORNIA_US_CCODE:
                    ucode = US_CCODE
                for dclass in [URBAN, RURAL]:
                    c3 = (urbdata == dclass)

                    # get the population data in those cells at MMI, in country, and density class
                    # I think I want an AND condition here
                    popcells = popdata[c1 & c2 & c3]

                    # get the population distribution across residential, non-residential, and outdoor.
                    res, nonres, outside = pop_dist(
                        popcells, wforce, time_of_day, dclass)

                    # get the inventory for urban residential
                    resrow, nresrow = self.getInventories(ccode, dclass)

                    # TODO - figure out why this is happening, make the following lines
                    # not necessary
                    if 'Unnamed: 0' in resrow:
                        resrow = resrow.drop('Unnamed: 0')
                    if 'Unnamed: 0' in nresrow:
                        nresrow = nresrow.drop('Unnamed: 0')
                    # now multiply the residential/non-residential population through the inventory data
                    numres = len(resrow)
                    numnonres = len(nresrow)
                    resmat = np.reshape(
                        resrow.values, (numres, 1)).astype(np.float32)
                    nresmat = np.reshape(
                        nresrow.values, (numnonres, 1)).astype(np.float32)
                    popres = np.tile(res, (numres, 1))
                    popnonres = np.tile(nonres, (numnonres, 1))
                    popresbuilding = (popres * resmat)
                    popnonresbuilding = (popnonres * nresmat)

                    # now we have the residential and non-residental population
                    # distributed through the building types for each cell that matches
                    # MMI,country, and density criteria.
                    # popresbuilding rows are building types, columns are population cells

                    # next, we get the collapse rates for these buildings
                    # and multiply them by the population by building.
                    collapse_res = self.getCollapse(ccode, mmi, resrow)
                    collapse_nonres = self.getCollapse(ccode, mmi, nresrow)
                    resrates = np.reshape(
                        collapse_res.values.astype(np.float32), (numres, 1))
                    nonresrates = np.reshape(
                        collapse_nonres.values.astype(np.float32), (numnonres, 1))
                    rescollapse = popresbuilding * resrates
                    nonrescollapse = popnonresbuilding * nonresrates

                    # get the fatality rates given collapse by building type and
                    # multiply through the result of collapse*population per building
                    resfatalcol = self.getFatalityRates(
                        ccode, time_of_day, resrow)
                    nonresfatalcol = self.getFatalityRates(
                        ccode, time_of_day, nresrow)
                    resfatal = np.reshape(
                        resfatalcol.values.astype(np.float32), (numres, 1))
                    nonresfatal = np.reshape(
                        nonresfatalcol.values.astype(np.float32), (numnonres, 1))
                    resfat = rescollapse * resfatal
                    nonresfat = nonrescollapse * nonresfatal

                    # zero out the cells where fatalities are less than 1 or nan
                    try:
                        if len(resfat) and len(resfat[0]):
                            resfat[np.ma.masked_less(resfat, 1).mask] = 0.0
                    except:
                        resfat[np.isnan(resfat)] = 0.0
                    try:
                        if len(nonresfat) and len(nonresfat[0]):
                            nonresfat[np.ma.masked_less(
                                nonresfat, 1).mask] = 0.0
                    except:
                        nonresfat[np.isnan(nonresfat)] = 0.0

                    # sum the fatalities per building through all cells
                    resfatbybuilding = np.nansum(resfat, axis=1)
                    nonresfatbybuilding = np.nansum(nonresfat, axis=1)
                    resfdict = dict(
                        zip(resrow.index, resfatbybuilding.tolist()))
                    nonresfdict = dict(
                        zip(nresrow.index, nonresfatbybuilding.tolist()))
                    res_fatal_by_btype = add_dicts(
                        res_fatal_by_btype, resfdict)
                    nonres_fatal_by_btype = add_dicts(
                        nonres_fatal_by_btype, nonresfdict)

            # add the fatalities by building type to the dictionary containing fatalities by country
            res_fatal_by_ccode[ccode] = res_fatal_by_btype.copy()
            nonres_fatal_by_ccode[ccode] = nonres_fatal_by_btype.copy()

            # increment the total number of fatalities
            ntotal += int(sum(res_fatal_by_btype.values())
                          + sum(nonres_fatal_by_btype.values()))

        return (ntotal, res_fatal_by_ccode, nonres_fatal_by_ccode)
Beispiel #5
0
    def calcExposure(self, shakefile):
        """Calculate population exposure to shaking, per country, plus total exposure across all countries.

        :param shakefile:
          Path to ShakeMap grid.xml file.
        :returns:
          Dictionary containing country code (ISO2) keys, and values of
          10 element arrays representing population exposure to MMI 1-10.
          Dictionary will contain an additional key 'TotalExposure', with value of exposure across all countries.
          Dictionary will also contain a field "maximum_border_mmi" which indicates the maximum MMI value along
          any edge of the ShakeMap.
        """
        # get shakemap geodict
        shakedict = ShakeGrid.getFileGeoDict(shakefile, adjust='res')

        # get population geodict
        popdict = get_file_geodict(self._popfile)

        # get country code geodict
        isodict = get_file_geodict(self._isofile)

        # special case for very high latitude events that may be outside the bounds
        # of our population data...
        if not popdict.intersects(shakedict):
            expdict = {'UK': np.zeros((10,)), 'TotalExposure': np.zeros((10,))}
            return expdict

        if popdict == shakedict == isodict:
            # special case, probably for testing...
            self._shakegrid = ShakeGrid.load(shakefile, adjust='res')
            self._popgrid = read(self._popfile)
            self._isogrid = read(self._isofile)
        else:
            sampledict = popdict.getBoundsWithin(shakedict)
            self._shakegrid = ShakeGrid.load(shakefile, samplegeodict=sampledict, resample=True,
                                             method='linear', adjust='res')
            self._popgrid = read(self._popfile, samplegeodict=sampledict,
                                 resample=False, doPadding=True, padValue=np.nan)
            self._isogrid = read(self._isofile, samplegeodict=sampledict,
                                 resample=True, method='nearest', doPadding=True, padValue=0)

        mmidata = self._shakegrid.getLayer('mmi').getData()
        popdata = self._popgrid.getData()
        isodata = self._isogrid.getData()

        eventyear = self._shakegrid.getEventDict()['event_timestamp'].year

        # in order to avoid crazy far-future scenarios where PAGER models are probably invalid,
        # check to see if the time gap between the date of population data collection and event year
        # reaches either of a couple of different thresholds.
        if eventyear > self._popyear:
            tdiff = (eventyear - self._popyear)
            if tdiff > SCENARIO_WARNING and tdiff < SCENARIO_ERROR:
                msg = '''The input ShakeMap event year is more than %i years from the population date.
                PAGER results for events this far in the future may not be valid.''' % SCENARIO_WARNING
                warnings.warn(msg)
            if tdiff > SCENARIO_ERROR:
                msg = '''The input ShakeMap event year is more than %i years from the population date.
                PAGER results for events this far in the future are not valid. Stopping.''' % SCENARIO_ERROR
                raise PagerException(msg)

        ucodes = np.unique(isodata[~np.isnan(isodata)])
        for ccode in ucodes:
            cidx = (isodata == ccode)
            popdata[cidx] = self._popgrowth.adjustPopulation(
                popdata[cidx], ccode, self._popyear, eventyear)

        exposure_dict = calc_exposure(mmidata, popdata, isodata)
        newdict = {}
        # Get rolled up exposures
        total = np.zeros((10,), dtype=np.uint32)
        for isocode, value in exposure_dict.items():
            cdict = self._country.getCountry(int(isocode))
            if cdict is None:
                ccode = 'UK'
            else:
                ccode = cdict['ISO2']
            newdict[ccode] = value
            total += value

        newdict['TotalExposure'] = total

        # get the maximum MMI value along any of the four map edges
        nrows, ncols = mmidata.shape
        top = mmidata[0, 0:ncols].max()
        bottom = mmidata[nrows - 1, 0:ncols].max()
        left = mmidata[0:nrows, 0].max()
        right = mmidata[0:nrows, ncols - 1].max()
        newdict['maximum_border_mmi'] = np.array(
            [top, bottom, left, right]).max()

        return newdict