Esempio n. 1
0
def mt_metrics(stack, out_prefix, metrics, rescale_to_datatype, to_power,
               outlier_removal, datelist):
    """

    :param stack:
    :param out_prefix:
    :param metrics:
    :param rescale_to_datatype:
    :param to_power:
    :param outlier_removal:
    :param datelist:
    :return:
    """

    logger.info(f'Creating timescan layers ({metrics}) of track/burst '
                f'{out_prefix.parent.parent.name} for {out_prefix.name}')

    warnings.filterwarnings('ignore', r'All-NaN (slice|axis) encountered')
    warnings.filterwarnings('ignore', r'Mean of empty slice')
    warnings.filterwarnings('ignore', r'Degrees of freedom', RuntimeWarning)

    harmonics = False
    if 'harmonics' in metrics:
        logger.info('Calculating harmonics')
        if not datelist:
            raise RuntimeWarning('Harmonics need the datelist. '
                                 'Harmonics will not be calculated')
        else:
            harmonics = True
            metrics.remove('harmonics')
            metrics.extend(
                ['amplitude', 'phase', 'residuals', 'trend', 'model_mean'])

    if 'percentiles' in metrics:
        metrics.remove('percentiles')
        metrics.extend(['p95', 'p5'])

    with rasterio.open(stack) as src:

        # get metadata
        meta = src.profile

        # update driver and reduced band count
        meta.update({'driver': 'GTiff'})
        meta.update({'count': 1})

        # write all different output files into a dictionary
        metric_dict = {}
        for metric in metrics:
            filename = f'{out_prefix}.{metric}.tif'
            metric_dict[metric] = rasterio.open(filename, 'w', **meta)

        # scaling factors in case we have to rescale to integer
        minimums = {
            'avg': int(-30),
            'max': int(-30),
            'min': int(-30),
            'median': -30,
            'p5': -30,
            'p95': -30,
            'std': 0.00001,
            'cov': 0.00001,
            'amplitude': -5,
            'phase': -np.pi,
            'residuals': -10,
            'trend': -5,
            'model_mean': -30
        }

        maximums = {
            'avg': 5,
            'max': 5,
            'min': 5,
            'median': 5,
            'p5': 5,
            'p95': 5,
            'std': 0.2,
            'cov': 1,
            'amplitude': 5,
            'phase': np.pi,
            'residuals': 10,
            'trend': 5,
            'model_mean': 5
        }

        if 'amplitude' in metrics:
            # construct independent variables
            dates, sines, cosines, intercept = [], [], [], []
            two_pi = np.multiply(2, np.pi)

            for date in sorted(datelist):
                delta = difference_in_years(
                    datetime.strptime('700101', "%y%m%d"),
                    datetime.strptime(date, "%y%m%d"))
                dates.append(delta)
                sines.append(np.sin(np.multiply(two_pi, delta)))
                cosines.append(np.cos(np.multiply(two_pi, delta)))
                intercept.append(1)

            x_array = np.array([dates, cosines, sines, intercept])

        # loop through blocks
        for _, window in src.block_windows(1):

            # read array with all bands
            stack = src.read(range(1, src.count + 1), window=window)

            # rescale to float
            if rescale_to_datatype is True and meta['dtype'] != 'float32':
                stack = ras.rescale_to_float(stack, meta['dtype'])

            # transform to power
            if to_power is True:
                stack = np.power(10, np.divide(stack, 10))

            # outlier removal (only applies if there are more than 5 bands)
            if outlier_removal is True and src.count >= 5:
                stack = remove_outliers(stack)

            # get stats
            arr = {
                'p95': (nan_percentile(stack, [95, 5]) if 'p95' in metrics else
                        (False, False))[0],
                'p5': (nan_percentile(stack, [95, 5]) if 'p95' in metrics else
                       (False, False))[1],
                'median': (np.nanmedian(stack, axis=0)
                           if 'median' in metrics else False),
                'avg':
                (np.nanmean(stack, axis=0) if 'avg' in metrics else False),
                'max':
                (np.nanmax(stack, axis=0) if 'max' in metrics else False),
                'min':
                (np.nanmin(stack, axis=0) if 'min' in metrics else False),
                'std':
                (np.nanstd(stack, axis=0) if 'std' in metrics else False),
                #'cov': (stats.variation(stack, axis=0, nan_policy='omit')
                'cov':
                (np.divide(np.nanstd(stack, axis=0), np.nanmean(stack, axis=0))
                 if 'cov' in metrics else False)
            }

            if 'amplitude' in metrics:

                stack_size = (stack.shape[1], stack.shape[2])
                if to_power is True:
                    y = ras.convert_to_db(stack).reshape(stack.shape[0], -1)
                else:
                    y = stack.reshape(stack.shape[0], -1)

                x, residuals, _, _ = np.linalg.lstsq(x_array.T, y, rcond=-1)
                arr['amplitude'] = np.hypot(x[1], x[2]).reshape(stack_size)
                arr['phase'] = np.arctan2(x[2], x[1]).reshape(stack_size)
                arr['trend'] = x[0].reshape(stack_size)
                arr['model_mean'] = x[3].reshape(stack_size)
                arr['residuals'] = np.sqrt(np.divide(
                    residuals, stack.shape[0])).reshape(stack_size)

            # the metrics to be re-turned to dB, in case to_power is True
            metrics_to_convert = ['avg', 'min', 'max', 'p95', 'p5', 'median']

            # do the back conversions and write to disk loop
            for metric in metrics:

                if to_power is True and metric in metrics_to_convert:
                    arr[metric] = ras.convert_to_db(arr[metric])

                if ((rescale_to_datatype is True
                     and meta['dtype'] != 'float32')
                        or (metric in ['cov', 'phase']
                            and meta['dtype'] != 'float32')):
                    arr[metric] = ras.scale_to_int(arr[metric],
                                                   minimums[metric],
                                                   maximums[metric],
                                                   meta['dtype'])

                # write to dest
                metric_dict[metric].write(np.nan_to_num(arr[metric]).astype(
                    meta['dtype']),
                                          window=window,
                                          indexes=1)
                metric_dict[metric].update_tags(
                    1, BAND_NAME=f'{Path(out_prefix).name}_{metric}')
                metric_dict[metric].set_band_description(
                    1, f'{Path(out_prefix).name}_{metric}')

    # close the output files
    for metric in metrics:
        # close rio opening
        metric_dict[metric].close()

        # construct filename
        filename = f'{str(out_prefix)}.{metric}.tif'
        return_code = h.check_out_tiff(filename)

        if return_code != 0:

            for metric_ in metrics:
                # remove all files and return
                filename = f'{str(out_prefix)}.{metric_}.tif'
                Path(filename).unlink()
                if Path(f'{filename}.xml').exists():
                    Path(f'{filename}.xml').unlink()

            return None, None, None, return_code

    # write out that it's been processed
    dirname = out_prefix.parent
    check_file = dirname.joinpath(f'.{out_prefix.name}.processed')
    with open(str(check_file), 'w') as file:
        file.write('passed all tests \n')

    target = out_prefix.parent.parent.name
    return target, out_prefix.name, metrics, None
Esempio n. 2
0
def ard_to_ts(list_of_files, burst, product, pol, config_file):

    # -------------------------------------------
    # 1 unpack list of args
    # convert list of files readable for snap
    list_of_files = f"\'{','.join(str(x) for x in list_of_files)}\'"

    # -------------------------------------------
    # 2 read config file
    with open(config_file, 'r') as file:
        config_dict = json.load(file)
        processing_dir = Path(config_dict['processing_dir'])
        ard = config_dict['processing']['single_ARD']
        ard_mt = config_dict['processing']['time-series_ARD']

    # -------------------------------------------
    # 3 get namespace of directories and check if already processed
    # get the burst directory
    burst_dir = processing_dir.joinpath(burst)

    # get timeseries directory and create if non existent
    out_dir = burst_dir.joinpath('Timeseries')
    Path.mkdir(out_dir, parents=True, exist_ok=True)

    # in case some processing has been done before, check if already processed
    check_file = out_dir.joinpath(f'.{product}.{pol}.processed')
    if Path.exists(check_file):
        logger.info(
            f'Timeseries of {burst} for {product} in {pol} '
            f'polarisation already processed.'
        )

        out_files = 'already_processed'
        out_vrt = 'already_processed'

        return (
            burst, list_of_files, out_files, out_vrt, f'{product}.{pol}', None
        )

    # -------------------------------------------
    # 4 adjust processing parameters according to config
    # get the db scaling right
    to_db = ard['to_db']
    if to_db or product != 'bs':
        to_db = False
        logger.debug(f'Not converting to dB for {product}')
    else:
        to_db = ard_mt['to_db']
        logger.debug(f'Converting to dB for {product}')

    if ard_mt['apply_ls_mask']:
        extent = burst_dir.joinpath(f'{burst}.valid.json')
    else:
        extent = burst_dir.joinpath(f'{burst}.min_bounds.json')

    # -------------------------------------------
    # 5 SNAP processing
    with TemporaryDirectory(prefix=f"{config_dict['temp_dir']}/") as temp:

        # turn to Path object
        temp = Path(temp)

        # create namespaces
        temp_stack = temp.joinpath(f'{burst}_{product}_{pol}')
        out_stack = temp.joinpath(f'{burst}_{product}_{pol}_mt')
        stack_log = out_dir.joinpath(f'{burst}_{product}_{pol}_stack.err_log')

        # run stacking routine
        if pol in ['Alpha', 'Anisotropy', 'Entropy']:
            logger.info(
                f'Creating multi-temporal stack of images of burst/track '
                f'{burst} for the {pol} band of the polarimetric '
                f'H-A-Alpha decomposition.'
            )
            try:
                create_stack(
                    list_of_files, temp_stack, stack_log, config_dict,
                    pattern=pol
                )
            except (GPTRuntimeError, NotValidFileError) as error:
                logger.info(error)
                return None, None, None, None, None, error
        else:
            logger.info(
                f'Creating multi-temporal stack of images of burst/track '
                f'{burst} for {product} product in {pol} polarization.'
            )
            try:
                create_stack(
                    list_of_files, temp_stack, stack_log, config_dict,
                    polarisation=pol
                )
            except (GPTRuntimeError, NotValidFileError) as error:
                logger.info(error)
                return None, None, None, None, None, error

        # run mt speckle filter
        if ard_mt['remove_mt_speckle'] is True:

            speckle_log = out_dir.joinpath(
                f'{burst}_{product}_{pol}_mt_speckle.err_log'
            )

            logger.debug('Applying multi-temporal speckle filter')
            try:
                mt_speckle_filter(
                    temp_stack.with_suffix('.dim'), out_stack, speckle_log,
                    config_dict
                )
            except (GPTRuntimeError, NotValidFileError) as error:
                logger.info(error)
                return None, None, None, None, None, error

            # remove tmp files
            h.delete_dimap(temp_stack)
        else:
            out_stack = temp_stack

        # -----------------------------------------------
        # 6 Conversion to GeoTiff

        # min max dict for stretching in case of 16 or 8 bit datatype
        mm_dict = {'bs': {'min': -30, 'max': 5},
                   'coh': {'min': 0.000001, 'max': 1},
                   'Alpha': {'min': 0.000001, 'max': 90},
                   'Anisotropy': {'min': 0.000001, 'max': 1},
                   'Entropy': {'min': 0.000001, 'max': 1}
                   }
        stretch = pol if pol in ['Alpha', 'Anisotropy', 'Entropy'] else product

        if product == 'coh':

            # get slave and master dates from file names and sort them
            mst_dates = sorted([
                dt.strptime(
                    file.name.split('_')[3].split('.')[0], SNAP_DATEFORMAT
                )
                for file in list(out_stack.with_suffix('.data').glob('*.img'))
            ])

            slv_dates = sorted([
                dt.strptime(
                    file.name.split('_')[4].split('.')[0], SNAP_DATEFORMAT
                )
                for file in list(out_stack.with_suffix('.data').glob('*.img'))
            ])

            # write them back to string for following loop
            mst_dates = [dt.strftime(ts, SNAP_DATEFORMAT) for ts in mst_dates]
            slv_dates = [dt.strftime(ts, SNAP_DATEFORMAT) for ts in slv_dates]

            out_files = []
            for i, (mst, slv) in enumerate(zip(mst_dates, slv_dates)):

                # re-construct namespace for input file
                infile = list(
                    out_stack.with_suffix('.data').glob(
                        f'*{pol}*{mst}_{slv}*img'
                    )
                )[0]

                # rename dates to YYYYMMDD format
                mst = dt.strftime(dt.strptime(mst, SNAP_DATEFORMAT), '%y%m%d')
                slv = dt.strftime(dt.strptime(slv, SNAP_DATEFORMAT), '%y%m%d')

                # create namespace for output file with renamed dates
                outfile = out_dir.joinpath(
                    f'{i+1:02d}.{mst}.{slv}.{product}.{pol}.tif'
                )

                # fill internal values if any
                #with rasterio.open(str(infile), 'r') as src:
                #    meta = src.meta.copy()
                #    filled = ras.fill_internal_nans(src.read())

                #with rasterio.open(str(infile), 'w', **meta) as dest:
                #    dest.write(filled)

                #print('filled')
                # produce final outputfile,
                # including dtype conversion and ls mask
                ras.mask_by_shape(
                    infile, outfile, extent, to_db=to_db,
                    datatype=ard_mt['dtype_output'],
                    min_value=mm_dict[stretch]['min'],
                    max_value=mm_dict[stretch]['max'],
                    ndv=0.0, description=True)

                # add ot a list for subsequent vrt creation
                out_files.append(str(outfile))

        else:
            # get the dates of the files
            dates = sorted([dt.strptime(
                file.name.split('_')[-1][:-4], SNAP_DATEFORMAT)
                for file in list(out_stack.with_suffix('.data').glob('*.img'))
            ])

            # write them back to string for following loop
            dates = [dt.strftime(ts, "%d%b%Y") for ts in dates]

            out_files = []
            for i, date in enumerate(dates):

                # re-construct namespace for input file
                infile = list(
                    out_stack.with_suffix('.data').glob(f'*{pol}*{date}*img')
                )[0]

                # restructure date to YYMMDD
                date = dt.strftime(
                    dt.strptime(date, SNAP_DATEFORMAT), '%y%m%d'
                )

                # create namespace for output file
                outfile = out_dir.joinpath(
                    f'{i+1:02d}.{date}.{product}.{pol}.tif'
                )

                # fill internal nodata
                #if ard['image_type'] == 'SLC':
                    #with rasterio.open(str(infile), 'r') as src:
                    #    meta = src.meta.copy()
                        #filled = ras.fill_internal_nans(src.read())

                    #with rasterio.open(str(infile), 'w', **meta) as dest:
                    #    dest.write(filled)
                    #print('filledbs')
                # run conversion routine
                ras.mask_by_shape(infile, outfile, extent,
                                  to_db=to_db,
                                  datatype=ard_mt['dtype_output'],
                                  min_value=mm_dict[stretch]['min'],
                                  max_value=mm_dict[stretch]['max'],
                                  ndv=0.0)

                # add ot a list for subsequent vrt creation
                out_files.append(str(outfile))

    # -----------------------------------------------
    # 7 Filechecks
    for file in out_files:
        return_code = h.check_out_tiff(file)
        if return_code != 0:

            for file_ in out_files:
                Path(file_).unlink()
                if Path(f'{file}.xml').exists():
                    Path(f'{file}.xml').unlink()

            return (
                burst, list_of_files, None, None,
                f'{product}.{pol}', return_code
            )

    # write file, so we know this ts has been successfully processed
    with open(str(check_file), 'w') as file:
        file.write('passed all tests \n')

    # -----------------------------------------------
    # 8 Create vrts
    vrt_options = gdal.BuildVRTOptions(srcNodata=0, separate=True)
    out_vrt = str(out_dir.joinpath(f'Timeseries.{product}.{pol}.vrt'))
    gdal.BuildVRT(
        out_vrt,
        out_files,
        options=vrt_options
    )

    return burst, list_of_files, out_files, out_vrt, f'{product}.{pol}', None
Esempio n. 3
0
def mosaic(filelist, outfile, temp_dir, cut_to_aoi=False):

    check_file = opj(os.path.dirname(outfile),
                     '.{}.processed'.format(os.path.basename(outfile)[:-4]))

    logfile = opj(os.path.dirname(outfile),
                  '{}.errLog'.format(os.path.basename(outfile)[:-4]))

    with rasterio.open(filelist.split(' ')[0]) as src:
        dtype = src.meta['dtype']
        dtype = 'float' if dtype == 'float32' else dtype

    if cut_to_aoi:
        tempfile = opj(temp_dir, os.path.basename(outfile))
    else:
        tempfile = outfile

    cmd = ('otbcli_Mosaic -ram 4096'
           ' -progress 1'
           ' -comp.feather large'
           ' -harmo.method band'
           ' -harmo.cost rmse'
           ' -temp_dir {}'
           ' -il {}'
           ' -out {} {}'.format(temp_dir, filelist, tempfile, dtype))

    return_code = h.run_command(cmd, logfile)
    if return_code != 0:
        if os.path.isfile(tempfile):
            os.remove(tempfile)

        return

    if cut_to_aoi:

        # get aoi ina way rasterio wants it
        features = vec.gdf_to_json_geometry(vec.wkt_to_gdf(cut_to_aoi))

        # import raster and mask
        with rasterio.open(tempfile) as src:
            out_image, out_transform = rasterio.mask.mask(src,
                                                          features,
                                                          crop=True)
            out_meta = src.meta.copy()
            ndv = src.nodata
            out_image = np.ma.masked_where(out_image == ndv, out_image)

        out_meta.update({
            'driver': 'GTiff',
            'height': out_image.shape[1],
            'width': out_image.shape[2],
            'transform': out_transform,
            'tiled': True,
            'blockxsize': 128,
            'blockysize': 128
        })

        with rasterio.open(outfile, 'w', **out_meta) as dest:
            dest.write(out_image.data)

        # remove intermediate file
        os.remove(tempfile)

    # check
    return_code = h.check_out_tiff(outfile)
    if return_code != 0:
        if os.path.isfile(outfile):
            os.remove(outfile)

    # write file, so we know this ts has been succesfully processed
    if return_code == 0:
        with open(str(check_file), 'w') as file:
            file.write('passed all tests \n')
Esempio n. 4
0
def ard_to_ts(list_of_files,
              processing_dir,
              temp_dir,
              burst,
              proc_file,
              product,
              pol,
              ncores=os.cpu_count()):
    if type(list_of_files) == str:
        list_of_files = list_of_files.replace("'", '').strip('][').split(', ')

    # get the burst directory
    burst_dir = opj(processing_dir, burst)

    # check routine if timeseries has already been processed
    check_file = opj(burst_dir, 'Timeseries',
                     '.{}.{}.processed'.format(product, pol))
    if os.path.isfile(check_file):
        print(' INFO: Timeseries of {} for {} in {} polarisation already'
              ' processed'.format(burst, product, pol))
        return

    # load ard parameters
    with open(proc_file, 'r') as ard_file:
        ard_params = json.load(ard_file)['processing parameters']
        ard = ard_params['single ARD']
        ard_mt = ard_params['time-series ARD']
        if ard_mt['remove mt speckle'] is True:
            ard_mt_speck = ard_params['time-series ARD']['mt speckle filter']
    # get the db scaling right
    to_db = ard['to db']
    if to_db or product != 'bs':
        to_db = False
        print('INFO: Not converting to dB for {}'.format(product))
    else:
        to_db = ard_mt['to db']
        print('INFO: Converting to dB for {}'.format(product))

    if ard['apply ls mask']:
        extent = opj(burst_dir, '{}.extent.masked.shp'.format(burst))
    else:
        extent = opj(burst_dir, '{}.extent.shp'.format(burst))

    # min max dict for stretching in case of 16 or 8 bit datatype
    mm_dict = {
        'bs': {
            'min': -30,
            'max': 5
        },
        'coh': {
            'min': 0.000001,
            'max': 1
        },
        'Alpha': {
            'min': 0.000001,
            'max': 90
        },
        'Anisotropy': {
            'min': 0.000001,
            'max': 1
        },
        'Entropy': {
            'min': 0.000001,
            'max': 1
        }
    }

    stretch = pol if pol in ['Alpha', 'Anisotropy', 'Entropy'] else product

    # define out_dir for stacking routine
    out_dir = opj(processing_dir, '{}'.format(burst), 'Timeseries')
    os.makedirs(out_dir, exist_ok=True)

    # create namespaces
    temp_stack = opj(temp_dir, '{}_{}_{}'.format(burst, product, pol))
    out_stack = opj(temp_dir, '{}_{}_{}_mt'.format(burst, product, pol))
    stack_log = opj(out_dir,
                    '{}_{}_{}_stack.err_log'.format(burst, product, pol))

    # run stacking routines
    # convert list of files readable for snap
    list_of_files = '\'{}\''.format(','.join(list_of_files))

    if pol in ['Alpha', 'Anisotropy', 'Entropy']:
        print(
            ' INFO: Creating multi-temporal stack of images of burst/track {} for'
            ' the {} band of the polarimetric H-A-Alpha'
            ' decomposition.'.format(burst, pol))
        create_stack(list_of_files, temp_stack, stack_log, pattern=pol)
    else:
        print(
            ' INFO: Creating multi-temporal stack of images of burst/track {} for'
            ' {} product in {} polarization.'.format(burst, product, pol))
        create_stack(list_of_files, temp_stack, stack_log, polarisation=pol)

    # run mt speckle filter
    if ard_mt['remove mt speckle'] is True:
        speckle_log = opj(
            out_dir, '{}_{}_{}_mt_speckle.err_log'.format(burst, product, pol))

        print(' INFO: Applying multi-temporal speckle filter')
        mt_speckle_filter('{}.dim'.format(temp_stack),
                          out_stack,
                          speckle_log,
                          speckle_dict=ard_mt_speck,
                          ncores=ncores)
        # remove tmp files
        h.delete_dimap(temp_stack)
    else:
        out_stack = temp_stack

    if product == 'coh':

        # get slave and master Date
        mstDates = [
            datetime.datetime.strptime(
                os.path.basename(x).split('_')[3].split('.')[0], '%d%b%Y')
            for x in glob.glob(opj('{}.data'.format(out_stack), '*img'))
        ]

        slvDates = [
            datetime.datetime.strptime(
                os.path.basename(x).split('_')[4].split('.')[0], '%d%b%Y')
            for x in glob.glob(opj('{}.data'.format(out_stack), '*img'))
        ]
        # sort them
        mstDates.sort()
        slvDates.sort()
        # write them back to string for following loop
        sortedMstDates = [
            datetime.datetime.strftime(ts, "%d%b%Y") for ts in mstDates
        ]
        sortedSlvDates = [
            datetime.datetime.strftime(ts, "%d%b%Y") for ts in slvDates
        ]

        i, outfiles = 1, []
        for mst, slv in zip(sortedMstDates, sortedSlvDates):

            inMst = datetime.datetime.strptime(mst, '%d%b%Y')
            inSlv = datetime.datetime.strptime(slv, '%d%b%Y')

            outMst = datetime.datetime.strftime(inMst, '%y%m%d')
            outSlv = datetime.datetime.strftime(inSlv, '%y%m%d')
            infile = glob.glob(
                opj('{}.data'.format(out_stack),
                    '*{}*{}_{}*img'.format(pol, mst, slv)))[0]

            outfile = opj(
                out_dir,
                '{:02d}.{}.{}.{}.{}.tif'.format(i, outMst, outSlv, product,
                                                pol))

            ras.mask_by_shape(infile,
                              outfile,
                              extent,
                              to_db=to_db,
                              datatype=ard_mt['dtype output'],
                              min_value=mm_dict[stretch]['min'],
                              max_value=mm_dict[stretch]['max'],
                              ndv=0.0,
                              description=True)
            # add ot a list for subsequent vrt creation
            outfiles.append(outfile)
            i += 1

    else:
        # get the dates of the files
        dates = [
            datetime.datetime.strptime(x.split('_')[-1][:-4], '%d%b%Y')
            for x in glob.glob(opj('{}.data'.format(out_stack), '*img'))
        ]
        # sort them
        dates.sort()
        # write them back to string for following loop
        sortedDates = [
            datetime.datetime.strftime(ts, "%d%b%Y") for ts in dates
        ]

        i, outfiles = 1, []
        for date in sortedDates:

            # restructure date to YYMMDD
            inDate = datetime.datetime.strptime(date, '%d%b%Y')
            outDate = datetime.datetime.strftime(inDate, '%y%m%d')

            infile = glob.glob(
                opj('{}.data'.format(out_stack),
                    '*{}*{}*img'.format(pol, date)))[0]

            # create outfile
            outfile = opj(
                out_dir, '{:02d}.{}.{}.{}.tif'.format(i, outDate, product,
                                                      pol))

            ras.mask_by_shape(infile,
                              outfile,
                              extent,
                              to_db=to_db,
                              datatype=ard_mt['dtype output'],
                              min_value=mm_dict[stretch]['min'],
                              max_value=mm_dict[stretch]['max'],
                              ndv=0.0)

            # add ot a list for subsequent vrt creation
            outfiles.append(outfile)
            i += 1

    for file in outfiles:
        return_code = h.check_out_tiff(file)
        if return_code != 0:
            h.remove_folder_content(temp_dir)
            os.remove(file)
            return return_code

    # write file, so we know this ts has been succesfully processed
    if return_code == 0:
        with open(str(check_file), 'w') as file:
            file.write('passed all tests \n')

    # build vrt of timeseries
    vrt_options = gdal.BuildVRTOptions(srcNodata=0, separate=True)
    gdal.BuildVRT(opj(out_dir, 'Timeseries.{}.{}.vrt'.format(product, pol)),
                  outfiles,
                  options=vrt_options)

    # remove tmp files
    h.delete_dimap(out_stack)
Esempio n. 5
0
def mt_metrics(stack, out_prefix, metrics, rescale_to_datatype=False,
               to_power=False, outlier_removal=False, datelist=None):

    # from datetime import datetime
    with rasterio.open(stack) as src:

        harmonics = False
        if 'harmonics' in metrics:
            print(' INFO: Calculating harmonics')
            if not datelist:
                print(' WARNING: Harmonics need the datelist. Harmonics will not be calculated')
            else:
                harmonics = True
                metrics.remove('harmonics')
                metrics.extend(['amplitude', 'phase', 'residuals'])
        
        if 'percentiles' in metrics:
            metrics.remove('percentiles')
            metrics.extend(['p95', 'p5'])
            
        # get metadata
        meta = src.profile

        # update driver and reduced band count
        meta.update({'driver': 'GTiff'})
        meta.update({'count': 1})

        # write all different output files into a dictionary
        metric_dict = {}
        for metric in metrics:
            filename = '{}.{}.tif'.format(out_prefix, metric)
            metric_dict[metric] = rasterio.open(
                filename, 'w', **meta)

        # scaling factors in case we have to rescale to integer
        minimums = {'avg': -30, 'max': -30, 'min': -30,
                    'std': 0.00001, 'cov': 0.00001}
        maximums = {'avg': 5, 'max': 5, 'min': 5, 'std': 15, 'cov': 1}

        
        if harmonics:
            # construct independent variables
            dates, sines, cosines = [], [], []
            two_pi = np.multiply(2, np.pi)
            for date in sorted(datelist):
                
                delta = difference_in_years(datetime.strptime('700101',"%y%m%d"), datetime.strptime(date,"%y%m%d"))
                dates.append(delta)
                sines.append(np.sin(np.multiply(two_pi, delta - 0.5)))
                cosines.append(np.cos(np.multiply(two_pi, delta - 0.5)))
            
            X = np.array([dates, cosines, sines])
    
        # loop through blocks
        for _, window in src.block_windows(1):

            # read array with all bands
            stack = src.read(range(1, src.count + 1), window=window)

            if rescale_to_datatype is True and meta['dtype'] != 'float32':
                stack = ras.rescale_to_float(stack, meta['dtype'])

            # transform to power
            if to_power is True:
                stack = ras.convert_to_power(stack)

            # outlier removal (only applies if there are more than 5 bands)
            if outlier_removal is True and src.count >= 5:
                stack = remove_outliers(stack)

            # get stats
            arr = {}
            arr['p95'], arr['p5'] = (np.nan_to_num(nan_percentile(stack, [95, 5]))
                                     if 'p95' in metrics else (False, False))
            arr['median'] = (np.nan_to_num(np.nanmedian(stack, axis=0))
                          if 'median' in metrics else False)
            arr['avg'] = (np.nan_to_num(np.nanmean(stack, axis=0))
                          if 'avg' in metrics else False)
            arr['max'] = (np.nan_to_num(np.nanmax(stack, axis=0))
                          if 'max' in metrics else False)
            arr['min'] = (np.nan_to_num(np.nanmin(stack, axis=0))
                          if 'min' in metrics else False)
            arr['std'] = (np.nan_to_num(np.nanstd(stack, axis=0))
                          if 'std' in metrics else False)
            arr['cov'] = (np.nan_to_num(stats.variation(stack, axis=0,
                                                        nan_policy='omit'))
                          if 'cov' in metrics else False)
            
            if harmonics:
                
                stack_size = (stack.shape[1], stack.shape[2])
                if to_power is True:
                    y = ras.convert_to_db(stack).reshape(stack.shape[0], -1)
                else:
                    y = stack.reshape(stack.shape[0], -1)
                    
                x, residuals, _, _ = np.linalg.lstsq(X.T, y)
                arr['amplitude'] = np.hypot(x[1], x[2]).reshape(stack_size)
                arr['phase'] = np.arctan2(x[2], x[1]).reshape(stack_size)
                arr['residuals'] = np.sqrt(np.divide(residuals, stack.shape[0])).reshape(stack_size)
                
                
            # the metrics to be re-turned to dB, in case to_power is True
            metrics_to_convert = ['avg', 'min', 'max', 'p95', 'p5', 'median']

            # do the back conversions and write to disk loop
            for metric in metrics:
                
                if to_power is True and metric in metrics_to_convert:
                    arr[metric] = ras.convert_to_db(arr[metric])

                if rescale_to_datatype is True and meta['dtype'] != 'float32':
                    arr[metric] = ras.scale_to_int(arr[metric], meta['dtype'],
                                                   minimums[metric],
                                                   maximums[metric])

                # write to dest
                metric_dict[metric].write(
                    np.float32(arr[metric]), window=window, indexes=1)
                metric_dict[metric].update_tags(1, 
                    BAND_NAME='{}_{}'.format(os.path.basename(out_prefix), metric))
                metric_dict[metric].set_band_description(1, 
                    '{}_{}'.format(os.path.basename(out_prefix), metric))

    # close the output files
    for metric in metrics:
        # close rio opening
        metric_dict[metric].close()
        # construct filename
        filename = '{}.{}.tif'.format(out_prefix, metric)
        return_code = h.check_out_tiff(filename)
        if return_code != 0:
            # remove all files and return
            for metric in metrics:
                filename = '{}.{}.tif'.format(out_prefix, metric)
                os.remove(filename)
            
            return return_code
        
    if return_code == 0:
        dirname = os.path.dirname(out_prefix)
        check_file = opj(dirname, '.{}.processed'.format(os.path.basename(out_prefix)))
        with open(str(check_file), 'w') as file:
            file.write('passed all tests \n')
Esempio n. 6
0
def mosaic(filelist, outfile, config_file, cut_to_aoi=None, harm=None):

    if outfile.parent.joinpath(f'.{outfile.name[:-4]}.processed').exists():
        logger.info(f'{outfile} already exists.')
        return

    logger.info(f'Mosaicking file {outfile}.')
    with open(config_file, 'r') as ard_file:
        config_dict = json.load(ard_file)
        temp_dir = config_dict['temp_dir']
        aoi = config_dict['aoi']
        epsg = config_dict['processing']['single_ARD']['dem']['out_projection']

        if not harm:
            harm = config_dict['processing']['mosaic']['harmonization']

        if not cut_to_aoi:
            cut_to_aoi = config_dict['processing']['mosaic']['cut_to_aoi']

    logfile = outfile.parent.joinpath(f'{str(outfile)[:-4]}.errLog')

    with TemporaryDirectory(prefix=f'{temp_dir}/') as temp:

        temp = Path(temp)

        # get datatype from first image in our mosaic filelist
        with rasterio.open(filelist.split(' ')[0]) as src:
            dtype = src.meta['dtype']
            dtype = 'float' if dtype == 'float32' else dtype

        if cut_to_aoi:
            tempfile = temp.joinpath(outfile.name)
        else:
            tempfile = outfile

        harm = 'band' if harm else 'none'

        cmd = (
            f"otbcli_Mosaic -ram 8192  -progress 1 "
            f"-comp.feather large "
            f"-harmo.method {harm} "
            f"-harmo.cost rmse "
            f"-tmpdir {str(temp)} "
            f"-interpolator bco"
            f" -il {filelist} "
            f" -out {str(tempfile)} {dtype}"
        )

        return_code = h.run_command(cmd, logfile)

        if return_code != 0:
            if tempfile.exists():
                tempfile.unlink()

            return

        if cut_to_aoi:

            # get aoi in a way rasterio wants it
            aoi_gdf = vec.wkt_to_gdf(aoi)
            features = vec.gdf_to_json_geometry(aoi_gdf.to_crs(epsg=epsg))

            # import raster and mask
            with rasterio.open(tempfile) as src:
                out_image, out_transform = rasterio.mask.mask(src, features,
                                                              crop=True)
                out_meta = src.meta.copy()
                ndv = src.nodata
                out_image = np.ma.masked_where(out_image == ndv, out_image)

                out_meta.update({
                    'driver': 'GTiff',
                    'height': out_image.shape[1],
                    'width': out_image.shape[2],
                    'transform': out_transform,
                    'tiled': True,
                    'blockxsize': 128,
                    'blockysize': 128
                })

                with rasterio.open(outfile, 'w', **out_meta) as dest:
                    dest.write(out_image.data)

            # remove intermediate file
            tempfile.unlink()

        # check
        return_code = h.check_out_tiff(outfile)
        if return_code != 0:
            if outfile.exists():
                outfile.unlink()
        else:
            check_file = outfile.parent.joinpath(
                f'.{outfile.name[:-4]}.processed'
            )
            with open(str(check_file), 'w') as file:
                file.write('passed all tests \n')