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
Solved! Go to Solution.
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.
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.
Thanks, Graeme. I resolved it with the .strip() function in the for loop.