2

I've created the following field calculation that I'd like to convert to a python script tool:

def blocklow(a, b, c, d):
  mylist = [a, b, c, d]  # create list from input range variables
  if mylist == [0, 0, 0, 0]:
    m = 0
  else:
    m = min(i for i in mylist if i > 0)  # find minimum value greater than zero
  if m > 0 and m < 99: 
    return 10 # value for minimum block number between 1-99
  elif m == 0:
    return 0
  else:
    return int(math.floor(m / 100.0)) * 100 # round minimum to lowest hundred

esri_field_calculator_splitter blocklow( !LLO!, !LHI!, !RLO!, !RHI!)

The gist of it is there are four address range fields (integer type) and I'm after the lowest numeric value, rounded down to an even hundred, that isn't zero. It works, although I'm sure there is room for improvement.

enter image description here

I'd like to convert this to a script tool. I've done it semi-successfully defining a parameter for the feature class and field to be calculated. However, if add parameters for the input fields of the calculation and then try to reference those in the function, it fails. Also, I'm using the arcpy.CalculateField_management approach but calling a function by as a string kind of sucks. I'm sure there's a more elegant and efficient way.

Here's what the script tool should look like:

enter image description here

I'm really a rookie at best.

Update: here's the script I'm working on. I'm still off somewhere (probably everywhere). Any ideas:

import arcpy
arcpy.env.overwriteOutput = True

try:
    inCenterline = arcpy.GetParameterAsText(0)
    inBlockF = arcpy.GetParameterAsText(1)
    inLF = arcpy.GetParameterAsText(2)
    inLT = arcpy.GetParameterAsText(3)
    inRF = arcpy.GetParameterAsText(4)
    inRT = arcpy.GetParameterAsText(5)
    expression = "blocklow(inLF, inLT, inRF, inRT)"
    codeblock = """import math
    def blocklow(a, b, c, d):
        mylist = [a, b, c, d]  # create list from input range variables
        if mylist == [0, 0, 0, 0]:
            m = 0
        else:
            m = min(i for i in mylist if i > 0)  # find minimum value greater than zero
        if m > 0 and m < 99: 
            return 10 # value for minimum block number between 1-99
        elif m == 0:
            return 0
        else:
            return int(math.floor(m / 100.0)) * 100 # round minimum to lowest hundred"""

    arcpy.CalculateField_management(inCenterline, inBlockF, "blocklow(inLF, inLT, inRF, inRT)", "PYTHON_9.3", codeblock)
    arcpy.AddMessage("Calculation complete")

except:
    arcpy.AddError("Could not complete the calculation")

    arcpy.AddMessage(arcpy.GetMessages())

UPDATE: Here's the finished script and tool based on the example and direction that Michael provided.

# ---------------------------------------------------------------------------
# Calculate_Addr_Blocks_Tool.py
# Author: Josse Allen: Product Instructor, Spillman Technologies, Inc
# Original creation date: 2015-09-15
# Version: beta
# Revisions:
#   Revision notes:
# Description: this script is used with an ArcGIS script tool.  The tool
# evaluates the from-to/left-right address range values for each row in the
# centerline, finds the lowest value, and rounds DOWN to the LOWEST hundred.
#
# Platform: developed using Python 2.6, ArcGIS Desktop 10.0 - Basic
#
# Input requirements:
#   - Input centerline (containing from-to/left-right address range fields
#   - To/from address range fields for left/right sides (4 total, short or long)
#
# ---------------------------------------------------------------------------

import arcpy
# The output result will be calculated in the input!
arcpy.env.overwriteOutput = True

# Prelogic used to pass through for calculating the low block number

CodeBlockL = """
def blocklow(a, b, c, d):
  mylist = [a, b, c, d]  # create list from input range variables
  if mylist == [0, 0, 0, 0]:
    m = 0
  else:
    m = min(i for i in mylist if i > 0)  # find minimum value greater than     zero
  if m > 0 and m < 99: 
    return 10 # value for minimum block number between 1-99
  elif m == 0:
    return 0
  else:
    return int(math.floor(m / 100.0)) * 100 # round minimum to lowest hundred
""" # Finished Code block

# Prelogic used to pass through for calculating the high block number

CodeBlockH = """
def blockhigh(a, b, c, d):
  h = max(a, b, c, d)  # find high value from input range variables
  if h == 0:
    return 0
  else:
    return int(math.ceil(h / 100.0)) * 100 - 1 # round to highest hundred less 1
""" # Finished Code block

try:
    # Set input parameters
    inCenterline = arcpy.GetParameterAsText(0)  # Source layer containing the address range fields
    inLF = arcpy.GetParameterAsText(1)  # Script tool parameter for left from address value
    inLT = arcpy.GetParameterAsText(2)  # Script tool parameter for left to address value
    inRF = arcpy.GetParameterAsText(3)  # Script tool parameter for right from address value
    inRT = arcpy.GetParameterAsText(4)  # Script tool parameter for right to address value

    # Set variables for fields to be added

    field_nameL = "BLOCKLO" # Field name to be created for the low block value
    field_nameH = "BLOCKHI" # Field name to be created for the high block value

    # create fields for output values
    arcpy.AddMessage("Adding fields for block low and high values")
    arcpy.AddField_management(inCenterline, field_nameL, "LONG", "", "", "", "", "NULLABLE", "NON_REQUIRED", "")
    arcpy.AddField_management(inCenterline, field_nameH, "LONG", "", "", "", "", "NULLABLE", "NON_REQUIRED", "")

    # Calculate the block low field
    expressionL = "blocklow(!{0}!, !{1}!, !{2}!, !{3}!)".format(inLF, inLT, inRF, inRT)
    arcpy.AddMessage("Calculating the low block value...")
    arcpy.CalculateField_management(inCenterline, field_nameL, expressionL, "PYTHON_9.3", CodeBlockL)
    arcpy.AddMessage("Low block calculation complete")

    # Calculate the block high field
    expressionH = "blockhigh(!{0}!, !{1}!, !{2}!, !{3}!)".format(inLF, inLT, inRF, inRT)
    arcpy.AddMessage("Calculating the high block value...")
    arcpy.CalculateField_management(inCenterline, field_nameH, expressionH, "PYTHON_9.3", CodeBlockH)
    arcpy.AddMessage("High block calculation complete")

except:
    arcpy.AddError("Could not complete the calculation")

    arcpy.AddMessage(arcpy.GetMessages())

enter image description here

enter image description here

  • Have a read of this gis.stackexchange.com/questions/93591/… about taking a multiline function and using it in field calculator... as I see it you need surrond the block with """ and it's good to go. Use your existing code as a block and then call it.. – Michael Stimson Sep 15 '15 at 22:19
  • @MichaelMiles-Stimson - I gave your suggestion a try - best I could apply it - but I'm still missing something. It's probably a terrible hack at best. I'm trying to post the code but can't figure out how to do that... give me a moment. Appreciate your response. – Hoss Sep 16 '15 at 0:07
  • 2
    If you want to keep it as Calculate Field, run it normally and then under the Results window, right click the result and copy to Python snippet. – Paul Sep 16 '15 at 0:10
  • Try doing it in a model, then when you get that to work export to python script to see how to code/indent/format the advanced syntax. I have used this approach before when the help docs weren't so helpful and I just needed to see how it converted... once you know how to format it properly you shouldn't need to go back to model builder.... ever. I like your approach @Paul - I've not done that before but it looks like a quicker way. – Michael Stimson Sep 16 '15 at 0:11
  • 1
    Put the exclamation marks in the string using a sting format like "!" + inLT + "!" or "!{0}!".format(inLT) should do it, the exclamation marks are still needed in the string so that the parser knows it's a parameter. – Michael Stimson Sep 16 '15 at 0:57
1

You were so close at getting it right... As per one of my previous comments the exclamation marks need to be there so:

"blocklow(inLF, inLT, inRF, inRT)"

needs to change to:

"blocklow(!{0}!, !{1}!, !{2}!, !{3}!)".format(inLF, inLT, inRF, inRT)

This is python string manipulation where the values in the format statement get substituted into the string, so let's assume the fields are called A,B,C and D respectively python expands this at runtime to:

>>> inLF = "A"
>>> inLT = "B"
>>> inRF = "C"
>>> inRT = "D"
>>> print "blocklow(!{0}!, !{1}!, !{2}!, !{3}!)".format(inLF, inLT, inRF, inRT)
blocklow(!A!, !B!, !C!, !D!)

which is how CalculateField wants it, the exclamation marks are required in the expression string to tell the parser that they're field names and not a constant value.

Putting that all together into a cohesive script:

CodeBlock = """
def blocklow(a, b, c, d):
  mylist = [a, b, c, d]  # create list from input range variables
  if mylist == [0, 0, 0, 0]:
    m = 0
  else:
    m = min(i for i in mylist if i > 0)  # find minimum value greater than zero
  if m > 0 and m < 99: 
    return 10 # value for minimum block number between 1-99
  elif m == 0:
    return 0
  else:
    return int(math.floor(m / 100.0)) * 100 # round minimum to lowest hundred
""" # Finished Code block

import arcpy
arcpy.env.overwriteOutput = True

try:
    inCenterline = arcpy.GetParameterAsText(0)
    inBlockF = arcpy.GetParameterAsText(1)
    inLF = arcpy.GetParameterAsText(2)
    inLT = arcpy.GetParameterAsText(3)
    inRF = arcpy.GetParameterAsText(4)
    inRT = arcpy.GetParameterAsText(5)
    expression = "blocklow(!{0}!, !{1}!, !{2}!, !{3}!)".format(inLF, inLT, inRF, inRT)

    arcpy.CalculateField_management(inCenterline, inBlockF, expression, "PYTHON_9.3", CodeBlock)
    arcpy.AddMessage("Calculation complete")

except:
    arcpy.AddError("Could not complete the calculation")

    arcpy.AddMessage(arcpy.GetMessages())
  • 1
    Awesome!! You have no idea how victorious I'm feeling right now - well, you probably do. Thank you! I don't have the rep to vote up your response or I would. Hopefully others will. I have some tweaks to improve it, but your answer gave me a great example and your explanation behind it is much appreciated. Having the code is great, but being enabled to break it down so we can adjust later is quite helpful. Thanks again! – Hoss Sep 16 '15 at 23:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.