ArcPy: Set Output Processing Extent (Bug!)

4894
8
Jump to solution
08-03-2015 10:17 AM
PeterWilson
Occasional Contributor III

I need some assistance to figure out if the following is a bug, if so what work around can I use. I've written a Python Script I need to change the Processing Extent for each feature layer being processed. I obtain the extent for each feature layer using the following Python code:

process_extent = arcpy.Describe(site_buffer).extent

If I print process_extent , the results are what I would expect:

process_extent.PNG

I then try to set the Processing Extent using the process_extent as input based on the following code:

arcpy.env.extent = process_extent

if I print arcpy.env.extent, the results are not what I would expect:

arcpyenvextent.PNG

1 Solution

Accepted Solutions
PeterWilson
Occasional Contributor III

Hi Xander

I figured out a workaround using ArcPy.da.SearchCursor:

'''
Created on Aug 3, 2015
Calculate the time to traverse
a terrain based on Toblers Hiking
Function based on slope
@author: PeterW
'''
# import system modules and site packages
import os
import time
import arcpy
from arcpy.sa import *
from difflib import Match
# process time Function
def hms_string(sec_elapsed):
    h = int(sec_elapsed / (60 * 60))
    m = int((sec_elapsed % (60 * 60)) / 60)
    s = sec_elapsed % 60
    return "{}h:{:>02}m:{:>05.2f}s".format(h, m, s)
# processing start time
start_time = time.time()
# set environment settings
arcpy.env.overwriteOutput = True
# check out extensions 
arcpy.CheckOutExtension("Spatial")
# set input and output workspace
inws = r"F:\Projects\2015\G111741\Model01\Rasters"
fgdb = r"F:\Projects\2015\G111741\Model01\Model01.gdb"
# calculate the slope in degrees
inras = os.path.join(inws, "raw2")
out_measurement = "DEGREE"
slope_deg = Slope(inras, out_measurement)
# spatial reference for output results
coordsys = arcpy.Describe(inras).spatialReference
# set site locations and input Toblers Hiking Function Vertical factor
sites = os.path.join(fgdb, "Schools_All")
sites_buffer = os.path.join(fgdb, "Schools_Buffer")
vertical_factor = arcpy.sa.VfTable(os.path.join(inws, "ToblerAway.txt")) #@UndefinedVariable
# create output feature class to store walk time polylines for each site_buffer
walk_poly = os.path.join(fgdb, "walk_poly")
# check if walk_poly exists else create it from scratch
if arcpy.Exists(walk_poly):
    arcpy.Delete_management(walk_poly)
    arcpy.CreateFeatureclass_management(fgdb, "walk_poly", "POLYLINE", "", "", "", coordsys, "", "", "", "")
    arcpy.AddField_management(walk_poly, "Name", "TEXT", "", "", 25, "", "", "", "")
    arcpy.AddField_management(walk_poly, "Contour", "SHORT", "", "", "", "", "", "", "")
else:
    arcpy.CreateFeatureclass_management(fgdb, "walk_poly", "POLYLINE", "", "", "", coordsys, "", "", "", "")
    arcpy.AddField_management(walk_poly, "Name", "TEXT", "", "", 25, "", "", "", "")
    arcpy.AddField_management(walk_poly, "Contour", "SHORT", "", "", "", "", "", "", "")
    
# walk distance\time (1km = 12min; 2km = 24min; 2.5km = 30min; 5km = 60min)
walk_int = [12, 24, 30, 60]
# create 5km buffers for each site to be used as processing extent
sites_buffer = os.path.join("in_memory", "sites_buffer")
arcpy.Buffer_analysis(sites, sites_buffer, "5 Kilometers")
with arcpy.da.SearchCursor(sites_buffer, ["OBJECTID", "NAME", "SHAPE@"]) as scur1: #@UndefinedVariable
    for row1 in scur1:
        with arcpy.da.SearchCursor(sites, ["OBJECTID", "NAME", "SHAPE@"]) as scur2: #@UndefinedVariable
            count = 0
            for row2 in scur2:
                count += 1
                if row1[1] == row2[1]:
                    # processing start time
                    start_time = time.time()
                    process_extent = row1[2].extent
                    arcpy.env.extent = process_extent
                    site_name = row2[1].replace(" ","_").replace("-","_").replace("(","").replace(")","")
                    sql_exp1 = "Name = '{}'".format(row2[1])
                    site_lyr = site_name
                    arcpy.MakeFeatureLayer_management(sites, site_lyr, sql_exp1)
                    cost = PathDistance(site_lyr, slope_deg, "", "", "", inras, vertical_factor, "","")
                    print("Processing {} Cost Raster").format(site_name)
                    costmin = Times(cost, 60)
                    print("Processing {} Cost per Minute Raster").format(site_name)
                    walk_name = "walk_cont" + "_" + str(count)
                    walk_cont = os.path.join("in_memory", walk_name)
                    print("Processing & Sorting {} Walk Contours").format(site_name)
                    ContourList(costmin, walk_cont, walk_int)
                    walk_name2 = walk_name + "_" + "sorted"
                    walk_sorted = os.path.join("in_memory", walk_name2)
                    arcpy.Sort_management(walk_cont, walk_sorted, [["Contour", "ASCENDING"]], "")
                    arcpy.AddField_management(walk_sorted, "Name", "TEXT", "", "", 50, "", "", "", "")
                    sql_exp2 = "'{}'".format(row2[1])
                    arcpy.CalculateField_management(walk_sorted, "Name", sql_exp2, "PYTHON_9.3")
                    with arcpy.da.SearchCursor(walk_sorted, ["SHAPE@", "Name", "Contour"]) as scur: #@UndefinedVariable
                        with arcpy.da.InsertCursor(walk_poly, ["SHAPE@", "Name", "Contour"]) as icur: #@UndefinedVariable
                            for srow in scur:
                                icur.insertRow(srow)
                    # Processing end time
                    end_time = time.time()
                    print "It took {} to execute {}".format(hms_string(end_time - start_time),site_name)
print ("Completed Processing All Sites")
# check in extensions 
arcpy.CheckInExtension("Spatial")
# processing end time
end_time = time.time()
print "It took {} to execute this".format(hms_string(end_time - start_time))

View solution in original post

8 Replies
PeterWilson
Occasional Contributor III

I should mention that I'm using ArcGIS 10.2.2 with Python  2.7.6

Regards

Peter Wilson

0 Kudos
XanderBakker
Esri Esteemed Contributor

That indeed looks like a bug. I just did a test looping through all the featureclasses in a File Geodatabase with version 10.3 I have here and there is no difference between the extent read from the fc and after the extent is set to the env.extent. When the "1,#QNAN 1,#QNAN 1,#QNAN 1,#QNAN NaN NaN NaN NaN" occurs it is already in the input featureclass extent.

Could you show the rest of the code to see if something else is happening with the extent?

0 Kudos
PeterWilson
Occasional Contributor III

Hi Xander

The full Python code is attached below:


'''
Created on Jul 16, 2015
Calculate the time to traverse a terrain based on
Toblers Hiking Function based on slope
@author: PeterW
'''
# import system modules and site packages
import os
import arcpy
from arcpy.sa import *
import time
def hms_string(sec_elapsed):
    h = int(sec_elapsed / (60 * 60))
    m = int((sec_elapsed % (60 * 60)) / 60)
    s = sec_elapsed % 60
    return "{}h:{:>02}m:{:>05.2f}s".format(h, m, s)
# End hms_string
start_time = time.time()
# set environment settings
arcpy.env.overwriteOutput = True
# check out extensions
arcpy.CheckOutExtension("Spatial")
# set input and output workspace
inws = r"F:\Projects\2015\G111741\Model01\Rasters"
fgdb = r"F:\Projects\2015\G111741\Model01\Model01.gdb"
arcpy.env.workspace = inws
# calculate the slope in degrees
inras = "raw2"
out_measurement = "DEGREE"
slope_deg = Slope(inras, out_measurement)
# spatial reference for output results
coordsys = arcpy.Describe(inras).spatialReference
# set site locations and input Tobler Hiking Function Vertical Factor
sites = os.path.join(fgdb,"Schools_All2")
vertical_factor = arcpy.sa.VfTable(os.path.join(inws, "ToblerAway.txt")) #@UndefinedVariable
# create output feature class to store walk time polylines for each site
walk_poly = os.path.join(fgdb, "walk_poly")
# check if walk_poly exists else create it from scratch
if arcpy.Exists(walk_poly):
    arcpy.Delete_management(walk_poly)
    arcpy.CreateFeatureclass_management(fgdb, "walk_poly", "POLYLINE", "", "", "", coordsys, "", "", "", "")
    arcpy.AddField_management(walk_poly, "Name", "TEXT", "", "", 25, "", "", "", "")
    arcpy.AddField_management(walk_poly, "Contour", "SHORT", "", "", "", "", "", "", "")
else:
    arcpy.CreateFeatureclass_management(fgdb, "walk_poly", "POLYLINE", "", "", "", coordsys, "", "", "", "")
    arcpy.AddField_management(walk_poly, "Name", "TEXT", "", "", 25, "", "", "", "")
    arcpy.AddField_management(walk_poly, "Contour", "SHORT", "", "", "", "", "", "", "")
    
# walking distance\time (1km = 12min; 2km = 24min; 2.5km = 30min; 5km = 60min)
walk_int = [12, 24, 30, 60]
with arcpy.da.SearchCursor(sites, ["NAME"]) as cursor: #@UndefinedVariable
    count = 0
    for row in cursor:
        count += 1
        site_name = row[0].replace(" ","_").replace("-","_").replace("(","").replace(")","") # remove non valid characters
        sql_exp1 = "Name = '{}'".format(row[0])
        site_lyr = site_name
        arcpy.MakeFeatureLayer_management(sites, site_lyr, sql_exp1) # create feature layer for each site
        site_buffer = os.path.join("in_memory", site_name)
        arcpy.Buffer_analysis(site_lyr, site_buffer, "5 Kilometers") # create 5 kilometer buffer for each site
        process_extent = arcpy.Describe(site_buffer).extent # set processing extent to 5 kilometer buffer of site
        arcpy.env.extent = process_extent # set processing extent to site buffer for each site location
        cost = PathDistance(site_lyr, slope_deg, "","","",inras, vertical_factor,"","")
        print ("Processing {} Cost Raster").format(site_name)
        costmin = Times(cost, 60)
        print ("Processing {} Cost per Minute Raster").format(site_name)
        walk_name = "walk_cont" + "_" + str(count)
        walk_cont = os.path.join("in_memory", walk_name) # save to in_memory
        print ("Processing & Sorting {} Walk Contours").format(site_name)
        ContourList(costmin, walk_cont, walk_int)
        walk_name2 = walk_name + "_" + "sorted"
        walk_sorted = os.path.join("in_memory", walk_name2)
        arcpy.Sort_management(walk_cont, walk_sorted, [["Contour", "ASCENDING"]],"")
        arcpy.AddField_management(walk_sorted, "Name", "TEXT", "", "", 50, "", "", "", "")
        sql_exp2 = "'{}'".format(row[0])
        arcpy.CalculateField_management(walk_sorted, "Name", sql_exp2, "PYTHON_9.3")
        with arcpy.da.SearchCursor(walk_sorted,["SHAPE@", "Name", "Contour"]) as scur: #@UndefinedVariable
            with arcpy.da.InsertCursor(walk_poly, ["SHAPE@", "Name", "Contour"]) as icur: #@UndefinedVariable
                for srow in scur:
                    icur.insertRow(srow)
print ("Completed Processing All Sites")
              
# check in extensions
arcpy.CheckInExtension("Spatial")
     
end_time = time.time()
print "It took {} to execute this".format(hms_string(end_time - start_time))

0 Kudos
PeterWilson
Occasional Contributor III

Hi Xander

I figured out a workaround using ArcPy.da.SearchCursor:

'''
Created on Aug 3, 2015
Calculate the time to traverse
a terrain based on Toblers Hiking
Function based on slope
@author: PeterW
'''
# import system modules and site packages
import os
import time
import arcpy
from arcpy.sa import *
from difflib import Match
# process time Function
def hms_string(sec_elapsed):
    h = int(sec_elapsed / (60 * 60))
    m = int((sec_elapsed % (60 * 60)) / 60)
    s = sec_elapsed % 60
    return "{}h:{:>02}m:{:>05.2f}s".format(h, m, s)
# processing start time
start_time = time.time()
# set environment settings
arcpy.env.overwriteOutput = True
# check out extensions 
arcpy.CheckOutExtension("Spatial")
# set input and output workspace
inws = r"F:\Projects\2015\G111741\Model01\Rasters"
fgdb = r"F:\Projects\2015\G111741\Model01\Model01.gdb"
# calculate the slope in degrees
inras = os.path.join(inws, "raw2")
out_measurement = "DEGREE"
slope_deg = Slope(inras, out_measurement)
# spatial reference for output results
coordsys = arcpy.Describe(inras).spatialReference
# set site locations and input Toblers Hiking Function Vertical factor
sites = os.path.join(fgdb, "Schools_All")
sites_buffer = os.path.join(fgdb, "Schools_Buffer")
vertical_factor = arcpy.sa.VfTable(os.path.join(inws, "ToblerAway.txt")) #@UndefinedVariable
# create output feature class to store walk time polylines for each site_buffer
walk_poly = os.path.join(fgdb, "walk_poly")
# check if walk_poly exists else create it from scratch
if arcpy.Exists(walk_poly):
    arcpy.Delete_management(walk_poly)
    arcpy.CreateFeatureclass_management(fgdb, "walk_poly", "POLYLINE", "", "", "", coordsys, "", "", "", "")
    arcpy.AddField_management(walk_poly, "Name", "TEXT", "", "", 25, "", "", "", "")
    arcpy.AddField_management(walk_poly, "Contour", "SHORT", "", "", "", "", "", "", "")
else:
    arcpy.CreateFeatureclass_management(fgdb, "walk_poly", "POLYLINE", "", "", "", coordsys, "", "", "", "")
    arcpy.AddField_management(walk_poly, "Name", "TEXT", "", "", 25, "", "", "", "")
    arcpy.AddField_management(walk_poly, "Contour", "SHORT", "", "", "", "", "", "", "")
    
# walk distance\time (1km = 12min; 2km = 24min; 2.5km = 30min; 5km = 60min)
walk_int = [12, 24, 30, 60]
# create 5km buffers for each site to be used as processing extent
sites_buffer = os.path.join("in_memory", "sites_buffer")
arcpy.Buffer_analysis(sites, sites_buffer, "5 Kilometers")
with arcpy.da.SearchCursor(sites_buffer, ["OBJECTID", "NAME", "SHAPE@"]) as scur1: #@UndefinedVariable
    for row1 in scur1:
        with arcpy.da.SearchCursor(sites, ["OBJECTID", "NAME", "SHAPE@"]) as scur2: #@UndefinedVariable
            count = 0
            for row2 in scur2:
                count += 1
                if row1[1] == row2[1]:
                    # processing start time
                    start_time = time.time()
                    process_extent = row1[2].extent
                    arcpy.env.extent = process_extent
                    site_name = row2[1].replace(" ","_").replace("-","_").replace("(","").replace(")","")
                    sql_exp1 = "Name = '{}'".format(row2[1])
                    site_lyr = site_name
                    arcpy.MakeFeatureLayer_management(sites, site_lyr, sql_exp1)
                    cost = PathDistance(site_lyr, slope_deg, "", "", "", inras, vertical_factor, "","")
                    print("Processing {} Cost Raster").format(site_name)
                    costmin = Times(cost, 60)
                    print("Processing {} Cost per Minute Raster").format(site_name)
                    walk_name = "walk_cont" + "_" + str(count)
                    walk_cont = os.path.join("in_memory", walk_name)
                    print("Processing & Sorting {} Walk Contours").format(site_name)
                    ContourList(costmin, walk_cont, walk_int)
                    walk_name2 = walk_name + "_" + "sorted"
                    walk_sorted = os.path.join("in_memory", walk_name2)
                    arcpy.Sort_management(walk_cont, walk_sorted, [["Contour", "ASCENDING"]], "")
                    arcpy.AddField_management(walk_sorted, "Name", "TEXT", "", "", 50, "", "", "", "")
                    sql_exp2 = "'{}'".format(row2[1])
                    arcpy.CalculateField_management(walk_sorted, "Name", sql_exp2, "PYTHON_9.3")
                    with arcpy.da.SearchCursor(walk_sorted, ["SHAPE@", "Name", "Contour"]) as scur: #@UndefinedVariable
                        with arcpy.da.InsertCursor(walk_poly, ["SHAPE@", "Name", "Contour"]) as icur: #@UndefinedVariable
                            for srow in scur:
                                icur.insertRow(srow)
                    # Processing end time
                    end_time = time.time()
                    print "It took {} to execute {}".format(hms_string(end_time - start_time),site_name)
print ("Completed Processing All Sites")
# check in extensions 
arcpy.CheckInExtension("Spatial")
# processing end time
end_time = time.time()
print "It took {} to execute this".format(hms_string(end_time - start_time))
XanderBakker
Esri Esteemed Contributor

Glad you resolved your problem. For speed considerations you may want to consider creating two dictionaries rather than using a nested cursor.

0 Kudos
JohnWall
Occasional Contributor

Peter,

Your code really helped me with a problem I was having! For anyone that can't be bothered to do a CTRL + F, look for Lines 66 & 67.

DanPatterson_Retired
MVP Emeritus

From the 10.3 help (since I don't know if 10.2 is fixed)

http://desktop.arcgis.com/en/desktop/latest/tools/environments/output-extent.htm

The extent entered is assumed to be in the coordinate system in which the input data is stored, even if the Output Coordinate System environment is set.  If the tool takes multiple input datasets, the first dataset defines the coordinate system of the extent

So technically, it should be reset if you reset it each time.

PeterWilson
Occasional Contributor III

HI Dan

Thanks for the reply, the coordinate systems are indeed the same.

Regards

0 Kudos