class DiscrertizeFields(ArcTool): """ """ implements(IGISTool) id = 0 def __init__(self, parameters, service, *args, **kwargs): ArcTool.__init__(self, parameters, service, *args, **kwargs) DiscrertizeFields.id = DiscrertizeFields.id + 1 self.name = self.__class__.__name__ # Identifier is a combination of the name and the class counter self.id = (self.name, DiscrertizeFields.id) self.log = ArcLogger("Zupport.%s" % (self.__class__.__name__), debugging=True) self.log.debug(msgInitStart) # Tool needs spatial analyst in order to run # TODO: this should be moved to ToolValidator try: self.register_extension("spatial") except LicenseError, e: self.log.error(e) return 1 self.service = service self.log.debug(msgInitSuccess)
class DiscrertizeFields(ArcTool): """ """ implements(IGISTool) id = 0 def __init__(self, parameters, service, *args, **kwargs): ArcTool.__init__(self, parameters, service, *args, **kwargs) DiscrertizeFields.id = DiscrertizeFields.id + 1 self.name = self.__class__.__name__ # Identifier is a combination of the name and the class counter self.id = (self.name, DiscrertizeFields.id) self.log = ArcLogger("Zupport.%s" % (self.__class__.__name__), debugging=True) self.log.debug(msgInitStart) # Tool needs spatial analyst in order to run # TODO: this should be moved to ToolValidator try: self.register_extension('spatial') except LicenseError, e: self.log.error(e) return 1 self.service = service self.log.debug(msgInitSuccess)
class CrossSelect(ArcTool): implements(IGISTool) """ ArcTool for batch selecting pixels from rasters in workspace based on a single raster. Tool works on a set of rasters in a workspace and uses a reference raster's unique values to select pixels from target rasters. For example, if reference raster would represent land use classes [1, 2, 3] and rasters A and B would have continuous values ArcCrossSelect would create 6 new rasters -> 1 / land use class / value raster. (0) Reference raster [refraster] - String representing reference raster based on which the selection will be made (required) (1) Input workspace [inworkspace] - String representing the source workspace that contains value rasters (required) (2) Output workspace [outworkspace] - String for the location of the resulting rasters (required) (3) Include values [include] - List holding values in the reference raster to include (optional, default: None) (4) Exclude values [exclude] - List holding values in the reference raster to exclude (optional, default: None) (5) Raster format [raster_type] - String for the output raster format (optional, default: "img") (6) Output name tag [tag] - String to tag the the output names. Will be placed between the raster name body and value identifier. tag = kp -> A_kp1.img """ id = 0 def __init__(self, parameters, service, *args, **kwargs): ArcTool.__init__(self, parameters, service, *args, **kwargs) CrossSelect.id = CrossSelect.id + 1 self.id = CrossSelect.id self.name = self.__module__.split('.')[-1] self.log = ArcLogger("Zupport.%s" % (self.__class__.__name__), debugging=True) self.log.debug(msgInitStart) # Tool needs spatial analyst in order to run # TODO: this should be moved to ToolValidator try: self.register_extension('spatial') except LicenseError, e: self.log.error(e) return 1 self.service = service self.log.debug(msgInitSuccess)
class ArcTool(Tool): """ Class to represent a generic ArcGIS scripting tool. This class is intended mostly to be subclassed and provides methods for getting the input parameters for the Geoprocessor (GP). ArcGIS must be present in the system. Class should not be instantiated, but instead used as a super class. Class and its derivatives have three user cases: 1. Subclasses are instantiated *from within ArcGIS* (Toolbox): the GP will get all the provided parameters and it is up to the subclasses to handle them correctly. 2. Subclasses are instantiated *directly from command line*: the GP will get all the provided parameters and it is up to the subclasses to handle them correctly. 3. Subclasses are instantiated *from elsewhere in code*: the parameters provided by the Geoprocessor will not be what expected. Setting the workspace is the responsibility of subclasses. """ def __init__(self, parameters, service, *args, **kwargs): """Constructor initializes the Geoprocessor using :mod:`arcgisscripting` and loops thorugh all the available parameters in the Geoprocessor. Will raise an exception if problems occur with the Geoprocessor. Parameters are intended to be used in subclasses. Parameters: parameters - parameters object holding parameter information """ Tool.__init__(self, parameters) self._backend = 'arcgis' # TODO: handle the ArcGIS versions correctly try: # Inialize the logger for this tool. Logging system must be set up # before doing thishis self.logger = ArcLogger("Zupport.ArcTool", debugging=True) self.logger.debug('[%s] Acquiring geoprocessor' % service) self.gp = get_geoprocessor(10) # Set the scratch workspace explicitly to ESRI default self.gp.ScratchWorkspace = os.path.join(os.path.expanduser('~'), 'AppData', 'Local', 'Temp') except ImportError, e: self.logger.error('ArcGIS not present in the system.') sys.exit(1) except:
class CalculateRasterGroup(ArcRasterTool): """ ArcTool for doing arithmetics on multiple rasters with NoData. Normally NoData propagates with summation (value + NoData = NoData), subtraction, multiplication and converting NoData to a value (e.g. 0) is not desirable in order not to mix NoData with real values. ArcCalculateRasterGroup works with a set of rasters (defined by a naming convention) and builds a result raster where NoData values are preserved and value cells are used in a simple raster algebra operation. Parameters: (0) Input workspace [inws] - String input workspace holding the rasters (required) (1) Output workspace [outws] - String path to output workspace (required) (2) Operator [operator] - String defining which raster algebra operation is applied to rasters (required) (3) Group template [template] - String used to define which rasters are grouped into a set (optional); syntax is based on key identifiers: name_body_<ID> where name body is a common sub string shared by all rasters in the same group and <ID> is identifies rasters within group (4) Group identifier [identifier] - String identifying template tag that is/are used as a basis for grouping (optional); (5) Reference table [reftable] - DataFrame object used for lookup (optional); this lookup table can be used for extra grouping not apparent from the naming convention. <ID2> is used for lookup link from <ID1>. For example index_PUULAJI_11.img with template <BODY1>_<BODY2>_<ID2> would match column "PUULAJI" with value 11 to a grouping variable in reference group [refgroup] (6) Reference fields [reffields] - Tuple defining the field mapping in the dataframe. ("A", "B") would match values from <ID1> in field A to a value in field "B". """ implements(IGISTool) id = 0 def __init__(self, parameters, service, *args, **kwargs): ArcRasterTool.__init__(self, parameters, service, *args, **kwargs) CalculateRasterGroup.id = CalculateRasterGroup.id + 1 self.name = self.__class__.__name__ # Identifier is a combination of the name and the class counter self.id = (self.name, CalculateRasterGroup.id) self.log = ArcLogger("Zupport.%s" % (self.__class__.__name__), debugging=True) self.service = service self.log.debug(msgInitSuccess) def __del__(self): if CalculateRasterGroup: CalculateRasterGroup.id = CalculateRasterGroup.id - 1 def run(self): try: self.validate_parameters() self.workspace = str(self.get_parameter(0)) self.log.debug('Found %s rasters in workspace %s' % (len(self.files), self.workspace)) wildcard = self.get_parameter(1) template = self.get_parameter(2) grouptags = self.get_parameter(3) operator = parse_operator(self.get_parameter(4)) outws = self.get_parameter(5) # These will be None if not provided reftable = self.get_parameter(6) reffields = self.get_parameter(7) self.log.debugging = bool(self.get_parameter(8)) # Use mapping reffields[0] -> reffields[1] from reftable fields = reftable.get_fields() if reffields[0] not in fields: raise ParameterError( "Provided reference field %s not found in refernce table %s" % (reffields[0], reftable)) if reffields[1] not in fields: raise ParameterError( "Provided reference field %s not found in refernce table %s" % (reffields[1], reftable)) mapping = {} for value in reftable.get_all_values(reffields[0]): # 9999 is missing value if value == 9999: continue match_row = reftable.where_field_equal(reffields[0], value) mapping[value] = getattr(match_row, reffields[1]) # Parse the raster files in the workspace based on the group tags rasteriterator = ArcRasterGroupIterator(self, self.workspace, wildcard, template) rasteriterator.set_grouping(grouptags, mapping) # Calculate the job queue length based on provided options. If no # identifying field is used, the length of rasters defines the job # queue length job_length = len(rasteriterator) # Set the tool progressor if self.log.gui: self.log.setProgressor("Summing multiple rasters...", max=job_length) # Iterate over the parsed workspace. Remember that group is a list # of ParsedFileNames for group_id, group in rasteriterator.iteritems(): self.log.debug('Group %s (%s) has %s rasters' % (int(group_id), reffields[1], len(group))) # Each raster within the group is summed to a common result # raster -> each group has one output raster # Check that body identifiers are ok name_bodies = [raster.body for raster in group] if not all([item == name_bodies[0] for item in name_bodies]): self.log.progressor( 'Multiple name bodies found, defaulting to %s' % (name_bodies[0]), log='warning') if group[0].extension: extension = group[0].extension else: extension = "" output_zero = os.path.join( outws, '%s_%s_%s%s%s' % (group[0].get_tag('BODY1'), int(group_id), group[0].get_tag('BODY3'), "TMP", extension)) cmds = [] for raster in group: cmds.append( "self.gp.sa.Con(self.gp.sa.IsNull('%s'), 0, '%s')" % (raster, raster)) cmd = operator.join(cmds) self.log.info('Summing rasters in group %s' % int(group_id)) #res = self.gp.sa.SingleOutputMapAlgebra(cmd, output_zero) op1 = eval(cmd) op1.save(output_zero) self.log.debug('Reclassifying NoData for group %s' % int(group_id)) output = output_zero.replace('TMP', '') cmd = 'self.gp.sa.SetNull(%s == 0)' % output_zero op2 = self.gp.sa.SetNull(output_zero, output_zero, "VALUE <= 0") op2.save(output) self.gp.Delete_management(output_zero) if self.log.gui: self.log.setProgressorPosition() return 1 except Exception, e: self.log.exception(e) return 1 self.log.info('Finished running tool %s' % self.name) return 0
class CalculateRasterGroup(ArcRasterTool): """ ArcTool for doing arithmetics on multiple rasters with NoData. Normally NoData propagates with summation (value + NoData = NoData), subtraction, multiplication and converting NoData to a value (e.g. 0) is not desirable in order not to mix NoData with real values. ArcCalculateRasterGroup works with a set of rasters (defined by a naming convention) and builds a result raster where NoData values are preserved and value cells are used in a simple raster algebra operation. Parameters: (0) Input workspace [inws] - String input workspace holding the rasters (required) (1) Output workspace [outws] - String path to output workspace (required) (2) Operator [operator] - String defining which raster algebra operation is applied to rasters (required) (3) Group template [template] - String used to define which rasters are grouped into a set (optional); syntax is based on key identifiers: name_body_<ID> where name body is a common sub string shared by all rasters in the same group and <ID> is identifies rasters within group (4) Group identifier [identifier] - String identifying template tag that is/are used as a basis for grouping (optional); (5) Reference table [reftable] - DataFrame object used for lookup (optional); this lookup table can be used for extra grouping not apparent from the naming convention. <ID2> is used for lookup link from <ID1>. For example index_PUULAJI_11.img with template <BODY1>_<BODY2>_<ID2> would match column "PUULAJI" with value 11 to a grouping variable in reference group [refgroup] (6) Reference fields [reffields] - Tuple defining the field mapping in the dataframe. ("A", "B") would match values from <ID1> in field A to a value in field "B". """ implements(IGISTool) id = 0 def __init__(self, parameters, service, *args, **kwargs): ArcRasterTool.__init__(self, parameters, service, *args, **kwargs) CalculateRasterGroup.id = CalculateRasterGroup.id + 1 self.name = self.__class__.__name__ # Identifier is a combination of the name and the class counter self.id = (self.name, CalculateRasterGroup.id) self.log = ArcLogger("Zupport.%s" % (self.__class__.__name__), debugging=True) self.service = service self.log.debug(msgInitSuccess) def __del__(self): if CalculateRasterGroup: CalculateRasterGroup.id = CalculateRasterGroup.id - 1 def run(self): try: self.validate_parameters() self.workspace = str(self.get_parameter(0)) self.log.debug('Found %s rasters in workspace %s' % (len(self.files), self.workspace)) wildcard = self.get_parameter(1) template = self.get_parameter(2) grouptags = self.get_parameter(3) operator = parse_operator(self.get_parameter(4)) outws = self.get_parameter(5) # These will be None if not provided reftable = self.get_parameter(6) reffields = self.get_parameter(7) self.log.debugging = bool(self.get_parameter(8)) # Use mapping reffields[0] -> reffields[1] from reftable fields = reftable.get_fields() if reffields[0] not in fields: raise ParameterError("Provided reference field %s not found in refernce table %s" % (reffields[0], reftable)) if reffields[1] not in fields: raise ParameterError("Provided reference field %s not found in refernce table %s" % (reffields[1], reftable)) mapping = {} for value in reftable.get_all_values(reffields[0]): # 9999 is missing value if value == 9999: continue match_row = reftable.where_field_equal(reffields[0], value) mapping[value] = getattr(match_row, reffields[1]) # Parse the raster files in the workspace based on the group tags rasteriterator = ArcRasterGroupIterator(self, self.workspace, wildcard, template) rasteriterator.set_grouping(grouptags, mapping) # Calculate the job queue length based on provided options. If no # identifying field is used, the length of rasters defines the job # queue length job_length = len(rasteriterator) # Set the tool progressor if self.log.gui: self.log.setProgressor("Summing multiple rasters...", max=job_length) # Iterate over the parsed workspace. Remember that group is a list # of ParsedFileNames for group_id, group in rasteriterator.iteritems(): self.log.debug('Group %s (%s) has %s rasters' % (int(group_id), reffields[1], len(group))) # Each raster within the group is summed to a common result # raster -> each group has one output raster # Check that body identifiers are ok name_bodies = [raster.body for raster in group] if not all([item == name_bodies[0] for item in name_bodies]): self.log.progressor('Multiple name bodies found, defaulting to %s' % (name_bodies[0]), log='warning') if group[0].extension: extension = group[0].extension else: extension = "" output_zero = os.path.join(outws, '%s_%s_%s%s%s' % (group[0].get_tag('BODY1'), int(group_id), group[0].get_tag('BODY3'), "TMP", extension)) cmds = [] for raster in group: cmds.append("self.gp.sa.Con(self.gp.sa.IsNull('%s'), 0, '%s')" % (raster, raster)) cmd = operator.join(cmds) self.log.info('Summing rasters in group %s' % int(group_id)) #res = self.gp.sa.SingleOutputMapAlgebra(cmd, output_zero) op1 = eval(cmd) op1.save(output_zero) self.log.debug('Reclassifying NoData for group %s' % int(group_id)) output = output_zero.replace('TMP', '') cmd = 'self.gp.sa.SetNull(%s == 0)' % output_zero op2 = self.gp.sa.SetNull(output_zero, output_zero, "VALUE <= 0") op2.save(output) self.gp.Delete_management(output_zero) if self.log.gui: self.log.setProgressorPosition() return 1 except Exception, e: self.log.exception(e) return 1 self.log.info('Finished running tool %s' % self.name) return 0
class Aggregate(ArcTool): """ ArcTool for aggregating data to desired resolution while preserving true NoData values. Tool will create new subfolders into the output workspace according to target resolution if they do not exist. Parameters: (1) Input raster [inraster] String representing the source workspace that contains rasters to be aggregated (required) (2) Output raster [outraster] String for the location of the resulting rasters (required) (3) Cell factor [factors] Integer cell factor (multiplier) to be used in the aggregation. Original cell size will be taken from the rasters in the input workspace. (required) (4) Nodata mode [nodata] Boolean defining whether NoData mode is used. True -> resulting aggregated rasters will have NoData False -> resulting aggregated rasters will not have NoData (required, default: True) (5) Extent [extent] String containing 'minX minY maxX maxY' that will be passed on to Arcpy.Extent class. (optional) (6) Mask (mask) String representing raster that will be used as a mask (optional) (7) Raster type [raster_type] String file extension for output raster type (optional) """ implements(IGISTool) id = 0 def __init__(self, parameters, service, *args, **kwargs): ArcTool.__init__(self, parameters, service, *args, **kwargs) Aggregate.id = Aggregate.id + 1 self.name = self.__class__.__name__ # Identifier is a combination of the name and the class counter self.id = (self.name, Aggregate.id) self.log = ArcLogger("Zupport.%s" % (self.__class__.__name__), debugging=True) self.log.debug(msgInitStart) # Tool needs spatial analyst in order to run # TODO: this should be moved to ToolValidator try: self.register_extension('spatial') except LicenseError, e: self.log.error(e) return 1 self.service = service self.log.debug(msgInitSuccess)