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 have 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