How to use MakeFeatureLayer_management within a for Loop

1961
9
Jump to solution
01-11-2018 03:08 PM
AlexanderAudet
New Contributor III

I really don't know how to phrase the question actually. I started by using a calculate field management, except that my codeblock started to run on for hundreds of lines and was hard to read and write in (in notepad++ you see solid orange code), but it worked. Specifically, I could create layers using MakeFeatureLayer that where isolated to the codeblock and never appeared in the table of contents of ArcGIS.

Because I will be handing my code down to others to interpret and modify as suits them I decided to try replacing the calculate field with an update cursor. Currently it is not able to run the MakeFeatureLayer within my For loop that I am using to implement my update cursor and it raises this error code:

MakeFeatureLayer     raise e ExecuteError: ERROR 999999: Error executing function. Cannot acquire a lock. Cannot acquire a lock. Failed to execute (MakeFeatureLayer).

Even if I do get it to work though, I do not know if it will try to create a layer outside of the loop over each iteration. (perhaps I could try deleting the layer in each loop after I am finished using it). 

Here is a code snipet. Any ideas how to get MakeFeatureLayer working?

# Set local variables
inTable = "Murray Sp2"


def Strike(shape, fid):

        ....

    if length >= 183 and length <= 185: 

        ....

        # I next make the shapelyr feature layer out of my current intable(Murray Sp2), but only select the current object being
        #calculated by using the where statement to select only the objct with the current FID
        arcpy.MakeFeatureLayer_management(inTable, 'shapelyr', '"FID" ='+str(fid))
        # I then make the PairedCheck6 feature layer out of my current intable(Zarza data), but excluding the current object being
        #calculated by using the where statement to select only objects that do not match the current FID. This layer will be used 
        #to select the matching dip symble that belongs with the strike symbol for which the azimuth is being calculated
        arcpy.MakeFeatureLayer_management(inTable, 'PairedCheck6', '"FID" <>'+str(fid))
        
        ....  

cursor = arcpy.da.UpdateCursor(inTable, ['Shape@', 'FID', fieldName])
for row in cursor:
    row[2] = Strike( row[0], row[1])
    cursor.updateRow(row)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
1 Solution

Accepted Solutions
AlexanderAudet
New Contributor III

Ok, so I figured something out in the end. What I had mentioned just one post above did not work since the feature layer was still connected to the source data which had been locked by the UpdateCursor. Thus what I did was to use the CopyFeatures to create two new shape files and then edit them within my function using SelectLayerByAttribute. However, I found this code be be rather slow and inefficient, so I will be looking into a new way of doing this.  

# Import system modules
import arcpy
import os

# Set local variables
datasource = r"C:\Alexsomewhere"
inTable = os.path.join(datasource, "Murray Sp2.shp")
fieldName = "DipDir"

arcpy.CopyFeatures_management(inTable, "C:\Alexsomewhere\shapelyr")
arcpy.CopyFeatures_management(inTable, "C:\Alexsomewhere\PairedCheck6")

def Strike(shape, fid):

    # here I have cut out trigonometry that calculates a line direction. 55 is arbitrary and meaningless. 
    degreeBearing = 55

    arcpy.SelectLayerByAttribute_management('shapelyr', 'NEW_SELECTION', "{0} = {1}".format("FID", fid))
    arcpy.SelectLayerByAttribute_management('PairedCheck6', 'NEW_SELECTION', "{0} <> {1}".format("FID", fid))

    #Next I Select the features from PairedCheck6 that are within a specified distance of the feature for which the azimuth is being calcualted
    arcpy.SelectLayerByLocation_management('PairedCheck6', 'WITHIN_A_DISTANCE', 'shapelyr', '11 Meters', 'SUBSET_SELECTION')

    # Next I create my curser object which I use to find the TrueCentroid and Length of each of the features Selected in PairedCheck6. Notice that only the selected objects in PairedCheck6 are used.

    cursor = arcpy.da.SearchCursor('PairedCheck6',['SHAPE@TRUECENTROID', 'SHAPE@LENGTH'])

    #Here I cut out logic to check that I have the correct number of selected rows from PairedCheck6, and if not, try to select the correct ones using their centroids and lengths. 
    #Once I have the correct number of other object(s) I create a pointgeometry and check its location relative to the current object then modify the degreeBearing that will be passed one last time depending on the result.

    #returns the degreebearing value as the dip direction
    return degreeBearing

with arcpy.da.UpdateCursor(inTable, ['SHAPE@', 'FID', fieldName]) as cursor:
    for row in cursor:
        row[2] = Strike( row[0], row[1])
        cursor.updateRow(row)
del cursor‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
del row‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

View solution in original post

0 Kudos
9 Replies
MitchHolley1
MVP Regular Contributor

You're getting that error because the datasource you're trying to get to it being used by another application (probably ArcMap or ArcCatalog).  

I edited the code snippet to include:

  • A total datasource path (perhaps you just didn't include in the snippet)
  • You can AddFieldDelimiters so you can query different datasources
  • Use can use the built-in format function when writing strings
  • Change 'Shape@' to 'SHAPE@' in line 23
  • Make sure to delete the cursor after you've exhausted it to ensure no future data locks
  • Have you tested the cursor?  I don't think it will work since you're assigning a row value to a function which seems to return two in-mem layers.  Please explain. 

Lastly, this code doesn't show the in-memory layers being used anywhere.  What is the purpose of making the layers exactly?

I hope this helps.  I can't be too sure this will work without seeing the entire code. 

datasource = r'path/to/datasource/folder/or/geodatabase'
inTable = os.path.join(datasource,"Murray Sp2")

fidField = arcpy.AddFieldDelimiters(datasource, 'FID')

def Strike(shape, fid):
    if length >= 183 and length <= 185: 
        arcpy.MakeFeatureLayer_management(inTable, 'shapelyr', "{0} = {1}".format(fidField, str(fid)))
        arcpy.MakeFeatureLayer_management(inTable, 'PairedCheck6', "{0} <> {1}".format(fidField, str(fid)))

cursor = arcpy.da.UpdateCursor(inTable, ['SHAPE@','FID',fieldName])
for row in cursor:
    row[2] = Strike(row[0], row[1])
    cursor.updateRow(row)
del cursor‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
AlexanderAudet
New Contributor III

I guess that I should specify that I am working within ArcMap with this code. 

I have included more of my code below showing more of what I had at the beginning setting up, and what I use the two layers for. I do not show a huge portion in the middle where I narrow down the the few selected records, and determine if the selected records are located right or left of the record being updated. Thus I end with a IsCentroidRight boolean. Then I return a short integer based on the results of the value of IsCentroidRight. 

When I run with those suggestions implemented as below, I now am getting:

Runtime error
Traceback (most recent call last):
File "<string>", line 55, in <module>

RuntimeError: cannot open 'D:/Work/Everything/trial/Murray Sp2'

# Import system modules
import arcpy
import os
 
# Set environment settings
arcpy.env.workspace = "D:/Work/Everything/trial"

# Set local variables
datasource = "D:/Work/Everything/trial/"
inTable = os.path.join(datasource,"Murray Sp2")
fieldName = "DipDir"

fidField = arcpy.AddFieldDelimiters(datasource, 'FID')


def Strike(shape, fid):
    length = shape.length

    # I make sure it correctly calculated the lenght
    if length == 0:
        return 7000

    if length >= 183 and length <= 185:
        # I calculate the strike azimuth based on Trig and what the program has recorded the first and last point of each
        # polyline to be
        
        ....

        degreeBearing = a calculated number
        
        # I next make the shapelyr feature layer out of my current intable(Zarza data), but only select the current object being
        #calculated by using the where statement to select only the objct with the current FID
        arcpy.MakeFeatureLayer_management(inTable, 'shapelyr', "{0} = {1}".format(fidField, str(fid)))
        # I then make the PairedCheck6 feature layer out of my current intable(Zarza data), but excluding the current object being
        #calculated by using the where statement to select only objects that do not match the current FID. This layer will be used 
        #to select the matching dip symble that belongs with the strike symbol for which the azimuth is being calculated
        arcpy.MakeFeatureLayer_management(inTable, 'PairedCheck6', "{0} <> {1}".format(fidField, str(fid)))

        #Next I Select the features from PairedCheck6 that are within a specified distance of the feature for which the azimuth
        #is being calcualted 
        arcpy.SelectLayerByLocation_management('PairedCheck6', 'WITHIN_A_DISTANCE', 'shapelyr', '11 Meters')

        # Next I create my curser object which I use to find the TrueCentroid and Length of each of the features Selected in PairedCheck6.
        # Notice that only the selected objects in PairedCheck6 are used.
        cursor = arcpy.da.SearchCursor('PairedCheck6',['SHAPE@TRUECENTROID', 'SHAPE@LENGTH'])

        ....

        if isCentroidRight:
            degreeBearing += 180
       #returns the degreebearing value as the dip direction
       return degreeBearing
    else:
        return 4000
cursor = arcpy.da.UpdateCursor(inTable, ['SHAPE@', 'FID', fieldName])
for row in cursor:
    row[2] = Strike( row[0], row[1])
    cursor.updateRow(row)‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
del cursor
0 Kudos
JoeBorgione
MVP Esteemed Contributor

Just a hunch but it may be choking on the space in "Murray SP2"  (line 10 where you set the variable and then again in line 55 where your reference it)  What kind of file is Murray SP2? does it need a file extension as well?

That should just about do it....
DanPatterson_Retired
MVP Esteemed Contributor

there seems to be a .gdb missing

'D:/Work/Everything/trial/Murray Sp2'

is trial the gdb? (ie trial.gdb) of is it a folder and Murray Sp2 a shapefile? (if the latter, it is missing a .shp).  Don't use paths or anything with spaces either, you are just asking for trouble at some point)

MitchHolley1
MVP Regular Contributor

You're going to want to wrap your path string in a r to make it "raw".  Also, you have a dangling '/'.  Please remove that, and the env setting (you're not using any of it's powers here) in line 6 and try again.  Does that table actually exist?

# Set local variables
datasource = r"D:\Work\Everything\trial"
inTable = os.path.join(datasource,"Murray Sp2")
fieldName = "DipDir"‍‍‍‍‍‍‍‍
0 Kudos
AlexanderAudet
New Contributor III

Thank you! The .shp seemed to open the file just fine. However, my problems have evolved slightly. The following error appeared on the right side (just Invalid expression "FID" = 2 on the left side). 

ERROR 000358: Invalid expression "FID" = 2
Cannot acquire a lock.
Cannot acquire a lock.
Failed to execute (MakeFeatureLayer).

The program seems more concerned about the SQL expression at this point, but only does so if I use

inTable = os.path.join(datasource,"Murray Sp2.shp")

rather than 

inTable = "Murray Sp2"

(I kept it, though inactive)

0 Kudos
JoeBorgione
MVP Esteemed Contributor

How are you running this script?  You might want to copy snd paste it line by line into a python window and see where it chokes.  Before you do, take a look at your shapefile and record FID = 2.  You may also want to convert your data from a shapefile to a file gdb feature class.  I don't have access to ArcGIS till Tuesday so I can't run your select statement to verify it; you could try a between statment....

That should just about do it....
0 Kudos
AlexanderAudet
New Contributor III

I think what was happening was that my cursor was on my inTable, which for some reason prevented me from performing a query on it when creating feature layers from the inTable. I will create the layers before creating the cursor and try to just modify the feature layers afterwords.  If it works I will post the results!

0 Kudos
AlexanderAudet
New Contributor III

Ok, so I figured something out in the end. What I had mentioned just one post above did not work since the feature layer was still connected to the source data which had been locked by the UpdateCursor. Thus what I did was to use the CopyFeatures to create two new shape files and then edit them within my function using SelectLayerByAttribute. However, I found this code be be rather slow and inefficient, so I will be looking into a new way of doing this.  

# Import system modules
import arcpy
import os

# Set local variables
datasource = r"C:\Alexsomewhere"
inTable = os.path.join(datasource, "Murray Sp2.shp")
fieldName = "DipDir"

arcpy.CopyFeatures_management(inTable, "C:\Alexsomewhere\shapelyr")
arcpy.CopyFeatures_management(inTable, "C:\Alexsomewhere\PairedCheck6")

def Strike(shape, fid):

    # here I have cut out trigonometry that calculates a line direction. 55 is arbitrary and meaningless. 
    degreeBearing = 55

    arcpy.SelectLayerByAttribute_management('shapelyr', 'NEW_SELECTION', "{0} = {1}".format("FID", fid))
    arcpy.SelectLayerByAttribute_management('PairedCheck6', 'NEW_SELECTION', "{0} <> {1}".format("FID", fid))

    #Next I Select the features from PairedCheck6 that are within a specified distance of the feature for which the azimuth is being calcualted
    arcpy.SelectLayerByLocation_management('PairedCheck6', 'WITHIN_A_DISTANCE', 'shapelyr', '11 Meters', 'SUBSET_SELECTION')

    # Next I create my curser object which I use to find the TrueCentroid and Length of each of the features Selected in PairedCheck6. Notice that only the selected objects in PairedCheck6 are used.

    cursor = arcpy.da.SearchCursor('PairedCheck6',['SHAPE@TRUECENTROID', 'SHAPE@LENGTH'])

    #Here I cut out logic to check that I have the correct number of selected rows from PairedCheck6, and if not, try to select the correct ones using their centroids and lengths. 
    #Once I have the correct number of other object(s) I create a pointgeometry and check its location relative to the current object then modify the degreeBearing that will be passed one last time depending on the result.

    #returns the degreebearing value as the dip direction
    return degreeBearing

with arcpy.da.UpdateCursor(inTable, ['SHAPE@', 'FID', fieldName]) as cursor:
    for row in cursor:
        row[2] = Strike( row[0], row[1])
        cursor.updateRow(row)
del cursor‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
del row‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos