Lessons 3 and 4 contain practice exercises that are longer than the previous practice exercises and are designed to prepare you specifically for the projects. You should make your best attempt at each practice exercise before looking at the solution. If you get stuck, study the solution until you understand it.
Don't spend so much time on the practice exercises that you neglect Project 3. However, successfully completing the practice exercises will make Project 3 much easier and quicker for you.
The data for the Lesson 3 practice exercises is very simple and, like some of the Project 2 practice exercise data, was derived from Washington State Department of Transportation [1] datasets. Download the data here [2].
Using the discussion forums is a great way to work towards figuring out the practice exercises. You are welcome to post blocks of code on the forums relating to these exercises.
When completing the actual Project 3, avoid posting blocks of code longer than a few lines. If you have a question about your Project 3 code, please email the instructor, or you can post general questions to the forums that don't contain more than a few lines of code.
If the practice exercises look daunting to you, you might start by practicing with your cursors a little bit using the sample data:
You can post thoughts on the above challenges on the forums.
In this practice exercise, you will programmatically select features by location and update a field for the selected features. You'll also use your selection to perform a calculation.
In your Lesson3PracticeExerciseA folder, you have a Washington geodatabase with two feature classes:
You want to find out which cities contain park and ride facilities and what percentage of cities have at least one facility.
You do not have to make a script tool for this assignment. You can hard-code the variable values. Try to group the hard-coded string variables at the beginning of the script.
For the purposes of these practice exercises, assume that each point in the ParkAndRide dataset represents one valid park and ride (ignore the value in the TYPE field).
You can jump into the assignment at this point, or read the following tips to give you some guidance.
Below is one possible solution to Practice Exercise A with comments to explain what is going on. If you find a more efficient way to code a solution, please share it through the discussion forums. Please note that in order to make the changes to citiesLayer permanent, you have to write the layer back to disk using the arcpy.CopyFeatures_management(...) function. This is not shown in the solution here.
# This script determines the percentage of cities in the # state with park and ride facilities import arcpy arcpy.env.overwriteOutput = True arcpy.env.workspace = r"C:\PSU\geog485\L3\PracticeExerciseA\Washington.gdb" cityBoundariesFC = "CityBoundaries" parkAndRideFC = "ParkAndRide" parkAndRideField = "HasParkAndRide" # Name of column with Park & Ride information citiesWithParkAndRide = 0 # Used for counting cities with Park & Ride try: # Narrow down the cities layer to only the cities that contain a park and ride citiesLayer = arcpy.SelectLayerByLocation_management(cityBoundariesFC, "CONTAINS", parkAndRideFC) # Create an update cursor and loop through the selected records with arcpy.da.UpdateCursor(citiesLayer, (parkAndRideField)) as cursor: for row in cursor: # Set the park and ride field to TRUE and keep a tally row[0] = "True" cursor.updateRow(row) citiesWithParkAndRide += 1 except: print ("There was a problem performing the spatial selection or updating the cities feature class") # Delete the feature layers even if there is an exception (error) raised finally: arcpy.Delete_management(citiesLayer) del row, cursor # Count the total number of cities (this tool saves you a loop) numCitiesCount = arcpy.GetCount_management(cityBoundariesFC) numCities = int(numCitiesCount[0]) # Get the number of cities in the feature layer #citiesWithParkAndRide = int(citiesLayer[2]) # Calculate the percentage and print it for the user percentCitiesWithParkAndRide = (citiesWithParkAndRide / numCities) * 100 print (str(round(percentCitiesWithParkAndRide,1)) + " percent of cities have a park and ride.")
Below is a video offering some line-by-line commentary on the structure of this solution:
Here is a different solution to Practice Exercise A, which uses the alternate syntax discussed in the lesson:
# This script determines the percentage of cities in the # state with park and ride facilities import arcpy arcpy.env.overwriteOutput = True arcpy.env.workspace = r"C:\PSU\geog485\L3\PracticeExerciseA\Washington.gdb" cityBoundariesFC = "CityBoundaries" parkAndRideFC = "ParkAndRide" parkAndRideField = "HasParkAndRide" # Name of column with Park & Ride information citiesWithParkAndRide = 0 # Used for counting cities with Park & Ride try: # Make a feature layer of all the park and ride facilities arcpy.MakeFeatureLayer_management(parkAndRideFC, "ParkAndRideLayer") # Make a feature layer of all the cities polygons arcpy.MakeFeatureLayer_management(cityBoundariesFC, "CitiesLayer") except: print ("Could not create feature layers") try: # Narrow down the cities layer to only the cities that contain a park and ride arcpy.SelectLayerByLocation_management("CitiesLayer", "CONTAINS", "ParkAndRideLayer") # Create an update cursor and loop through the selected records with arcpy.da.UpdateCursor("CitiesLayer", (parkAndRideField)) as cursor: for row in cursor: # Set the park and ride field to TRUE and keep a tally row[0] = "True" cursor.updateRow(row) citiesWithParkAndRide +=1 except: print ("There was a problem performing the spatial selection or updating the cities feature class") # Delete the feature layers even if there is an exception (error) raised finally: arcpy.Delete_management("ParkAndRideLayer") arcpy.Delete_management("CitiesLayer") del row, cursor # Count the total number of cities (this tool saves you a loop) numCitiesCount = arcpy.GetCount_management(cityBoundariesFC) numCities = int(numCitiesCount[0]) # Calculate the percentage and print it for the user percentCitiesWithParkAndRide = (citiesWithParkAndRide / numCities) * 100 print (str(round(percentCitiesWithParkAndRide,1)) + " percent of cities have a park and ride.")
Below is a video offering some line-by-line commentary on the structure of this solution:
If you look in your Lesson3PracticeExerciseB folder, you'll notice the data is exactly the same as for Practice Exercise A...except, this time the field is "HasTwoParkAndRides."
In Practice Exercise B, your assignment is to find which cities have at least two park and rides within their boundaries.
This simple modification in requirements is a game changer. The following is one way you can approach the task. Notice that it is very different from what you did in Practice Exercise A:
Below is one possible solution to Practice Exercise B with comments to explain what is going on. If you find a more efficient way to code a solution, please share it through the discussion forums.
# This script determines the percentage of cities with two park # and ride facilities import arcpy arcpy.env.overwriteOutput = True arcpy.env.workspace = r"C:\PSU\geog485\L3\PracticeExerciseB\Washington.gdb" cityBoundariesFC = "CityBoundaries" parkAndRideFC = "ParkAndRide" parkAndRideField = "HasTwoParkAndRides" # Name of column for storing the Park & Ride information cityIDStringField = "CI_FIPS" # Name of column with city IDs citiesWithTwoParkAndRides = 0 # Used for counting cities with at least two P & R facilities numCities = 0 # Used for counting cities in total # Make an update cursor and loop through each city with arcpy.da.UpdateCursor(cityBoundariesFC, (cityIDStringField, parkAndRideField)) as cityRows: for city in cityRows: # Create a query string for the current city cityIDString = city[0] whereClause = cityIDStringField + " = '" + cityIDString + "'" print("Processing city " + cityIDString) # Make a feature layer of just the current city polygon currentCityLayer = arcpy.SelectLayerByAttribute_management(cityBoundariesFC, "NEW_SELECTION", whereClause) try: # Narrow down the park and ride layer by selecting only the park and rides in the current city selectedParkAndRideLayer = arcpy.SelectLayerByLocation_management(parkAndRideFC, "CONTAINED_BY", currentCityLayer) # Count the number of park and ride facilities selected numSelectedParkAndRide = int(selectedParkAndRideLayer[2]) # If more than two park and ride facilities found, update the row to TRUE if numSelectedParkAndRide >= 2: city[1] = "TRUE" # Don't forget to call updateRow cityRows.updateRow(city) # Add 1 to your tally of cities with two park and rides citiesWithTwoParkAndRides += 1 numCities += 1 except: print("Problem determining number of ParkAndRides in " + cityIDString) finally: # Clean up feature layers arcpy.Delete_management(selectedParkAndRideLayer) arcpy.Delete_management(currentCityLayer) del city, cityRows # Calculate and report the number of cities with two park and rides if numCities != 0: percentCitiesWithParkAndRide = (citiesWithTwoParkAndRides / numCities) * 100 print (str(round(percentCitiesWithParkAndRide,1)) + " percent of cities have two park and rides.") else: print ("Error with input dataset. No cities found.")
The video below offers some line-by-line commentary on the structure of the above solution:
Here is a different solution to Practice Exercise B, which uses the alternate syntax discussed in the lesson.
# This script determines the percentage of cities with two park # and ride facilities import arcpy arcpy.env.overwriteOutput = True arcpy.env.workspace = r"C:\PSU\geog485\L3\PracticeExerciseB\Washington.gdb" cityBoundariesFC = "CityBoundaries" parkAndRideFC = "ParkAndRide" parkAndRideField = "HasTwoParkAndRides" # Name of column for storing the Park & Ride information cityIDStringField = "CI_FIPS" # Name of column with city IDs citiesWithTwoParkAndRides = 0 # Used for counting cities with at least two P & R facilities numCities = 0 # Used for counting cities in total # Make a feature layer of all the park and ride facilities arcpy.MakeFeatureLayer_management(parkAndRideFC, "ParkAndRideLayer") # Make an update cursor and loop through each city with arcpy.da.UpdateCursor(cityBoundariesFC, (cityIDStringField, parkAndRideField)) as cityRows: for city in cityRows: # Create a query string for the current city cityIDString = city[0] whereClause = cityIDStringField + " = '" + cityIDString + "'" print("Processing city " + cityIDString) # Make a feature layer of just the current city polygon arcpy.MakeFeatureLayer_management(cityBoundariesFC, "CurrentCityLayer", whereClause) try: # Narrow down the park and ride layer by selecting only the park and rides # in the current city arcpy.SelectLayerByLocation_management("ParkAndRideLayer", "CONTAINED_BY", "CurrentCityLayer") # Count the number of park and ride facilities selected selectedParkAndRideCount = arcpy.GetCount_management("ParkAndRideLayer") numSelectedParkAndRide = int(selectedParkAndRideCount[0]) # If more than two park and ride facilities found, update the row to TRUE if numSelectedParkAndRide >= 2: city[1] = "TRUE" # Don't forget to call updateRow cityRows.updateRow(city) # Add 1 to your tally of cities with two park and rides citiesWithTwoParkAndRides += 1 numCities += 1 except: print("Problem determining number of ParkAndRides in " + cityIDString) finally: # Clean up feature layer arcpy.Delete_management("CurrentCityLayer") # Clean up feature layer arcpy.Delete_management("ParkAndRideLayer") del city, cityRows # Calculate and report the number of cities with two park and rides if numCities != 0: percentCitiesWithParkAndRide = (citiesWithTwoParkAndRides / numCities) * 100 print (str(round(percentCitiesWithParkAndRide,1)) + " percent of cities have two park and rides.") else: print ("Error with input dataset. No cities found.")
The video below offers some line-by-line commentary on the structure of the above solution:
Testing these scripts on a ~2-year-old Dell laptop running Windows 10 with 16GB of RAM yielded some very different results. In 5 trials of both scripts, the first one needed an average of 240 seconds to complete. The second one (using the older syntax) needed only 83 seconds. This may seem counterintuitive, though it's interesting to note that the newer syntax was the faster performing version of the scripts for the other 3 exercises (times shown in seconds):
Exercise | Old | New |
A | 0.99 | 0.45 |
B | 83.67 | 240.33 |
C | 1.78 | 0.85 |
D | 1.46 | 0.92 |
You can check the timing of the scripts on your machine by adding the following lines just after the import arcpy statement:
import time process_start_time = time.time()
and this line at the end of the script:
print ("--- %s seconds ---" % (time.time() - process_start_time))
If you're interested in learning more about testing your script's performance along with methods for determining how long it takes to execute various parts of your script (e.g., to explore why there is such a difference in performance between the two Exercise B solutions), you should consider enrolling in our GEOG 489 class [5].
This practice exercise uses the same starting data as Lesson 3 Practice Exercise A. It is designed to give you practice with extracting data based on an attribute query.
Select all park and ride facilities with a capacity of more than 500 parking spaces and put them into their own feature class. The capacity of each park and ride is stored in the "Approx_Par" field.
Use the SelectLayerByAttribute_management tool to perform the selection. You will set up a SQL expression and pass it in as the third parameter for this tool.
Once you make the selection, use the Copy Features [6] tool to create the new feature class. You can pass a feature layer directly into the Copy Features tool, and it will make a new feature class from all features in the selection.
Remember to delete your feature layers when you are done.
Below is one approach to Lesson 3 Practice Exercise C. The number of spaces to query is stored in a variable at the top of the script, allowing for easy testing with other values.
# Selects park and ride facilities with over a certain number of parking spots # and exports them to a new feature class using CopyFeatures import arcpy parkingSpaces = 500 arcpy.env.workspace = r"C:\PSU\geog485\L3\PracticeExerciseC\Washington.gdb" arcpy.env.overwriteOutput = True # Set up the SQL expression to query the parking capacity parkingQuery = "Approx_Par > " + str(parkingSpaces) # Select the park and rides that applies the SQL expression parkAndRideLayer = arcpy.SelectLayerByAttribute_management("ParkAndRide", "NEW_SELECTION", parkingQuery) # Copy the features to a new feature class and clean up arcpy.CopyFeatures_management(parkAndRideLayer, "BigParkAndRideFacilities") arcpy.Delete_management(parkAndRideLayer)
The video below offers some line-by-line commentary on the structure of the above solution:
Below is an alternate approach to the exercise.
# Selects park and ride facilities with over a certain number of parking spots # and exports them to a new feature class using CopyFeatures import arcpy parkingSpaces = 500 arcpy.env.workspace = r"C:\PSU\geog485\L3\PracticeExerciseC\Washington.gdb" arcpy.env.overwriteOutput = True # Set up the SQL expression to query the parking capacity parkingQuery = "Approx_Par > " + str(parkingSpaces) # Make a feature layer of park and rides that applies the SQL expression arcpy.MakeFeatureLayer_management("ParkAndRide", "ParkAndRideLayer", parkingQuery) # Copy the features to a new feature class and clean up arcpy.CopyFeatures_management("ParkAndRideLayer", "BigParkAndRideFacilities") arcpy.Delete_management("ParkAndRideLayer")
The video below offers some line-by-line commentary on the structure of the above solution:
This practice exercise requires applying both an attribute selection and a spatial selection. It is directly applicable to Project 3 in many ways. The data is the same that you used in exercises A and C.
Write a script that selects all the park and ride facilities in a given city and saves them out to a new feature class. You can test with the city of 'Federal Way'.
Start by making a feature layer from the CityBoundaries feature class that contains just the city in question. You'll then need to make a feature layer from the park and ride feature class and perform a spatial selection on it using the "WITHIN" operation. Then use the Copy Features tool as in the previous lesson to move the selected park and ride facilities into their own feature class.
Below is one possible approach to Lesson 3 Practice Exercise D. Notice that the city name is stored near the top of the script in a variable so that it can be tested with other values.
# Selects park and ride facilities in a given target city and # exports them to a new feature class import arcpy targetCity = "Federal Way" # Name of target city arcpy.env.workspace = r"C:\PSU\geog485\L3\PracticeExerciseD\Washington.gdb" arcpy.env.overwriteOutput = True parkAndRideFC = "ParkAndRide" # Name of P & R feature class citiesFC = "CityBoundaries" # Name of city feature class # Set up the SQL expression of the query for the target city cityQuery = "NAME = '" + targetCity + "'" # Select just the target city cityLayer = arcpy.SelectLayerByAttribute_management(citiesFC, "NEW_SELECTION", cityQuery) # Select all park and rides in the target city parkAndRideLayer = arcpy.SelectLayerByLocation_management(parkAndRideFC, "CONTAINED_BY", cityLayer) # Copy the features to a new feature class and clean up arcpy.CopyFeatures_management(parkAndRideLayer, "TargetParkAndRideFacilities") arcpy.Delete_management(parkAndRideLayer) arcpy.Delete_management(cityLayer)
See the video below for some line-by-line commentary on the above solution:
Here is an alternate solution for this exercise:
# Selects park and ride facilities in a given target city and # exports them to a new feature class import arcpy targetCity = "Federal Way" # Name of target city arcpy.env.workspace = r"C:\PSU\geog485\L3\PracticeExerciseD\Washington.gdb" arcpy.env.overwriteOutput = True parkAndRideFC = "ParkAndRide" # Name of P & R feature class citiesFC = "CityBoundaries" # Name of city feature class # Set up the SQL expression of the query for the target city cityQuery = "NAME = '" + targetCity + "'" # Make feature layers for the target city and park and rides arcpy.MakeFeatureLayer_management(citiesFC, "CityLayer", cityQuery) arcpy.MakeFeatureLayer_management(parkAndRideFC, "ParkAndRideLayer") # Select all park and rides in the target city arcpy.SelectLayerByLocation_management("ParkAndRideLayer", "CONTAINED_BY", "CityLayer") # Copy the features to a new feature class and clean up arcpy.CopyFeatures_management("ParkAndRideLayer", "TargetParkAndRideFacilities") arcpy.Delete_management("ParkAndRideLayer") arcpy.Delete_management("CityLayer")
See the video below for some line-by-line commentary on the above solution:
Links
[1] https://gisdata-wsdot.opendata.arcgis.com/
[2] https://www.e-education.psu.edu/geog485/sites/www.e-education.psu.edu.geog485/files/data/Lesson3PracticeExercises.zip
[3] https://pro.arcgis.com/en/pro-app/tool-reference/data-management/get-count.htm
[4] https://creativecommons.org/licenses/by-nc-sa/4.0/
[5] https://www.e-education.psu.edu/geog489/
[6] https://pro.arcgis.com/en/pro-app/tool-reference/data-management/copy-features.htm