Example #1
0
 def __init__(self):
     """
     The registry contains all the recipes available for the wcst import
     To add one please register it in the _init_registry method
     """
     self.registry = {}
     self._init_registry()
     self.sentence_evaluator = SentenceEvaluator(ExpressionEvaluatorFactory())
Example #2
0
    def _get_coverage(self):
        """
        Returns the coverage to be used for the importer
        """
        gdal_dataset = GDALGmlUtil.open_gdal_dataset_from_any_file(self.session.get_files())
        crs = gdal_dataset.get_crs()

        general_recipe = GeneralRecipe(self.session)
        global_metadata_fields = general_recipe._global_metadata_fields()
        local_metadata_fields = general_recipe._local_metadata_fields()

        sentence_evaluator = SentenceEvaluator(ExpressionEvaluatorFactory())

        gdal_coverage_converter = GdalToCoverageConverter(self.resumer, self.session.get_default_null_values(),
                                                          self.recipe_type, sentence_evaluator,
                                                          self.session.get_coverage_id(),
                                                          None, self.session.get_files(),
                                                          crs, None, None,
                                                          global_metadata_fields, local_metadata_fields,
                                                          None, None, general_recipe._metadata_type(),
                                                          None, None)

        coverage_slices = self._get_coverage_slices(crs, gdal_coverage_converter)
        fields = GdalRangeFieldsGenerator(gdal_dataset, self.options['band_names']).get_range_fields()

        global_metadata = None
        if len(coverage_slices) > 0:
            global_metadata = gdal_coverage_converter._generate_global_metadata(coverage_slices[0], self.evaluator_slice)

        coverage = Coverage(self.session.get_coverage_id(), coverage_slices, fields, gdal_dataset.get_crs(),
                            gdal_dataset.get_band_gdal_type(), self.options['tiling'], global_metadata)

        return coverage
Example #3
0
    def _get_grib_coverage(self, recipe_type):
        """
        Returns a coverage that uses the grib slicer
        :param: string recipe_type the type of grib
        :rtype: master.importer.coverage.Coverage
        """
        crs = self._resolve_crs(self.options['coverage']['crs'])
        sentence_evaluator = SentenceEvaluator(ExpressionEvaluatorFactory())
        pixel_is_point = False
        if 'pixelIsPoint' in self.options['coverage']['slicer'] and self.options['coverage']['slicer']['pixelIsPoint']:
            pixel_is_point = True

        coverage = GRIBToCoverageConverter(self.resumer, self.session.default_null_values,
                                           recipe_type,
                                           sentence_evaluator, self.session.get_coverage_id(),
                                           self._read_bands(),
                                           self.session.get_files(), crs, self._read_axes(crs),
                                           self.options['tiling'], self._global_metadata_fields(),
                                           self._bands_metadata_fields(),
                                           self._local_metadata_fields(),
                                           self._axes_metadata_fields(),
                                           self._metadata_type(),
                                           self.options['coverage']['grid_coverage'], pixel_is_point,
                                           self.options['import_order']).to_coverage()

        return coverage
Example #4
0
    def _get_gdal_coverages(self, recipe_type):
        """
        Returns a list of coverage that uses the gdal slicer
        :param string: recipe_type the type of recipe
        :rtype: master.importer.coverage.Coverage
        """
        crs = self._resolve_crs(self.options['coverage']['crs'])
        sentence_evaluator = SentenceEvaluator(ExpressionEvaluatorFactory())

        input_files = self.session.get_files()
        user_axes = self._read_axes(crs)
        DEFAULT_NUMBER_GDAL_DIMENSIONS = 2
        self.__validate_data_bound_axes(user_axes, DEFAULT_NUMBER_GDAL_DIMENSIONS)

        coverages = GdalToCoverageConverter(self.resumer, self.session.default_null_values,
                                           recipe_type, sentence_evaluator, self.session.get_coverage_id(),
                                           self._read_bands(),
                                            input_files, crs, user_axes,
                                           self.options['tiling'], self._global_metadata_fields(),
                                           self._local_metadata_fields(),
                                           self._bands_metadata_fields(),
                                           self._axes_metadata_fields(),
                                           self._metadata_type(),
                                           self.options['coverage']['grid_coverage'],
                                           self.options['import_order'],
                                           self.session).to_coverages()
        return coverages
Example #5
0
    def _create_convertor(self, convertors, cov_id, crs_code, level, res):
        recipe_type = GdalToCoverageConverter.RECIPE_TYPE
        sentence_evaluator = SentenceEvaluator(ExpressionEvaluatorFactory())
        files = []
        crs = self._get_crs(crs_code)
        bands_metadata_fields = {}
        axis_metadata_fields = {}

        default_null_values = [self.DEFAULT_NULL_VALUE]

        return GdalToCoverageConverter(self.resumer,
                                       default_null_values,
                                       recipe_type, 
                                       sentence_evaluator, 
                                       cov_id, 
                                       self.BANDS[level][res],
                                       files, 
                                       crs,
                                       self._read_axes(crs),
                                       self.options['tiling'],
                                       self._global_metadata_fields(),
                                       self._local_metadata_fields(),
                                       bands_metadata_fields,
                                       axis_metadata_fields,
                                       self._metadata_type(),
                                       self.grid_cov,
                                       self.import_order,
                                       self.session)
Example #6
0
    def _get_coverages(self):
        """
        Returns the list of coverages to be used for the importer
        """
        gdal_dataset = GDALGmlUtil.open_gdal_dataset_from_any_file(
            self.session.get_files())
        crs = CRSUtil.get_compound_crs(
            [self.options['time_crs'],
             gdal_dataset.get_crs()])

        general_recipe = GeneralRecipe(self.session)
        global_metadata_fields = general_recipe._global_metadata_fields()
        local_metadata_fields = general_recipe._local_metadata_fields()

        sentence_evaluator = SentenceEvaluator(ExpressionEvaluatorFactory())

        gdal_coverage_converter = GdalToCoverageConverter(
            self.resumer, self.session.get_default_null_values(),
            self.recipe_type,
            sentence_evaluator, self.session.get_coverage_id(), None,
            self.session.get_files(), crs, None, None,
            global_metadata_fields, local_metadata_fields, None, None,
            general_recipe._metadata_type(), None, None, self.session)

        coverage_slices_dict = self._get_coverage_slices(
            crs, gdal_coverage_converter)
        fields = GdalRangeFieldsGenerator(
            gdal_dataset, self.options['band_names']).get_range_fields()

        global_metadata = None
        if len(coverage_slices_dict["base"]) > 0:
            global_metadata = gdal_coverage_converter._generate_global_metadata(
                coverage_slices_dict["base"][0], self.evaluator_slice)

        results = []
        base_coverage_id = self.session.get_coverage_id()
        for key, value in coverage_slices_dict.items():
            if key == "base":
                # base coverage
                coverage = Coverage(base_coverage_id,
                                    coverage_slices_dict[key], fields, crs,
                                    gdal_dataset.get_band_gdal_type(),
                                    self.options['tiling'], global_metadata)
            else:
                # overview coverage (key = overview_index)
                coverage_id = create_coverage_id_for_overview(
                    base_coverage_id, key)
                coverage = Coverage(coverage_id, coverage_slices_dict[key],
                                    fields, crs,
                                    gdal_dataset.get_band_gdal_type(),
                                    self.options['tiling'], global_metadata,
                                    base_coverage_id, key)

            results.append(coverage)

        return results
Example #7
0
 def _get_gdal_coverage(self, recipe_type):
     """
     Returns a coverage that uses the gdal slicer
     :param string: recipe_type the type of recipe
     :rtype: master.importer.coverage.Coverage
     """
     crs = self._resolve_crs(self.options['coverage']['crs'])
     sentence_evaluator = SentenceEvaluator(ExpressionEvaluatorFactory())
     coverage = GdalToCoverageConverter(recipe_type, sentence_evaluator, self.session.get_coverage_id(),
                                        self._read_bands(),
                                        self.session.get_files(), crs, self._read_axes(crs),
                                        self.options['tiling'], self._global_metadata_fields(),
                                        self._local_metadata_fields(), self._bands_metadata_fields(),
                                        self._axes_metadata_fields(),
                                        self._metadata_type(),
                                        self.options['coverage']['grid_coverage']).to_coverage()
     return coverage
Example #8
0
    def _create_convertor(self, cov_id):
        recipe_type = GdalToCoverageConverter.RECIPE_TYPE
        sentence_evaluator = SentenceEvaluator(ExpressionEvaluatorFactory())
        files = []
        bands_metadata_fields = {}
        axis_metadata_fields = {}

        default_null_values = []

        if self.product == self.DEFAULT_PRODUCT:
            default_null_values = [self.DEFAULT_NULL_VALUE]

        return GdalToCoverageConverter(
            self.resumer, default_null_values, recipe_type,
            sentence_evaluator, cov_id, [self.BAND], files, self.crs,
            self._read_axes(self.crs), self.options['tiling'],
            self._global_metadata_fields(), self._local_metadata_fields(),
            bands_metadata_fields, axis_metadata_fields, self._metadata_type(),
            self.grid_cov, self.import_order)
Example #9
0
    def _get_netcdf_coverage(self, recipe_type):
        """
        Returns a coverage that uses the netcdf slicer
        :param: string recipe_type the type of netcdf
        :rtype: master.importer.coverage.Coverage
        """
        crs = self._resolve_crs(self.options['coverage']['crs'])
        sentence_evaluator = SentenceEvaluator(ExpressionEvaluatorFactory())
        # by default pixelIsPoint is not set to true in ingredient file
        pixel_is_point = False
        if 'pixelIsPoint' in self.options['coverage']['slicer'] and self.options['coverage']['slicer']['pixelIsPoint']:
            pixel_is_point = True

        coverage = NetcdfToCoverageConverter(recipe_type, sentence_evaluator, self.session.get_coverage_id(),
                                             self._read_bands(),
                                             self.session.get_files(), crs, self._read_axes(crs),
                                             self.options['tiling'], self._netcdf_global_metadata_fields(),
                                             self._local_metadata_fields(),
                                             self._netcdf_bands_metadata_fields(),
                                             self._netcdf_axes_metadata_fields(),
                                             self._metadata_type(),
                                             self.options['coverage']['grid_coverage'], pixel_is_point).to_coverage()
        return coverage
Example #10
0
    def _get_netcdf_coverage(self, recipe_type):
        """
        Returns a coverage that uses the netcdf slicer
        :param: string recipe_type the type of netcdf
        :rtype: master.importer.coverage.Coverage
        """
        crs = self._resolve_crs(self.options['coverage']['crs'])
        sentence_evaluator = SentenceEvaluator(ExpressionEvaluatorFactory())
        # by default pixelIsPoint is not set to true in ingredient file
        pixel_is_point = False
        if 'pixelIsPoint' in self.options['coverage']['slicer'] and self.options['coverage']['slicer']['pixelIsPoint']:
            pixel_is_point = True

        input_files = self.session.get_files()
        user_axes = self._read_axes(crs)
        bands = self._read_bands()
        first_band = bands[0]
        first_band_variable_identifier = first_band.identifier

        netCDF4 = import_netcdf4()
        # Get the number of dimensions of band variable to validate with number of axes specified in the ingredients file
        number_of_dimensions = len(netCDF4.Dataset(input_files[0], 'r').variables[first_band_variable_identifier].dimensions)
        self.__validate_data_bound_axes(user_axes, number_of_dimensions)

        coverage = NetcdfToCoverageConverter(self.resumer, self.session.default_null_values,
                                             recipe_type, sentence_evaluator, self.session.get_coverage_id(),
                                             bands,
                                             input_files, crs, user_axes,
                                             self.options['tiling'], self._global_metadata_fields(),
                                             self._local_metadata_fields(),
                                             self._netcdf_bands_metadata_fields(),
                                             self._netcdf_axes_metadata_fields(),
                                             self._metadata_type(),
                                             self.options['coverage']['grid_coverage'], pixel_is_point,
                                             self.options['import_order'],
                                             self.session).to_coverages()
        return coverage
Example #11
0
class RecipeRegistry:
    def __init__(self):
        """
        The registry contains all the recipes available for the wcst import
        To add one please register it in the _init_registry method
        """
        self.registry = {}
        self._init_registry()
        self.sentence_evaluator = SentenceEvaluator(ExpressionEvaluatorFactory())

    def _init_registry(self):
        """
        Initializes the registry with all the recipes available by looking into the recipes folder
        """
        util = ReflectionUtil()

        # load "official" rasdaman recipes manually
        import recipes.general_coverage
        import recipes.map_mosaic
        import recipes.time_series_irregular
        import recipes.time_series_regular
        import recipes.wcs_extract

        util.import_submodules(recipes.general_coverage)
        util.import_submodules(recipes.map_mosaic)
        util.import_submodules(recipes.time_series_irregular)
        util.import_submodules(recipes.time_series_regular)
        util.import_submodules(recipes.wcs_extract)

        # user-created recipes are put in the recipes_custom directory, so
        # wcst_import needs to load all possible recipes in this directory.
        import recipes_custom
        util.import_submodules(recipes_custom)

        # register all loaded recipes
        all_recipes = util.get_all_subclasses(BaseRecipe)
        for recipe in all_recipes:
            self.registry[recipe.get_name()] = recipe

    def __run_shell_command(self, command, abort_on_error=False):
        """
        Run a shell command and exit wcst_import if needed
        :param str command: shell command to run
        """
        try:
            log.info("Executing shell command '{}'...".format(command))
            output = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True)
            output = decode_res(output)
            if output != "":
                log.info("Output result '{}'".format(output))
        except subprocess.CalledProcessError as exc:
            log.warn("Failed, status code '{}', error message '{}'.".format(exc.returncode, str(exc.output).strip()))
            if abort_on_error:
                log.error("wcst_import terminated on running hook command.")
                exit(1)

    def __run_hooks(self, session, hooks, after_ingestion=False):
        """
        Run some hooks before/after analyzing input files
        :param Session session:
        :param dict[str:str] hooks: dictionary of before and after ingestion hooks
        """
        # gdal (default), netcdf or grib
        recipe_type = GdalToCoverageConverter.RECIPE_TYPE
        if session.recipe["name"] == GeneralRecipe.RECIPE_TYPE:
            recipe_type = session.recipe["options"]["coverage"]["slicer"]["type"]

        for hook in hooks:
            abort_on_error = False if "abort_on_error" not in hook else bool(hook["abort_on_error"])

            if "description" in hook:
                log.info("Executing hook '{}'...".format(make_bold(hook["description"])))

            replace_paths = []
            replace_path_template = None
            if "replace_path" in hook:
                # All replaced input files share same template format (e.g: file:path -> file:path.projected)
                replace_path_template = hook["replace_path"][0]

            # Evaluate shell command expression to get a runnable shell command
            cmd_template = hook["cmd"]

            files = session.files
            if after_ingestion is True:
                files = session.imported_files

            for file in files:
                evaluator_slice = EvaluatorSliceFactory.get_evaluator_slice(recipe_type, file)
                cmd = self.sentence_evaluator.evaluate(cmd_template, evaluator_slice)
                self.__run_shell_command(cmd, abort_on_error)

                if FileExpressionEvaluator.PREFIX not in cmd_template:
                    # Only need to run hook once if ${...} does not exist in cmd command,
                    # otherwise it runs duplicate commands multiple times (!)
                    if replace_path_template is not None:
                        replace_path = self.sentence_evaluator.evaluate(replace_path_template, evaluator_slice)
                        replace_paths.append(FilePair(replace_path, file.filepath))
                    break

                if replace_path_template is not None:
                    # Evaluate replace path expression to get a valid file input path
                    replace_path = self.sentence_evaluator.evaluate(replace_path_template, evaluator_slice)
                    tmp_files = glob.glob(replace_path, recursive=True)
                    for tmp_file in tmp_files:
                        if not isinstance(file, FilePair):
                            # The first replacement (must keep original input file path)
                            replace_paths.append(FilePair(tmp_file, file.filepath))
                        else:
                            # From the second replacement
                            replace_paths.append(FilePair(tmp_file, file.original_file_path))

            if len(replace_paths) > 0:
                # Use replaced file paths instead of original input file paths to analyze and create coverage slices
                session.files = replace_paths

    def __run_recipe(self, session, recipe):
        """
        Run recipe
        :param Session session:
        :param BaseRecipe recipe:
        """

        if session.before_hooks:
            log.info(make_bold("Executing before ingestion hook(s)..."))
            self.__run_hooks(session, session.before_hooks, False)

        recipe.describe()

        if session.blocking and not session.is_automated():
            input("Press Enter to Continue...: ")

        log.title("\nRunning")

        if session.blocking:
            # It needs to display progress bar (how many files imported)
            t = Thread(target=run_status, args=(recipe,))
            t.daemon = True
            t.start()
            recipe.run()
            t.join()
        else:
            # Non-blocking mode, only 1 file is importing, no need to show progress bar (it takes longer time to join threads)
            recipe.run()

        recipe.importer = None

        if session.after_hooks:
            log.info(make_bold("Executing after ingestion hook(s)..."))
            self.__run_hooks(session, session.after_hooks, True)

    def run_recipe(self, session):
        """
        Recipe session
        :param Session session: the session of the import
        :rtype BaseRecipe
        """
        recipe_name = session.get_recipe()['name']
        if recipe_name not in self.registry:
            raise RecipeValidationException("Recipe '" + session.get_recipe()['name'] + "' not found; "
                                            "if it's a custom recipe, please put it in the "
                                            "'$RMANHOME/share/rasdaman/wcst_import/recipes_custom' folder.")
        else:
            recipe = self.registry[session.get_recipe()['name']](session)
            log.title("Initialization")

            if recipe_name != virtual_coverage_recipe.RECIPE_NAME:
                number_of_files = len(session.get_files())
                if number_of_files > 10:
                    number_of_files = 10
                log.info("Collected first " + str(number_of_files) + " files: "
                         + str([str(f) for f in session.get_files()[:10]]) + "...")

            log.title("\nValidation")
            recipe.validate()

            # Show what recipe and coverage are imported only once
            super(recipe.__class__, recipe).describe()

            if session.blocking is True:
                # Default blocking import mode (analyze all files -> import)
                self.__run_recipe(session, recipe)
            else:
                # Non blocking import mode (analyze 1 file -> import then next file)
                files = list(session.get_files())

                for file in files:
                    session.files = [file]
                    self.__run_recipe(session, recipe)

            log.success("Recipe executed successfully")