def calculateMaxWind(df, dtname='ISO_TIME'): """ Calculate a maximum gust wind speed based on the central pressure deficit and the wind-pressure relation defined in Holland (2008). This uses the function defined in the TCRM code base, and simply passes the correct variables from the data frame to the function This returns a `DataFrame` with an additional column (`vmax`), which represents an estimated 0.2 second maximum gust wind speed. """ LOGGER.debug("Calculating maximum wind speed") idx = df.num.values varidx = np.ones(len(idx)) varidx[1:][idx[1:] == idx[:-1]] = 0 dt = (df[dtname] - df[dtname].shift()).fillna( pd.Timedelta(seconds=0)).apply( lambda x: x / np.timedelta64(1, 'h')).astype('int64') % (24 * 60) df['vmax'] = maxWindSpeed(varidx, dt.values, df.lon.values, df.lat.values, df.pmin.values, df.poci.values, gustfactor=1.223) return df
def interpolate(track, delta, interpolation_type=None): """ Interpolate the records in time to have a uniform time difference between records. Each of the input arrays represent the values for a single TC event. :param track: :class:`Track` object containing all data for the track. :param delta: `float` time difference to interpolate the dataset to. Must be positive. :param interpolation_type: Optional ['linear', 'akima'], specify the type of interpolation used for the locations (i.e. longitude and latitude) of the records. # FIXME: Need to address masking values - scipy.interpolate.interp1d handles numpy.ma masked arrays. """ LOG.debug("Performing interpolation of TC track") if not hasattr(track, 'Datetime'): day_ = [ datetime(*x) for x in zip(track.Year, track.Month, track.Day, track.Hour, track.Minute) ] else: day_ = track.Datetime timestep = timedelta(delta / 24.) try: time_ = np.array( [d.toordinal() + (d.hour + d.minute / 60.) / 24.0 for d in day_], dtype=float) except AttributeError: import cftime if isinstance(day_[0], cftime.DatetimeJulian): day__ = [d._to_real_datetime() for d in day_] time_ = np.array([ d.toordinal() + (d.hour + d.minute / 60.) / 24. for d in day__ ], dtype=float) else: raise dt_ = 24.0 * np.diff(time_) dt = np.zeros(len(track.data), dtype=float) dt[1:] = dt_ # Convert all times to a time after initial observation: timestep = 24.0 * (time_ - time_[0]) newtime = np.arange(timestep[0], timestep[-1] + .01, delta) newtime[-1] = timestep[-1] _newtime = (newtime / 24.) + time_[0] newdates = num2date(_newtime) newdates = np.array([n.replace(tzinfo=None) for n in newdates]) if not hasattr(track, 'Speed'): idx = np.zeros(len(track.data)) idx[0] = 1 # TODO: Possibly could change `np.mean(dt)` to `dt`? track.WindSpeed = maxWindSpeed(idx, np.mean(dt), track.Longitude, track.Latitude, track.CentralPressure, track.EnvPressure) # Find the indices of valid pressure observations: validIdx = np.where(track.CentralPressure < sys.maxsize)[0] # FIXME: Need to address the issue when the time between obs is less # than delta (e.g. only two obs 5 hrs apart, but delta = 6 hrs). if len(track.data) <= 3: # Use linear interpolation only (only a start and end point given): nLon = interp1d(timestep, track.Longitude, kind='linear')(newtime) nLat = interp1d(timestep, track.Latitude, kind='linear')(newtime) if len(validIdx) >= 2: npCentre = interp1d(timestep, track.CentralPressure, kind='linear')(newtime) nwSpd = interp1d(timestep, track.WindSpeed, kind='linear')(newtime) elif len(validIdx) == 1: # If one valid observation, assume no change and # apply value to all times npCentre = np.ones(len(newtime)) * track.CentralPressure[validIdx] nwSpd = np.ones(len(newtime)) * track.WindSpeed[validIdx] else: npCentre = np.zeros(len(newtime)) nwSpd = np.zeros(len(newtime)) npEnv = interp1d(timestep, track.EnvPressure, kind='linear')(newtime) nrMax = interp1d(timestep, track.rMax, kind='linear')(newtime) else: if interpolation_type == 'akima': # Use the Akima interpolation method: try: import akima except ImportError: LOG.exception(("Akima interpolation module unavailable " " - default to scipy.interpolate")) nLon = splev(newtime, splrep(timestep, track.Longitude, s=0), der=0) nLat = splev(newtime, splrep(timestep, track.Latitude, s=0), der=0) else: nLon = akima.interpolate(timestep, track.Longitude, newtime) nLat = akima.interpolate(timestep, track.Latitude, newtime) elif interpolation_type == 'linear': nLon = interp1d(timestep, track.Longitude, kind='linear')(newtime) nLat = interp1d(timestep, track.Latitude, kind='linear')(newtime) else: nLon = splev(newtime, splrep(timestep, track.Longitude, s=0), der=0) nLat = splev(newtime, splrep(timestep, track.Latitude, s=0), der=0) if len(validIdx) >= 2: # No valid data at the final new time, # would require extrapolation: firsttime = np.where(newtime >= timestep[validIdx[0]])[0][0] lasttime = np.where(newtime <= timestep[validIdx[-1]])[0][-1] if firsttime == lasttime: # only one valid observation: npCentre = np.zeros(len(newtime)) nwSpd = np.zeros(len(newtime)) npCentre[firsttime] = track.CentralPressure[validIdx[0]] nwSpd[firsttime] = track.WindSpeed[validIdx[0]] else: npCentre = np.zeros(len(newtime)) nwSpd = np.zeros(len(newtime)) _npCentre = interp1d(timestep[validIdx], track.CentralPressure[validIdx], kind='linear')( newtime[firsttime:lasttime]) _nwSpd = interp1d(timestep[validIdx], track.Speed[validIdx], kind='linear')(newtime[firsttime:lasttime]) npCentre[firsttime:lasttime] = _npCentre nwSpd[firsttime:lasttime] = _nwSpd npCentre[lasttime] = _npCentre[-1] nwSpd[lasttime] = _nwSpd[-1] elif len(validIdx) == 1: npCentre = np.ones(len(newtime)) * track.CentralPressure[validIdx] nwSpd = np.ones(len(newtime)) * track.WindSpeed[validIdx] else: npCentre = np.zeros(len(newtime)) nwSpd = np.zeros(len(newtime)) npEnv = interp1d(timestep, track.EnvPressure, kind='linear')(newtime) nrMax = interp1d(timestep, track.rMax, kind='linear')(newtime) if len(nLat) >= 2: bear_, dist_ = latLon2Azi(nLat, nLon, 1, azimuth=0) nthetaFm = np.zeros(newtime.size, dtype=float) nthetaFm[:-1] = bear_ nthetaFm[-1] = bear_[-1] dist = np.zeros(newtime.size, dtype=float) dist[:-1] = dist_ dist[-1] = dist_[-1] nvFm = dist / delta else: nvFm = track.Speed[-1] nthetaFm = track.Bearing[-1] nYear = [date.year for date in newdates] nMonth = [date.month for date in newdates] nDay = [date.day for date in newdates] nHour = [date.hour for date in newdates] nMin = [date.minute for date in newdates] np.putmask(npCentre, npCentre > 10e+6, sys.maxsize) np.putmask(npCentre, npCentre < 700, sys.maxsize) newindex = np.zeros(len(newtime)) newindex[0] = 1 newTCID = np.ones(len(newtime)) * track.trackId[0] newdata = np.empty(len(newtime), dtype={ 'names': TRACKFILE_COLS, 'formats': TRACKFILE_FMTS }) for key, val in zip(TRACKFILE_COLS, [ newindex, newTCID, nYear, nMonth, nDay, nHour, nMin, newtime, newdates, nLon, nLat, nvFm, nthetaFm, npCentre, nwSpd, nrMax, npEnv ]): newdata[key] = val newtrack = Track(newdata) newtrack.trackId = track.trackId newtrack.trackfile = track.trackfile return newtrack