def test_ValueslatLon2Azi(self): """Test latLon2Azi function""" [A, D] = maputils.latLon2Azi(self.lat, self.lon) for aa, azi in zip(A, self.AZI): self.assertAlmostEqual(aa, azi, 2) for dd, dist in zip(D, self.Dist): self.assertAlmostEqual(dd, dist, 2)
def interpolateTrack(configFile, trackFile, source, delta=0.1, interpolation_type=None): """ Interpolate the data in a track file to the time interval delta hours. :param str configFile: Configuration file that contains information on the source format of the track file. :param str trackFile: Path to csv format track file. :param str source: Name of the data source. There must be a corresponding section in the configuration file that contains the description of the data. :param float delta: Time interval in hours to interpolate to. Default is 0.1 hours :param str interpolation_type: Optionally use Akima or linear interpolation for the track positions. Default is linear 1-dimensional spline interpolation. :returns: 10 arrays (id, time, date, lon, lat, bearing, forward speed, central pressure, environmental pressure and radius to maximum wind) that describe the track at ``delta`` hours intervals. """ logger = logging.getLogger() indicator, year, month, day, hour, minute, lon, lat, \ pressure, speed, bearing, windspeed, rmax, penv = \ loadTrackFile(configFile, trackFile, source) # Time between observations: day_ = [ datetime.datetime(year[i], month[i], day[i], hour[i], minute[i]) for i in xrange(year.size) ] time_ = date2num(day_) dt_ = 24.0 * numpy.diff(time_) dt = numpy.empty(hour.size, 'f') dt[1:] = dt_ # At this stage, convert all times to a time after initial observation: timestep = 24.0 * (time_ - time_[0]) newtime = numpy.arange(timestep[0], timestep[-1] + .01, delta) newtime[-1] = timestep[-1] _newtime = (newtime / 24.) + time_[0] newdates = num2date(_newtime) nid = numpy.ones(newtime.size) logger.info("Interpolating data...") if len(indicator) <= 2: # Use linear interpolation only (only a start and end point given): nLon = scint.interp1d(timestep, lon, kind='linear')(newtime) nLat = scint.interp1d(timestep, lat, kind='linear')(newtime) npCentre = scint.interp1d(timestep, pressure, kind='linear')(newtime) npEnv = scint.interp1d(timestep, penv, kind='linear')(newtime) nrMax = scint.interp1d(timestep, rmax, kind='linear')(newtime) else: if interpolation_type == 'akima': # Use the Akima interpolation method: try: import _akima except ImportError: logger.exception(("Akima interpolation module unavailable - " "default to scipy.interpolate")) nLon = scint.splev(newtime, scint.splrep(timestep, lon, s=0), der=0) nLat = scint.splev(newtime, scint.splrep(timestep, lat, s=0), der=0) else: nLon = _akima.interpolate(timestep, lon, newtime) nLat = _akima.interpolate(timestep, lat, newtime) elif interpolation_type == 'linear': nLon = scint.interp1d(timestep, lon, kind='linear')(newtime) nLat = scint.interp1d(timestep, lat, kind='linear')(newtime) else: nLon = scint.splev(newtime, scint.splrep(timestep, lon, s=0), der=0) nLat = scint.splev(newtime, scint.splrep(timestep, lat, s=0), der=0) npCentre = scint.interp1d(timestep, pressure, kind='linear')(newtime) npEnv = scint.interp1d(timestep, penv, kind='linear')(newtime) nrMax = scint.interp1d(timestep, rmax, kind='linear')(newtime) bear_, dist_ = maputils.latLon2Azi(nLat, nLon, 1, azimuth=0) nthetaFm = numpy.zeros(newtime.size, 'f') nthetaFm[:-1] = bear_ nthetaFm[-1] = bear_[-1] dist = numpy.zeros(newtime.size, 'f') dist[:-1] = dist_ dist[-1] = dist_[-1] nvFm = dist / delta return nid, newtime, newdates, nLon, nLat, nthetaFm, nvFm, npCentre, npEnv, nrMax
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. """ day_ = [datetime(*x) for x in zip(track.Year, track.Month, track.Day, track.Hour, track.Minute)] timestep = timedelta(delta/24.) time_ = np.array([d.toordinal() + d.hour/24.0 for d in day_], dtype=float) dt_ = 24.0 * np.diff(time_) dt = np.empty(track.Hour.size, 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]) nid = track.trackId[0] * np.ones(newtime.size) # Find the indices of valid pressure observations: validIdx = np.where(track.CentralPressure < sys.maxint)[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) <= 2: # 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: logger.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.WindSpeed[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.maxint) np.putmask(npCentre, npCentre < 700, sys.maxint) 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
def processData(self, restrictToWindfieldDomain=False): """ Process raw data into ASCII files that can be read by the main components of the system :param bool restrictToWindfieldDomain: if True, only process data within the wind field domain, otherwise, process data from across the track generation domain. """ config = ConfigParser() config.read(self.configFile) self.logger.info("Running {0}".format(flModuleName())) if config.has_option('DataProcess', 'InputFile'): inputFile = config.get('DataProcess', 'InputFile') if config.has_option('DataProcess', 'Source'): source = config.get('DataProcess', 'Source') self.logger.info('Loading %s dataset', source) fn = config.get(source, 'filename') path = config.get(source, 'path') inputFile = pjoin(path, fn) # If input file has no path information, default to tcrm input folder if len(os.path.dirname(inputFile)) == 0: inputFile = pjoin(self.tcrm_input_dir, inputFile) self.logger.info("Processing {0}".format(inputFile)) self.source = config.get('DataProcess', 'Source') inputData = colReadCSV(self.configFile, inputFile, self.source) inputSpeedUnits = config.get(self.source, 'SpeedUnits') inputPressureUnits = config.get(self.source, 'PressureUnits') inputLengthUnits = config.get(self.source, 'LengthUnits') startSeason = config.getint('DataProcess', 'StartSeason') indicator = loadData.getInitialPositions(inputData) lat = np.array(inputData['lat'], 'd') lon = np.mod(np.array(inputData['lon'], 'd'), 360) if restrictToWindfieldDomain: # Filter the input arrays to only retain the tracks that # pass through the windfield domain. CD = CalcTrackDomain(self.configFile) self.domain = CD.calcDomainFromTracks(indicator, lon, lat) domainIndex = self.extractTracks(indicator, lon, lat) inputData = inputData[domainIndex] indicator = indicator[domainIndex] lon = lon[domainIndex] lat = lat[domainIndex] if self.progressbar is not None: self.progressbar.update(0.125) # Sort date/time information try: dt = np.empty(indicator.size, 'f') dt[1:] = np.diff(inputData['age']) except (ValueError, KeyError): try: self.logger.info(("Filtering input data by season:" "season > {0}".format(startSeason))) # Find indicies that satisfy minimum season filter idx = np.where(inputData['season'] >= startSeason)[0] # Filter records: inputData = inputData[idx] indicator = indicator[idx] lon = lon[idx] lat = lat[idx] except (ValueError, KeyError): pass year, month, day, hour, minute, datetimes \ = loadData.parseDates(inputData, indicator) # Time between observations: dt = loadData.getTimeDelta(year, month, day, hour, minute) # Calculate julian days: jdays = loadData.julianDays(year, month, day, hour, minute) delta_lon = np.diff(lon) delta_lat = np.diff(lat) # Split into separate tracks if large jump occurs (delta_lon > # 15 degrees or delta_lat > 5 degrees) This avoids two tracks # being accidentally combined when seasons and track numbers # match but basins are different as occurs in the IBTrACS # dataset. This problem can also be prevented if the # 'tcserialno' column is specified. indicator[np.where(delta_lon > 15)[0] + 1] = 1 indicator[np.where(delta_lat > 5)[0] + 1] = 1 # Save information required for frequency auto-calculation try: origin_seasonOrYear = np.array(inputData['season'], 'i').compress(indicator) header = 'Season' except (ValueError, KeyError): origin_seasonOrYear = year.compress(indicator) header = 'Year' flSaveFile(self.origin_year, np.transpose(origin_seasonOrYear), header, ',', fmt='%d') pressure = np.array(inputData['pressure'], 'd') novalue_index = np.where(pressure == sys.maxint) pressure = metutils.convert(pressure, inputPressureUnits, "hPa") pressure[novalue_index] = sys.maxint # Convert any non-physical central pressure values to maximum integer # This is required because IBTrACS has a mix of missing value codes # (i.e. -999, 0, 9999) in the same global dataset. pressure = np.where((pressure < 600) | (pressure > 1100), sys.maxint, pressure) if self.progressbar is not None: self.progressbar.update(0.25) try: vmax = np.array(inputData['vmax'], 'd') except (ValueError, KeyError): self.logger.warning("No max wind speed data") vmax = np.empty(indicator.size, 'f') else: novalue_index = np.where(vmax == sys.maxint) vmax = metutils.convert(vmax, inputSpeedUnits, "mps") vmax[novalue_index] = sys.maxint assert lat.size == indicator.size assert lon.size == indicator.size assert pressure.size == indicator.size #assert vmax.size == indicator.size try: rmax = np.array(inputData['rmax']) novalue_index = np.where(rmax == sys.maxint) rmax = metutils.convert(rmax, inputLengthUnits, "km") rmax[novalue_index] = sys.maxint self._rmax(rmax, indicator) self._rmaxRate(rmax, dt, indicator) except (ValueError, KeyError): self.logger.warning("No rmax data available") if self.ncflag: self.data['index'] = indicator # ieast : parameter used in latLon2Azi # FIXME: should be a config setting describing the input data. ieast = 1 # Determine the index of initial cyclone observations, excluding # those cyclones that have only one observation. This is used # for calculating initial bearing and speed indicator2 = np.where(indicator > 0, 1, 0) initIndex = np.concatenate( [np.where(np.diff(indicator2) == -1, 1, 0), [0]]) # Calculate the bearing and distance (km) of every two # consecutive records using ll2azi bear_, dist_ = maputils.latLon2Azi(lat, lon, ieast, azimuth=0) assert bear_.size == indicator.size - 1 assert dist_.size == indicator.size - 1 bear = np.empty(indicator.size, 'f') bear[1:] = bear_ dist = np.empty(indicator.size, 'f') dist[1:] = dist_ self._lonLat(lon, lat, indicator, initIndex) self._bearing(bear, indicator, initIndex) self._bearingRate(bear, dt, indicator) if self.progressbar is not None: self.progressbar.update(0.375) self._speed(dist, dt, indicator, initIndex) self._speedRate(dist, dt, indicator) self._pressure(pressure, indicator) self._pressureRate(pressure, dt, indicator) self._windSpeed(vmax) try: self._frequency(year, indicator) self._juliandays(jdays, indicator, year) except (ValueError, KeyError): pass self.logger.info("Completed {0}".format(flModuleName())) if self.progressbar is not None: self.progressbar.update(0.5)
def processData(self, restrictToWindfieldDomain=False): """ Process raw data into ASCII files that can be read by the main components of the system :param bool restrictToWindfieldDomain: if True, only process data within the wind field domain, otherwise, process data from across the track generation domain. """ config = ConfigParser() config.read(self.configFile) self.logger.info("Running %s" % flModuleName()) if config.has_option('DataProcess', 'InputFile'): inputFile = config.get('DataProcess', 'InputFile') if config.has_option('DataProcess', 'Source'): source = config.get('DataProcess', 'Source') self.logger.info('Loading %s dataset', source) fn = config.get(source, 'filename') path = config.get(source, 'path') inputFile = pjoin(path, fn) # If input file has no path information, default to tcrm input folder if len(os.path.dirname(inputFile)) == 0: inputFile = pjoin(self.tcrm_input_dir, inputFile) self.logger.info("Processing %s" % inputFile) self.source = config.get('DataProcess', 'Source') inputData = colReadCSV(self.configFile, inputFile, self.source) inputSpeedUnits = config.get(self.source, 'SpeedUnits') inputPressureUnits = config.get(self.source, 'PressureUnits') inputLengthUnits = config.get(self.source, 'LengthUnits') startSeason = config.getint('DataProcess', 'StartSeason') indicator = loadData.getInitialPositions(inputData) lat = np.array(inputData['lat'], 'd') lon = np.mod(np.array(inputData['lon'], 'd'), 360) if restrictToWindfieldDomain: # Filter the input arrays to only retain the tracks that # pass through the windfield domain. CD = CalcTrackDomain(self.configFile) self.domain = CD.calcDomainFromTracks(indicator, lon, lat) domainIndex = self.extractTracks(indicator, lon, lat) inputData = inputData[domainIndex] indicator = indicator[domainIndex] lon = lon[domainIndex] lat = lat[domainIndex] if self.progressbar is not None: self.progressbar.update(0.125) # Sort date/time information try: dt = np.empty(indicator.size, 'f') dt[1:] = np.diff(inputData['age']) except (ValueError, KeyError): try: self.logger.info("Filtering input data by season: season > %d"%startSeason) # Find indicies that satisfy minimum season filter idx = np.where(inputData['season'] >= startSeason)[0] # Filter records: inputData = inputData[idx] indicator = indicator[idx] lon = lon[idx] lat = lat[idx] except (ValueError, KeyError): pass year, month, day, hour, minute, datetimes \ = loadData.parseDates(inputData, indicator) # Time between observations: dt = loadData.getTimeDelta(year, month, day, hour, minute) # Calculate julian days: jdays = loadData.julianDays(year, month, day, hour, minute) delta_lon = np.diff(lon) delta_lat = np.diff(lat) # Split into separate tracks if large jump occurs (delta_lon > # 15 degrees or delta_lat > 5 degrees) This avoids two tracks # being accidentally combined when seasons and track numbers # match but basins are different as occurs in the IBTrACS # dataset. This problem can also be prevented if the # 'tcserialno' column is specified. indicator[np.where(delta_lon > 15)[0] + 1] = 1 indicator[np.where(delta_lat > 5)[0] + 1] = 1 # Save information required for frequency auto-calculation try: origin_seasonOrYear = np.array( inputData['season'], 'i').compress(indicator) header = 'Season' except (ValueError, KeyError): origin_seasonOrYear = year.compress(indicator) header = 'Year' flSaveFile(self.origin_year, np.transpose(origin_seasonOrYear), header, ',', fmt='%d') pressure = np.array(inputData['pressure'], 'd') novalue_index = np.where(pressure == sys.maxint) pressure = metutils.convert(pressure, inputPressureUnits, "hPa") pressure[novalue_index] = sys.maxint # Convert any non-physical central pressure values to maximum integer # This is required because IBTrACS has a mix of missing value codes # (i.e. -999, 0, 9999) in the same global dataset. pressure = np.where((pressure < 600) | (pressure > 1100), sys.maxint, pressure) if self.progressbar is not None: self.progressbar.update(0.25) try: vmax = np.array(inputData['vmax'], 'd') except (ValueError, KeyError): self.logger.warning("No max wind speed data") vmax = np.empty(indicator.size, 'f') else: novalue_index = np.where(vmax == sys.maxint) vmax = metutils.convert(vmax, inputSpeedUnits, "mps") vmax[novalue_index] = sys.maxint assert lat.size == indicator.size assert lon.size == indicator.size assert pressure.size == indicator.size #assert vmax.size == indicator.size try: rmax = np.array(inputData['rmax']) novalue_index = np.where(rmax == sys.maxint) rmax = metutils.convert(rmax, inputLengthUnits, "km") rmax[novalue_index] = sys.maxint self._rmax(rmax, indicator) self._rmaxRate(rmax, dt, indicator) except (ValueError, KeyError): self.logger.warning("No rmax data available") if self.ncflag: self.data['index'] = indicator # ieast : parameter used in latLon2Azi # FIXME: should be a config setting describing the input data. ieast = 1 # Determine the index of initial cyclone observations, excluding # those cyclones that have only one observation. This is used # for calculating initial bearing and speed indicator2 = np.where(indicator > 0, 1, 0) initIndex = np.concatenate([np.where(np.diff(indicator2) == -1, 1, 0), [0]]) # Calculate the bearing and distance (km) of every two # consecutive records using ll2azi bear_, dist_ = maputils.latLon2Azi(lat, lon, ieast, azimuth=0) assert bear_.size == indicator.size - 1 assert dist_.size == indicator.size - 1 bear = np.empty(indicator.size, 'f') bear[1:] = bear_ dist = np.empty(indicator.size, 'f') dist[1:] = dist_ self._lonLat(lon, lat, indicator, initIndex) self._bearing(bear, indicator, initIndex) self._bearingRate(bear, dt, indicator) if self.progressbar is not None: self.progressbar.update(0.375) self._speed(dist, dt, indicator, initIndex) self._speedRate(dist, dt, indicator) self._pressure(pressure, indicator) self._pressureRate(pressure, dt, indicator) self._windSpeed(vmax) try: self._frequency(year, indicator) self._juliandays(jdays, indicator, year) except (ValueError, KeyError): pass self.logger.info("Completed %s" % flModuleName()) if self.progressbar is not None: self.progressbar.update(0.5)
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