How to get user defined field values (multi-criteria, arcpy)?

924
3
10-27-2018 08:26 AM
NinaKrašovec
New Contributor

Hello!

I am currently involved in the project where we are creating a tool to assess flood risk using the multi-criteria analysis. We would like to give the user the option to decide on his/her own weights for each input. E.g. one of the inputs is geology (vector data) and we would like to make a script in a way that the user, after selecting the geology input, gets all of the geology classes from the attribute table and then chooses the weight for each geology class (the possible weights would be: 1,2,5,8 or 10).

This part of code is currently hardcoded and therefore the script is applicable only for our specific region:

arcpy.AddField_management('geologyDiss', 'weight', 'SHORT', 0)
try:
    with arcpy.da.UpdateCursor('geologyDiss',('geolS_eng','weight')) as cursor:
        for row in cursor:
            if row [0] == 'clay slate, marl, breccia, sandstone, conglomerate, chert':
                row[1] = 8
            elif row[0] == 'dolomite':
                row[1] = 2
            elif row[0] == 'flysch':
                row[1] = 10
            elif row[0] == 'igneous rock (diabase, spilite, porphyry, pyroclastics, keratophyre)':
                row[1] = 10
            elif row[0] == 'limestone, limestone with dolomite':
                row[1] = 1
            elif row[0] == 'moraine sediment':
                row[1] = 5
            elif row[0] == 'rivers and stream deposit, diluvium':
                row[1] = 5
            else:
               row[1] = 2
            cursor.updateRow(row)
        del row
        del cursor
except Exception as e:
    print e.message
0 Kudos
3 Replies
RandyBurton
MVP Alum

Are you creating a script tool or Python toolbox?  Both have an updateParameters function that can help with creating a choice list.  This blog Generating a choice list from a field discusses programming the validator in a script tool.

And to help me understand your project...

In your list, 'dolomite' has a weight of 2.  Would you want the user to select 'dolomite' and give it a weight other than 2?

Do you want the user to be able to select from other features?  And if so, are they similarly structured (same field layout)?

NinaKrašovec
New Contributor

We are creating a script tool and the idea is that:

  1. The user puts the geology data for his study area.
  2. Then the user gets the list of his geology classes and on the right side he/she could choose 1,2,5,8 or 10 (=the weight) for each class.
  3. The chosen weights are saved in a new field “weight”.

In our case there is some dolomite on our study area and we weighted it with a 2, but the user can have other classes and this is why we do not want to hardcode it.

0 Kudos
RandyBurton
MVP Alum

Here are some sample python toolboxes that might give you some ideas for your project.  The first is basically a python toolbox version of the script tool described in the blog post mentioned in my previous post.  I have added a dropdown for the weight values.  Select a feature layer or feature class; select a field in that feature; choose an attribute and select the weight.

import arcpy


class Toolbox(object):
    def __init__(self):
        """Define the toolbox (the name of the toolbox is the name of the
        .pyt file)."""
        self.label = "Select Value"
        self.alias = "selection"

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


class SelectValue(object):
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Select Value"
        self.description = ""
        self.canRunInBackground = False

        self.fcfield = (None, None) # global to remember field selection

    def getParameterInfo(self):
        """Define parameter definitions"""

        # First parameter 
        inFeature = arcpy.Parameter(
            displayName="Input Features",
            name="inFeature",
            datatype=["DEFeatureClass","GPFeatureLayer"],
            parameterType="Required",
            direction="Input")

        # Second parameter 
        fieldName = arcpy.Parameter(
            displayName="Field Name",
            name="fieldName",
            datatype="Field",
            parameterType="Required",
            direction="Input")
 
        fieldName.parameterDependencies = [inFeature.name]
        fieldName.filter.list = ["Short", "Long", "Double", "Float", "Text"]

        # Third parameter 
        fldProperty = arcpy.Parameter(
            displayName="Property",
            name="fldProperty",
            datatype="String",
            parameterType="Required",
            direction="Input")

        fldProperty.parameterDependencies = [inFeature.name, fieldName.name]

        # Fourth parameter 
        weightVal = arcpy.Parameter(
            displayName="Weight",
            name="weightVal",
            datatype="String",
            parameterType="Required",
            direction="Input")

        weightVal.filter.list = [ 1, 2, 5, 8, 10 ]
        weightVal.value = weightVal.filter.list[0]


        params = [inFeature, fieldName, fldProperty, weightVal]
        return params

    def isLicensed(self):
        """Set whether tool is licensed to execute."""
        return True

    def updateParameters(self, parameters):
        """Modify the values and properties of parameters before internal
        validation is performed.  This method is called whenever a parameter
        has been changed."""
        
        if parameters[0].value and parameters[1].value: # get first field and value list
            fc, col = parameters[0].valueAsText, parameters[1].valueAsText
            desc = arcpy.Describe(parameters[0])
            if desc.dataType == 'FeatureLayer': # if feature layer, use catalog path
                fc = desc.catalogPath  
            if self.fcfield != (fc, col):
                self.fcfield = (fc, col)
                parameters[2].filter.list = [str(val) for val in sorted(set(row[0] for row in arcpy.da.SearchCursor(fc,col)))]
            if parameters[2].value not in parameters[2].filter.list:
                parameters[2].value = parameters[2].filter.list[0]

        return

    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter.  This method is called after internal validation."""
        return

    def execute(self, parameters, messages):
        """The source code of the tool."""

        inFeature = parameters[0].valueAsText
        fieldName = parameters[1].valueAsText
        fldProperty = parameters[2].valueAsText
        weightVal = parameters[3].valueAsText

        messages.addMessage(
            "\nInput Feature: {}".format(inFeature))

        messages.addMessage(
            "\nSelected Field: {}".format(fieldName))
        
        messages.addMessage(
            "\nWeight Value: {}".format(weightVal))

        messages.addMessage(
            "\nField Property: {}".format(fldProperty))

        desc = arcpy.Describe(parameters[0])

        messages.addMessage(
            "\nDataType: {}\nPath: {}".format(desc.dataType, desc.catalogPath))

        return
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

The second reads domain information into a list for the dropdown. In this example, the domain is hard-coded to a specific domain in a specific database (lines 32-33).  It could be modified to allow the user to select the database and domain name.

import arcpy, operator

def getDomain(gdb, domainName):
    domDict = {} # empty dictionary
    domains = arcpy.da.ListDomains(gdb)
    for domain in domains: # assumes domainName references a coded value domain
        if domain.name == domainName:
            coded_values = domain.codedValues
            for val, desc in coded_values.items():
                domDict[desc] = val # use { desc: val, ... } for dropdown menu
    return domDict


class Toolbox(object):
    def __init__(self):
        """Define the toolbox (the name of the toolbox is the name of the
        .pyt file)."""
        self.label = "Toolbox"
        self.alias = ""

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


class Tool(object):
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Tool"
        self.description = ""
        self.canRunInBackground = False

        self.domain = getDomain(r'C:\Path\to\file.gdb',
                               'DomainName')  # domain description: domain code

    def getParameterInfo(self):
        """Define parameter definitions"""

        domValue = arcpy.Parameter(
            displayName = "Select Domain",
            name = "domValue",
            datatype = "GPString",
            parameterType = "Required",
            direction = "Input")

        domValue.filter.type = "ValueList"
        # set filter list; descriptions sorted in code order
        domValue.filter.list = [x[0] for x in sorted(self.domain.items(), key=operator.itemgetter(1))]
        domValue.value = domValue.filter.list[0] # default first item in list

        return [domValue]
    
    def isLicensed(self):
        """Set whether tool is licensed to execute."""
        return True

    def updateParameters(self, parameters):
        """Modify the values and properties of parameters before internal
        validation is performed.  This method is called whenever a parameter
        has been changed."""
        return

    def updateMessages(self, parameters):
        """Modify the messages created by internal validation for each tool
        parameter.  This method is called after internal validation."""
        return

    def execute(self, parameters, messages):
        """The source code of the tool."""

        messages.addMessage('Selected: {} -- Code: {}'.format(parameters[0].value, self.domain[parameters[0].value])) 

        return
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Hope this gives you some ideas.