Select to view content in your preferred language

extent.projectas fails only in python toolbox

505
1
01-13-2020 04:15 PM
AlanStewart
Frequent Contributor

I'm new to writing Python toolboxes for ArcMap, but I have several other tools I've written in this toolbox that do work.

This standalone script works:

import arcpy
from arcpy.mapping import MapDocument
import arcview

input = r'Y:\Shared with Fusion\spatial_query.mxd'

md = MapDocument(input)
map = md.activeDataFrame
extent = map.extent
print('old:')
print(extent.spatialReference.name)
print(extent)
out_sr = arcpy.SpatialReference(4326)
new_extent = extent.projectAs(out_sr)
print('new:')
print(new_extent.spatialReference.name)
print(new_extent)

But similar logic in a Python toolbox tool fails. (Simplified from a larger tool for debugging.) Inputs using the same MXD as above are the map's current display extent and 4326.

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

        """ProjectAsTest"""
        self.label = "ProjectAsTest"
        self.description = "Test projecting a query extent to an EPSG value"
        self.canRunInBackground = False

    def getParameterInfo(self):
        """Define parameter definitions"""
        param0 = arcpy.Parameter(displayName = "Query Extent",
                                 name = "query_extent",
                                 datatype = "GPExtent",
                                 parameterType = "Required",
                                 direction = "Input")
        param1 = arcpy.Parameter(displayName = "Force Coordinate System (EPSG code)",
                                 name = "force_epsg",
                                 datatype = "GPLong",
                                 parameterType = "Optional",
                                 direction = "Input",
                                 enabled = False)
        params = [param0, param1]
        return params

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

    def updateParameters(self, parameters):
        """Validate parameters."""
        # Query Extent
        if parameters[0].altered:
            if parameters[0].value:
                parameters[1].enabled = True
            else:
                parameters[1].value = None
                parameters[1].enabled = False
        return

    def updateMessages(self, parameters):
        """Set validation messages"""
        return

    def execute(self, parameters, messages):
        """The source code of the tool."""
        query_extent = parameters[0].value
        force_epsg = parameters[1].value
        messages.AddMessage('Query extent is {0} {1} {2} {3}'.format(query_extent.XMin,
                            query_extent.YMin, query_extent.XMax, query_extent.YMax))
        try:
            epsg = query_extent.spatialReference.factoryCode
        except:
            messages.AddErrorMessage("An extent with a spatial reference must be specified.")
            raise arcpy.ExecuteError
            return
        messages.AddMessage('Query extent EPSG is {0}'.format(epsg))
        active_epsg = None
        active_extent = None
        if not force_epsg == None:
            messages.AddMessage('Query extent will be projected.')
            force_spatial_ref = None
            try:
                force_spatial_ref = arcpy.SpatialReference(force_epsg)
            except:
                messages.AddErrorMessage('{0}'.format(sys.exc_info()))
                messages.AddErrorMessage('EPSG code {0} is not recognized.'.format(force_epsg))
                raise arcpy.ExecuteError
                return
            try:
                active_extent = query_extent
                active_extent.projectAs(force_spatial_ref)
            except:
                messages.AddErrorMessage('{0}'.format(sys.exc_info()))
                messages.AddErrorMessage('Cannot reproject extent to EPSG code {0}.'.format(force_epsg))
                raise arcpy.ExecuteError
                return
            active_epsg = force_epsg
        if not active_epsg:
            active_epsg = epsg
            active_extent = query_extent
        messages.AddMessage('Active EPSG is {0}'.format(active_epsg))
        messages.AddMessage('Active extent is {0} {1} {2} {3}'.format(active_extent.XMin,
                            active_extent.YMin, active_extent.XMax, active_extent.YMax))
        return

# end class ProjectAsTest

The error output is:

(<type 'exceptions.RuntimeError'>, RuntimeError(u'Object: CreateObject error creating spatial reference',), <traceback object at 0x18ADECB0>)

Cannot reproject extent to EPSG code 4326.

Is there something different I have to do in the Python toolbox environment to use Extent.projetctAs? Is the extent I'm getting from the user dialog somehow different from the one from the MapDocument?

Tags (1)
0 Kudos
1 Reply
AlanStewart
Frequent Contributor

And the answer is that the GPExtent parameter value is not an arcpy.Extent object. To work correctly an arcpy.Extent object needs to be constructed from the GPExtent coordinates and spatial reference. And despite what the documentation I've read says, Extent.spatialReference is writeable.

         query_extent = arcpy.Extent(XMin = parameters[0].value.XMin,
                                    YMin = parameters[0].value.YMin,
                                    XMax = parameters[0].value.XMax,
                                    YMax = parameters[0].value.YMax)
        query_extent.spatialReference = parameters[0].value.spatialReference