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
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
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()
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)
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