Automatically updating variables inside a for loop

5879
8
06-10-2015 07:40 AM
RachaelJohnson
Deactivated User

This question is back to basics; I looked around for an answer or a clue and I can't find what I'm looking for.  I'm stuck.

I've got 3 variables with assigned string values (shapefile paths) and I am running them through a loop to check that their projected coordinate systems (PCS) are the same as my DEM's PCS.  I want the code to check if they're the same, and if not, I want to project the file and reassign the projected file path to the original variable.

This is my code:

print("Verifying that coordinate systems are the same...")
InSHP = [outline, DA, soil]
DEMSR = arcpy.Describe(DEM).spatialReference.PCSCode
for i, l in enumerate(InSHP):
    print InSHP
    sr = arcpy.Describe(l).spatialReference.PCScode
    if sr != DEMSR:
        l = arcpy.Project_management(l, l[:-4] + "PRJ.shp", DEMSR)
        InSHP = l
        print InSHP
print outline

print InSHP produces the modified file path but print outline does not.

So, l = the projected shapefile but the l doesn't automatically connect the original variable name (outline) to the new path.  I was initially expecting the program to automatically deduce that l = new path = outline, which was kind of stupid of me to think in retrospect.  Now I explicitly know that l only refers to the entry in the list and is in no way connected to the original variable aside from having the same starting values.  Is there a way to automate this sort of assignment or am I doomed to adding the following at the end of my code?

outline = InSHP[0]
DA = InSHP[1]
soil = InSHP[2]

I guess it's not a big deal because there's only 3 of them but what if I need to check more inputs?

As a side question, this loop tells me that my soils file does not have a PCS, but when I look at the file properties in GIS or Catalog, it does have a PCS.  Any idea why that's happening?

EDIT: Ugh.  Adding those next 3 lines at the end didn't actually accomplish what I wanted it to accomplish.  I'm so used to working with cursors and GIS tables that my plain list skills are lacking. :S

EDIT 2: Okay I got my list to update using the enumerate function and I updated my code accordingly.  My original question(s) still stand, though.  Is there a way to assign the new values to the original variables names without having to explicitly type the variable names again?  I'm starting to think there isn't but I already posted this and I'm going to keep it here in case any other novice has this same problem. 

0 Kudos
8 Replies
JeffWard
Honored Contributor

You are assigning the projected shapefile to l, this doesn't modify the original variable in your list.  When you assign a variable to another variable you are actually making a copy of the original.  Somewhere in your code you are assigning a path name to outline.  You then create a list that contains variables.  You run that list through a for loop that takes the contents of that list one at a time and makes a copy of its contents in l.  Changing l doesn't change the original variable.  In fact if you change InSHP[0], it won't change the original contents in the outline variable.

>>> a = "This"
>>> b = "That"
>>> c = "The Other"
>>> these = [a, b, c]
>>> print these
['This', 'That', 'The Other']
>>> these[0] = "Now This"
>>> print these
['Now This', 'That', 'The Other']
>>> print a
This
>>> for x in range(0, 3):
    print x

   
0
1
2
>>> print x
2

It might be better to make a for loop like:

for x in range(0, len(InSHP)):
  sr = arcpy.Describe(InShP).spatialReference.PCScode
  print(InSHP, sr)  
  if sr != DEMSR:  
      InSHP = arcpy.Project_management(InSHP, InSHP[:-4] + "PRJ.shp", DEMSR) 
print InSHP[0]
Jeff Ward
Summit County, Utah
RachaelJohnson
Deactivated User

Is that code you posted preferable to my current code using enumerate?  Your code still requires me to update the variables manually outside of the loop, correct?

0 Kudos
andysp
by
Occasional Contributor

I'm sure this is do-able but it's a little difficult to make a recommendation without seeing your whole code. In particular, it would be useful to see the original definitions for your three variables and a little more information about how you intend to use them later in the script. (One approach may be to have "before" and "after" variables, or something like that.)

I would be careful about trying to re-assign l while inside that loop (and in any case, just working off the top of my head, I'm not sure Project_management returns a value you can or would want to use that way anyway, though I'd be grateful to stand corrected on that).

RachaelJohnson
Deactivated User

These are my original inputs:

outline = r"K:\GradWork\GIS\CollegePark\CP_outline.shp"
DA = r"K:\GradWork\GIS\CollegePark\CPDrainage.shp"
DAID = "Id"  # field in DA shapefile where unique DA values exist
soil = r"C:\Users\Rachael Johnson\Documents\GradWork\Project Files\BMPGIS\soils\VB_Soils.shp"
WTin = r"K:\GradWork\GIS\CollegePark\CPGW_adj1"
DEMin = r"K:\GradWork\GIS\CollegePark\cpelev"
MapLoc = r"K:\GradWork\GIS\CollegePark\CP.mxd"
WT = arcpy.Raster(WTin)
DEM = arcpy.Raster(DEMin)

My code uses these inputs in the context of other arcpy functions to create new variables or set environments.  The outline variable, for example, is used here and then isn't used again:

### _____________SET PROCESSING EXTENTS____________ ###
# Set cell size
description = arcpy.Describe(DEM)
cellsize = description.children[0].meanCellHeight
print("Setting cell size to DEM cell size: " + str(cellsize) + " ft...")
arcpy.env.cellSize = cellsize


# Create buffer around outline to use as mask
# Buffer distance is in feet
print("Creating an environment mask from the site outline shapefile...")
maskshp = arcpy.Buffer_analysis(outline, ScratchPath + r"\outline_buff", "50 Feet", "", "", "ALL",)


# Convert buffer to raster
mask = arcpy.Raster(arcpy.PolygonToRaster_conversion(maskshp, "Id", ScratchPath + r"\rastermask"))
mask.save(ScratchPath + r"\rastermask")


# Set raster mask and snap raster
print("Setting raster mask and snap raster for project...")
arcpy.env.mask = mask
arcpy.env.snapRaster = mask
arcpy.env.extent = mask.extent

Here's an example of using one of the inputs to create a new variable and how that new variable is later transformed.

# Convert soils shapefile to raster and assign integer values to HSG.
# A=1, B=2, C=3, 4=D and dual groups A/D=14, B/D=24, C/D=34
# "---" is treated as a D soil
print("Converting dual group soils to single groups...")
SoilUnclass = arcpy.PolygonToRaster_conversion(soil, "HSG", ScratchPath + r"\SoilUnclass",
                                     "MAXIMUM_COMBINED_AREA")
SoilClass = arcpy.sa.Reclassify(SoilUnclass, "HSG", arcpy.sa.RemapValue([["A", 1],
                       ["B", 2],
                       ["C", 3],
                       ["D", 4],
                       ["A/D", 14],
                       ["B/D", 24],
                       ["C/D", 34],
                       ["---", 4]]), "NODATA")
SoilClass.save(ScratchPath + r"\HSGraster")

Project_management does return a variable that I can use in that way; with most of these arcpy functions, setting a variable to them and then calling the variable prints the file path of the resulting shapefile or grid.  So, in the context of the above code, "print SoilClass" returns the path "K:\GradWork\GIS\CollegePark\CP\scratch\HSGraster". 

andysp
by
Occasional Contributor

Would this kind of construct be useful?

my_layers = [     \
               ['outline','K:\GradWork\GIS\CollegePark\CP_outline.shp'],\
               ['DA','K:\GradWork\GIS\CollegePark\CPDrainage.shp'],\
               ['soil','C:\Users\Rachael\VB_Soils.shp']\
          ]

for layer in my_layers:
     print 'debug 1 key: ' + layer[0] + ' value: ' + layer[1]
     # this is just an arbitrary test; you might test for PCS equivalency here
     if layer[1] == 'C:\Users\Rachael\VB_Soils.shp':
          layer[1] = 'C:\Users\Rachel\New_File_Name.shp'
     print 'debug 2 key: ' + layer[0] + ' value: ' + layer[1]

for resulting_layer in my_layers:
     print 'debug 3 key: ' + resulting_layer[0] + ' value: ' + resulting_layer[1]

Output:

debug 1 key: outline value: K:\GradWork\GIS\CollegePark\CP_outline.shp
debug 2 key: outline value: K:\GradWork\GIS\CollegePark\CP_outline.shp
debug 1 key: DA value: K:\GradWork\GIS\CollegePark\CPDrainage.shp
debug 2 key: DA value: K:\GradWork\GIS\CollegePark\CPDrainage.shp
debug 1 key: soil value: C:\Users\Rachael\VB_Soils.shp
debug 2 key: soil value: C:\Users\Rachel\New_File_Name.shp
debug 3 key: outline value: K:\GradWork\GIS\CollegePark\CP_outline.shp
debug 3 key: DA value: K:\GradWork\GIS\CollegePark\CPDrainage.shp
debug 3 key: soil value: C:\Users\Rachel\New_File_Name.shp
Luke_Pinner
MVP Regular Contributor

You could use a dict instead of a list

    print("Verifying that coordinate systems are the same...")  
    InSHP = {'outline':outline, 'DA':DA, 'soil':soil}  
    DEMSR = arcpy.Describe(DEM).spatialReference.PCSCode  
    for l in InSHP:  
        print l, InSHP
        sr = arcpy.Describe(InSHP).spatialReference.PCScode  
        if sr != DEMSR:  
            InSHP = arcpy.Project_management(InSHP, InSHP[:-4] + "PRJ.shp", DEMSR)  
            print InSHP 
    print InSHP['outline']
RachaelJohnson
Deactivated User

Of course!   I can't try this out yet but I bet this is the solution! 

0 Kudos
JeffWard
Honored Contributor

Good call, Luke.  I was going to recommend the same but didn't have time to get back to replying.

PS - You need a curly brace instead of a square bracket at the end of your dictionary assignment.

Jeff Ward
Summit County, Utah
0 Kudos