# Source code for grass.temporal.extract

    """
    Extract functions for space time raster, 3d raster and vector datasets

    (C) 2012-2013 by the GRASS Development Team
    This program is free software under the GNU General Public
    License (>=v2). Read the file COPYING that comes with GRASS
    for details.

    :authors: Soeren Gebbert
    """
    from .core import (
        get_tgis_message_interface,
        get_current_mapset,
        SQLDatabaseInterfaceConnection,
    )
    from .abstract_map_dataset import AbstractMapDataset
    from .open_stds import open_old_stds, check_new_stds, open_new_stds
    from .datetime_math import create_suffix_from_datetime
    from .datetime_math import create_time_suffix
    from .datetime_math import create_numeric_suffix
    from multiprocessing import Process
    import grass.script as gscript
    from grass.exceptions import CalledModuleError

    ############################################################################

    def extract_dataset(
        input,
        output,
        type,
        where,
        expression,
        base,
        time_suffix,
        nprocs=1,
        register_null=False,
        layer=1,
        vtype="point,line,boundary,centroid,area,face",
    ):
        """Extract a subset of a space time raster, raster3d or vector dataset

        A mapcalc expression can be provided to process the temporal extracted
        maps.
        Mapcalc expressions are supported for raster and raster3d maps.

        :param input: The name of the input space time raster/raster3d dataset
        :param output: The name of the extracted new space time raster/raster3d
                      dataset
        :param type: The type of the dataset: "raster", "raster3d" or vector
        :param where: The temporal SQL WHERE statement for subset extraction
        :param expression: The r(3).mapcalc expression or the v.extract where
                          statement
        :param base: The base name of the new created maps in case a mapclac
                    expression is provided
        :param time_suffix: string to choose which suffix to use: gran, time, num%*
                           (where * are digits)
        :param nprocs: The number of parallel processes to be used for mapcalc
                      processing
        :param register_null: Set this number True to register empty maps
                             (only raster and raster3d maps)
        :param layer: The vector layer number to be used when no timestamped
               layer is present, default is 1
        :param vtype: The feature type to be extracted for vector maps, default
               is point,line,boundary,centroid,area and face
        """

        # Check the parameters
        msgr = get_tgis_message_interface()

        if expression and not base:
            msgr.fatal(_("You need to specify the base name of new created maps"))

        mapset = get_current_mapset()

        dbif = SQLDatabaseInterfaceConnection()
        dbif.connect()

        sp = open_old_stds(input, type, dbif)
        # Check the new stds
        new_sp = check_new_stds(output, type, dbif, gscript.overwrite())
        if type == "vector":
            rows = sp.get_registered_maps("id,name,mapset,layer", where, "start_time", dbif)
        else:
            rows = sp.get_registered_maps("id", where, "start_time", dbif)

        new_maps = {}
        if rows:
            num_rows = len(rows)

            msgr.percent(0, num_rows, 1)

            # Run the mapcalc expression
            if expression:
                count = 0
                proc_count = 0
                proc_list = []

                for row in rows:
                    count += 1

                    if count % 10 == 0:
                        msgr.percent(count, num_rows, 1)

                    if sp.get_temporal_type() == "absolute" and time_suffix == "gran":
                        old_map = sp.get_new_map_instance(row["id"])
                        old_map.select(dbif)
                        suffix = create_suffix_from_datetime(
                            old_map.temporal_extent.get_start_time(), sp.get_granularity()
                        )
                        map_name = "{ba}_{su}".format(ba=base, su=suffix)
                    elif sp.get_temporal_type() == "absolute" and time_suffix == "time":
                        old_map = sp.get_new_map_instance(row["id"])
                        old_map.select(dbif)
                        suffix = create_time_suffix(old_map)
                        map_name = "{ba}_{su}".format(ba=base, su=suffix)
                    else:
                        map_name = create_numeric_suffix(base, count, time_suffix)

                    # We need to modify the r(3).mapcalc expression
                    if type != "vector":
                        expr = expression
                        expr = expr.replace(sp.base.get_map_id(), row["id"])
                        expr = expr.replace(sp.base.get_name(), row["id"])

                        expr = "%s = %s" % (map_name, expr)

                        # We need to build the id
                        map_id = AbstractMapDataset.build_id(map_name, mapset)
                    else:
                        map_id = AbstractMapDataset.build_id(map_name, mapset, row["layer"])

                    new_map = sp.get_new_map_instance(map_id)

                    # Check if new map is in the temporal database
                    if new_map.is_in_db(dbif):
                        if gscript.overwrite():
                            # Remove the existing temporal database entry
                            new_map.delete(dbif)
                            new_map = sp.get_new_map_instance(map_id)
                        else:
                            msgr.error(
                                _(
                                    "Map <%s> is already in temporal database"
                                    ", use overwrite flag to overwrite"
                                )
                                % (new_map.get_map_id())
                            )
                            continue

                    # Add process to the process list
                    if type == "raster":
                        msgr.verbose(_('Applying r.mapcalc expression: "%s"') % expr)
                        proc_list.append(Process(target=run_mapcalc2d, args=(expr,)))
                    elif type == "raster3d":
                        msgr.verbose(_('Applying r3.mapcalc expression: "%s"') % expr)
                        proc_list.append(Process(target=run_mapcalc3d, args=(expr,)))
                    elif type == "vector":
                        msgr.verbose(
                            _('Applying v.extract where statement: "%s"') % expression
                        )
                        if row["layer"]:
                            proc_list.append(
                                Process(
                                    target=run_vector_extraction,
                                    args=(
                                        row["name"] + "@" + row["mapset"],
                                        map_name,
                                        row["layer"],
                                        vtype,
                                        expression,
                                    ),
                                )
                            )
                        else:
                            proc_list.append(
                                Process(
                                    target=run_vector_extraction,
                                    args=(
                                        row["name"] + "@" + row["mapset"],
                                        map_name,
                                        layer,
                                        vtype,
                                        expression,
                                    ),
                                )
                            )

                    proc_list[proc_count].start()
                    proc_count += 1

                    # Join processes if the maximum number of processes are
                    # reached or the end of the loop is reached
                    if proc_count == nprocs or count == num_rows:
                        proc_count = 0
                        exitcodes = 0
                        for proc in proc_list:
                            proc.join()
                            exitcodes += proc.exitcode
                        if exitcodes != 0:
                            dbif.close()
                            msgr.fatal(_("Error in computation process"))

                        # Empty process list
                        proc_list = []

                    # Store the new maps
                    new_maps[row["id"]] = new_map

            msgr.percent(0, num_rows, 1)

            temporal_type, semantic_type, title, description = sp.get_initial_values()
            new_sp = open_new_stds(
                output,
                type,
                sp.get_temporal_type(),
                title,
                description,
                semantic_type,
                dbif,
                gscript.overwrite(),
            )

            # collect empty maps to remove them
            empty_maps = []

            # Register the maps in the database
            count = 0
            for row in rows:
                count += 1

                if count % 10 == 0:
                    msgr.percent(count, num_rows, 1)

                old_map = sp.get_new_map_instance(row["id"])
                old_map.select(dbif)

                if expression:
                    # Register the new maps
                    if row["id"] in new_maps:
                        new_map = new_maps[row["id"]]

                        # Read the raster map data
                        new_map.load()

                        # In case of a empty map continue, do not register empty
                        # maps
                        if type == "raster" or type == "raster3d":
                            if (
                                new_map.metadata.get_min() is None
                                and new_map.metadata.get_max() is None
                            ):
                                if not register_null:
                                    empty_maps.append(new_map)
                                    continue
                        elif type == "vector":
                            if (
                                new_map.metadata.get_number_of_primitives() == 0
                                or new_map.metadata.get_number_of_primitives() is None
                            ):
                                if not register_null:
                                    empty_maps.append(new_map)
                                    continue

                        # Set the time stamp
                        new_map.set_temporal_extent(old_map.get_temporal_extent())

                        if type == "raster":
                            # Set the semantic label
                            semantic_label = old_map.metadata.get_semantic_label()
                            if semantic_label is not None:
                                new_map.set_semantic_label(semantic_label)

                        # Insert map in temporal database
                        new_map.insert(dbif)

                        new_sp.register_map(new_map, dbif)
                else:
                    new_sp.register_map(old_map, dbif)

            # Update the spatio-temporal extent and the metadata table entries
            new_sp.update_from_registered_maps(dbif)

            msgr.percent(num_rows, num_rows, 1)

            # Remove empty maps
            if len(empty_maps) > 0:
                names = ""
                count = 0
                for map in empty_maps:
                    if count == 0:
                        names += "%s" % (map.get_name())
                    else:
                        names += ",%s" % (map.get_name())
                    count += 1
                if type == "raster":
                    gscript.run_command(
                        "g.remove", flags="f", type="raster", name=names, quiet=True
                    )
                elif type == "raster3d":
                    gscript.run_command(
                        "g.remove", flags="f", type="raster_3d", name=names, quiet=True
                    )
                elif type == "vector":
                    gscript.run_command(
                        "g.remove", flags="f", type="vector", name=names, quiet=True
                    )

        dbif.close()

    ###############################################################################

    def run_mapcalc2d(expr):
        """Helper function to run r.mapcalc in parallel"""
        try:
            gscript.run_command(
                "r.mapcalc", expression=expr, overwrite=gscript.overwrite(), quiet=True
            )
        except CalledModuleError:
            exit(1)

    def run_mapcalc3d(expr):
        """Helper function to run r3.mapcalc in parallel"""
        try:
            gscript.run_command(
                "r3.mapcalc", expression=expr, overwrite=gscript.overwrite(), quiet=True
            )
        except CalledModuleError:
            exit(1)

    def run_vector_extraction(input, output, layer, type, where):
        """Helper function to run r.mapcalc in parallel"""
        try:
            gscript.run_command(
                "v.extract",
                input=input,
                output=output,
                layer=layer,
                type=type,
                where=where,
                overwrite=gscript.overwrite(),
                quiet=True,
            )
        except CalledModuleError:
            exit(1)

Help Index | Topics Index | Keywords Index | Full Index

© 2003-2023 GRASS Development Team, GRASS GIS 8.2.1dev Reference Manual