Using a python toolbox for automation

663
5
Jump to solution
08-23-2021 06:41 AM
Labels (1)
TobiasPlischke
New Contributor II

Hello there.

I'm currently trying to set up a python toolbox in order to automate the process of updating .csv-files, importing them, setting up the layers in the .gdb-file and finally colorizing the polygons.
If i paste the raw code (without the layout the toolbox needs) all methods work as desired but as soon as i fill the python toolbox with my methods and call them in the execute method, some of arcpy's methods don't seem to work properly.

The update of the .gdb-file seems to work in this toolbox, the TableToTable-conversion is the first one to fail. The table doesn't get imported into ArcGIS, which leads to problems later on when it comes to the colorization. The reason why i use arcpy.TableToTable_conversion() is because otherwise it wouldn't let me use these methods at all.

I am fairly new to ArcGIS/Arcpy and these toolboxes but i think the reason behind this could be the nonexisting access to some database/libraries..?

It just bothers me that everything works perfectly fine in the python window but doesn't work the same way when using the toolbox.

BTW: The goal is to be able to execute a single tool, pasting the code everytime should be avoided.

Let me know if you need more information and thanks in advance!

from arcgis.gis import GIS
import arcpy


class businessLogic(object):
    global gdbPath
    gdbPath = r"D:\example\test.gdb"
    global featureClass
    featureClass = r"Kreisgrenzen_2020\polygons"

    def updateGDB(self):
        arcpy.Delete_management(gdbPath)
        arcpy.CreateFileGDB_management(r"D:\example", "test.gdb")
        lyr_Kreisgrenzen = r"D:\example\Kreisgrenzen_2020.lyrx"
        arcpy.mp.LayerFile(lyr_Kreisgrenzen)
        aprx = arcpy.mp.ArcGISProject("CURRENT")
        aprxMap = aprx.listMaps("Karte")[0]
        aprxMap.addDataFromPath(lyr_Kreisgrenzen)

        arcpy.AddMessage(fr"{gdbPath}")

    def setLayers(self):
        arcpy.env.workspace = gdbPath
        arcpy.TableToTable_conversion(r"D:\example\qryexport01.csv", gdbPath,
                                      "name")
        arcpy.FeatureClassToFeatureClass_conversion(featureClass, gdbPath, "Kreisgrenzen_name")
        arcpy.CreateRelationshipClass_management("name", fr"{gdbPath}\Kreisgrenzen_name",
                                                 "joined_layer_name", "SIMPLE", "polygons", "name",
                                                 "NONE", "ONE_TO_MANY", "NONE", "ags_name", "ags", '', '')
        arcpy.AddJoin_management("Kreisgrenzen_name", "AGS", "name", "ags_name", "KEEP_ALL")
        arcpy.FeatureClassToFeatureClass_conversion(f"Kreisgrenzen_name", gdbPath, "Faelle_name")


def parameter(displayName, name, datatype, defaultValue=None, parameterType='Optional', direction='Input'):
    # create parameter with a few defaults
    param = arcpy.Parameter(
        displayName=displayName,
        name=name,
        datatype=datatype,
        parameterType=parameterType,
        direction=direction)

    # set new parameter to a default value
    param.value = defaultValue

    # return the complete parameter object
    return param


class Toolbox(object):
    def __init__(self):
        self.label = 'Toolbox'
        self.alias = ''

        # List of tool classes associated with this toolbox
        self.tools = [Tool1]


class Tool1(object):

    def __init__(self):
        self.label = 'Tool'
        self.canRunInBackground = True

        self.parameters = [
            parameter('username', 'un', 'GPString', '', 'Required'),
            parameter('password', 'pw', 'GPStringHidden', '', 'Required'),
        ]

    def execute(self, parameters, messages):
        instance = businessLogic()
        instance.updateGDB()
        instance.setLayers()
        return

 

0 Kudos
1 Solution

Accepted Solutions
HannesZiegler
Esri Contributor

If I'm understanding correctly, you'd like to see the intermediate and result data added to the map?

I think the issue is this: Python Window will add the results of a geoprocessing tool to the map, but when the same code is run in a tool only the output parameter(s) (if specified) is added to the map. So while your code works for Python Window, when it is run in a tool, the expectation that the intermediate data (like that produced on line 25) will be available in the map/contents is no longer valid. You'll need to define an output parameter.

Can you try this (this is not the full solution but might get you unstuck):

1) Define another parameter, make it an output parameter

param1 = arcpy.Parameter(
            name='outjoin',
            direction='Output',
            datatype='GPFeatureLayer',
            parameterType='Required'
        )

2) Collect that parameter in your execute method and pass it on to your setLayers method

outjoin = parameters[1].valueAsText

instance = businessLogic()
instance.updateGDB()
instance.setLayers(outjoin)

3) Add the new output parameter to your setLayers method and update as follows:

def setLayers(self, outjoin):
        arcpy.env.workspace = gdbPath
        arcpy.TableToTable_conversion(r"D:\example\qryexport01.csv", gdbPath,
                                      "name")
        arcpy.FeatureClassToFeatureClass_conversion(featureClass, gdbPath, "Kreisgrenzen_name")
        arcpy.CreateRelationshipClass_management("name", fr"{gdbPath}\Kreisgrenzen_name",
                                                 "joined_layer_name", "SIMPLE", "polygons", "name",
                                                 "NONE", "ONE_TO_MANY", "NONE", "ags_name", "ags", '', '')
        joined = arcpy.AddJoin_management("Kreisgrenzen_name", "AGS", "name", "ags_name", "KEEP_ALL")
        arcpy.FeatureClassToFeatureClass_conversion(joined, gdbPath, outjoin)

 

Or you could use the same approach you used in the updateGDB method to add data to your map.

table = arcpy.TableToTable_conversion(r"D:\example\qryexport01.csv", gdbPath, "name")
aprx = arcpy.mp.ArcGISProject("CURRENT")
aprxMap = aprx.listMaps()[0]
aprxMap.addDataFromPath(table)

 

View solution in original post

5 Replies
HannesZiegler
Esri Contributor

It looks like you are relying on layers produced while working in a Python Window session.

This can't be relied on when running a script or script tool.

For example, under your setLayers function you have arcpy.AddJoin_management("Kreisgrenzen_name", ... which relies on "Kreisgrenzen_name" to be present in the map view/contents pane, but it likely isn't there since this is produced in an intermediate step above. Furthermore, AddJoin requires a layer so you can't simply join to the "Kreisgrenzen_name" because FeatureClassToFeatureClass_conversion returns a feature class in this context. You'll probably want to explicitly add that result to the map with an additional step and then subsequently operate on that instance of the layer.

Hope that helps!

--edit 8/25/2021--

Regarding strike-through text, see TobiasPlischke response below. It is also false that "Kreisgrenzen_name" could not be used by name, because arcpy.env.workspace is set, which provides access to that data by name. Sorry about the oversight.

0 Kudos
TobiasPlischke
New Contributor II

Thanks for your answer!

I think my explanation was a bit confusing: I tried both ways (python window & python toolbox) completely separately. In addition to that i tried to outsource it to a standalone python script so i won't have to open ArcGIS at all. Unfortunately i couldn't access the feature layer from outside, that's why i need a solution in ArcGIS.

Maybe i'll give you some more detail about the steps:

  1. Normally, TableToTable_conversion() should result in importing the .csv-file to the current project and also showing it being added on the left sidebar. This is the first step which isn't working with the python toolbox, even though it fills the .gdb-file with data during these few steps.
  2. arcpy.conversion.FeatureClassToFeatureClass(in_features, out_path, out_name, {where_clause}, {field_mapping}, {config_keyword}) converts the given feature-layer into the featureclass "Kreisgrenzen_name"
  3. arcpy.management.CreateRelationshipClass(origin_table, destination_table, out_relationship_class, relationship_type, forward_label, backward_label, message_direction, cardinality, attributed, origin_primary_key, origin_foreign_key, {destination_primary_key}, {destination_foreign_key}) establishes a connection between the imported table and the created feature class.
  4. arcpy.management.AddJoin(in_layer_or_view, in_field, join_table, join_field, {join_type}) again connects the 2 existing layers to my desired final layer. 

According to https://pro.arcgis.com/en/pro-app/latest/tool-reference/data-management/add-join.htm a feature class is allowed as input.

If the input is a feature class or dataset path, this tool will automatically create and return a new layer with the result of the tool applied.

Since you suggested to add the layer explicitly beforehand, do you have certain methods in mind or was that just a general proposal? I still have no clue why it works in the python window, but not in the toolbox.

Thanks in advance, Tobido

0 Kudos
HannesZiegler
Esri Contributor

If I'm understanding correctly, you'd like to see the intermediate and result data added to the map?

I think the issue is this: Python Window will add the results of a geoprocessing tool to the map, but when the same code is run in a tool only the output parameter(s) (if specified) is added to the map. So while your code works for Python Window, when it is run in a tool, the expectation that the intermediate data (like that produced on line 25) will be available in the map/contents is no longer valid. You'll need to define an output parameter.

Can you try this (this is not the full solution but might get you unstuck):

1) Define another parameter, make it an output parameter

param1 = arcpy.Parameter(
            name='outjoin',
            direction='Output',
            datatype='GPFeatureLayer',
            parameterType='Required'
        )

2) Collect that parameter in your execute method and pass it on to your setLayers method

outjoin = parameters[1].valueAsText

instance = businessLogic()
instance.updateGDB()
instance.setLayers(outjoin)

3) Add the new output parameter to your setLayers method and update as follows:

def setLayers(self, outjoin):
        arcpy.env.workspace = gdbPath
        arcpy.TableToTable_conversion(r"D:\example\qryexport01.csv", gdbPath,
                                      "name")
        arcpy.FeatureClassToFeatureClass_conversion(featureClass, gdbPath, "Kreisgrenzen_name")
        arcpy.CreateRelationshipClass_management("name", fr"{gdbPath}\Kreisgrenzen_name",
                                                 "joined_layer_name", "SIMPLE", "polygons", "name",
                                                 "NONE", "ONE_TO_MANY", "NONE", "ags_name", "ags", '', '')
        joined = arcpy.AddJoin_management("Kreisgrenzen_name", "AGS", "name", "ags_name", "KEEP_ALL")
        arcpy.FeatureClassToFeatureClass_conversion(joined, gdbPath, outjoin)

 

Or you could use the same approach you used in the updateGDB method to add data to your map.

table = arcpy.TableToTable_conversion(r"D:\example\qryexport01.csv", gdbPath, "name")
aprx = arcpy.mp.ArcGISProject("CURRENT")
aprxMap = aprx.listMaps()[0]
aprxMap.addDataFromPath(table)

 

TobiasPlischke
New Contributor II

I've tried both ways and both worked for me. Thank you very much!

0 Kudos
HannesZiegler
Esri Contributor

Glad to be able to help!

0 Kudos