Drive Time Trade Areas pass field variable as a distance

754
2
02-12-2020 07:14 AM
ClintonCooper2
New Contributor III

I have a dataset of 600,000 records where I need to calculate a drive time that is different for each based on model that estimates the actual/variable drive time needed at that location.  Within ArcPro (2.5) I can create a drive time trade area but it only allows input for 1 specific drive time point.  How can I change the python code so that it reads from the record/file the specific drive time that I need for it to calculate?

Here is the Business Analyst drive time code:

arcpy.ba.GenerateDriveTimeTradeArea("Heat_Map_Grids_Point", r"C:\Default.gdb\Heat_Map_Drive_Times", "Driving Time", [5], "MINUTES", "GRID_ID", "OVERLAP", "KEEP_OVERLAP", "TOWARD_STORES", None, "TIME_ZONE_AT_LOCATION", None, "STANDARD")

the [5] is the variable I need to convert to a field value.

2 Replies
JohnHendrickson
Esri Contributor

Currently it does not look like there is a parameter that would allow you to set that right away. It can be achieved with some additional python logic – essentially you would create a list of unique drive time values, make feature layers that only contain the points that equal each unique drive time (looping through), execute the drive time trade area tool for each grouping (looping through), and then merge them all together to create the final output.

 

There is an option for doing this in the Network Analyst extension; where you can create service areas which are essentially the same thing as the drive time trade areas. There is a parameter in there that allows you to set the value from a table when you are adding facilities. In this case you would set the “Breaks_TravelTime” field to the one in your table and those values would be used when solved. See the help documentation, under “Breaks_[Cost]” section, about this here: https://pro.arcgis.com/en/pro-app/help/analysis/networks/service-area-analysis-layer.htm

I have included a sample of how you may do this with python below. It is just an example and not necessarily optimized or full proof but is there for you to reference; works with in memory data along the way and you would need to make some changes to get it to work on your data.

import arcpy, os, datetime
#set start time
startTime = datetime.datetime.now()
current_workspace = arcpy.env.workspace

## Set Variables
#-----------------------------------------------------------------------------#
input_fc=r"C:\Sample_Data\ExampleGDB.gdb\Tessellations_Points"
output_fc =r"C:\Sample_Data\ExampleGDB.gdb\Drive_Times"
drive_time_Field = "Drive_Time_Value"
lry_lst = []
drive_time_lry_lst = []


###Functions to be executed
#-----------------------------------------------------------------------------#
## Function to return a list of unique drive time values.
def unique_values(table , field):
with arcpy.da.SearchCursor(table, [field]) as cursor:
return sorted({row[0] for row in cursor})

# Function to loop through each unique drive time and make a feature layer.
def tmp_lry(in_lry,drive_time_Field,dvr_times):
for row in dvr_times:
sql="""{0} = {1}""".format(arcpy.AddFieldDelimiters(in_lry, drive_time_Field),row)
slry_name = "Layer_Time_{0}".format(row)
print(sql)
arcpy.MakeFeatureLayer_management(in_features = in_lry, out_layer=slry_name ,where_clause = sql)
lry_lst.append(slry_name)

# Function to run GenerateDriveTimeTradeArea for each feature layer.
def drv_times(layer_list,id_field):
for val in lry_lst:
netwrok_time = val.split("_")[2]
lry_name = "Drive_Time_{0}".format(netwrok_time)
print(lry_name)
arcpy.ba.GenerateDriveTimeTradeArea(val, os.path.join("memory", lry_name), "Driving Time", netwrok_time, "MINUTES", id_field, "OVERLAP", "KEEP_OVERLAP", "TOWARD_STORES", None, "TIME_ZONE_AT_LOCATION", None, "STANDARD")
drive_time_lry_lst.append(lry_name)

##Run the functions
#-----------------------------------------------------------------------------#
myValues = unique_values(input_fc ,drive_time_Field)
print (myValues)
tmp_lry(input_fc,drive_time_Field,myValues)
drv_times(lry_lst,"GRID_ID")

#Merge separate drivetimes layers to one
arcpy.Merge_management(drive_time_lry_lst, output_fc)


#Clear memory datasets
arcpy.env.Workspace = "memory"
del_list = lry_lst+drive_time_lry_lst
for itm in del_list:
arcpy.Delete_management(itm)
print("Removed {0} from in memomry workspace".format(itm))

# Set workspace back to starting workspace
arcpy.env.workspace = current_workspace
print ("done")
print(datetime.datetime.now() - startTime)

I would also suggest that you post this on the ArcGIS Ideas page to request them adding the functionality to the tool in the future. This way others with the same request can vote on it if they also want to see it happen. - ArcGIS Ideas 

Good luck!

KyleWatson
Esri Contributor

John - thanks for pointing me to this GeoNet Post.

Clinton - we have some exciting development news in ArcGIS Pro 2.7 Business Analyst that should help you with situations like this.  We are adding support for Arcade expressions in Trade Area tools involving distance calculations.  This allows for awesome flexibility to control how your trade areas can be built, directly from fields in your data.

Here's a couple examples.

I have restaurants across a regional area, spanning cities and small towns.  I have a field in my restaurant database that tags "1" for urban designated stores, and "2" for rural stores.  I need to consider a farther driving distance for rural stores.  So the expression below can be used to build drive times that represent 30 min for all rural stores and 15 for urban areas.  Using the Generate Drive Time Trade Areas tool.

if ($feature.franchise_type == "1") {return 15} if ($feature.franchise_type == "2") {return 30}

In Telecom we are placing 5G locations.  On the main drag of Michigan Ave we want to analyze customer/home coverage within 400 meter buffers of each proposed location.  For all other locations we want to model them a bit differently, seeing what is included in 100 and 200 meter areas.  The expression below shows this.  Using the Generate Trade Area Rings tool.

if ($feature.street_name == "Michigan") {return 400} else {return [100, 200]}

There are countless other custom ways to use expressions, including controlling the size of areas based on ranges of data and threshold values.

Please feel free to reach out.

Kyle