9 votos

Acelerar Python calculado campo de marca de hora en ArcGIS Desktop?

Soy nuevo en Python y han comenzado a crear secuencias de comandos para ArcGIS flujos de trabajo. Me pregunto cómo se puede acelerar la velocidad de mi código para generar un "Horas" doble campo numérico de un campo de marca de hora. Voy a empezar con una pista de registro de punto (ruta de navegación) shapefile generado por DNR Garmin, con un LTIME campo de marca de hora (un campo de texto, longitud 20) para cuando cada trackpoint registro fue tomado. El script calcula la diferencia en Horas entre cada una de las sucesivas marca de tiempo ("LTIME"), y pone que en un nuevo campo ("las Horas").

De que manera puedo volver atrás y resumir cuánto tiempo pasé en una determinada área o polígono. La parte principal es después de la print "Executing getnextLTIME.py script..." Aquí está el código:

# ---------------------------------------------------------------------------
# 
# Created on: Sept 9, 2010
# Created by: The Nature Conservancy
# Calculates delta time (hours) between successive rows based on timestamp field
#
# Credit should go to Richard Crissup, ESRI DTC, Washington DC for his
# 6-27-2008 date_diff.py posted as an ArcScript
'''
    This script assumes the format "month/day/year hours:minutes:seconds".
    The hour needs to be in military time. 
    If you are using another format please alter the script accordingly. 
    I do a little checking to see if the input string is in the format
    "month/day/year hours:minutes:seconds" as this is a common date time
    format. Also the hours:minute:seconds is included, otherwise we could 
    be off by almost a day.

    I am not sure if the time functions do any conversion to GMT, 
    so if the times passed in are in another time zone than the computer
    running the script, you will need to pad the time given back in 
    seconds by the difference in time from where the computer is in relation
    to where they were collected.

'''
# ---------------------------------------------------------------------------
#       FUNCTIONS
#----------------------------------------------------------------------------        
import arcgisscripting, sys, os, re
import time, calendar, string, decimal
def func_check_format(time_string):
    if time_string.find("/") == -1:
        print "Error: time string doesn't contain any '/' expected format \
            is month/day/year hour:minutes:seconds"
    elif time_string.find(":") == -1:
        print "Error: time string doesn't contain any ':' expected format \
            is month/day/year hour:minutes:seconds"

        list = time_string.split()
        if (len(list)) <> 2:
            print "Error time string doesn't contain and date and time separated \
                by a space. Expected format is 'month/day/year hour:minutes:seconds'"


def func_parse_time(time_string):
'''
    take the time value and make it into a tuple with 9 values
    example = "2004/03/01 23:50:00". If the date values don't look like this
    then the script will fail. 
'''
    year=0;month=0;day=0;hour=0;minute=0;sec=0;
    time_string = str(time_string)
    l=time_string.split()
    if not len(l) == 2:
        gp.AddError("Error: func_parse_time, expected 2 items in list l got" + \
            str(len(l)) + "time field value = " + time_string)
        raise Exception 
    cal=l[0];cal=cal.split("/")
    if not len(cal) == 3:
        gp.AddError("Error: func_parse_time, expected 3 items in list cal got " + \
            str(len(cal)) + "time field value = " + time_string)
        raise Exception
    ti=l[1];ti=ti.split(":")
    if not len(ti) == 3:
        gp.AddError("Error: func_parse_time, expected 3 items in list ti got " + \
            str(len(ti)) + "time field value = " + time_string)
        raise Exception
    if int(len(cal[0]))== 4:
        year=int(cal[0])
        month=int(cal[1])
        day=int(cal[2])
    else:
        year=int(cal[2])
        month=int(cal[0])
        day=int(cal[1])       
    hour=int(ti[0])
    minute=int(ti[1])
    sec=int(ti[2])
    # formated tuple to match input for time functions
    result=(year,month,day,hour,minute,sec,0,0,0)
    return result


#----------------------------------------------------------------------------

def func_time_diff(start_t,end_t):
    '''
    Take the two numbers that represent seconds
    since Jan 1 1970 and return the difference of
    those two numbers in hours. There are 3600 seconds
    in an hour. 60 secs * 60 min   '''

    start_secs = calendar.timegm(start_t)
    end_secs = calendar.timegm(end_t)

    x=abs(end_secs - start_secs)
    #diff = number hours difference
    #as ((x/60)/60)
    diff = float(x)/float(3600)   
    return diff

#----------------------------------------------------------------------------

print "Executing getnextLTIME.py script..."

try:
    gp = arcgisscripting.create(9.3)

    # set parameter to what user drags in
    fcdrag = gp.GetParameterAsText(0)
    psplit = os.path.split(fcdrag)

    folder = str(psplit[0]) #containing folder
    fc = str(psplit[1]) #feature class
    fullpath = str(fcdrag)

    gp.Workspace = folder

    fldA = gp.GetParameterAsText(1) # Timestamp field
    fldDiff = gp.GetParameterAsText(2) # Hours field

    # set the toolbox for adding the field to data managment
    gp.Toolbox = "management"
    # add the user named hours field to the feature class
    gp.addfield (fc,fldDiff,"double")
    #gp.addindex(fc,fldA,"indA","NON_UNIQUE", "ASCENDING")

    desc = gp.describe(fullpath)
    updateCursor = gp.UpdateCursor(fullpath, "", desc.SpatialReference, \
        fldA+"; "+ fldDiff, fldA)
    row = updateCursor.Next()
    count = 0
    oldtime = str(row.GetValue(fldA))
    #check datetime to see if parseable
    func_check_format(oldtime)
    gp.addmessage("Calculating " + fldDiff + " field...")

    while row <> None:
        if count == 0:
            row.SetValue(fldDiff, 0)
        else:
            start_t = func_parse_time(oldtime)
            b = str(row.GetValue(fldA))
            end_t = func_parse_time(b)
            diff_hrs = func_time_diff(start_t, end_t)
            row.SetValue(fldDiff, diff_hrs)
            oldtime = b

        count += 1
        updateCursor.UpdateRow(row)
        row = updateCursor.Next()

    gp.addmessage("Updated " +str(count+1)+ " rows.")
    #gp.removeindex(fc,"indA")
    del updateCursor
    del row

except Exception, ErrDesc:
    import traceback;traceback.print_exc()

print "Script complete."

12voto

Tom Puntos 720

Cursor son siempre muy lento en el entorno de geoprocesamiento. La forma más sencilla de evitar esto es para pasar un bloque de código Python en el CalculateField herramienta de geoprocesamiento.

Algo como esto debería funcionar:

import arcgisscripting
gp = arcgisscripting.create(9.3)

# Create a code block to be executed for each row in the table
# The code block is necessary for anything over a one-liner.
codeblock = """
import datetime
class CalcDiff(object):
    # Class attributes are static, that is, only one exists for all 
    # instances, kind of like a global variable for classes.
    Last = None
    def calcDiff(self,timestring):
        # parse the time string according to our format.
        t = datetime.datetime.strptime(timestring, '%m/%d/%Y %H:%M:%S')
        # return the difference from the last date/time
        if CalcDiff.Last:
            diff =  t - CalcDiff.Last
        else:
            diff = datetime.timedelta()
        CalcDiff.Last = t
        return float(diff.seconds)/3600.0
"""

expression = """CalcDiff().calcDiff(!timelabel!)"""

gp.CalculateField_management(r'c:\workspace\test.gdb\test','timediff',expression,   "PYTHON", codeblock)

Obviamente habría que modificarla para que tome los campos y como parámetros, pero debe ser bastante rápido.

Tenga en cuenta que aunque su fecha y hora funciones de análisis son en realidad un cabello más rápido que el strptime() la función de la biblioteca estándar es casi siempre más libre de errores.

3voto

Eric Scrivner Puntos 1392

@David ha dado una solución muy limpia. +1 para el uso de las fortalezas de la arcgisscripting base de código.

Otra opción es copiar el conjunto de datos en memoria utilizando:

  • gp.CopyFeatureclass("ruta de acceso de su origen", "in_memory\copiado nombre de la característica") - para una Clase de entidad de Geodatabase, o shapefile,
  • gp.CopyRows("ruta de acceso de su origen", ) - para una tabla de Geodatabase, dbf, etc

Esto elimina la sobrecarga que se produce cuando se solicita un cursor de ESRI COM base de código.

La sobrecarga viene alrededor de la conversión de python tipos de datos en C tipos de datos y el acceso a la ESRI COM base de código.

Cuando usted tiene los datos en la memoria, se están reduciendo la necesidad de acceder a un disco (un alto costo de proceso). También, se reduce la necesidad de python y C/C++ bibliotecas para la transferencia de datos, cuando se utiliza arcgisscripting.

Espero que esto ayude.

1voto

UnkwnTech Puntos 21942

Una excelente alternativa al uso de un estilo antiguo UpdateCursor de arcgisscripting, que ha estado disponible ha estado disponible desde ArcGIS 10.1 for Desktop, es arcpy.da.UpdateCursor.

He encontrado que estos son típicamente alrededor de 10 veces más rápido.

Estos/no puede haber sido una opción cuando esta pregunta fue escrito, pero no debe ser pasado por alto por cualquier persona que lea esto Q&A ahora.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X