Select to view content in your preferred language

Python Toolbox can't match input parameter to dictionary key in dictionary.get() function

1266
2
Jump to solution
10-25-2022 04:01 PM
Labels (2)
elmort
by
New Contributor II
I am writing a Python Toolbox for a parcel prioritization tool. My input data (a set of 11 rasters) is organized into a dictionary, raster_dict, where keys are raster names (i.e. 'Important Bird Areas') and values are raster file names (i.e. importantbirdareas.tif).

 

To create drop-down menus in the tool GUI, in getParameterInfo function I have used the parameter.filter.list() function on a list of the keys from my raster dictionary. The user selects multiple raster inputs from the list of raster names.

In the execute function, I convert the multiple user input into a list using parameters[0].valueAsText.split(";"), which creates a list of raster names. I create an empty list for paths to my raster files, then iterate through the names in the raster name list, passing each raster name into the raster_dict.get() function to retrieve the corresponding raster file name in the dictionary. I concatenate the raster file name to the end of the path to the folder where the raster file is stored, and then append each path to the raster paths list. The list of paths is the input for the rest of my script (a raster calculation, zonal stats, and spatial join).

The tool fails at the concatenate step:

File "", line 107, in execute TypeError: can only concatenate str (not "NoneType") to str

After debugging, I found that the problem is with passing my raster names into the raster_dict.get() function. raster_dict.get() is returning nothing, meaning there is no “match” between the dictionary keys and the raster names passed to the get() function. I have tested the code in IDLE (replacing the parameters[0].valueAsText with hard-coded values), and it runs as expected. I have also used Messages in the Python Toolbox to check that the variable I am passing to the get() function is a string that matches one of the keys in raster_dict.

I have also tried rewriting the code without dictionaries, using lists instead. When I try to match the input parameter(s) to a value in the list (for example: if raster_name in raster_name_list), the code still fails, not recognizing the matching values.

The error occurs in the second line of the following block:

for key in raster_names:
   raster_path = "F:\\GIS\\SEALT_prioritization\\Rasters\\" + raster_dict.get(key)
   rasterList.append(raster_path)

Here is my complete .pyt:

import arcpy
from arcpy.sa import *
from arcpy.ia import *
import os

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

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

class PrioritizeParcels(object):
    def __init__(self):
        """Define the tool (tool name is the name of the class)."""
        self.label = "Prioritize Parcels"
        self.description = "This tool accepts multiple raster layer inputs and sums the overlaid pixels Then it averages the pixel values within the polygons of a designated feature class."
        self.canRunInBackground = False

    def getParameterInfo(self):
        """Define parameter definitions"""
        input_rasters = arcpy.Parameter(
            name="input_rasters",
            displayName="Select input datasets",
            datatype="GPString",
            parameterType="Required",
            direction="Input",
            multiValue=True)

        raster_dict={"Freshwater forested shrub wetland":'Freshwater_Forested_Shrub_Wetland.tif', "Anadromous waters":
                    'MergedBuffer_300ft_AWC.tif', "Eelgrass":'MergedBuffer_300ft_Eelgrass.tif', "Estuarine and marine wetlands":
                     'MergedBuffer_300ft_Estuarine_Marine_Wetlands.tif', "Public Lands": 'MergedBuffer_300ft_PublicLands.tif',
                     "Rivers and streams":'MergedBuffer_300ft_Rivers_Streams.tif', "Lakes and ponds":'MergedBuffer_300ft_Waterbodies.tif',
                     "Freshwater emergent wetlands":'MergedFreshwater_Emergent_Wetland.tif', "Important Bird Areas":
                     'mergedibas', "Productive old growth forest":'mergedpog', "Protected areas":'protected'}
        input_rasters.filter.type="ValueList"
        input_rasters.filter.list= list(raster_dict.keys())
            
        in_parcels = arcpy.Parameter(
            displayName="Select parcels to be scored",
            name="in_parcels",
            datatype="GPString",
            parameterType="Required",
            direction="Input")
            #,multiValue=True)

        service_area_dict={"Yakutat": 'YakutatParcels', "Wrangell": 'WrangellParcels', "Sitka": 'SitkaParcels',
                            "Petersburg":'Petersburg_Lots', "Ketchikan": 'KetchikanParcels', "Juneau":'JuneauParcels',
                            "Haines":'HainesParcels', "Skagway": 'SkagwayParcels'}
        in_parcels.filter.type="ValueList"
        in_parcels.filter.list= list(service_area_dict.keys())

        out_path = arcpy.Parameter(
            name="out_path",
            displayName="Save results as",
            datatype= "GPString",
            parameterType="Required",
            direction="Output")
            

        parameters = [input_rasters, in_parcels, out_path]
        return parameters
  

    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."""

        #arcpy.env.workspace = r"F:\GIS\SEALT_prioritization\Rasters"
        arcpy.env.overwriteOutput = True

        raster_dict={"Freshwater forested shrub wetland":'Freshwater_Forested_Shrub_Wetland.tif', "Anadromous waters":
                    'MergedBuffer_300ft_AWC.tif', "Eelgrass":'MergedBuffer_300ft_Eelgrass.tif', "Estuarine and marine wetlands":
                     'MergedBuffer_300ft_Estuarine_Marine_Wetlands.tif', "Public Lands": 'MergedBuffer_300ft_PublicLands.tif',
                     "Rivers and streams":'MergedBuffer_300ft_Rivers_Streams.tif', "Lakes and ponds":'MergedBuffer_300ft_Waterbodies.tif',
                     "Freshwater emergent wetlands":'MergedFreshwater_Emergent_Wetland.tif', "Important Bird Areas":
                     'mergedibas', "Productive old growth forest":'mergedpog', "Protected areas":'protected'}

        service_area_dict={"Yakutat": 'YakutatParcels', "Wrangell": 'WrangellParcels', "Sitka": 'SitkaParcels',
                            "Petersburg":'Petersburg_Lots', "Ketchikan": 'KetchikanParcels', "Juneau":'JuneauParcels',
                            "Haines":'HainesParcels', "Skagway": 'SkagwayParcels'}

        raster_names=parameters[0].valueAsText.split(";")
        rasterList = []

        for key in raster_names:
            raster_path = "F:\\GIS\\SEALT_prioritization\\Rasters\\" + raster_dict.get(key)
            rasterList.append(raster_path)

        rasterCount = len(rasterList)

        if rasterCount == 11:
            ras1 = arcpy.Raster(rasterList[0])
            ras2 = arcpy.Raster(rasterList[1])
            ras3 = arcpy.Raster(rasterList[2])
            ras4 = arcpy.Raster(rasterList[3])
            ras5 = arcpy.Raster(rasterList[4])
            ras6 = arcpy.Raster(rasterList[5])
            ras7 = arcpy.Raster(rasterList[6])
            ras8 = arcpy.Raster(rasterList[7])
            ras9 = arcpy.Raster(rasterList[8])
            ras10 = arcpy.Raster(rasterList[9])
            ras11 = arcpy.Raster(rasterList[10])
            outRaster = ras1 + ras2 + ras3 + ras4 + ras5 + ras6 + ras7 + ras8 + ras9 + ras10 + ras11

        elif rasterCount == 10:
            ras1 = arcpy.Raster(rasterList[0])
            ras2 = arcpy.Raster(rasterList[1])
            ras3 = arcpy.Raster(rasterList[2])
            ras4 = arcpy.Raster(rasterList[3])
            ras5 = arcpy.Raster(rasterList[4])
            ras6 = arcpy.Raster(rasterList[5])
            ras7 = arcpy.Raster(rasterList[6])
            ras8 = arcpy.Raster(rasterList[7])
            ras9 = arcpy.Raster(rasterList[8])
            ras10 = arcpy.Raster(rasterList[9])
            outRaster = ras1 + ras2 + ras3 + ras4 + ras5 + ras6 + ras7 + ras8 + ras9 + ras10
  
        elif rasterCount == 9:
            ras1 = arcpy.Raster(rasterList[0])
            ras2 = arcpy.Raster(rasterList[1])
            ras3 = arcpy.Raster(rasterList[2])
            ras4 = arcpy.Raster(rasterList[3])
            ras5 = arcpy.Raster(rasterList[4])
            ras6 = arcpy.Raster(rasterList[5])
            ras7 = arcpy.Raster(rasterList[6])
            ras8 = arcpy.Raster(rasterList[7])
            ras9 = arcpy.Raster(rasterList[8])
            outRaster = ras1 + ras2 + ras3 + ras4 + ras5 + ras6 + ras7 + ras8 + ras9
    
        elif rasterCount == 8:
            ras1 = arcpy.Raster(rasterList[0])
            ras2 = arcpy.Raster(rasterList[1])
            ras3 = arcpy.Raster(rasterList[2])
            ras4 = arcpy.Raster(rasterList[3])
            ras5 = arcpy.Raster(rasterList[4])
            ras6 = arcpy.Raster(rasterList[5])
            ras7 = arcpy.Raster(rasterList[6])
            ras8 = arcpy.Raster(rasterList[7])
            outRaster = ras1 + ras2 + ras3 + ras4 + ras5 + ras6 + ras7 + ras8
            
        elif rasterCount == 7:
            ras1 = arcpy.Raster(rasterList[0])
            ras2 = arcpy.Raster(rasterList[1])
            ras3 = arcpy.Raster(rasterList[2])
            ras4 = arcpy.Raster(rasterList[3])
            ras5 = arcpy.Raster(rasterList[4])
            ras6 = arcpy.Raster(rasterList[5])
            ras7 = arcpy.Raster(rasterList[6])
            outRaster = ras1 + ras2 + ras3 + ras4 + ras5 + ras6 + ras7
           
        elif rasterCount == 6:
            ras1 = arcpy.Raster(rasterList[0])
            ras2 = arcpy.Raster(rasterList[1])
            ras3 = arcpy.Raster(rasterList[2])
            ras4 = arcpy.Raster(rasterList[3])
            ras5 = arcpy.Raster(rasterList[4])
            ras6 = arcpy.Raster(rasterList[5])
            outRaster = ras1 + ras2 + ras3 + ras4 + ras5 + ras6
            
        elif rasterCount == 5:
            ras1 = arcpy.Raster(rasterList[0])
            ras2 = arcpy.Raster(rasterList[1])
            ras3 = arcpy.Raster(rasterList[2])
            ras4 = arcpy.Raster(rasterList[3])
            ras5 = arcpy.Raster(rasterList[4])
            outRaster = ras1 + ras2 + ras3 + ras4 + ras5
            
        elif rasterCount == 4:
            ras1 = arcpy.Raster(rasterList[0])
            ras2 = arcpy.Raster(rasterList[1])
            ras3 = arcpy.Raster(rasterList[2])
            ras4 = arcpy.Raster(rasterList[3])
            outRaster = ras1 + ras2 + ras3 + ras4
            
        elif rasterCount == 3:
            ras1 = arcpy.Raster(rasterList[0])
            ras2 = arcpy.Raster(rasterList[1])
            ras3 = arcpy.Raster(rasterList[2])
            outRaster = ras1 + ras2 + ras3
            
        elif rasterCount == 2:
            ras1 = arcpy.Raster(rasterList[0])
            ras2 = arcpy.Raster(rasterList[1])
            outRaster = ras1 + ras2
            
        elif rasterCount == 1:
            ras1 = arcpy.Raster(rasterList[0])
            outRaster = ras1
            
        serviceArea = parameters[1].valueAsText
        parcels = "F:\\GIS\\Projects\\Tongass\\SEALTrust_Prioritization\\SEALT.gdb\\Parcels\\" + service_area_dict.get(serviceArea)

        zonalStats = ZonalStatistics(in_zone_data=parcels, zone_field="primary_key", in_value_raster=outRaster, statistics_type="MEAN", ignore_nodata="DATA")
        #zonalStats.save(r"F:\GIS\SEALT_prioritization\Output\zonal_out")

        centroids = r"memory\centroids"
        out_points = r"memory\outPoints"

        arcpy.management.FeatureToPoint(in_features=parcels, out_feature_class=centroids, point_location="INSIDE")

        ExtractValuesToPoints(in_point_features=centroids, in_raster=zonalStats,out_point_features=out_points,
                              interpolate_values="NONE", add_attributes="VALUE_ONLY")

        scoredParcels = parameters[2].valueAsText

        arcpy.analysis.SpatialJoin(target_features=parcels, join_features=out_points, out_feature_class=scoredParcels,
                                   join_operation="JOIN_ONE_TO_ONE", join_type="KEEP_ALL", match_option="CONTAINS")

        return

    def postExecute(self, parameters):
        """This method takes place after outputs are outputs are processed and
        added to the display."""
        return 
0 Kudos
1 Solution

Accepted Solutions
graeme_hill
New Contributor III

For some reason the split of the parameter(0) is returning a quoted list ie the variable raster_names looks like ["'Freshwater forested shrub wetland'", "'Anadromous waters'"] and these do not match your raster dict so the get is returning None. You can simply remove the single quotes when you reference 'key' in your raster_name loop, eg raster_path = "F:\\GIS\\SEALT_prioritization\\Rasters\\" + raster_dict.get(key.replace("'", "")) or you can try and figure out why the list is quoted.

Cheers, Graeme

View solution in original post

0 Kudos
2 Replies
graeme_hill
New Contributor III

For some reason the split of the parameter(0) is returning a quoted list ie the variable raster_names looks like ["'Freshwater forested shrub wetland'", "'Anadromous waters'"] and these do not match your raster dict so the get is returning None. You can simply remove the single quotes when you reference 'key' in your raster_name loop, eg raster_path = "F:\\GIS\\SEALT_prioritization\\Rasters\\" + raster_dict.get(key.replace("'", "")) or you can try and figure out why the list is quoted.

Cheers, Graeme
0 Kudos
elmort
by
New Contributor II

Thanks, Graeme. I resolved it with the .strip() function in the for loop. 

0 Kudos