Convert centroid to GPIN

829
6
08-25-2011 12:23 PM
DarrenJones
New Contributor II
Hello all,

I've been working on converting our VBA code for GPIN calculation to python.  I can create a single instance GPIN via python, though really inelegant, but processing more than one is causing me a problem. I've created X and Y fields in our Feature Class. 

Our original code, used in Field Calculator, is:

Dim dblX As Double
Dim dblY As Double
Dim strX As String
Dim strY As String
Dim strGPIN As String
Dim pArea As IArea
Set pArea = [Shape]
dblX = pArea.Centroid.X
dblY = pArea.Centroid.Y
strX = CStr(dblX)
strY = CStr(dblY)
strGPIN = Mid(strX,3,1)+Mid(strY,2,1)+Mid(strX,4,1)+Mid(strY,3,1)+"-"+Mid(strX,5,1)+Mid(strY,4,1)+"-"+Mid(strX,6,1)+Mid(strY,5,1)+Mid(strX,7,1)+Mid(strY,6,1)

My solution, so far, is:

import arcpy
from arcpy import env
import os

env.workspace=r"***\Default.gdb"
#set local variables
inFeatures = "FeatureClass"
XCentroid="xCentroid"
YCentroid="yCentroid"
tableField="GPIN2"
#Load values into Variables
scurfc=arcpy.SearchCursor(inFeatures)
for sf in scurfc:
X=sf.getValue(XCentroid)
strX=repr(X)
Y=sf.getValue(YCentroid)
strY=repr(Y)
GPIN=(strX[2:3]+strY[1:2]+strX[3:4]+strY[2:3]+"-"+strX[4:5]+strY[3:4]+"-"+strX[5:6]+strY[4:5]+strX[6:7]+strY[5:6])

#Delete variables
del tableField, scurfc, X, Y, strX, strY, XCentroid, YCentroid, GPIN

I have tried the CalculateField_management route, but it fails because my GPIN variable isn't a SQL statement.  Can anyone give me a push in the right direction?  The online code examples do not reflect a SearchCursor and an UpdateCursor function in the same listing.


Thanks in advance
Tags (2)
0 Kudos
6 Replies
MarcNakleh
New Contributor III
Hello there,

What kind of error are you getting, exactly?

First, there's no need to slice your Python lists if you're looking to read single values. You could therefore rewrite your GPIN as:
GPIN= strX[2] + strY[1] + strX[3] + strY[2] + '-' + strX[4] + strY[3] + '-' + strX[5] + strY[4] + strX[6] + strY[5]


Second, I assume that there is a line in your actual code where you write your value to the GPIN2 field.
SearchCursor is a Read-only iterator for values in a table. As you want to write as well, you should be using UpdateCursor, appending the code with:

sf.setValue(tableField, GPIN)
scurfc.updateRow(sf)

so as to write the value to the field, then update the row with the value.

Finally, I don't know if repr is what you're looking for. If the X and Y coordinates are being stored in stand-alone columns (as strings or integer, for example), then you can just call them directly.

I was able to get something working using the following:

import arcpy
import os

#set local variables
inFeatures = 'FeatureClass'
XCentroid = 'xCentroid'
YCentroid = 'yCentroid'
tableField = 'GPIN2'

#Load values into Variables
arcpy.env.workspace = r'***\Default.gdb'
scurfc = arcpy.SearchCursor(inFeatures)
for sf in scurfc:
    strX = str(sf.getValue(XCentroid))
    strY = str(sf.getValue(YCentroid))
    GPIN = (strX[2] + strY[1] + strX[3] + strY[2] + '-' +
                 strX[4] + strY[3] + '-' +
                 strX[5] + strY[4] + strX[6] + strY[5])

#Update values and delete variables
sf.setValue(tableField, GPIN)
scurfc.updateRow(sf)
del tableField, scurfc, X, Y, strX, strY, XCentroid, YCentroid, GPIN


Hope this helps!
0 Kudos
DarrenJones
New Contributor II
Marc,

Thank you very much for the assistance and, more importantly, the code example.  I wasn't aware,as I am quite new to Python, that you did not have to simply call the individual characters from the string, and not have to slice them.  That is most helpful for future reference.  I was aware that SearchCursor was read-only.  The updating aspect is what has stymied me.

I have been trying to get the output of my operation to write to the table, and again, my limited Python knowledge, I wasn't able to figure out how to take what I had created and export it out, without running through another set of loops. All my attempts have not worked.

I used repr() to turn the numbers into strings, as per a few things in the help documents, but the str() function appears to do the same job.

I tried your code in PythonWin and Field Calculator, and they both failed on the setValue() function.  I realized that I needed to run something else, but it seems that my idea isn't going to work.
Three questions:

1: Do I need to run the setValue() function within another loop (as per my code below) or is there another way execute it that I am missing?  I have looked online, but there do not seem to be that many examples of what I am trying to accomplish.
2: Will I have to use an array and iterate it so I can get the correct value to load into the correct field?
3: If not, and using another loop writes the last value to each field, how will I go about loading the correct value into each subsequent field?

My current code, with your changes:

import arcpy
from arcpy import env
import os
env.workspace=r'***\Default.gdb'
#set local variables
inFeatures = 'Feature Class'
XCentroid='xCentroid'
YCentroid='yCentroid'
tableField='GPIN2'
#Load values into Variables
scurfc = arcpy.SearchCursor(inFeatures)
ucurfc = arcpy.UpdateCursor(inFeatures)
for sf in scurfc:
....strX = repr(sf.getValue(XCentroid))
....strY = repr(sf.getValue(YCentroid))
....GPIN = (strX[2] + strY[1] + strX[3] + strY[2] + "-" +
            strX[4] + strY[3] + "-" +
            strX[5] + strY[4] + strX[6] + strY[5])
#Update values and delete variables - (This only writes the last value, I am guessing because it only sees the end of the list?)
for uc in ucurfc:
....uc.setValue(tableField, GPIN)
....ucurfc.updateRow(uc)
del tableField, scurfc, ucurfc, strX, strY, XCentroid, YCentroid, GPIN

Again, thank you very much for your reply and input.
0 Kudos
MarcNakleh
New Contributor III
hahaha this might be because I gave you sloppy code. Apologies!
Here is the proper code that I have just retested (but with different variable values.) The changes I made are indicated by comments

import arcpy
import os

#set local variables
inFeatures = 'Feature Class'
XCentroid = 'xCentroid'
YCentroid = 'yCentroid'
tableField = 'GPIN2'

#Load values into Variables
scurfc = arcpy.UpdateCursor(inFeatures)                                        # UpdateCursor needs to be called, not SearchCursor
for sf in scurfc:
    strX = repr(sf.getValue(XCentroid))
    strY = repr(sf.getValue(YCentroid))
    GPIN = (strX[2] + strY[1] + strX[3] + strY[2] + "-" +
                 strX[4] + strY[3] + "-" +
                 strX[5] + strY[4] + strX[6] + strY[5])

    #Update values and delete variables
    sf.setValue(tableField, GPIN)                                                      # setValue and updateRow need to be called IN the loop
    scurfc.updateRow(sf)
del tableField, scurfc, strX, strY, XCentroid, YCentroid, GPIN          # X and Y no longer exist, so there shouldn't be an attempt to delete them.


I tend not to use repr() as it's a bit less common and it has some funky properties in limit cases (like returning hex values for non-Unicode strings) but you are right: it does do the job here.

In answer to your questions:
setValue is called inline of the loop cycling through the rows (which is the for sf in scurfc line.) The process is usually as follows for updating values based on other values in a table:
1) Cycle through rows, one by one
2) Get input from the value of a given column on the current row (like the sf.getValue(XCentroid) line in the code)
3) Do calculations to get the output value for the current row (like your GPIN = expression line)
4) Write value to the output field of the current row (this is the sf.setValue(tableField, GPIN) line)
5) Ask the table to update the current row so that the values are written to the actual table (this is the scurfc.updateRow(sf) line)
6) Rinse, repeat

As you can see, these steps need to be done for each row, which is why everything is inside the "for sf in scurfc" loop. There's no need to loop a second time, or write the values to an array, as the values are updated directly into the table, one row at a time.

Cheers,
Marc
0 Kudos
DarrenJones
New Contributor II
No worries Marc!

I should have seen the cursor type change. 😮

That worked like a champ in both PythonWin and in the Python console, but for some reason it is having issues in the Field Calculator. *shrug*  I can handle that part by just running it from a script tied to a button...that'll be easier for my users anyway. My next step is to have it read the geometry directly and I think I know how that can be accomplished.

Thank you very much for your help.
0 Kudos
MarcNakleh
New Contributor III
Hi Darren,

No problem at all!
The only thing I would add is that the Field Calculator works a bit differently from the other environments you mentioned. The Field Calculator itself actually functions as an Update Cursor, so there's no need to scroll through values one row at a time: Field Calculator will do that for you!

In fact, the code would look much simpler (though there is usually a need to separate the bulk of the code into a function under Code Block so as not to clutter the one-line Expression field)

########
# GPIN2 = #
########
compile_GPIN(!XCentroid!, !YCentroid!)


########################
# Code Block / Pre-Logic Script Code#
########################
def compile_GPIN(XCentroid, YCentroid):    
    strX = str(XCentroid)
    strY = str(YCentroid)
    GPIN = (strX[2] + strY[1] + strX[3] + strY[2] + "-" +
                 strX[4] + strY[3] + "-" +
                 strX[5] + strY[4] + strX[6] + strY[5])
    return GPIN


The !XCentroid! and !YCentroid! passed to the compile_GPIN function keep sending the appropriate values for those fields, one row at a time. And, for each row, the 'return' sends the GPIN value back to the field to be written, one row at a time.

Best of luck in the rest of your program!
Marc
0 Kudos
DarrenJones
New Contributor II
Once again, thank you Marc!

I have a lot to learn and your assistance has put me on the right path.

Darren
0 Kudos