Select to view content in your preferred language

Python toolbox - add output data to current map display

256
5
4 weeks ago
HeatherDulaney1
Emerging Contributor

Hi all, I can't seem to figure out how to add my output layers to my current map display through my python toolbox code. Anyone have any idea how to do so? 

import arcpy
import pandas as pd
from arcpy.sa import *

class Toolbox(object):
    def __init__(self):
        
        self.label = "Mapping Trees Model"
        self.alias = "mapping trees"

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


class MappingTrees(object):
    def __init__(self):

        self.label = "Mapping Trees Model"
        self.description = "This model analyzes information from a raster with tree data such as tree height, canopy cover percent, etc. for an area of interest defined by the user. "

    def getParameterInfo(self):

        #Define the parameters
        Extent = arcpy.Parameter(
            displayName = "Define Extent",
            name = "Extent",
            datatype = "GPExtent",
            parameterType = "Required",
            direction = "Input")
            
        Identify_Workspace = arcpy.Parameter(
            displayName = "Identify Workspace",
            name = "Identify_Workspace",
            datatype = "DEWorkspace",
            parameterType = "Required",
            direction = "Input")

        Study_Area = arcpy.Parameter(
            displayName="Study Area",
            name="Study_Area",
            datatype="GPFeatureLayer",
            parameterType="Required",
            direction="Input")
        Study_Area.controlCLSID = "{60061247-BCA8-473E-A7AF-A2026DDE1C2D}"

        Base_Raster = arcpy.Parameter(
            displayName="Base Tree Raster (such as tree height, canopy cover percentage, etc.)",
            name="Base_Raster",
            datatype="GPRasterLayer",
            parameterType="Required",
            direction="Input")
        
        #Base_Raster.filter.type = "ValueList"

        dBASE_file = arcpy.Parameter(
            displayName = "Class Ranges (dBASE file)",
            name = "dBASE_file",
            datatype = "GPTableView",
            parameterType = "Required",
            direction = "Input")

##        Patches = arcpy.Parameter(
##            displayName = "Raster to Polygon",
##            name = "Patches",
##            datatype = "GPLayer",
##            parameter = "Derived",
##            direction = "Output")
	
        parameters = [Extent, Identify_Workspace, Study_Area, Base_Raster, dBASE_file]
        return parameters

    def isLicensed(self):

        try:
            if arcpy.CheckExtension("Spatial") != "Available":
                raise Exception
            else:
                arcpy.CheckOutExtension("Spatial")
        except Exception:
            arcpy.AddMessage('Spatial Analyst Extension not available')
            return False  # tool cannot be executed
        return True  # tool can be executed

    def updateParameters(self, parameters):
        return

    def updateMessages(self, parameters):
        return

    def execute(self, parameters, messages):

        #allow tool to overwrite the layers in the file being used
        arcpy.env.overwriteOutput == True

        #Make it so I can use the parameters in the tool
        Extent = parameters[0].valueAsText
        Identify_Workspace = parameters[1].valueAsText
        Study_Area = parameters[2].valueAsText
        Base_Raster = parameters[3].valueAsText
        dBASE_file = parameters[4].valueAsText

        #Define workspace
        arcpy.env.workspace = Identify_Workspace

	# Extract by Mask: extract out the study area out of the base raster
        Extract_Mask = "Extract_Raster.tif"
        Extract_Mask1 = arcpy.sa.ExtractByMask(Base_Raster, Study_Area, "INSIDE")
        Extract_Mask1.save(Extract_Mask)
        
        # Reclassify: classify a raster with the range of what you would like it to be
        array = arcpy.da.TableToNumPyArray(dBASE_file, "*")
        df = pd.DataFrame(array)
        class_list = df.values.tolist()
    
        for i in range(len(class_list)):
            class_list[i] = class_list[i][1:-1]

        myRemapRange = arcpy.sa.RemapRange(class_list)
        User_Reclassified_Raster = "User_Reclassified_Raster.tif"
        reclassified_raster = arcpy.sa.Reclassify(Extract_Mask1, "Value", myRemapRange, "NODATA")
        reclassified_raster.save(User_Reclassified_Raster)

        # Add Field: add the field Range to the reclassified raster
        # to help user know what ranges go with what classified value
        arcpy.management.AddField(User_Reclassified_Raster, "Range", "TEXT", field_alias = "Height Range")

        # Join Field: join the from and to fields from the dBASE file
        arcpy.management.JoinField(User_Reclassified_Raster, "Value", dBASE_file, "OUT",["FROM_", "TO"])

        # Calculate Field
        arcpy.management.CalculateField(User_Reclassified_Raster,"Range", "\"!FROM_! - !TO!\"")

        # Delete Field
        arcpy.management.DeleteField(User_Reclassified_Raster, ["FROM_", "TO"])

        #List the fields in the Study Area polygon and if it does not have the fild ID, add it
        fields = arcpy.ListFields(Study_Area)

        if "Id" not in fields:
            arcpy.management.AddField(Study_Area, "Id", "LONG")
            arcpy.management.CalculateField(Study_Area, "Id", 0)

        # Tabulate Area: calculate the area of each range of heights, etc.
        Tabulate_Area = "Tabulate_Area.dbf"
        arcpy.sa.TabulateArea(User_Reclassified_Raster, "Range", Study_Area, "Id", Tabulate_Area, User_Reclassified_Raster, "CLASSES_AS_FIELDS")

        # Join, Add Field, Calculate Field, Delete Field: the tabulate area table to the reclassified raster, then add and calculate
        # the field Area so one knows what field they are looking at, Delete the field "ID_0"
        arcpy.management.JoinField(User_Reclassified_Raster, "Range", Tabulate_Area, "RANGE", ["ID_0"])
        arcpy.management.AddField(User_Reclassified_Raster, "Area", "DOUBLE")
        arcpy.management.CalculateField(User_Reclassified_Raster, "Area", "!ID_0!")
        arcpy.management.DeleteField(User_Reclassified_Raster, "ID_0")

        # Raster to Polygon: convert the reclassified raster to polygons
        R2P = "R2P"
        arcpy.conversion.RasterToPolygon(User_Reclassified_Raster, R2P,"NO_SIMPLIFY", "Range")

        # Dissolve: combine all the polygons together that touch
        Dissolve_Polys = "Dissolve_Polys"
        arcpy.management.Dissolve(R2P, Dissolve_Polys, "", "", "SINGLE_PART")

        #Eliminate: smooth out the polygons
        Patches = "Patches"
        arcpy.management.EliminatePolygonPart(Dissolve_Polys, Patches, "AREA", 1000)

        # Zonal Statistics: calculate the statistics (min, max, and mean) of the tree values
        ZonalStats = "ZonalStats.dbf"
        arcpy.sa.ZonalStatisticsAsTable(Patches, "ORIG_FID", Base_Raster, ZonalStats, "DATA", "MIN_MAX_MEAN")

        # Join Field: join the zonal stats fields to the smoothed out polygons
        arcpy.management.JoinField(Patches, "ORIG_FID", ZonalStats, "ORIG_FID", ["AREA", "MIN", "MAX", "MEAN"])
        arcpy.management.DeleteField(Patches, ["Id", "ORIG_FID"])

        arcpy.management.AddField(Patches, "Acres", "DOUBLE")
        arcpy.management.CalculateGeometryAttributes(Patches, [["Acres","AREA"]], area_unit="ACRES_US")

	# Delete: delete the following layers:
        arcpy.management.Delete(Extract_Mask1)
        arcpy.management.Delete(R2P)
        arcpy.management.Delete(Dissolve_Polys)
        arcpy.management.Delete(ZonalStats)
        arcpy.management.Delete(Tabulate_Area)

        c_project=arcpy.mp.ArcGISProject("CURRENT")


        return

    def postExecute(self, parameters):
        return

 

0 Kudos
5 Replies
DavidPike
MVP Frequent Contributor

have you set the parameter in the toolbox to 'output'

If that doesn't work then try adding a derived parameter as output as per:
https://community.esri.com/t5/arcgis-pro-questions/how-to-display-feature-layer-from-python-toolbox/...

0 Kudos
HeatherDulaney1
Emerging Contributor

That does not seem to work. I believe that is because I'm writing a python toolbox and not a script tool in a normal toolbox.

DanPatterson
MVP Esteemed Contributor

It is an environment setting and can only be changed within the python window or a notebook in ArcGIS Pro

List tools, toolboxes, and environment settings—ArcGIS Pro | Documentation

env—ArcGIS Pro | Documentation

import arcpy

environments = arcpy.ListEnvironments()

# Sort the environment list, disregarding capitalization
environments.sort(key=str.lower)
for environment in environments:
    # As the environment is passed as a variable, use Python's getattr to 
    # evaluate the environment's value
    env_value = getattr(arcpy.env, environment)
    # Format and print each environment and its current setting
    print(f"{environment:<30}: {env_value}")
    
addOutputsToMap               : True
.... snip ....

 


... sort of retired...
0 Kudos
DavidSolari
MVP Regular Contributor

If your derived output is being created by the tool, you can set the value by doing this at the end of your execute method:

 

parameters[5].value = Patches

 

Note that you might need to adjust the parameter's data type, you picked "GPLayer" but you probably want "GPFeatureLayer" or "DEFeatureClass".

0 Kudos
HaydenWelch
MVP

Why are you assigning c_project then doing nothing with it?

To interact with an active project, you usually assign that at the beginning of the tool (I usually throw it in the tool __init__ function so I can use it with a "self.project" call. Then you can do map operations while the tool is executing (like addLayerFromDatasource, or moveLayer)

 

You can then pass layers generated using arcpy tools to the appropriate .mp calls on that project and it's associated objects.

0 Kudos