poets.io.source_base — poets 0.3.2 documentation

# Source code for poets.io.source_base

    # Copyright (c) 2014, Vienna University of Technology (TU Wien), Department
    # of Geodesy and Geoinformation (GEO).
    # All rights reserved.

    # Redistribution and use in source and binary forms, with or without
    # modification, are permitted provided that the following conditions are met:
    #
    # * Redistributions of source code must retain the above copyright notice, this
    #   list of conditions and the following disclaimer.
    #
    # * Redistributions in binary form must reproduce the above copyright notice,
    #   this list of conditions and the following disclaimer in the documentation
    #   and/or other materials provided with the distribution.
    #
    # * Neither the name of the Vienna University of Technology - Department of
    #   Geodesy and Geoinformation nor the names of its contributors may be used to
    #   endorse or promote products derived from this software without specific
    #   prior written permission.
    #
    # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    # ARE DISCLAIMED. IN NO EVENT SHALL VIENNA UNIVERSITY OF TECHNOLOGY,
    # DEPARTMENT OF GEODESY AND GEOINFORMATION BE LIABLE FOR ANY DIRECT, INDIRECT,
    # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
    # OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
    # EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

    # Author: Thomas Mistelbauer Thomas.Mistelbauer@geo.tuwien.ac.at
    # Creation date: 2014-06-30

    import os
    import shutil
    import pandas as pd
    import numpy as np
    import poets.image.netcdf as nc
    import poets.timedate.dateindex as dt
    import poets.grid.grids as gr
    from netCDF4 import Dataset, num2date, date2num
    from datetime import datetime, timedelta
    from poets.grid.grids import ShapeGrid, RegularGrid
    from poets.image.resampling import resample_to_shape, average_layers
    from poets.io.download import download_http, download_ftp, download_sftp, \
        get_file_date, download_local
    from poets.io.fileformats import select_file
    from poets.io.unpack import unpack, check_compressed

    class BasicSource(object):
        """Base Class for data sources.

        Parameters
        ----------
        name : str
            Name of the data source.
        filename : str
            Structure/convention of the file name.
        filedate : dict
            Position of date fields in filename, given as tuple.
        temp_res : str
            Temporal resolution of the source.
        rootpath : str
            Root path where all data will be stored.
        host : str
            Link to data host.
        protocol : str
            Protocol for data transfer.
        username : str, optional
            Username for data access.
        password : str, optional
            Password for data access.
        port : int, optional
            Port to data host, defaults to 22.
        directory : str, optional
            Path to data on host.
        dirstruct : list of strings, optional
            Structure of source directory, each list item represents a
            subdirectory.
        begin_date : datetime, optional
            Date from which on data is available.
        variables : string or list of strings, optional
            Variables used from data source, defaults to ['dataset'].
        nan_value : int, float, optional
            Nan value of the original data as given by the data provider.
        valid_range : tuple of int of float, optional
            Valid range of data, given as (minimum, maximum).
        data_range : tuple of int of float, optional
            Range of the values as data given in rawdata (minimum, maximum).
            Will be scaled to valid_range.
        ffilter : str, optional
            Pattern that apperas in filename. Can be used to select out not
            needed files if multiple files per date are provided.
        colorbar : str, optional
            Colorbar to use, use one from
            http://matplotlib.org/examples/color/colormaps_reference.html,
            defaults to jet.
        unit : str, optional
            Unit of dataset for displaying in legend. Does not have to be set
            if unit is specified in input file metadata. Defaults to None.
        dest_nan_value : int, float, optional
            NaN value in the final NetCDF file.
        dest_regions : list of str, optional
            Regions of interest where data should be resampled to.
        dest_sp_res : int, float, optional
            Spatial resolution of the destination NetCDF file, defaults to 0.25
            degree.
        dest_temp_res : string, optional
            Temporal resolution of the destination NetCDF file, possible values:
            ('day', 'week', 'dekad', 'month'), defaults to dekad.
        dest_start_date : datetime, optional
            Start date of the destination NetCDF file, defaults to 2000-01-01.

        Attributes
        ----------
        name : str
            Name of the data source.
        filename : str
            Structure/convention of the file name.
        filedate : dict
            Position of date fields in filename, given as tuple.
        temp_res : str
            Temporal resolution of the source.
        host : str
            Link to data host.
        protocol : str
            Protocol for data transfer.
        username : str
            Username for data access.
        password : str
            Password for data access.
        port : int
            Port to data host.
        directory : str
            Path to data on host.
        dirstruct : list of strings
            Structure of source directory, each list item represents a
            subdirectory.
        begin_date : datetime
            Date from which on data is available.
        ffilter : str
            Pattern that apperas in filename.
        colorbar : str, optional
            Colorbar to used.
        unit : str
            Unit of dataset for displaying in legend.
        variables : list of strings
            Variables used from data source.
        nan_value : int, float
            Not a number value of the original data as given by the data provider.
        valid_range : tuple of int of float
            Valid range of data, given as (minimum, maximum).
        data_range : tuple of int of float
            Range of the values as data given in rawdata (minimum, maximum).
        dest_nan_value : int, float, optional
            NaN value in the final NetCDF file.
        tmp_path : str
            Path where temporary files are stored.
        rawdata_path : str
            Path where original files are stored.
        data_path : str
            Path where resampled NetCDF file is stored.
        dest_regions : list of str
            Regions of interest where data is resampled to.
        dest_sp_res : int, float
            Spatial resolution of the destination NetCDF file.
        dest_temp_res : string
            Temporal resolution of the destination NetCDF file.
        dest_start_date : datetime.datetime
            First date of the dataset in the destination NetCDF file.
        """

        def __init__(self, name, filename, filedate, temp_res, rootpath,
                     host, protocol, username=None, password=None, port=22,
                     directory=None, dirstruct=None,
                     begin_date=None, ffilter=None, colorbar='jet',
                     variables=None, nan_value=None, valid_range=None, unit=None,
                     dest_nan_value=-99, dest_regions=None, dest_sp_res=0.25,
                     dest_temp_res='dekad', dest_start_date=datetime(2000, 1, 1),
                     data_range=None):

            self.name = name
            self.filename = filename
            self.filedate = filedate
            self.temp_res = temp_res
            self.host = host
            self.protocol = protocol
            self.username = username
            self.password = password
            self.port = port
            self.directory = directory
            self.dirstruct = dirstruct
            if begin_date is None:
                self.begin_date = dest_start_date
            else:
                self.begin_date = begin_date
            if type(variables) == str:
                self.variables = [variables]
            else:
                self.variables = variables
            self.ffilter = ffilter
            self.unit = unit
            self.nan_value = nan_value
            self.valid_range = valid_range
            self.data_range = data_range
            self.colorbar = colorbar
            self.dest_nan_value = dest_nan_value
            self.dest_regions = dest_regions
            self.dest_sp_res = dest_sp_res
            self.dest_temp_res = dest_temp_res
            self.dest_start_date = dest_start_date
            self.rawdata_path = os.path.join(rootpath, 'RAWDATA', name)
            self.tmp_path = os.path.join(rootpath, 'TMP')
            if not os.path.exists(self.tmp_path):
                os.mkdir(self.tmp_path)
            self.data_path = os.path.join(rootpath, 'DATA')
            if not os.path.exists(self.data_path):
                os.mkdir(self.data_path)

            if self.host[-1] != '/':
                self.host += '/'

            if self.directory is not None and self.directory[-1] != '/':
                self.directory += '/'

        def _check_current_date(self, begin=True, end=True):
            """Helper method that checks the current date of individual variables
            in the netCDF data file.

            Parameters
            ----------
            begin : bool, optional
                If set True, begin will be returned as None.
            end : bool, optional
                If set True, end will be returned as None.
            Returns
            -------
            dates : dict of dicts
                Dictionary with dates of each parameter. None if no date available.
            """

            dates = {}

            for region in self.dest_regions:
                nc_name = os.path.join(self.data_path, region + '_'
                                       + str(self.dest_sp_res) + '_'
                                       + str(self.dest_temp_res) + '.nc')
                if os.path.exists(nc_name):
                    dates[region] = {}

                    variables = self.get_variables()

                    with Dataset(nc_name, 'r', format='NETCDF4') as nc:
                        for var in variables:
                            dates[region][var] = []
                            if begin:
                                for i in range(0, nc.variables['time'].size - 1):
                                    if nc.variables[var][i].mask.min():
                                        continue
                                    else:
                                        times = nc.variables['time']
                                        dat = num2date(nc.variables['time'][i],
                                                       units=times.units,
                                                       calendar=times.calendar)
                                        dates[region][var].append(dat)
                                        break
                            else:
                                dates[region][var].append(None)

                            if end:
                                for i in range(nc.variables['time'].size - 1,
                                               - 1, -1):
                                    if nc.variables[var][i].mask.min():
                                        continue
                                    else:
                                        times = nc.variables['time']
                                        dat = num2date(nc.variables['time'][i],
                                                       units=times.units,
                                                       calendar=times.calendar)
                                        dates[region][var].append(dat)
                                        break
                            else:
                                dates[region][var].append(None)

                            if dates[region][var] in [[None], []]:
                                dates[region][var] = [None, None]
                else:
                    dates = None
                    break

            return dates

        def _get_download_date(self):
            """Gets the date from which to start the data download.

            Returns
            -------
            begin : datetime
                date from which to start the data download.
            """

            dates = self._check_current_date(begin=False)

            if dates is not None:
                begin = datetime.now()
                for region in self.dest_regions:
                    variables = self.get_variables()
                    if variables == []:
                        begin = self.dest_start_date
                    else:
                        for var in variables:
                            if dates[region][var][1] is not None:
                                if dates[region][var][1] < begin:
                                    begin = dates[region][var][1]
                                    begin += timedelta(days=1)
                            else:
                                if self.dest_start_date < self.begin_date:
                                    begin = self.begin_date
                                else:
                                    begin = self.dest_start_date
            else:
                begin = self.begin_date

            return begin

        def _get_tmp_filepath(self, prefix, region):
            """Creates path to a temporary directory.

            Returns
            -------
            str
                Path to the temporary direcotry
            """
            filename = ('_' + prefix + '_' + region + '_' + str(self.dest_sp_res)
                        + '_' + str(self.dest_temp_res) + '.nc')
            return os.path.join(self.tmp_path, filename)

        def _resample_spatial(self, region, begin, end, delete_rawdata,
                              shapefile=None):
            """Helper method that calls spatial resampling routines.

            Parameters:
            region : str
                FIPS country code (https://en.wikipedia.org/wiki/FIPS_country_code)
            begin : datetime
                Start date of resampling
            end : datetime
                End date of resampling
            delete_rawdata : bool
                True if original downloaded files should be deleted after
                resampling
            """

            dest_file = self._get_tmp_filepath('spatial', region)

            dirList = os.listdir(self.rawdata_path)
            dirList.sort()

            if region == 'global':
                grid = gr.RegularGrid(sp_res=self.dest_sp_res)
            else:
                grid = gr.ShapeGrid(region, self.dest_sp_res, shapefile)

            for item in dirList:

                src_file = os.path.join(self.rawdata_path, item)

                fdate = get_file_date(item, self.filedate)

                if begin is not None:
                    if fdate < begin:
                        continue

                if end is not None:
                    if fdate > end:
                        continue

                if check_compressed(src_file):
                    dirname = os.path.splitext(item)[0]
                    dirpath = os.path.join(self.rawdata_path, dirname)
                    unpack(src_file)
                    src_file = select_file(os.listdir(dirpath))
                    src_file = os.path.join(dirpath, src_file)

                if begin is not None:
                    if fdate < begin:
                        if check_compressed(item):
                            shutil.rmtree(os.path.join(self.rawdata_path,
                                                       os.path.splitext(item)[0]))
                        continue
                if end is not None:
                    if fdate > end:
                        if check_compressed(item):
                            shutil.rmtree(os.path.join(self.rawdata_path,
                                                       os.path.splitext(item)[0]))
                        continue

                print '.',

                image, _, _, _, timestamp, metadata = \
                    resample_to_shape(src_file, region, self.dest_sp_res, grid,
                                      self.name, self.nan_value,
                                      self.dest_nan_value, self.variables,
                                      shapefile)

                if timestamp is None:
                    timestamp = get_file_date(item, self.filedate)

                if self.temp_res == self.dest_temp_res:
                    filename = (region + '_' + str(self.dest_sp_res) + '_'
                                + str(self.dest_temp_res) + '.nc')
                    dfile = os.path.join(self.data_path, filename)
                    nc.save_image(image, timestamp, region, metadata, dfile,
                                  self.dest_start_date, self.dest_sp_res,
                                  self.dest_nan_value, shapefile,
                                  self.dest_temp_res)
                else:
                    nc.write_tmp_file(image, timestamp, region, metadata,
                                      dest_file, self.dest_start_date,
                                      self.dest_sp_res, self.dest_nan_value,
                                      shapefile)

                # deletes unpacked files if existing
                if check_compressed(item):
                    shutil.rmtree(os.path.join(self.rawdata_path,
                                               os.path.splitext(item)[0]))

            print ''

        def _resample_temporal(self, region, shapefile=None):
            """Helper method that calls temporal resampling routines.

            Parameters:
            region : str
                Identifier of the region in the shapefile. If the default shapefile
                is used, this would be the FIPS country code.

            shapefile : str, optional
                Path to shape file, uses "world country admin boundary shapefile"
                by default.
            """

            src_file = self._get_tmp_filepath('spatial', region)

            if not os.path.exists(src_file):
                print '[Info] No data available for this period'
                return False

            data = {}
            variables, _, period = nc.get_properties(src_file)

            dtindex = dt.get_dtindex(self.dest_temp_res, period[0], period[1])

            for date in dtindex:
                if date > period[1]:
                    continue
                print date
                if self.dest_temp_res == 'dekad':
                    if date.day < 21:
                        begin = datetime(date.year, date.month, date.day - 10 + 1)
                    else:
                        begin = datetime(date.year, date.month, 21)
                    end = date
                else:
                    begin = period[0]
                    end = date

                data = {}
                metadata = {}

                for var in variables:
                    img, _, _, meta = \
                        nc.read_variable(src_file, var, begin, end)

                    metadata[var] = meta
                    data[var] = average_layers(img, self.dest_nan_value)

                filename = (region + '_' + str(self.dest_sp_res) + '_'
                            + str(self.dest_temp_res) + '.nc')
                dest_file = os.path.join(self.data_path, filename)

                nc.save_image(data, date, region, metadata, dest_file,
                              self.dest_start_date, self.dest_sp_res,
                              self.dest_nan_value, shapefile, self.dest_temp_res)

            # delete intermediate netCDF file
            print ''
            os.unlink(src_file)

        def _scale_values(self, data):

            if self.valid_range is not None:
                if self.data_range is not None:
                    data = ((data - self.data_range[0]) /
                            (self.data_range[1] - self.data_range[0]) *
                            (self.valid_range[1] - self.valid_range[0]) +
                            self.valid_range[0])

            return data

        def download(self, download_path=None, begin=None, end=None):
            """"Download data

            Parameters
            ----------
            begin : datetime, optional
                start date of download, default to None
            end : datetime, optional
                start date of download, default to None
            """

            if begin is None:
                if self.dest_start_date < self.begin_date:
                    begin = self.begin_date
                else:
                    begin = self.dest_start_date

            if self.protocol in ['HTTP', 'http']:
                check = download_http(self.rawdata_path, self.host,
                                      self.directory, self.filename, self.filedate,
                                      self.dirstruct, begin=begin, end=end,
                                      ffilter=self.ffilter)
            elif self.protocol in ['FTP', 'ftp']:
                check = download_ftp(self.rawdata_path, self.host, self.directory,
                                     self.filedate, self.port, self.username,
                                     self.password, self.dirstruct, begin=begin,
                                     end=end, ffilter=self.ffilter)
            elif self.protocol in ['SFTP', 'sftp']:
                check = download_sftp(self.rawdata_path, self.host,
                                      self.directory, self.port, self.username,
                                      self.password, self.filedate, self.dirstruct,
                                      begin=begin, end=end, ffilter=self.ffilter)
            elif self.protocol in ['local', 'LOCAL']:
                check = download_local(self.rawdata_path, directory=self.host,
                                       filedate=self.filedate,
                                       dirstruct=self.dirstruct, begin=begin,
                                       end=end, ffilter=self.ffilter)

            return check

        def resample(self, begin=None, end=None, delete_rawdata=False,
                     shapefile=None, stepwise=True):
            """Resamples source data to given spatial and temporal resolution.

            Writes resampled images into a netCDF data file. Deletes original
            files if flag delete_rawdata is set True.

            Parameters
            ----------
            begin : datetime
                Start date of resampling.
            end : datetime
                End date of resampling.
            delete_rawdata : bool
                Original files will be deleted from rawdata_path if set 'True'.
            shapefile : str, optional
                Path to shape file, uses "world country admin boundary shapefile"
                by default.
            """

            if len(os.listdir(self.tmp_path)) != 0:
                for fname in os.listdir(self.tmp_path):
                    if '.nc' in fname:
                        os.remove(os.path.join(self.tmp_path, fname))

            if begin is None:
                if self.dest_start_date < self.begin_date:
                    begin = self.begin_date
                else:
                    begin = self.dest_start_date

                if begin < self._get_download_date():
                    begin = self._get_download_date()

            if end is None:
                end = datetime.now()

            if begin > end:
                print '[INFO] everything up to date'
                return '[INFO] everything up to date'

            if stepwise:

                drange = dt.get_dtindex(self.dest_temp_res, begin, end)

                for i, date in enumerate(drange):
                    if date > end:
                        continue
                    if i == 0:
                        start = begin
                    else:
                        if self.dest_temp_res in ['dekadal', 'weekly']:
                            start = drange[i - 1] + timedelta(days=1)
                        else:
                            start = date

                    stop = date

                    print '[INFO] ' + str(start) + '-' + str(stop)

                    for region in self.dest_regions:

                        print '[INFO] resampling to region ' + region
                        print '[INFO] performing spatial resampling ',

                        self._resample_spatial(region, start, stop, delete_rawdata,
                                               shapefile)

                        if self.temp_res == self.dest_temp_res:
                            print '[INFO] skipping temporal resampling'
                        else:
                            print '[INFO] performing temporal resampling ',
                            self._resample_temporal(region, shapefile)

            else:

                print '[INFO] ' + str(begin) + '-' + str(end)

                for region in self.dest_regions:

                    print '[INFO] resampling to region ' + region
                    print '[INFO] performing spatial resampling ',

                    self._resample_spatial(region, begin, end, delete_rawdata,
                                           shapefile)

                    if self.temp_res == self.dest_temp_res:
                        print '[INFO] skipping temporal resampling'
                    else:
                        print '[INFO] performing temporal resampling ',
                        self._resample_temporal(region, shapefile)

            if delete_rawdata:
                print '[INFO] Cleaning up rawdata'
                dirList = os.listdir(self.rawdata_path)
                dirList.sort()

                for item in dirList:
                    src_file = os.path.join(self.rawdata_path, item)
                    os.unlink(src_file)

        def download_and_resample(self, download_path=None, begin=None, end=None,
                                  delete_rawdata=False, shapefile=None):
            """Downloads and resamples data.

            Parameters
            ----------
            download_path : str
                Path where to save the downloaded files.
            begin : datetime.date, optional
                set either to first date of remote repository or date of
                last file in local repository
            end : datetime.date, optional
                set to today if none given
            delete_rawdata : bool, optional
                Original files will be deleted from rawdata_path if set True
            shapefile : str, optional
                Path to shape file, uses "world country admin boundary shapefile"
                by default.
            """

            if begin is None:
                if self.dest_start_date < self.begin_date:
                    begin = self.begin_date
                else:
                    begin = self.dest_start_date

                if begin < self._get_download_date():
                    begin = self._get_download_date()

            if end is None:
                end = datetime.now()

            if begin > end:
                print '[INFO] everything up to date'
                return '[INFO] everything up to date'

            drange = dt.get_dtindex(self.dest_temp_res, begin, end)

            intervals = []

            for i, date in enumerate(drange):
                if date > end:
                    continue
                if i == 0:
                    start = begin
                else:
                    if self.dest_temp_res in ['dekad', 'dekadal', 'week',
                                              'weekly']:
                        start = drange[i - 1] + timedelta(days=1)
                    else:
                        start = date

                stop = date

                filecheck = self.download(download_path, start, stop)
                if filecheck is True:
                    self.resample(start, stop, delete_rawdata, shapefile, False)
                else:
                    print '[WARNING] no data available for this date'

        def read_ts(self, location, region=None, variable=None, shapefile=None,
                    scaled=True):
            """Gets timeseries from netCDF file for a gridpoint.

            Parameters
            ----------
            location : int or tuple of floats
                Either Grid point index as integer value or Longitude/Latitude
                given as tuple.
            region : str, optional
                Region of interest, set to first defined region if not set.
            variable : str, optional
                Variable to display, selects all available variables if None.
            shapefile : str, optional
                Path to custom shapefile.
            scaled : bool, optional
                If true, data will be scaled to a predefined range; if false, data
                will be shown as given in rawdata file; defaults to True

            Returns
            -------
            df : pd.DataFrame
                Timeseries for selected variables.
            """

            if region is None:
                region = self.dest_regions[0]

            if type(location) is tuple:
                if region == 'global':
                    grid = RegularGrid(self.dest_sp_res)
                else:
                    grid = ShapeGrid(region, self.dest_sp_res, shapefile)

                gp, _ = grid.find_nearest_gpi(location[0], location[1])
            else:
                gp = location

            if variable is None:
                if self.variables is None:
                    variable = self.get_variables()
                else:
                    variable = self.variables
            else:
                variable = [variable]

            source_file = os.path.join(self.data_path,
                                       region + '_' + str(self.dest_sp_res) + '_'
                                       + str(self.dest_temp_res) + '.nc')

            var_dates = self._check_current_date()

            with Dataset(source_file, 'r', format='NETCDF4') as nc:

                time = nc.variables['time']
                dates = num2date(time[:], units=time.units, calendar=time.calendar)
                position = np.where(nc.variables['gpi'][:] == gp)
                lat_pos = position[0][0]
                lon_pos = position[1][0]
                df = pd.DataFrame(index=pd.DatetimeIndex(dates))

                for var in variable:
                    if self.name not in var:
                        ncvar = self.name + '_' + var
                    else:
                        ncvar = var
                    begin = np.where(dates == var_dates[region][ncvar][0])[0][0]
                    end = np.where(dates == var_dates[region][ncvar][1])[0][0]
                    df[ncvar] = np.NAN
                    for i in range(begin, end + 1):
                        df[ncvar][i] = nc.variables[ncvar][i, lat_pos, lon_pos]

                    if 'scaling_factor' in nc.variables[ncvar].ncattrs():
                        vvar = nc.variables[ncvar]
                        if vvar.getncattr('scaling_factor') < 0:
                            df[ncvar] = (df[ncvar] *
                                         float(vvar.getncattr('scaling_factor')))
                        else:
                            df[ncvar] = (df[ncvar] /
                                         float(vvar.getncattr('scaling_factor')))
                    if scaled:
                        if self.valid_range is not None:
                            if self.data_range is not None:
                                df[ncvar] = self._scale_values(df[ncvar])

            return df

        def read_img(self, date, region=None, variable=None, scaled=True):
            """Gets images from netCDF file for certain date

            Parameters
            ----------
            date : datetime
                Date of the image.
            region : str, optional
                Region of interest, set to first defined region if not set.
            variable : str, optional
                Variable to display, selects first available variables if None.
            scaled : bool, optional
                If true, data will be scaled to a predefined range; if false, data
                will be shown as given in rawdata file; defaults to True.

            Returns
            -------
            img : numpy.ndarray
                Image of selected date.
            lon : numpy.array
                Array with longitudes.
            lat : numpy.array
                Array with latitudes.
            metadata : dict
                Dictionary containing metadata of the variable.
            """

            if region is None:
                region = self.dest_regions[0]

            if variable is None:
                if self.variables is None:
                    variable = self.get_variables()[0]
                else:
                    variable = self.name + '_' + self.variables[0]
            else:
                # Renames variable name to SOURCE_variable
                if self.name not in variable:
                    variable = self.name + '_' + variable

            source_file = os.path.join(self.data_path,
                                       region + '_' + str(self.dest_sp_res)
                                       + '_' + str(self.dest_temp_res) + '.nc')

            # get dekad of date:
            date = dt.check_period(self.dest_temp_res, date)

            with Dataset(source_file, 'r', format='NETCDF4') as nc:
                time = nc.variables['time']

                datenum = date2num(date, units=time.units, calendar=time.calendar)

                position = np.where(time[:] == datenum)[0][0]

                var = nc.variables[variable]
                img = var[position]
                lon = nc.variables['lon'][:]
                lat = nc.variables['lat'][:]

                metadata = {}

                for attr in var.ncattrs():
                    if attr[0] != '_' and attr != 'scale_factor':
                        metadata[attr] = var.getncattr(attr)

                if not metadata:
                    metadata = None

                if 'scaling_factor' in var.ncattrs():
                    if metadata['scaling_factor'] < 0:
                        img = img * float(metadata['scaling_factor'])
                    else:
                        img = img / float(metadata['scaling_factor'])

            if scaled:
                if self.valid_range is not None:
                    if self.data_range is not None:
                        img = self._scale_values(img)

            return img, lon, lat, metadata

        def get_variables(self):
            """
            Gets all variables given in the NetCDF file.

            Returns
            -------
            variables : list of str
                Variables from given in the NetCDF file.
            """

            nc_name = os.path.join(self.data_path, self.dest_regions[0] + '_'
                                   + str(self.dest_sp_res) + '_'
                                   + str(self.dest_temp_res) + '.nc')

            nc_vars, _, _ = nc.get_properties(nc_name)

            variables = []

            for var in nc_vars:
                if self.name in var:
                    variables.append(var)

            return variables