Select to view content in your preferred language

Code in python the equivalent of the VB Format function

8245
26
01-14-2011 03:32 AM
JoseSanchez
Frequent Contributor
Hi all

I am using ArcGIs 9.3 and Model Bulder. I was able to code a calculate field in VB that formats a field as follows Format("08.00.00") and converts it to "08:00:00 AM"

How can I do the same thing in Python (phyton 3 or python)?

Please advise!
Tags (2)
0 Kudos
26 Replies
KarlZimmer
Deactivated User
Hi Brad,

Thanks for the explanation. That helps a lot.

I got both field calculations to work perfectly using the following in the code block:

import datetime
def Calc(strDate):
       lstDate = map(int, strDate.split("/")) # create list of date integers  D(D), M(M), YYYY
       objDate = datetime.date(lstDate[2], lstDate[0], lstDate[1]) # create date object
       intDay = int(objDate.strftime("%j")) # integer day of the year
       for i in range(1, 366, 16):
   if intDay >= i and intDay < i+16:
               return  i
    return 0
import datetime
def Calc(strDate, strImageDate):
      lstDate = map(int, strDate.split("/")) # create list of date integers  D(D), M(M), YYYY
      objDate = datetime.date(lstDate[2], lstDate[0], lstDate[1]) # create date object
  objImage = "00" + strImageDate
      strTif = r"J:\GEOG683\HEB_NDVI_GEO" + r"\Montana_Canada_NDVI_A" + objDate.strftime("%Y") + "D" +          objImage[-3:] + ".tif"
      return strTif
Just was hoping you could clarify a couple more things. First in the Expression I put either Calc(!Date_Time!) or Calc(!Date_Time!,!ImgDate!) ....do I put this expression because it means the code block is calculating using values from the fields Date_Time and ImgDate?

Unfortunately, I am still having another issue that I can't resolve. I tried all weekend to resolve it, but couldn't figure it out. My issue is when I export the above codeblocks to a python script outside of model builder that allows the user to input the name of the field (i.e. a field name other than ImgDate) and dates interval  (i.e value other then 16) using sys.argv it doesn't work. It seems the code within the code block string doesn't recognize these variables from outside the code block expression.

Specifically its not recognizing the variable assigned to DatesInterval inside the code block.

Here is the orignial python code exported from Modelbuilder that works great as long as the field is ImgDate and Date range is 16.

gp.CalculateField_management(InputFC, "ImgDate", "Calc(!Date_Time!)", "PYTHON", "import datetime\\ndef Calc(strDate):\\n    lstDate = map(int, strDate.split(\"/\")) # create list of date integers  D(D), M(M), YYYY\\n    objDate = datetime.date(lstDate[2], lstDate[0], lstDate[1]) # create date object\\n    intDay = int(objDate.strftime(\"%j\")) # integer day of the year\\n    for i in range(1, 366, 16):\\n        if intDay >= i and intDay < i+16:\\n            return  i\\n    return 0")
##print "Calculated ImgDate"
However, when I attmpet to alter this code with the variables for the field name (FieldName1) and variable for the image dates (DatesInterval) the code doesn't seem to pass the DatesInterval assignment within the codeblock string. I tried to assign it within the code block but it still says ExecuteError: ERROR 000539: Error running expression: Calc("5/29/2006") <type 'exceptions.UnboundLocalError'>: local variable 'DatesInterval' referenced before assignment

gp.CalculateField_management(InputFC, FieldName1, "Calc(!Date_Time!)", "PYTHON", "import datetime\\ndef Calc(strDate):\\n    DatesInterval = +DatesInterval\\n    lstDate = map(int, strDate.split(\"/\")) # create list of date integers  D(D), M(M), YYYY\\n    objDate = datetime.date(lstDate[2], lstDate[0], lstDate[1]) # create date object\\n    intDay = int(objDate.strftime(\"%j\")) # integer day of the year\\n    for i in range(1, 366, DatesInterval):\\n        if intDay >= i and intDay < i+DatesInterval:\\n            return  i\\n    return 0")
print "Calculated ImgDate"
Do you mind showing me how to pass a python variable/argument from outside the codeblock. I've attached the entire code of python if that helps.

Thank you so much for your detailed input.  Your effort and help is greatly appreciated.
0 Kudos
BradPosthumus
Frequent Contributor
Just was hoping you could clarify a couple more things. First in the Expression I put either Calc(!Date_Time!) or Calc(!Date_Time!,!ImgDate!) ....do I put this expression because it means the code block is calculating using values from the fields Date_Time and ImgDate?

Yes, that's exactly right. It iterates through each feature, passing the Date_Time and ImgDate value to the Calc function each time.

Do you mind showing me how to pass a python variable/argument from outside the codeblock. I've attached the entire code of python if that helps.


Once you move into a Python script, any CalculateField processes that contain code blocks should be thrown out. Code blocks are tolerable in ModelBuilder but they are a real nightmare to maintain in a script. Instead use an update cursor to manually iterate through each feature and do the every calculation you can.

I rearranged your code to add all the required fields first and then iterates through each feature using an update cursor to calculate the image date and raster path fields. The code is untested so if you run into any problems let me know.

One other thing: I'm not sure if you want the line "DatesInterval = +DatesInterval". This will continually change your DateInterval each time you pass through the loop. For example, if you're starting interval is 16, then the DateInterval for each feature will continue like so:
First feature:     DateInterval = 16
Second feature: DateInterval = 32
Third feature:    DateInterval = 48
etc.

I commented it out below if that's what you wanted then simply remove the comment.

try:
    # Process: Add Field containing Image Date...
    gp.AddField_management(InputFC, FieldName1, "TEXT", "", "", "", "", "NON_NULLABLE", "NON_REQUIRED", "")
    print "Added Field " + FieldName1
    gp.addmessage("Added Field " + FieldName1)

    # Process: Add Field to hold sampled value...
    gp.AddField_management(InputFC, FieldName2, "FLOAT", "", "", "", "", "NON_NULLABLE", "NON_REQUIRED", "")
    print "Added Sample Value Field"
    gp.addmessage("Added Field " + FieldName2)
    
    # Process: Add Field to hold Raster Path...
    gp.AddField_management(InputFC, FieldName3, "TEXT", "", "", "", "", "NON_NULLABLE", "NON_REQUIRED", "")
    print " Added Field containing Raster Path"
    gp.addmessage("Added Field " + FieldName3)    
   
    objFeatures = gp.UpdateCursor(InputFC):
    objFeature = objFeatures.Next()
    while objFeature:
        # Calculate Image Date
        # DatesInterval = +DatesInterval
        lstDate = map(int, strDate.split(\"/\"))
        objDate = datetime.date(lstDate[2], lstDate[0], lstDate[1])
        intDay = int(objDate.strftime(\"%j\"))
        intInterval = 0
        for i in range(1, 366, DatesInterval):
            if intDay >= i and intDay < i+DatesInterval:
                intInterval = i
                break
        objFeature.SetValue(FieldName1, intInterval)

        # Calculate Raster Path
        objImage = \"00\" + strImageDate
        strTif = RasterFolder + RasterPrefix + objDate.strftime(\"%Y\") + DatePrefix + objImage[-3:] + \".tif\"
        objFeature.SetValue(FieldName3, strTif)  

        objFeatures.UpdateRow(objFeature)
        objFeature = objFeatures.Next()
    del objFeature
    del objFeatures

    print "Calculated ImgDate"
    print "Calculated NDVI Raster Path"
    gp.addmessage("Calculated Raster Path in " + FieldName3)

    # Process: Sample Rasters Listed in Fields...
    gp.ArcGISRasterSamplerSampleRastersInFields_GeoEco(InputFC, FieldName3, FieldName2, "", "NEAREST", "", "", "")
    ...etc....
0 Kudos
KarlZimmer
Deactivated User
Hi Brad,

Thanks for the advice and updated code. Had I known, code blocks weren't so easily transferable to python scripts I would have never bothered to redevelop that code block. Oh well lesson learned I guess.  Thanks for the great advice though as it seems an update cursor would be a much more efficient solution. However, it seems the code you provided is not working correctly when I copy it over and try as I might I am unable to correct the issue. It seems to center around the use of the \ / characters in the code. Specifically, I am getting a syntax error for the line

objFeatures = gp.UpdateCursor(InputFC): 
  - which I think I resolved by removing the colon at the end.

The more complex errors that I am failing to understand are the backslash and forward slash characters that are giving an error message indicated an unexpected character after line continuation character within the following code lines:

lstDate = map(int, strDate.split(\"/\"))  
intDay = int(objDate.strftime(\"%j\")) 

objImage = \"00\" + strImageDate
strTif = RasterFolder + RasterPrefix + objDate.strftime(\"%Y\") + DatePrefix + objImage[-3:] + \".tif\"
The error in each line seems to occur after the first backslash in each line. I understand backslash can be line continuation characters, but I don't understand which ones I should be removing or why the code lines include these at all.

Thanks so much for all your time and effort on this. It is greatly appreciated and nice to be learning the rationale for why things are coded a certain way so I can hopefully avoid similar issues in the future. In anycase, your help is appreciated very very much! 🙂
0 Kudos
BradPosthumus
Frequent Contributor
Sorry, that's a careless mistake on my part. The backslashes were used an escape character for the original CalculateField code with the code block within it. In this case the backslash allows you to use a double-quote inside a string designated by double-quotes. So something like:

strString = "Field = "OBJECTID" "

doesn't work, but adding the backslash in front of the inner quotes will fix it:

strString = "Field = \"OBJECTID\" "

A better way in Python is to change the outer quotes to single quotes:

strString = 'Field = "OBJECTID" '

In your code, since these quotes are no longer encased in other double quotes, the escape character is no longer required. Change it then to:

lstDate = map(int, strDate.split("/"))  
intDay = int(objDate.strftime("%j")) 

objImage = "00" + strImageDate
strTif = RasterFolder + RasterPrefix + objDate.strftime("%Y") + DatePrefix + objImage[-3:] + ".tif"


Try that. Code is still untested so don't be surprised if other errors crop up....
0 Kudos
KarlZimmer
Deactivated User
Hi Brad,

Thanks for clearing that up. Thats sort of what I was thinking and had tried it but it didn't work, but I think thats due to another issue as its still not working. Now I get an error  for

    lstDate = map(int,strDate.split("/"))
ValueError: invalid literal for int() with base 10: 'DATE_TIME'

Thanks so much for your quick responses and effort on this!
0 Kudos
BradPosthumus
Frequent Contributor
Try this:

try:
    # Process: Add Field containing Image Date...
    gp.AddField_management(InputFC, FieldName1, "TEXT", "", "", "", "", "NULLABLE", "NON_REQUIRED", "")
    print "Added Field " + FieldName1
    gp.addmessage("Added Field " + FieldName1)

    # Process: Add Field to hold sampled value...
    gp.AddField_management(InputFC, FieldName2, "FLOAT", "", "", "", "", "NULLABLE", "NON_REQUIRED", "")
    print "Added Sample Value Field"
    gp.addmessage("Added Field " + FieldName2)
    
    # Process: Add Field to hold Raster Path...
    gp.AddField_management(InputFC, FieldName3, "TEXT", "", "", "", "", "NULLABLE", "NON_REQUIRED", "")
    print " Added Field containing Raster Path"
    gp.addmessage("Added Field " + FieldName3)    
   
    objFeatures = gp.UpdateCursor(InputFC)
    objFeature = objFeatures.Next()

    while objFeature:
        # Calculate Image Date
        # DatesInterval = +DatesInterval
        strDate = objFeature.GetValue("Date_Time")
    
        lstDate = map(int, strDate.split("/"))
        objDate = datetime.date(lstDate[2], lstDate[0], lstDate[1])
        intDay = int(objDate.strftime("%j"))
        intInterval = 0
        for i in range(1, 366, DatesInterval):
            if intDay >= i and intDay < i+DatesInterval:
                intInterval = i
                break
        objFeature.SetValue(FieldName1, str(intInterval))

        # Calculate Raster Path
        objImage = "00" + str(intInterval)
        strTif = RasterFolder + RasterPrefix + objDate.strftime("%Y") + DatePrefix + objImage[-3:] + ".tif"
        objFeature.SetValue(FieldName3, strTif)  

        objFeatures.UpdateRow(objFeature)
        objFeature = objFeatures.Next()
    del objFeature
    del objFeatures
0 Kudos
KarlZimmer
Deactivated User
Hi Brad,

Just realized I forgot to thank you for your last post! A couple minor changes and it worked like a charm! Thanks so much for your help and explaining the rationale behind things!

Cheers,
Karl
0 Kudos