GEOG 485:
GIS Programming and Automation

Lesson 3 Practice Exercise B Solution

PrintPrint

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 solution uses arcpy.da and is, hence, only working on ArcGIS 10.1 and higher. A very similar solution but one that also works for 10.0 is given below together with a video that explains that solution step by step.

# This script determines the percentage of cities with two park
#  and ride facilities

import arcpy
arcpy.env.overwriteOutput = True

cityBoundaries = "D:\\Data\\Geog485\\Lesson3PracticeExerciseB\\Washington.gdb\\CityBoundaries"
parkAndRide = "D:\\Data\\Geog485\\Lesson3PracticeExerciseB\\Washington.gdb\\ParkAndRide"
parkAndRideField = "HasTwoParkAndRides"
cityIDStringField = "CI_FIPS"
citiesWithTwoParkAndRides = 0
numCities = 0

# Make a feature layer of all the park and ride facilities
arcpy.MakeFeatureLayer_management(parkAndRide, "ParkAndRideLayer")

# Make an update cursor and loop through each city
with arcpy.da.UpdateCursor(cityBoundaries, (cityIDStringField, parkAndRideField)) as cityRows:
    for city in cityRows:
        # Create a query string for the current city    
        cityIDString = city[0]
        queryString = '"' + cityIDStringField + '" = ' + "'" + cityIDString + "'"

        # Make a feature layer of just the current city polygon    
        arcpy.MakeFeatureLayer_management(cityBoundaries, "CurrentCityLayer", queryString)

        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.getOutput(0))

            # If more the one park and ride 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

        finally:
            # Delete current cities layer to prepare for next run of loop
            arcpy.Delete_management("CurrentCityLayer")
            numCities +=1

# Clean up park and ride feature layer
arcpy.Delete_management("ParkAndRideLayer")

# Calculate and report the number of cities with two park and rides
if numCities <> 0:
    percentCitiesWithParkAndRide = ((1.0 * citiesWithTwoParkAndRides) / numCities) * 100
else:
    print "Error with input dataset. No cities found."

print str(percentCitiesWithParkAndRide) + " percent of cities have two park and rides."

Alternate solution for ArcGIS 10.0

The following solution shows how to solve the problem using arcpy.UpdateCursor(). The general approach is the same as in the solution above. Although this example is coded using a "while" loop, it could also be written using a "for" loop. The video at the end explains this solution step by step.

# This script determines the percentage of cities with two park
#  and ride facilities

import arcpy
arcpy.env.overwriteOutput = True

cityBoundaries = "C:\\Data\\Lesson3PracticeExerciseB\\Washington.gdb\\CityBoundaries"
parkAndRide = "C:\\Data\\Lesson3PracticeExerciseB\\Washington.gdb\\ParkAndRide"
parkAndRideField = "HasTwoParkAndRides"
cityIDStringField = "CI_FIPS"
citiesWithTwoParkAndRides = 0
numCities = 0

# Make a feature layer of all the park and ride facilities
arcpy.MakeFeatureLayer_management(parkAndRide, "ParkAndRideLayer")

# Start looping through each city
cityRows = arcpy.UpdateCursor(cityBoundaries)
city = cityRows.next()

while city:

    # Create a query string for the current city    
    cityIDString = city.getValue(cityIDStringField)
    queryString = '"' + cityIDStringField + '" = ' + "'" + cityIDString + "'"

    # Make a feature layer of just the current city polygon    
    arcpy.MakeFeatureLayer_management(cityBoundaries, "CurrentCityLayer", queryString)
    
    # 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")

    try:
        # Try to get the first park and ride in the list
        selectedParkAndRideRows = arcpy.SearchCursor("ParkAndRideLayer")
        firstParkAndRide = selectedParkAndRideRows.next()
        
        # If a first park and ride was found, look for a second one
        if firstParkAndRide:
            secondParkAndRide = selectedParkAndRideRows.next()
            
            # Mark the park and ride field TRUE if a second park and ride was found
            if secondParkAndRide:
                city.setValue(parkAndRideField, "TRUE")

                # Don't forget to call UpdateRow
                cityRows.updateRow(city)

                # Add 1 to your tally of cities with two park and rides                
                citiesWithTwoParkAndRides += 1          

        # Get ready to repeat above process with the next city        
        numCities += 1
        city = cityRows.next() 

    finally:
        # Delete feature layer to get ready for next run of loop          
        arcpy.Delete_management("CurrentCityLayer")      
    
# Clean up update cursor and feature layer containing all park and rides
del city
del cityRows
arcpy.Delete_management("ParkAndRideLayer")

# Calculate and report the number of cities with two park and rides
percentCitiesWithParkAndRide = ((1.0 * citiesWithTwoParkAndRides) / numCities) * 100

print str(percentCitiesWithParkAndRide) + " percent of cities have two park and rides."

Below is an explanatory video of the solution. Note that the video was recorded showing a slightly less efficient technique of making the "ParkAndRideLayer" feature layer each time the loop runs. Since recording this video, I have discovered that you only have to create ParkAndRideLayer once (before the loop, as shown above), then the SelectLayerByLocation just performs a new selection on it each time the loop runs.

Practice Exercise B for Lesson 3