how to access map document layers in a python script tool?

5810
6
Jump to solution
02-07-2012 01:39 PM
markcheyne
Occasional Contributor
I've got a script tool that works in ArcGIS 10 Desktop.

I want to publish it to ArcGIS Server.

The tool needs access to layers. So I placed the tool as a 'tool layer' in a 'source map document', and published the map document as a geoprocessing service. I was trying to follow the advice of the ArcGIS Help that suggested that doing so, rather than publishing the toolbox itself, will prevent my tool from having to spool up expensive connections with each execution.

In the Python script, I use
arcpy.mapping.MapDocument("CURRENT")
to get the current map document, so I can get its layers.

I get this error when I try to run the task from the server: Object: CreateObject cannot open map document Failed to execute.

I've seen reference to that error when trying to use arcpy.mapping.MapDocument("CURRENT") if a script tool doesn't have its 'always run in foreground' box checked. It does. See http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#//001500000021000000.htm

Makes me wonder if server tasks run in the background anyway, and arcpy.mapping.MapDocument("CURRENT") can't work in a script used as a server geoprocessing task. 

Then how does my script get layers from the tool layer's source map document?

thanks!
0 Kudos
1 Solution

Accepted Solutions
markcheyne
Occasional Contributor
I'm a doofus.

1) tools that take a layer as input can also take a string instead (or a geometry object, another subject) - a path, or a TOC layer name in the case of a a tool running in an ArcMap document (Desktop) or a tool layer (Desktop or Server).

2) any tool that produces an output parameter will expose it in script as part of a Result object. In my case that's where I needed to get my layer object from.

RTFM: http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/Using_tools_in_Python/002z0000000n0000...

So:

inpoint = arcpy.Point(arcpy.GetParameter(0), arcpy.GetParameter(1)) inpointgeo = arcpy.PointGeometry(inpoint) bufferradius = arcpy.GetParameter(2) result = arcpy.SelectLayerByLocation_management("TOC layer name", "WITHIN_A_DISTANCE", inpointgeo, bufferradius, "NEW_SELECTION") layer = result.getOutput(0) rows = arcpy.SearchCursor(layer) for row in rows:   do stuff with a row


D'oh!

thanks all, MC

View solution in original post

0 Kudos
6 Replies
KevinHibma
Esri Regular Contributor
Mark,

You cant access "CURRENT" within a GP Service. You're correct, its the same idea as background processing. You'd have to set explicit paths to the map document you want to build your GP Service for arcpy.Mapping around.
From your post the other day - what is your end goal?
--To use layers inside tools within your service (the use of layers to save the re-open cost on execution)
or
--To interact with layers, and map documents with the end goal of creating a jpeg or pdf which could be printed

While these ideas both use "layers", technically they go about it differently. Yes, all things equal they're all layers, but when it comes down to it:
arcpy.Mapping.Layer (http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/Layer/00s300000008000000/) does not equal "layers" you use in a tool (http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#//001700000073000000)

If you goal is to use layers in a service for tool execution, your workflow is basically:

-Add the layers you want to use into your map document
-Create your script. Where your tool executes against a "layer", simple use a string inside the tool parameter with the layers name.
--Its this 1:1 match up of the layers name in the ToC and the string inside your script which makes this work
-Create a tool layer. (drag the tool into the ToC).
-Publish using "Add New Service" - look for the topic title "Geoprocessing service with a source map document" located at: http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/Publishing_geoprocessing_services/002v...
0 Kudos
markcheyne
Occasional Contributor
hi kevin, i really appreciate your reply. a little more elaboration/clarification?

yes, my goal is to avoid having to, with each execution of my script, expend the processing time to create a new connection to a layer on which to do the geoprocessing task's work. In other words, avoid this:

arcpy.MakeFeatureLayer_management("..mygdb/mydataset/myfeatureclass", outlayer)


where outlayer becomes an instance of arcpy.mapping.layer.

So I thought that publishing a geoprocessing service based on a map document with a tool layer would keep the map document's layers in memory, and  I could grab them from the map document's arcpy.mapping layers collection after I got hold of the current document with 'CURRENT'.

But you've confirmed I can't use 'CURRENT' in a server-based script.

I read your reply to mean this - I can get a given layer, for purposes of passing it as an input to a tool that normally requires a layer as input, by passing the name of the layer to that tool. Like:

arcpy.Buffer_analysis('layer name in source map doc TOC', '%scratchworkspace%\out.shp', '100 Miles')


Do I have that right?

But what I want is this - the code of my script wants to actually use the layer object, not pass it as input to another tool. Specifically in my case, I want my script to loop over the selected rows in the layer in a for loop. So again, how do I get hold of an actual arcpy.mapping.layer instance? Maybe I read your reply to mean:

arcpy.MakeFeatureLayer_management("layer name in source map doc TOC", outlayer)
rows = arcpy.SearchCursor(outlayer)
  for row in rows:
    do stuff with the row




thanks again, MC
0 Kudos
markcheyne
Occasional Contributor
After a few days out of office, I tried the approach suggested in my last post above, without joy. If I try in Python code to get a layer object using the layer's name from the source map document TOC, I get the following error, using the tool on Desktop of after publishing the source map document as a geoprocessing service:


Error executing tool.: <class 'arcgisscripting.ExecuteError'>: Failed to execute. Parameters are not valid.
ERROR 000735: Input Feature Layer: Value is required
Failed to execute (SelectLayerByLocation).

I'd really appreciate any suggestions.

Thanks, MC
0 Kudos
markcheyne
Occasional Contributor
I'm a doofus.

1) tools that take a layer as input can also take a string instead (or a geometry object, another subject) - a path, or a TOC layer name in the case of a a tool running in an ArcMap document (Desktop) or a tool layer (Desktop or Server).

2) any tool that produces an output parameter will expose it in script as part of a Result object. In my case that's where I needed to get my layer object from.

RTFM: http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/Using_tools_in_Python/002z0000000n0000...

So:

inpoint = arcpy.Point(arcpy.GetParameter(0), arcpy.GetParameter(1)) inpointgeo = arcpy.PointGeometry(inpoint) bufferradius = arcpy.GetParameter(2) result = arcpy.SelectLayerByLocation_management("TOC layer name", "WITHIN_A_DISTANCE", inpointgeo, bufferradius, "NEW_SELECTION") layer = result.getOutput(0) rows = arcpy.SearchCursor(layer) for row in rows:   do stuff with a row


D'oh!

thanks all, MC
0 Kudos
KevinHibma
Esri Regular Contributor
Glad you got it figured out - I had just created a little script tool and map document to prove what you're trying to do, but came on and see you got it.

Cheers!
0 Kudos
simoxu
by MVP Regular Contributor
MVP Regular Contributor
Mark,

You cant access "CURRENT" within a GP Service. You're correct, its the same idea as background processing. You'd have to set explicit paths to the map document you want to build your GP Service for arcpy.Mapping around.
From your post the other day - what is your end goal?
--To use layers inside tools within your service (the use of layers to save the re-open cost on execution)
or
--To interact with layers, and map documents with the end goal of creating a jpeg or pdf which could be printed

While these ideas both use "layers", technically they go about it differently. Yes, all things equal they're all layers, but when it comes down to it:
arcpy.Mapping.Layer (http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/Layer/00s300000008000000/) does not equal "layers" you use in a tool (http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#//001700000073000000)

If you goal is to use layers in a service for tool execution, your workflow is basically:

-Add the layers you want to use into your map document
-Create your script. Where your tool executes against a "layer", simple use a string inside the tool parameter with the layers name.
--Its this 1:1 match up of the layers name in the ToC and the string inside your script which makes this work
-Create a tool layer. (drag the tool into the ToC).
-Publish using "Add New Service" - look for the topic title "Geoprocessing service with a source map document" located at: http://help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/Publishing_geoprocessing_services/002v...


Hi Kevin,

I have the second end goal: manipulate the layers and using the source map document to generate a pdf.

Instead of accessing the layers, I want to access the map document itself, and need to export a pdf file using the map document. currently, I open mxd file in the python script by something like the following:
mxd = arcpy.mapping.MapDocument(r"C:\Project\Project.mxd")

it's veeeery slow. I reckon the cost of opening the mxd if quite high, as I have a heavy mxd with tens of layers. so, naturally I think it will be handy just keep the map document open once the GP service starts, and just reference the map document like the following:

mxd = arcpy.mapping.MapDocument("CURRENT")

I know it's not gonna working, because CURRENT only works for ArcMap and foreground execution.

Now, I am really scratching my head: is there a shortcut to the reference of the map document in the server memory? is there a map document in the memory if I publish a GP service using a toollayer in the related map document? if not, why I can access layers in the map document by name?

Any help or comments are highly appreciated.
0 Kudos