コード例 #1
0
    def run_command(self):

        # Get the command parameter values.
        pv_GeoLayerIDs = self.get_parameter_value("GeoLayerIDs")
        pv_OutputGeoLayerID = self.get_parameter_value("OutputGeoLayerID")
        pv_AttributeMap = self.get_parameter_value("AttributeMap",
                                                   default_value="")

        # Expand for ${Property} syntax.
        pv_GeoLayerIDs = self.command_processor.expand_parameter_value(
            pv_GeoLayerIDs, self)

        # Convert the AttributeMap parameter from string to a list of mapping entries.
        attribute_map_entry_list = string_util.delimited_string_to_list(
            pv_AttributeMap, delimiter=',')

        # The attribute map dictionary contains the attributes of the output merged GeoLayer and the corresponding
        # attributes of the input GeoLayers.
        # key (str): an attribute of the output, merged GeoLayer
        # value (list): a list of attributes from the input GeoLayers that should be mapped to the output attribute
        attribute_map_dic = {}

        # Iterate over each attribute mapping entry.
        for attribute_map_entry in attribute_map_entry_list:

            # Get a list of the keys (merged attributes) currently in the attribute map dictionary.
            curr_merged_attributes = list(attribute_map_dic.keys())

            # If the attribute map entry has the correct format, continue.
            if ':' in attribute_map_entry:

                # Get the output merged attribute name from the entry.
                merged_attr = attribute_map_entry.split(':')[1].strip()

                # Get the input attribute name from the entry.
                input_attr = attribute_map_entry.split(':')[0].strip()

            # If the attribute map entry does has the correct format, the merged_attr and the input_attr variables
            # are set to empty strings.
            else:
                merged_attr = ''
                input_attr = ''

            # If the merged attribute name is already registered in the attribute mapping dictionary.
            if merged_attr in curr_merged_attributes:

                # Add the input attribute to the list of input attributes within the dictionary (associated with
                # the corresponding merged_attribute).
                curr_input_attrs = attribute_map_dic[merged_attr]
                curr_input_attrs.append(input_attr)
                attribute_map_dic[merged_attr] = curr_input_attrs

            # If the merged attribute is not already registered in the attribute mapping dictionary, add the input
            # attribute (as a one-item list) to the dictionary (associated with the corresponding merged_attribute).
            else:
                attribute_map_dic[merged_attr] = [input_attr]

        # Convert the GeoLayerIDs parameter from string to list format.
        # If configured, list all of the registered GeoLayer IDs.
        if pv_GeoLayerIDs == "*":
            list_of_geolayer_ids = []

            # Iterate over each GeoLayer registered within the GeoProcessor. Add each GeoLayer's ID to the list.
            for geolayer_obj in self.command_processor.geolayers:
                list_of_geolayer_ids.append(geolayer_obj.id)

        # If specific GeoLayer IDs are listed, convert the string into list format.
        else:
            list_of_geolayer_ids = string_util.delimited_string_to_list(
                pv_GeoLayerIDs)

        # Run the checks on the parameter values. Only continue if the checks passed.
        if self.__should_merge(list_of_geolayer_ids, pv_OutputGeoLayerID):

            try:

                # A list to hold the GeoLayer IDs of the copied GeoLayers. Copied GeoLayers are only required for this
                # command. They will be removed from the GeoProcessor (to save processing space and speed) after the
                # processing has been completed. This list will be used to remove the copied GeoLayers.
                copied_geolayer_ids = []

                # A list to hold the full pathname of the copied GeoLayers (written to disk). The
                # qgis:mergevectorlayers requires that the QGSVectorLayer objects are not in memory. This list will be
                # used as an input to the qgis:mergevectorlayers algorithm.
                copied_geolayer_sourcepath = []

                first_geolayer = self.command_processor.get_geolayer(
                    list_of_geolayer_ids[0])
                first_crs = first_geolayer.get_crs()

                # Iterate over the GeoLayers to be merged.
                for geolayer_id in list_of_geolayer_ids:

                    # Get the appropriate GeoLayer based on the GeoLayer ID.
                    geolayer = self.command_processor.get_geolayer(geolayer_id)

                    # Get an attribute dictionary mapping the GeoLayer attributes that are to be renamed.
                    # Key: Existing attribute name. Value: New attribute name.
                    attribute_dictionary = self.__create_attribute_dictionary(
                        geolayer, attribute_map_dic)

                    # Make a copy of the GeoLayer and add it to the GeoProcessor. Renaming of attributes will occur on
                    # a copy of the GeoLayer so that the original GeoLayer's attribute values are not affected.
                    copied_geolayer = geolayer.deepcopy(
                        "{}_copyForMerge".format(geolayer_id))
                    self.command_processor.add_geolayer(copied_geolayer)

                    # Add the copied GeoLayer ID to the master list.
                    copied_geolayer_ids.append(copied_geolayer.id)

                    # Iterate over the GeoLayer's attribute dictionary.
                    for existing_attr_name, new_attr_name in attribute_dictionary.items(
                    ):

                        # If the attribute should be renamed, then rename the attribute in the copied GeoLayer.
                        if not (existing_attr_name == new_attr_name):
                            copied_geolayer.rename_attribute(
                                existing_attr_name, new_attr_name)

                    # Write copied GeoLayer (memory) to the temporary directory (written to disk).
                    output_file_absolute = os.path.join(
                        self.command_processor.get_property('TempDir'),
                        copied_geolayer.id)
                    on_disk_geolayer = copied_geolayer.write_to_disk(
                        output_file_absolute)

                    # Overwrite the copied (memory) GeoLayer in the geoprocessor with the on-disk GeoLayer.
                    self.command_processor.add_geolayer(on_disk_geolayer)

                    # Add the source path of the copied on-disk GeoLayer to the master list.
                    copied_geolayer_sourcepath.append(
                        on_disk_geolayer.source_path)

                # Merge all of the copied GeoLayers (the GeoLayers with the new attribute names).
                # Using QGIS algorithm but can also use saga:mergelayers algorithm.
                # saga:mergelayers documentation at http://www.saga-gis.org/saga_tool_doc/2.3.0/shapes_tools_2.html
                alg_parameters = {
                    "LAYERS": copied_geolayer_sourcepath,
                    "CRS": first_crs,
                    "OUTPUT": "memory:"
                }
                merged_output = self.command_processor.qgis_processor.runAlgorithm(
                    "qgis:mergevectorlayers", alg_parameters)

                # Create a new GeoLayer and add it to the GeoProcessor's geolayers list.
                # in QGIS3, merged_output["OUTPUT"] returns the returns the QGS vector layer object
                # see ClipGeoLayer.py for information about value in QGIS2 environment
                self.command_processor.add_geolayer(
                    GeoLayer(pv_OutputGeoLayerID, merged_output["OUTPUT"],
                             "MEMORY"))

                # Release the copied GeoLayers from the GeoProcessor.
                for copied_geolayer_id in copied_geolayer_ids:

                    # Get the copied GeoLayer based on the GeoLayer ID.
                    copied_geolayer = self.command_processor.get_geolayer(
                        copied_geolayer_id)

                    # Remove the copied GeoLayer from the GeoProcessor
                    self.command_processor.free_geolayer(copied_geolayer)

            # Raise an exception if an unexpected error occurs during the process
            except Exception as e:
                self.warning_count += 1
                message = "Unexpected error merging the following GeoLayers {}.".format(
                    pv_GeoLayerIDs)
                recommendation = "Check the log file for details."
                self.logger.error(message, exc_info=True)
                self.command_status.add_to_log(
                    CommandPhaseType.RUN,
                    CommandLogRecord(CommandStatusType.FAILURE, message,
                                     recommendation))

        # Determine success of command processing. Raise Runtime Error if any errors occurred
        if self.warning_count > 0:
            message = "There were {} warnings proceeding this command.".format(
                self.warning_count)
            raise RuntimeError(message)

        # Set command status type as SUCCESS if there are no errors.
        else:
            self.command_status.refresh_phase_severity(
                CommandPhaseType.RUN, CommandStatusType.SUCCESS)
コード例 #2
0
    def run_command(self):
        """
        Run the command. Read the feature classes within a file geodatabase. For each desired feature class (can be
        specified by the Subset_Pattern parameter), create a GeoLayer object, and add to the GeoProcessor's geolayer
        list.

        Returns: None.

        Raises:
            RuntimeError if any warnings occurred during run_command method.
        """

        # Obtain the required and optional parameter values
        pv_SpatialDataFolder = self.get_parameter_value("SpatialDataFolder")
        pv_ReadOnlyOneFeatureClass = self.get_parameter_value(
            "ReadOnlyOneFeatureClass")
        pv_Subset_Pattern = self.get_parameter_value("Subset_Pattern")
        pv_GeoLayerID_prefix = self.get_parameter_value("GeoLayerID_prefix")
        pv_GeoLayerID = self.get_parameter_value("GeoLayerID")
        pv_FeatureClass = self.get_parameter_value("FeatureClass")

        # Convert the ReadOnlyOneFeatureClass from a string value to a Boolean value.
        pv_ReadOnlyOneFeatureClass = string_util.str_to_bool(
            pv_ReadOnlyOneFeatureClass)

        # Convert the SpatialDataFolder parameter value relative path to an absolute path
        sd_folder_abs = io_util.verify_path_for_os(
            io_util.to_absolute_path(
                self.command_processor.get_property('WorkingDir'),
                self.command_processor.expand_parameter_value(
                    pv_SpatialDataFolder, self)))

        # Run the initial checks on the parameter values. Only continue if the checks passed.
        if self.__should_read_gdb(sd_folder_abs):

            # If configured to only read one Feature Class into one GeoLayer.
            if pv_ReadOnlyOneFeatureClass:

                # Run the check to see if the GeoLayer should be read.
                if self.__should_read_geolayer(pv_GeoLayerID, True,
                                               pv_FeatureClass, sd_folder_abs):

                    try:

                        # Get the full pathname to the feature class
                        # TODO egiles 2018-01-04 Need to research how to properly document feature class source path
                        spatial_data_file_absolute = os.path.join(
                            sd_folder_abs, str(pv_FeatureClass))

                        # Create a QgsVectorLayer object from the feature class.
                        qgs_vector_layer = qgis_util.read_qgsvectorlayer_from_feature_class(
                            sd_folder_abs, pv_FeatureClass)

                        # Create a GeoLayer and add it to the geoprocessor's GeoLayers list
                        geolayer_obj = GeoLayer(pv_GeoLayerID,
                                                qgs_vector_layer,
                                                spatial_data_file_absolute)
                        self.command_processor.add_geolayer(geolayer_obj)

                    # Raise an exception if an unexpected error occurs during the process
                    except Exception as e:

                        self.warning_count += 1
                        message = "Unexpected error reading feature class ({}) from file geodatabase ({}).".format(
                            pv_FeatureClass, sd_folder_abs)
                        recommendation = "Check the log file for details."
                        self.logger.error(message, exc_info=True)
                        self.command_status.add_to_log(
                            CommandPhaseType.RUN,
                            CommandLogRecord(CommandStatusType.FAILURE,
                                             message, recommendation))

            # If configured to read multiple Feature Classes into multiple GeoLayers.
            else:

                # Get a list of all of the feature classes in the file geodatabase.
                fc_list = self.__return_a_list_of_fc(sd_folder_abs)

                # Filter the fc_list to only include feature classes that meet the Subset Pattern configuration. If
                # the Subset Pattern configuration is None, all feature classes will remain in the fc_list.
                fc_list = string_util.filter_list_of_strings(
                    fc_list, [pv_Subset_Pattern])

                # Iterate over the feature classes in the fc_list.
                for feature_class in fc_list:

                    # Determine the GeoLayerID.
                    if pv_GeoLayerID_prefix:
                        geolayer_id = "{}_{}".format(pv_GeoLayerID_prefix,
                                                     feature_class)
                    else:
                        geolayer_id = feature_class

                    # Run the secondary checks on the parameter values. Only continue if the checks passed.
                    if self.__should_read_geolayer(geolayer_id, False):

                        try:

                            # Get the full pathname to the feature class
                            # TODO egiles 2018-01-04 Need to research how to properly document feature class source path
                            spatial_data_file_absolute = os.path.join(
                                sd_folder_abs, str(feature_class))

                            # Create a QgsVectorLayer object from the feature class.
                            qgs_vector_layer = qgis_util.read_qgsvectorlayer_from_feature_class(
                                sd_folder_abs, feature_class)

                            # Create a GeoLayer and add it to the geoprocessor's GeoLayers list
                            geolayer_obj = GeoLayer(
                                geolayer_id, qgs_vector_layer,
                                spatial_data_file_absolute)
                            self.command_processor.add_geolayer(geolayer_obj)

                        # Raise an exception if an unexpected error occurs during the process
                        except Exception as e:

                            self.warning_count += 1
                            message = "Unexpected error reading feature class ({}) from file geodatabase ({}).".format(
                                feature_class, sd_folder_abs)
                            recommendation = "Check the log file for details."
                            self.logger.error(message, exc_info=True)
                            self.command_status.add_to_log(
                                CommandPhaseType.RUN,
                                CommandLogRecord(CommandStatusType.FAILURE,
                                                 message, recommendation))

        # Determine success of command processing. Raise Runtime Error if any errors occurred
        if self.warning_count > 0:
            message = "There were {} warnings proceeding this command.".format(
                self.warning_count)
            raise RuntimeError(message)

        # Set command status type as SUCCESS if there are no errors.
        else:
            self.command_status.refresh_phase_severity(
                CommandPhaseType.RUN, CommandStatusType.SUCCESS)
コード例 #3
0
    def run_command(self):
        """
        Run the command. Create the GeoLayer with the input geometries. Add GeoLayer to the GeoProcessor's geolayers
         list.

        Returns: None.

        Raises:
            RuntimeError if any warnings occurred during run_command method.
        """

        # Obtain the parameter values.
        pv_NewGeoLayerID = self.get_parameter_value("NewGeoLayerID")
        pv_GeometryFormat = self.get_parameter_value("GeometryFormat").upper()
        pv_GeometryData = self.get_parameter_value("GeometryData")
        pv_CRS = self.get_parameter_value("CRS")

        if self.__should_geolayer_be_created(pv_NewGeoLayerID, pv_CRS,
                                             pv_GeometryFormat,
                                             pv_GeometryData):

            try:

                # If the geometry format is bounding box, continue.
                if pv_GeometryFormat == "BOUNDINGBOX":

                    # Convert the geometry input from a string to a list of strings.
                    # Items are in the following order:
                    #   1. Left (West) bound coordinate
                    #   2. Bottom (South) bound coordinate
                    #   3. Right (East) bound coordinate
                    #   4. Top (North) bound coordinate
                    NSWE_extents = string_util.delimited_string_to_list(
                        pv_GeometryData)
                    NW = "{} {}".format(NSWE_extents[0], NSWE_extents[3])
                    NE = "{} {}".format(NSWE_extents[2], NSWE_extents[3])
                    SE = "{} {}".format(NSWE_extents[2], NSWE_extents[1])
                    SW = "{} {}".format(NSWE_extents[0], NSWE_extents[1])
                    wkt_conversion = "POLYGON(({}, {}, {}, {}))".format(
                        NW, NE, SE, SW)

                    # Create the QgsVectorLayer. BoundingBox will always create a POLYGON layer.
                    layer = qgis_util.create_qgsvectorlayer(
                        "Polygon", pv_CRS, "layer")

                    # Create the QgsGeometry object for the bounding box geometry.
                    qgs_geometry = qgis_util.create_qgsgeometry(
                        "WKT", wkt_conversion)

                # If the geometry format is Well-Known Text, continue.
                elif pv_GeometryFormat == "WKT":

                    # Get the equivalent QGS geometry type to the input WKT geometry.
                    # Ex: MultiLineString is converted to LineString.
                    qgsvectorlayer_geom_type = qgis_util.get_geometrytype_qgis_from_wkt(
                        pv_GeometryData)

                    # Create the QgsVectorLayer. The geometry type will be determined from the WKT specifications.
                    layer = qgis_util.create_qgsvectorlayer(
                        qgsvectorlayer_geom_type, pv_CRS, "layer")

                    # Create the QgsGeometry object for the Well-Known Text geometry.
                    qgs_geometry = qgis_util.create_qgsgeometry(
                        "WKT", pv_GeometryData)

                # If the geometry format is Well-Known Binary, continue.
                elif pv_GeometryFormat == "WKB":

                    # Create the QgsGeometry object for the Well-Known Binary geometry.
                    qgs_geometry = qgis_util.create_qgsgeometry(
                        "WKB", pv_GeometryData)

                    # Get the equivalent Well-Known Text for the geometry.
                    qgs_geometry_as_wkt = qgs_geometry.exportToWkt()

                    # Get the equivalent QGS geometry type to the input WKT geometry.
                    # Ex: MultiLineString is converted to LineString.
                    qgsvectorlayer_geom_type = qgis_util.get_geometrytype_qgis_from_wkt(
                        qgs_geometry_as_wkt)

                    # Create the QgsVectorLayer. The geometry type will be determined from the WKB specifications.
                    layer = qgis_util.create_qgsvectorlayer(
                        qgsvectorlayer_geom_type, pv_CRS, "layer")

                # Add the feature (with the appropriate geometry) to the Qgs Vector Layer.
                qgis_util.add_feature_to_qgsvectorlayer(layer, qgs_geometry)

                # Create a new GeoLayer with the QgsVectorLayer and add it to the GeoProcesor's geolayers list.
                new_geolayer = GeoLayer(pv_NewGeoLayerID, layer, "MEMORY")
                self.command_processor.add_geolayer(new_geolayer)

            # Raise an exception if an unexpected error occurs during the process.
            except Exception as e:

                self.warning_count += 1
                message = "Unexpected error creating GeoLayer ({}).".format(
                    pv_NewGeoLayerID)
                recommendation = "Check the log file for details."
                self.logger.error(message, exc_info=True)
                self.command_status.add_to_log(
                    CommandPhaseType.RUN,
                    CommandLogRecord(CommandStatusType.FAILURE, message,
                                     recommendation))

        # Determine success of command processing. Raise Runtime Error if any errors occurred
        if self.warning_count > 0:
            message = "There were {} warnings proceeding this command.".format(
                self.warning_count)
            raise RuntimeError(message)

        # Set command status type as SUCCESS if there are no errors.
        else:
            self.command_status.refresh_phase_severity(
                CommandPhaseType.RUN, CommandStatusType.SUCCESS)
コード例 #4
0
    def run_command(self):
        """
        Run the command. Read the layer file from a GeoJSON file, create a GeoLayer object, and add to the
        GeoProcessor's geolayer list.

        Returns: None.

        Raises:
            RuntimeError if any warnings occurred during run_command method.
        """
        logger = logging.getLogger(__name__)

        # Obtain the parameter values.
        pv_SpatialDataFile = self.get_parameter_value("SpatialDataFile")
        pv_GeoLayerID = self.get_parameter_value("GeoLayerID", default_value='%f')

        # Expand for ${Property} syntax.
        pv_GeoLayerID = self.command_processor.expand_parameter_value(pv_GeoLayerID, self)

        # Convert the SpatialDataFile parameter value relative path to an absolute path and expand for ${Property}
        # syntax
        spatial_data_file_absolute = io_util.verify_path_for_os(
            io_util.to_absolute_path(self.command_processor.get_property('WorkingDir'),
                                     self.command_processor.expand_parameter_value(pv_SpatialDataFile, self)))

        # If the pv_GeoLayerID is a valid %-formatter, assign the pv_GeoLayerID the corresponding value.
        if pv_GeoLayerID in ['%f', '%F', '%E', '%P', '%p']:
            pv_GeoLayerID = io_util.expand_formatter(spatial_data_file_absolute, pv_GeoLayerID)

        # Run the checks on the parameter values. Only continue if the checks passed.
        if self.__should_read_geolayer(spatial_data_file_absolute, pv_GeoLayerID):

            try:
                # Create a QGSVectorLayer object with the GeoJSON SpatialDataFile.
                qgs_vector_layer = qgis_util.read_qgsvectorlayer_from_file(spatial_data_file_absolute)

                # Create a GeoLayer and add it to the geoprocessor's GeoLayers list.
                geolayer_obj = GeoLayer(geolayer_id=pv_GeoLayerID,
                                        geolayer_qgs_vector_layer=qgs_vector_layer,
                                        geolayer_source_path=spatial_data_file_absolute)
                self.command_processor.add_geolayer(geolayer_obj)

            # Raise an exception if an unexpected error occurs during the process.
            except Exception as e:
                self.warning_count += 1
                message = "Unexpected error reading GeoLayer {} from GeoJSON file {}.".format(pv_GeoLayerID,
                                                                                              pv_SpatialDataFile)
                recommendation = "Check the log file for details."
                self.logger.error(message, exc_info=True)
                self.command_status.add_to_log(CommandPhaseType.RUN,
                                               CommandLogRecord(CommandStatusType.FAILURE, message, recommendation))

        # Determine success of command processing. Raise Runtime Error if any errors occurred
        if self.warning_count > 0:
            message = "There were {} warnings proceeding this command.".format(self.warning_count)
            logger.error(message)
            raise RuntimeError(message)

        # Set command status type as SUCCESS if there are no errors.
        else:
            self.command_status.refresh_phase_severity(CommandPhaseType.RUN, CommandStatusType.SUCCESS)
    def run_command(self):
        """
        Run the command. Read the layer file from a delimited file, create a GeoLayer object, and add to the
        GeoProcessor's geolayer list.

        Returns: None.

        Raises:
            RuntimeError if any warnings occurred during run_command method.
        """

        # Obtain the parameter values.
        pv_DelimitedFile = self.get_parameter_value("DelimitedFile")
        pv_Delimiter = self.get_parameter_value("Delimiter", default_value=',')
        pv_GeometryFormat = self.get_parameter_value("GeometryFormat")
        pv_XColumn = self.get_parameter_value("XColumn", default_value=None)
        pv_YColumn = self.get_parameter_value("YColumn", default_value=None)
        pv_WKTColumn = self.get_parameter_value("WKTColumn",
                                                default_value=None)
        pv_CRS = self.get_parameter_value("CRS")
        pv_GeoLayerID = self.get_parameter_value("GeoLayerID",
                                                 default_value='%f')

        # Convert the DelimitedFile parameter value relative path to an absolute path and expand for ${Property}
        # syntax
        delimited_file_abs = io_util.verify_path_for_os(
            io_util.to_absolute_path(
                self.command_processor.get_property('WorkingDir'),
                self.command_processor.expand_parameter_value(
                    pv_DelimitedFile, self)))

        # If the pv_GeoLayerID is a valid %-formatter, assign the pv_GeoLayerID the corresponding value.
        if pv_GeoLayerID in ['%f', '%F', '%E', '%P', '%p']:
            pv_GeoLayerID = io_util.expand_formatter(delimited_file_abs,
                                                     pv_GeoLayerID)

        # Run the checks on the parameter values. Only continue if the checks passed.
        if self.__should_read_geolayer(delimited_file_abs, pv_Delimiter,
                                       pv_GeometryFormat, pv_XColumn,
                                       pv_YColumn, pv_WKTColumn, pv_CRS,
                                       pv_GeoLayerID):

            try:

                if pv_GeometryFormat.upper() == "XY":

                    # Create a QGSVectorLayer object with the delimited file.
                    qgs_vector_layer = qgis_util.read_qgsvectorlayer_from_delimited_file_xy(
                        delimited_file_abs, pv_Delimiter, pv_CRS, pv_XColumn,
                        pv_YColumn)

                else:
                    # Create a QGSVectorLayer object with the delimited file.
                    qgs_vector_layer = qgis_util.read_qgsvectorlayer_from_delimited_file_wkt(
                        delimited_file_abs, pv_Delimiter, pv_CRS, pv_WKTColumn)

                # Create a GeoLayer and add it to the geoprocessor's GeoLayers list.
                geolayer_obj = GeoLayer(pv_GeoLayerID, qgs_vector_layer,
                                        delimited_file_abs)
                self.command_processor.add_geolayer(geolayer_obj)

            # Raise an exception if an unexpected error occurs during the process.
            except Exception as e:
                self.warning_count += 1
                message = "Unexpected error reading GeoLayer {} from delimited file {}.".format(
                    pv_GeoLayerID, pv_DelimitedFile)
                recommendation = "Check the log file for details."
                self.logger.error(message, exc_info=True)
                self.command_status.add_to_log(
                    CommandPhaseType.RUN,
                    CommandLogRecord(CommandStatusType.FAILURE, message,
                                     recommendation))

        # Determine success of command processing. Raise Runtime Error if any errors occurred
        if self.warning_count > 0:
            message = "There were {} warnings proceeding this command.".format(
                self.warning_count)
            raise RuntimeError(message)

        # Set command status type as SUCCESS if there are no errors.
        else:
            self.command_status.refresh_phase_severity(
                CommandPhaseType.RUN, CommandStatusType.SUCCESS)
コード例 #6
0
    def run_command(self):
        """
        Run the command. Split the input GeoLayer by the selected Attribute. Create new GeoLayers based on
        unique attribute values.
        Returns:
            None.
        Raises:
            RuntimeError if any warnings occurred during run_command method.
        """

        # Obtain the parameter values.

        # @jurentie
        # 1. Parameter values are passed into the SplitGeoLayerByAttribute command editor -> GenericCommandEditor
        # 2. The SplitGEoLayerByAttribute command is updated when user changes input to parameter
        #    and parsed by AbstractCommand into parameter values and saved as command_parameters.
        # 3. Obtain parameter value by calling parent function get_parameter_value from AbstractCommand

        # Get the 'Input GeoLayerID' parameter
        pv_InputGeoLayerID = self.get_parameter_value("InputGeoLayerID")
        pv_AttributeName = self.get_parameter_value("AttributeName")

        # TODO jurentie 01/26/2019 Need to figure out how default should work in this case
        # @jurentie
        # I've commented out the below, this is specific to ClipGeoLayer, creating a default
        # value that makes sense for 'Value_splityBy_value' but we are looking for Output GeoLayerID's for this
        # specific command. Default might just need to be the name of the file's automatically output by
        # runAlgorithm("qgis:splitvectorlayer", alg_parameters) which can be handled down below...

        # pv_OutputGeoLayerIDs = self.get_parameter_value("OutputGeoLayerIDs", default_value="{}_splitBy_{}".format(
        #     pv_InputGeoLayerID, pv_AttributeName))

        # Get OutputGeoLayerID's and split on ',' to create an array of Output GeoLayerId's
        # ex OutputGeoLayerIDs = 'ouput1, output1, output3'
        # pv_OutputGeoLayerIDs = ['output1', 'output2', 'output3']
        try:
            pv_OutputGeoLayerIDs = self.get_parameter_value(
                "OutputGeoLayerIDs").split(',')
        except:
            # Get the list of features from the GeoLayer. This returns all attributes for each feature listed.
            pv_OutputGeoLayerIDs = None

        # Create logger
        logger = logging.getLogger(__name__)

        # Run the checks on the parameter values. Only continue if the checks passed.

        # @jurentie
        # The following line is currently commented out but needs to be added back in once
        # __should_split_geolayer is functioning properly...

        if self.check_command_input(pv_InputGeoLayerID, pv_AttributeName,
                                    pv_OutputGeoLayerIDs):

            try:

                # Get the Input GeoLayer.

                # Get the GeoLayer which will be QgsVectorLayer
                # https://qgis.org/api/classQgsVectorLayer.html
                # Passes a GeoLayerID to GeoProcessor to return the GeoLayer that matches the ID
                input_geolayer = self.command_processor.get_geolayer(
                    pv_InputGeoLayerID)

                logger.info('Input GeoLayer [GeoLayerID: ' +
                            pv_InputGeoLayerID +
                            '] has been read in successfully')

                # TODO jurentie 01/26/2019 still need to figure out what the code below is for...
                # If the input GeoLayer is an in-memory GeoLayer, make it an on-disk GeoLayer.
                if input_geolayer.source_path is None or input_geolayer.source_path.upper(
                ) in ["", "MEMORY"]:

                    # Get the absolute path of the GeoLayer to write to disk.
                    geolayer_disk_abs_path = os.path.join(
                        self.command_processor.get_property('TempDir'),
                        input_geolayer.id)
                    logger.info('GeoLayer path ' + geolayer_disk_abs_path)
                    # Write the GeoLayer to disk. Overwrite the (memory) GeoLayer in the geoprocessor with the
                    # on-disk GeoLayer.
                    input_geolayer = input_geolayer.write_to_disk(
                        geolayer_disk_abs_path)
                    self.command_processor.add_geolayer(input_geolayer)

                # Select the Attribute
                # NEED TO CREATE A 'input_geolayer.select_attribute' FUNCTION IN GEOLAYER.PY??
                # SOMETHING LIKE:  attribute_name = input_geolayer.select_attribute(pv_AttributeName)
                # OR SHOULD THE FOLLOWING JUST WORK?
                attribute_name = pv_AttributeName
                working_dir = self.command_processor.properties['WorkingDir']

                # @jurentie
                # TODO @jurentie 01/31/2019 How to handle absolute path
                # Assuming relative path for the moment....

                # Perform the QGIS split vector layer function. Refer to the reference below for parameter descriptions.
                # REF: https://docs.qgis.org/2.8/en/docs/user_manual/processing_algs/qgis/vector_general_tools.html#split-vector-layer

                # @jurentie
                # Check to see if parameter temporary folder has been specified, otherwise use the
                # default environment temp folder directory.

                #boolean to see if working with custom temp folder
                temp_directory_custom = False
                try:
                    # Append specified temporary folder to working directory to create temp files in current
                    # command file location.
                    temp_directory = working_dir + "/" + self.get_parameter_value(
                        "TemporaryFolder")
                    temp_directory_custom = True
                except:
                    # If using the default temp directory from environment variables create a temp folder to
                    # easily remove all files
                    temp_directory = tempfile.gettempdir()
                    temp_directory += "/qgissplitvectorlayer-outputfiles"
                # @jurentie
                # Assign parameter to pass into runAlgorithm for splitting the GeoLayer
                # Input = input GeoLayer (QgsVectorLayer)
                # Field = Attribute name to split by
                # Output = path to write output GeoLayers to this creates a list of files following the naming
                #          convention attributeName_attribute.extension
                #          ex: GNIS_ID_00030007.shp
                #          file types generated = .dbf, .prj, .qpj, .shp, .shx
                alg_parameters = {
                    "INPUT": input_geolayer.qgs_vector_layer,
                    "FIELD": attribute_name,
                    "OUTPUT": temp_directory
                }
                # @jurentie
                # call runAlgorithm with the parameter "qgis:splitvectorlayer" (a list of possible parameters
                # that can be passed here can be found here
                # https://gist.github.com/jurentie/7b6c53d5a592991b6bb2491fcc5f01eb)
                # pass in the parameters defined above
                # This should result in separate GeoLayer shapefiles being written to the OUTPUT directory
                split_output = self.command_processor.qgis_processor.runAlgorithm(
                    "qgis:splitvectorlayer", alg_parameters)

                # Create new GeoLayers and add them to the GeoProcessor's geolayers list.

                # @jurentie
                # TODO jurentie 01/26/2019 There probably needs to be some error handling happening below
                # Get the list of features from the GeoLayer. This returns all attributes for each feature listed.
                features = input_geolayer.qgs_vector_layer.getFeatures()
                # Set the extension for the filename's to get the geolayer from
                filename_extension = ".shp"
                # Parse through the list of features and also enumerate to get the index which
                # is used for accessing which OutputGeoLayerIDs to name each GeoLayer.
                # TODO jurentie 01/26/2019 need to decide what to do with a default OutputGeoLayerIDs
                # 1. Get the attribute of interest from each feature
                # TODO jurentie 01/26/2019 need to handle parsing out unique attributes only...
                # 2. Create the path name using the output folder specified in alg_parameters and passed in to
                #    split_output, and the naming convention defaults for qgis:splitvectorlayer
                # 3. Construct a QgsVectorLayer()
                #    Parameters:
                #       path: The path or url of the parameter. Typically this encodes parameters used by the data
                #       provider as url query items.
                #       baseName: The name used to represent the layer in the legend
                #       providerLib: The name of the data provider, e.g., "memory", "postgres"
                #       options: layer load options
                #    For more info see:
                #       https://qgis.org/api/classQgsVectorLayer.html#a1e7827a9d7bd33549babdc3bd7a279fd
                # 4. Construct a new GeoLayer from the QgsVectorLayer()
                #    Parameters:
                #       geolayer_id (str): String that is the GeoLayer's reference ID. This ID is used to access the
                #       GeoLayer from the GeoProcessor for manipulation.
                #       geolayer_qgs_vector_layer (QGSVectorLayer): Object created by the QGIS processor.
                #       All GeoLayer spatial manipulations are performed on the GeoLayer's qgs_vector_layer.
                #       geolayer_source_path (str): The full pathname to the original spatial data file on the
                #       user's local computer. If the geolayer was made in memory from the GeoProcessor, this value
                #       is set to `MEMORY`.
                # 5. Add the new GeoLayer to the GeoProcessor
                for i, feature in enumerate(features):
                    attribute = feature[attribute_name]
                    path = temp_directory + "/" + attribute_name + "_" + str(
                        attribute) + filename_extension
                    layer = QgsVectorLayer(path, "layer" + str(attribute),
                                           "ogr")
                    try:
                        new_geolayer = GeoLayer(pv_OutputGeoLayerIDs[i], layer,
                                                path)
                    except:
                        # Default Output GeoLayerID's will be default title of output files from .runAlgorithm above
                        new_geolayer = GeoLayer(
                            attribute_name + "_" + str(attribute), layer, path)
                    self.command_processor.add_geolayer(new_geolayer)

                # @jurentie
                # remove files if specified in parameters
                # TODO @jurentie figure out how to delete files after using them...
                # remove_files = self.get_parameter_value("RemoveTemporaryFiles")
                # files = glob.glob(temp_directory + "/*")
                # print(files)
                # if remove_files == None:
                #     # Remove all files from directory
                #     for f in files:
                #         os.remove(f)
                #     os.rmdir(temp_directory)

                # In QGIS 2 the clipped_output["OUTPUT"] returned the full file pathname of the memory output layer
                # (saved in a QGIS temporary folder)
                # qgs_vector_layer = qgis_util.read_qgsvectorlayer_from_file(clipped_output["OUTPUT"])
                # new_geolayer = GeoLayer(pv_OutputGeoLayerID, qgs_vector_layer, "MEMORY")
                # Get this list of ID's, name can be changed later to make more sense
                # in a dynamic fashion

                # In QGIS 3 the clipped_output["OUTPUT"] returns the QGS vector layer object
                # new_geolayer = GeoLayer(pv_OutputGeoLayerIDs, split_output["OUTPUT"], "MEMORY")
                # self.command_processor.add_geolayer(new_geolayer)

            # Raise an exception if an unexpected error occurs during the process
            except Exception as e:
                self.warning_count += 1
                message = "Unexpected error splitting GeoLayer {}.".format(
                    pv_InputGeoLayerID)
                recommendation = "Check the log file for details."
                self.logger.error(message, exc_info=True)
                self.command_status.add_to_log(
                    CommandPhaseType.RUN,
                    CommandLogRecord(CommandStatusType.FAILURE, message,
                                     recommendation))

        # Determine success of command processing. Raise Runtime Error if any errors occurred
        if self.warning_count > 0:
            message = "There were {} warnings proceeding this command.".format(
                self.warning_count)
            raise RuntimeError(message)

        # Set command status type as SUCCESS if there are no errors.
        else:
            self.command_status.refresh_phase_severity(
                CommandPhaseType.RUN, CommandStatusType.SUCCESS)
コード例 #7
0
    def run_command(self):
        """
        Run the command. Simplify the GeoLayer by the tolerance.

        Returns:  None.

        Raises:
            RuntimeError if any warnings occurred during run_command method.
        """

        # Obtain the parameter values.
        pv_GeoLayerID = self.get_parameter_value("GeoLayerID")
        pv_Tolerance = self.get_parameter_value("Tolerance")
        tolerance_float = float(pv_Tolerance)
        pv_SimplifyMethod = self.get_parameter_value(
            "SimplifyMethod", default_value="DouglasPeucker").upper()
        default_simple_id = "{}_simple_{}".format(pv_GeoLayerID, pv_Tolerance)
        pv_SimplifiedGeoLayerID = self.get_parameter_value(
            "SimplifiedGeoLayerID", default_value=default_simple_id)

        # Run the checks on the parameter values. Only continue if the checks passed.
        if self.__should_simplify_geolayer(pv_GeoLayerID,
                                           pv_SimplifiedGeoLayerID):

            try:

                # Get the GeoLayer.
                geolayer = self.command_processor.get_geolayer(pv_GeoLayerID)

                # If the GeoLayer is an in-memory GeoLayer, make it an on-disk GeoLayer.
                if geolayer.source_path is None or geolayer.source_path.upper(
                ) in ["", "MEMORY"]:

                    # Get the absolute path of the GeoLayer to write to disk.
                    geolayer_disk_abs_path = os.path.join(
                        self.command_processor.get_property('TempDir'),
                        geolayer.id)

                    # Write the GeoLayer to disk. Overwrite the (memory) GeoLayer in the geoprocessor with the
                    # on-disk GeoLayer.
                    geolayer = geolayer.write_to_disk(geolayer_disk_abs_path)
                    self.command_processor.add_geolayer(geolayer)

                # If the SimplifyMethod is DouglasPeucker, continue.
                if pv_SimplifyMethod == "DOUGLASPEUCKER":

                    # Perform the QGIS simplify geometries function. Refer to the REF below for parameter descriptions.
                    # REF: https://docs.qgis.org/2.8/en/docs/user_manual/processing_algs/qgis/
                    #       vector_geometry_tools/simplifygeometries.html
                    alg_parameters = {
                        "INPUT": geolayer.qgs_vector_layer,
                        "METHOD": 0,
                        "TOLERANCE": tolerance_float,
                        "OUTPUT": "memory:"
                    }
                    simple_output = self.command_processor.qgis_processor.runAlgorithm(
                        "native:simplifygeometries", alg_parameters)

                    # Create a new GeoLayer and add it to the GeoProcessor's geolayers list.
                    # in QGIS3, simple_output["OUTPUT"] returns the returns the QGS vector layer object
                    # see ClipGeoLayer.py for information about value in QGIS2 environment
                    new_geolayer = GeoLayer(pv_SimplifiedGeoLayerID,
                                            simple_output["OUTPUT"], "MEMORY")
                    self.command_processor.add_geolayer(new_geolayer)

            # Raise an exception if an unexpected error occurs during the process.
            except Exception as e:
                self.warning_count += 1
                message = "Unexpected error simplifying GeoLayer {}.".format(
                    pv_GeoLayerID)
                recommendation = "Check the log file for details."
                self.logger.error(message, exc_info=True)
                self.command_status.add_to_log(
                    CommandPhaseType.RUN,
                    CommandLogRecord(CommandStatusType.FAILURE, message,
                                     recommendation))

        # Determine success of command processing. Raise Runtime Error if any errors occurred
        if self.warning_count > 0:
            message = "There were {} warnings proceeding this command.".format(
                self.warning_count)
            raise RuntimeError(message)

        # Set command status type as SUCCESS if there are no errors.
        else:
            self.command_status.refresh_phase_severity(
                CommandPhaseType.RUN, CommandStatusType.SUCCESS)
コード例 #8
0
    def run_command(self):
        """
        Run the command. Read all spatial data files within the folder. For each desired spatial data file (can be
        specified by the Subset_Pattern parameter), create a GeoLayer object, and add to the GeoProcessor's geolayer
        list.

        Returns: None.

        Raises:
            RuntimeError if any warnings occurred during run_command method.
        """

        # Obtain the parameter values.
        pv_SpatialDataFolder = self.get_parameter_value("SpatialDataFolder")
        pv_Subset_Pattern = self.get_parameter_value("Subset_Pattern")
        pv_GeoLayerID_prefix = self.get_parameter_value("GeoLayerID_prefix")

        # Convert the SpatialDataFolder parameter value relative path to an absolute path
        sd_folder_abs = io_util.verify_path_for_os(
            io_util.to_absolute_path(self.command_processor.get_property('WorkingDir'),
                                     self.command_processor.expand_parameter_value(pv_SpatialDataFolder, self)))

        # Run the initial checks on the parameter values. Only continue if the checks passed.
        if self.__should_read_folder(sd_folder_abs):

            # Determine which files within the folder should be processed. All files will be processed if
            # pv_Subset_Pattern is set to None. Otherwise only files that match the given pattern will be processed.
            # Check that each file in the folder is:
            #   1. a file
            #   2. a spatial data file (ends in .shp or .geojson)
            #   3. follows the given pattern (if Subset_Pattern parameter value does not equal None)
            if pv_Subset_Pattern:
                spatial_data_files_abs = [os.path.join(sd_folder_abs, source_file)
                                          for source_file in glob.glob(os.path.join(sd_folder_abs, pv_Subset_Pattern))
                                          if os.path.isfile(os.path.join(sd_folder_abs, source_file))
                                          and (source_file.endswith(".shp") or source_file.endswith(".geojson"))]

            else:
                spatial_data_files_abs = [os.path.join(sd_folder_abs, source_file)
                                          for source_file in os.listdir(sd_folder_abs)
                                          if os.path.isfile(os.path.join(sd_folder_abs, source_file))
                                          and (source_file.endswith(".shp") or source_file.endswith(".geojson"))]

            # Iterate through the desired spatial data files
            for spatial_data_file_absolute in spatial_data_files_abs:

                # Determine the GeoLayerID.
                if pv_GeoLayerID_prefix:
                    geolayer_id = "{}_{}".format(pv_GeoLayerID_prefix,
                                                 io_util.expand_formatter(spatial_data_file_absolute, '%f'))
                else:
                    geolayer_id = io_util.expand_formatter(spatial_data_file_absolute, '%f')

                # Run the secondary checks on the parameter values. Only continue if the checks passed.
                if self.__should_read_geolayer(geolayer_id):

                    try:
                        # Create a QGSVectorLayer object with the GeoJSON SpatialDataFile
                        qgs_vector_layer = qgis_util.read_qgsvectorlayer_from_file(spatial_data_file_absolute)

                        # Create a GeoLayer and add it to the geoprocessor's GeoLayers list
                        geolayer_obj = GeoLayer(geolayer_id, qgs_vector_layer, spatial_data_file_absolute)
                        self.command_processor.add_geolayer(geolayer_obj)

                    # Raise an exception if an unexpected error occurs during the process
                    except Exception as e:
                        self.warning_count += 1
                        message = "Unexpected error reading GeoLayer {} from" \
                                  " file {}.".format(geolayer_id, spatial_data_file_absolute)
                        recommendation = "Check the log file for details."
                        self.logger.error(message, exc_info=True)
                        self.command_status.add_to_log(CommandPhaseType.RUN,
                                                       CommandLogRecord(CommandStatusType.FAILURE, message,
                                                                        recommendation))

        # Determine success of command processing. Raise Runtime Error if any errors occurred
        if self.warning_count > 0:
            message = "There were {} warnings proceeding this command.".format(self.warning_count)
            raise RuntimeError(message)

        # Set command status type as SUCCESS if there are no errors.
        else:
            self.command_status.refresh_phase_severity(CommandPhaseType.RUN, CommandStatusType.SUCCESS)
コード例 #9
0
    def run_command(self):
        """
        Run the command. Set the GeoLayer coordinate reference system.

        Returns: None.

        Raises:
            RuntimeError if any warnings occurred during run_command method.
        """

        # Obtain the parameter values.
        pv_GeoLayerID = self.get_parameter_value("GeoLayerID")
        pv_CRS = self.get_parameter_value("CRS")

        # Convert the pv_GeoLayerID parameter to expand for ${Property} syntax.
        pv_GeoLayerID = self.command_processor.expand_parameter_value(
            pv_GeoLayerID, self)

        # Run the checks on the parameter values. Only continue if the checks passed.
        if self.__should_set_crs(pv_GeoLayerID, pv_CRS):

            # Run the process.
            try:

                # Get the input GeoLayer.
                input_geolayer = self.command_processor.get_geolayer(
                    pv_GeoLayerID)

                # Check if the input GeoLayer already has an assigned CRS.
                if input_geolayer.get_crs():

                    # Reproject the GeoLayer.
                    alg_parameters = {
                        "INPUT": input_geolayer.qgs_vector_layer,
                        "TARGET_CRS": pv_CRS,
                        "OUTPUT": "memory:"
                    }
                    reprojected = self.command_processor.qgis_processor.runAlgorithm(
                        "native:reprojectlayer", alg_parameters)

                    # Create a new GeoLayer and add it to the GeoProcessor's geolayers list.

                    # In QGIS 2 the reprojected["OUTPUT"] returned the full file pathname of the memory output layer
                    # (saved in a QGIS temporary folder)
                    # qgs_vector_layer = qgis_util.read_qgsvectorlayer_from_file(reprojected["OUTPUT"])
                    # new_geolayer = GeoLayer(input_geolayer.id, qgs_vector_layer, "MEMORY")

                    # In QGIS 3 the reprojected["OUTPUT"] returns the QGS vector layer object
                    new_geolayer = GeoLayer(input_geolayer.id,
                                            reprojected["OUTPUT"], "MEMORY")
                    self.command_processor.add_geolayer(new_geolayer)

                else:
                    alg_parameters = {
                        "INPUT": input_geolayer.qgs_vector_layer,
                        "CRS": pv_CRS
                    }
                    self.command_processor.qgis_processor.runAlgorithm(
                        "qgis:definecurrentprojection", alg_parameters)

            # Raise an exception if an unexpected error occurs during the process
            except Exception as e:
                self.warning_count += 1
                message = "Unexpected error setting CRS ({}) of GeoLayer ({})".format(
                    pv_CRS, pv_GeoLayerID)
                recommendation = "Check the log file for details."
                self.logger.error(message, exc_info=True)
                self.command_status.add_to_log(
                    CommandPhaseType.RUN,
                    CommandLogRecord(CommandStatusType.FAILURE, message,
                                     recommendation))

        # Determine success of command processing. Raise Runtime Error if any errors occurred
        if self.warning_count > 0:
            message = "There were {} warnings proceeding this command.".format(
                self.warning_count)
            raise RuntimeError(message)

        # Set command status type as SUCCESS if there are no errors.
        else:
            self.command_status.refresh_phase_severity(
                CommandPhaseType.RUN, CommandStatusType.SUCCESS)