Using Model Builder, how to define directory while using a Python script?

1870
10
Jump to solution
08-13-2014 12:08 PM
LanceWilson
New Contributor III

I have a model (shown below) in which at the very end of it I run a Python script ('SumFieldInsertNew'). One issue I'm having is that I'm creating this model for an end-user that will have little to no knowledge of most things computer-wise. Ergo, I'm trying to make it as hassle-free and flexible as possible. My question is, I want the user to be able to name the file the script will run itself on, but am unsure of how to do something like this. So essentially, I want to make 'Empty FC' a parameter in which the user can decide what they want their file to be named. Then have the Python script directory code (where it's defining the feature class), know that whatever the user named it is conditional to it being able to run its processes. I appreciate any help and suggestions, thank you!

oNhpE.jpg

My model runs as follows:

1. It establishes a connection to an Oracle dB

2. It creates a Query Layer based on the pre-filled 'Query' parameter
3. It creates an empty feature class (Empty FC) into a pre-filled GdB parameter
4. It then exectures the 'Copy Features' tool on the Query Layer and outputs the copy into the Empty FC
5. This newly populated feature class is then given a new field, called 'SUM_Quantity_Solid'

6. In which lastly, this new field is populated via the Python script 'SUM_FIELD_INSERT_NEW', which (as seen below), SUMs up a field and then inserts the values.


  import arcpy > 
  # Define the feature class
    fc = r'User_Defined_Path'
 
  # find the unique 'id' values
    Slist = list() for row in arcpy.da.SearchCursor(fc, 'id'):
     # if the value isn't in the list then add it to the list
     if not row[0] in Slist:
         Slist.append(row[0])
 
 for Value in Slist:
     # definition query to limit the rows in the cursor
    DefQ = 'id = ' + str(Value)
 
     # Use a generator expression to populate a list from the 'QUANTITY_SOLID' field
     b = sum(row[0] for row in arcpy.da.SearchCursor(fc, 'QUANTITY_SOLID',DefQ))
 
     with arcpy.da.UpdateCursor(fc, ['SUM_Quantity_Solid'],DefQ) as cursor:
         for row in cursor:
             row[0] = b
             cursor.updateRow(row)








As referenced, I would like to be able to have

fc = r'User_Defined_Path'

be what the user initially defined the 'EMPTY FC' filename parameter as.

0 Kudos
1 Solution

Accepted Solutions
curtvprice
MVP Esteemed Contributor

To use the Calculate Value tool, instead of a script tool, you set things up as a function that reads your input feature class path from a model element, for example:

Expression:

f(r"%SUM_Quantity_Solid%")

The "r" is important because the path will probably have backslashes in it.

Code block:

import arcpy

def f(fc)

    # find the unique 'SEGMENT_LENGTH' values

    Slist = list() for row in arcpy.da.SearchCursor(fc, 'SEGMENT_LENGTH'):

    # if the value isn't in the list then add it to the list

    if not row[0] in Slist:

        Slist.append(row[0])

    for Value in Slist:

        # definition query to limit the rows in the cursor

        DefQ = 'SEGMENT_LENGTH = ' + str(Value)

        # Use a generator expression to populate a list from the 'QUANTITY_SOLID' field

        b = sum(row[0] for row in arcpy.da.SearchCursor(fc, 'QUANTITY_SOLID'),DefQ)

        with arcpy.da.UpdateCursor(fc, ['QUANTITY_SOLID_SUM'],DefQ) as cursor:

            for row in cursor:

                row[0] = b

                cursor.updateRow(row)

    return fc

UPDATE: one more thing - you need to set up your input fc as a precondition to your Calculate Value tool to make sure the fc is ready to be processed when the Calculate Value runs.

If you want to go the python script route you need to set up the script tool with parameters as described in the desktop help. If you do this the parameters will pop up and connect in ModelBuilder just like any other tool.

ArcGIS Help (10.2, 10.2.1, and 10.2.2)

View solution in original post

10 Replies
RiyasDeen
Occasional Contributor III

Hi Lance,

Create a String parameter for feature class name and use it in your "Create Feature Class" process as "Feature Class Name" to create an empty feature class.

Set input parameter for your script tool of type feature class

Untitled.png

Access the input parameter in your script line 3 as

fc = str(sys.argv[1])

LanceWilson
New Contributor III

Riyas,

     Thank you very much for your help! I haven't had a chance to try your solution yet but will do so. Can you please explain to me what the 'sys.argv' is referencing?

0 Kudos
RiyasDeen
Occasional Contributor III

sys.argv[1] - will return you the value of first parameter. In my above example it'll return the value set for the parameter Source.

0 Kudos
curtvprice
MVP Esteemed Contributor

You could use the Calculate Value tool to do this, providing the feature class input as an argument to a function. This avoids the overhead and hassle of setting up a script tool.

LanceWilson
New Contributor III

@ Curtis -

Could you please expound on this a little bit? I apologize I'm just trying to wrap my head around the thought process behind this. As you can see in my model picture below, I have no clue what to do next lol... Thank you!

test.jpg

0 Kudos
curtvprice
MVP Esteemed Contributor

To use the Calculate Value tool, instead of a script tool, you set things up as a function that reads your input feature class path from a model element, for example:

Expression:

f(r"%SUM_Quantity_Solid%")

The "r" is important because the path will probably have backslashes in it.

Code block:

import arcpy

def f(fc)

    # find the unique 'SEGMENT_LENGTH' values

    Slist = list() for row in arcpy.da.SearchCursor(fc, 'SEGMENT_LENGTH'):

    # if the value isn't in the list then add it to the list

    if not row[0] in Slist:

        Slist.append(row[0])

    for Value in Slist:

        # definition query to limit the rows in the cursor

        DefQ = 'SEGMENT_LENGTH = ' + str(Value)

        # Use a generator expression to populate a list from the 'QUANTITY_SOLID' field

        b = sum(row[0] for row in arcpy.da.SearchCursor(fc, 'QUANTITY_SOLID'),DefQ)

        with arcpy.da.UpdateCursor(fc, ['QUANTITY_SOLID_SUM'],DefQ) as cursor:

            for row in cursor:

                row[0] = b

                cursor.updateRow(row)

    return fc

UPDATE: one more thing - you need to set up your input fc as a precondition to your Calculate Value tool to make sure the fc is ready to be processed when the Calculate Value runs.

If you want to go the python script route you need to set up the script tool with parameters as described in the desktop help. If you do this the parameters will pop up and connect in ModelBuilder just like any other tool.

ArcGIS Help (10.2, 10.2.1, and 10.2.2)

XanderBakker
Esri Esteemed Contributor

I wonder if the code is correct. Seems to me there are a few strange things going on. On the next line there are two brackets missing:

b = sum(row[0] for row in arcpy.da.SearchCursor(fc, 'QUANTITY_SOLID'),DefQ)

should probably be:

b = sum([row[0] for row in arcpy.da.SearchCursor(fc, ('QUANTITY_SOLID'),DefQ)])

First to create a list of unique values in a field you can do that on a single line like this:

# find the unique 'SEGMENT_LENGTH' values, maybe use SHAPE@LENGTH instead?

lst_seglen = list(set([row[0] for row in arcpy.da.SearchCursor(fc, ('SEGMENT_LENGTH'))]))

I would probably do it differently and use a dictionary to store the unique length vs the sum of the Quantity Solid. Next a simple update cursor to store the results.

def f(fc):

    dct = {}

    flds = ('SEGMENT_LENGTH', 'QUANTITY_SOLID')

    with arpy.da.SearchCursor(f, flds) as curs:

        for row in curs:

            if row[0] in dct:

                dct[row[0]] += row[1]

            else:

                dct[row[0]] = row[1]

    flds = ('SEGMENT_LENGTH', 'QUANTITY_SOLID_SUM')

    with arcpy.da.UpdateCursor(fc, flds) as curs:

        for row in curs:

            if row[0] in dct:

                row[1] = dct[row[0]]

            else:

                row[1] = 0 # should not occur

            cursor.updateRow(row)

  return fc

It still sounds strange to look for unique segment lengths and sum values for each unique length. Normally you see this the other way (sum length for a unique value)...

LanceWilson
New Contributor III

@ Xander Bakker -

    I made a mistake and posted an earlier version of the script, it is supposed to be '(fc, 'QUANTITY_SOLID',DefQ))' -

but otherwise the code works as I need it to, although It may very well not be the most efficient way possible.

     As for it 'sounding strange', I have a particular reason for looking for a unique segment length and wanting to SUM up the 'QUANTITY_SOLID' values in order to get a total. This is because each unique segment length corresponds to a particular road segment, whereas there is no other column in which to identify it by such other than the length (as all of the same road segments will be the exact length).

0 Kudos
XanderBakker
Esri Esteemed Contributor

Hi Lance,

As long as you know what you need, there is no problem whatsoever on summarizing on unique length.

Good luck, Xander