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 arcviewinput = 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?
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