示例#1
0
 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)
示例#2
0
 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)
示例#3
0
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
示例#5
0
    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)
示例#6
0
    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)
示例#7
0
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