def runTool(outDir, outGDB, inSQLDbase, inNetworkDataset, imp, BufferSize,
            restrictions, TrimSettings):
    try:

        # ----- Set up the run -----
        try:

            BBB_SharedFunctions.CheckArcVersion(min_version_pro="1.2")
            BBB_SharedFunctions.CheckArcInfoLicense()
            BBB_SharedFunctions.CheckOutNALicense()

            BBB_SharedFunctions.CheckWorkspace()

            # It's okay to overwrite in-memory stuff.
            OverwriteOutput = arcpy.env.overwriteOutput  # Get the orignal value so we can reset it.
            arcpy.env.overwriteOutput = True

            # Append .gdb to geodatabase name.
            if not outGDB.lower().endswith(".gdb"):
                outGDB += ".gdb"
            outGDBwPath = os.path.join(outDir, outGDB)
            # Create a file geodatabase for the results.
            arcpy.management.CreateFileGDB(outDir, outGDB)

            # Make a copy of the input SQL file in the Step 1 output so we can modify it.
            SQLDbase = os.path.join(outGDBwPath, "Step1_GTFS.sql")
            copyfile(inSQLDbase, SQLDbase)
            # Connect to or create the SQL file.
            conn = sqlite3.connect(SQLDbase)
            c = BBB_SharedFunctions.c = conn.cursor()

            impedanceAttribute = BBB_SharedFunctions.CleanUpImpedance(imp)
            TrimPolys, TrimPolysValue = BBB_SharedFunctions.CleanUpTrimSettings(
                TrimSettings)

        except:
            arcpy.AddError("Error setting up run.")
            raise

    #----- Make a feature class of GTFS stops that we can use for buffers -----
        try:
            # Create a feature class of transit stops
            arcpy.AddMessage("Creating a feature class of GTFS stops...")
            StopsLayer, StopIDList = BBB_SharedFunctions.MakeStopsFeatureClass(
                os.path.join(outGDBwPath, "Step1_Stops"))
        except:
            arcpy.AddError("Error creating a feature class of GTFS stops.")
            raise

    #----- Create Service Areas around all stops in the system -----
        try:
            arcpy.AddMessage("Creating service areas around stops...")
            arcpy.AddMessage(
                "(This step will take a while for large networks.)")
            polygons = BBB_SharedFunctions.MakeServiceAreasAroundStops(
                StopsLayer, inNetworkDataset, impedanceAttribute, BufferSize,
                restrictions, TrimPolys, TrimPolysValue)
        except:
            arcpy.AddError("Error creating service areas around stops.")
            raise

    #----- Post-process the polygons to prepare for Step 2 -----
        try:

            arcpy.AddMessage("Reformatting polygons for further analysis...")
            arcpy.AddMessage(
                "(This step will take a while for large networks.)")

            # ----- Flatten the overlapping service area polygons -----

            # Use World Cylindrical Equal Area (WKID 54034) to ensure proper use of cluster tolerance in meters
            arcpy.env.outputCoordinateSystem = BBB_SharedFunctions.WorldCylindrical

            # Flatten the overlapping polygons.  This will ultimately be our output.
            # Dummy points to use in FeatureToPolygon to get rid of unnecessary fields.
            dummypoints = arcpy.management.CreateFeatureclass(
                "in_memory", "DummyPoints", "POINT")

            # The flattened polygons will be our ultimate output in the end (final
            # output of step 2).
            FlatPolys = os.path.join(outGDBwPath, "Step1_FlatPolys")
            # FeatureToPolygon flattens overalpping polys.
            # Set a large cluster tolerance to eliminate small sliver polygons and to
            # keep the output file size down.  Boundaries may move up to the distance
            # specified in the cluster tolerance, but some amount of movement is
            # acceptable, as service area polygons are inexact anyway.
            # The large cluster tolerance may cause some geometry issues with the output
            # later, but this is the best solution I've found so far that doesn't eat
            # up too much analysis time and memory
            clusTol = "5 meters"
            arcpy.management.FeatureToPolygon(polygons, FlatPolys, clusTol, "",
                                              dummypoints)
            arcpy.management.Delete(dummypoints)

            # Add a field to the output file for number of trips and num trips / hour.
            # Also create a polygon id field so we can keep track of them.
            arcpy.management.AddField(FlatPolys, "PolyID", "LONG")
            arcpy.management.AddField(FlatPolys, "NumTrips", "LONG")
            arcpy.management.AddField(FlatPolys, "NumTripsPerHr", "DOUBLE")
            arcpy.management.AddField(FlatPolys, "NumStopsInRange", "LONG")
            arcpy.management.AddField(FlatPolys, "MaxWaitTime", "DOUBLE")

            # ----- Create stacked points, one for each original SA polygon -----

            # Create points for use in the Identity tool (one point per poly)
            FlattenedPoints = os.path.join(outGDBwPath,
                                           "Step1_FlattenedPoints")
            arcpy.management.FeatureToPoint(FlatPolys, FlattenedPoints,
                                            "INSIDE")

            # Use Identity to stack points and keep the stop_ids from the original SAs.
            # Results in a points layer with fields ORIG_FID for the IDs of the
            # flattened polygons and a stop_id column with the stop ids.
            # Points are stacked, and each has only one stop_id.
            StackedPoints = os.path.join(outGDBwPath, "Step1_StackedPoints")
            arcpy.analysis.Identity(FlattenedPoints, polygons, StackedPoints)
            arcpy.management.Delete(FlattenedPoints)

            # ----- Read the Stacked Points into an SQL table -----

            # Create a SQL table associating the Polygon FID with the stop_ids that serve it.
            c.execute("DROP TABLE IF EXISTS StackedPoints;")
            schema = "Polygon_FID LONG, stop_id TEXT"
            create_stmt = "CREATE TABLE StackedPoints (%s);" % schema
            c.execute(create_stmt)

            # Add data to the table. Track Polygon IDs with no associated stop_ids so we can delete them.
            FIDsToDelete = []
            AddToStackedPts = []
            with arcpy.da.SearchCursor(
                    StackedPoints, ["ORIG_FID", "stop_id"]) as StackedPtCursor:
                for row in StackedPtCursor:
                    if not row[1]:
                        FIDsToDelete.append(row[0])
                    else:
                        AddToStackedPts.append((
                            row[0],
                            row[1],
                        ))
            # Add the OD items to the SQL table
            c.executemany(
                '''INSERT INTO StackedPoints \
                            (Polygon_FID, stop_id) \
                            VALUES (?, ?);''', AddToStackedPts)
            conn.commit()
            arcpy.management.Delete(StackedPoints)
            FIDsToDelete = set(FIDsToDelete)

            # ----- Delete polygons not associated with any stop_ids -----
            # These were generated by the FeatureToPolygon tool in areas completely
            # surrounded by other polygons and aren't associated with any stops.

            # Make feature layer containing only the polygons we want to delete.
            desc2 = arcpy.Describe(FlatPolys)
            OutputOIDName = desc2.OIDFieldName
            # Anything with 0 area will just cause problems later.
            WhereClause = '"Shape_Area" = 0'
            if FIDsToDelete:
                WhereClause += ' OR "' + OutputOIDName + '" IN ('
                for FID in FIDsToDelete:
                    WhereClause += str(FID) + ", "
                WhereClause = WhereClause[:-2] + ")"
            arcpy.management.MakeFeatureLayer(FlatPolys, "FlatPolysLayer",
                                              WhereClause)

            # Delete the polygons that don't correspond to any stop_ids.
            arcpy.management.DeleteFeatures("FlatPolysLayer")

            # ----- Populate the PolyID field -----

            # Set PolyID equal to the OID.
            expression = "!" + OutputOIDName + "!"
            arcpy.management.CalculateField(FlatPolys, "PolyID", expression,
                                            "PYTHON")

        except:
            arcpy.AddError("Error post-processing polygons")
            raise

        arcpy.AddMessage("Done!")
        arcpy.AddMessage("Files written to output geodatabase " + outGDBwPath +
                         ":")
        arcpy.AddMessage("- Step1_Stops")
        arcpy.AddMessage("- Step1_FlatPolys")
        arcpy.AddMessage("- Step1_GTFS.sql")

        # Tell the tool that this is output. This will add the output to the map.
        arcpy.SetParameterAsText(8, os.path.join(outGDBwPath, "Step1_Stops"))
        arcpy.SetParameterAsText(9, os.path.join(outGDBwPath,
                                                 "Step1_FlatPolys"))
        arcpy.SetParameterAsText(10, os.path.join(outGDBwPath,
                                                  "Step1_GTFS.sql"))

    except BBB_SharedFunctions.CustomError:
        arcpy.AddError("Failed to create BetterBusBuffers polygons.")
        pass
    except:
        arcpy.AddError("Failed to create BetterBusBuffers polygons.")
        raise
示例#2
0
def runTool(outGDB, SQLDbase, RouteText, inNetworkDataset, imp, BufferSize,
            restrictions, TrimSettings):
    try:
        OverwriteOutput = arcpy.env.overwriteOutput  # Get the orignal value so we can reset it.
        arcpy.env.overwriteOutput = True
        # Source FC names are not prepended to field names.
        arcpy.env.qualifiedFieldNames = False

        BBB_SharedFunctions.CheckArcVersion(min_version_pro="1.2")
        BBB_SharedFunctions.CheckOutNALicense()
        BBB_SharedFunctions.CheckWorkspace()

        # ===== Get trips and stops associated with this route =====

        # ----- Figure out which route the user wants to analyze based on the text input -----
        try:

            arcpy.AddMessage("Gathering route, trip, and stop information...")

            # Connect to or create the SQL file.
            conn = sqlite3.connect(SQLDbase)
            c = BBB_SharedFunctions.c = conn.cursor()

            # Get list of routes in the GTFS data
            routefetch = "SELECT route_short_name, route_long_name, route_id FROM routes;"
            c.execute(routefetch)
            # Extract the route_id based on what the user picked from the GUI list
            # It's better to do it by searching the database instead of trying to extract
            # the route_id from the text they chose because we don't know what kind of
            # characters will be in the route names and id, so parsing could be unreliable
            route_id = ""
            for route in c:
                routecheck = route[0] + ": " + route[1] + " [" + route[2] + "]"
                if routecheck == RouteText:
                    route_id = route[2]
                    route_short_name = route[0]
                    break

            if not route_id:
                arcpy.AddError("Could not parse route selection.")
                raise BBB_SharedFunctions.CustomError

            # Name feature classes
            outStopsname = arcpy.ValidateTableName("Stops_" + route_short_name,
                                                   outGDB)
            outPolysname = arcpy.ValidateTableName(
                "Buffers_" + route_short_name, outGDB)

        except:
            arcpy.AddError("Error determining route_id for analysis.")
            raise

        # ----- Get trips associated with route and split into directions -----
        try:
            # Some GTFS datasets use the same route_id to identify trips traveling in
            # either direction along a route. Others identify it as a different route.
            # We will consider each direction separately if there is more than one.

            # Get list of trips
            trip_route_dict = {}
            triproutefetch = '''
                SELECT trip_id, direction_id FROM trips
                WHERE route_id='%s'
                ;''' % route_id
            c.execute(triproutefetch)

            # Fill some dictionaries for use later.
            trip_dir_dict = {}  # {Direction: [trip_id, trip_id, ...]}
            for triproute in c:
                trip_dir_dict.setdefault(triproute[1], []).append(triproute[0])
            if not trip_dir_dict:
                arcpy.AddError(
                    "There are no trips in the GTFS data for the route \
you have selected (%s).  Please select a different route or fix your GTFS \
dataset." % RouteText)
                raise BBB_SharedFunctions.CustomError

        except:
            arcpy.AddError("Error getting trips associated with route.")
            raise

        # ----- Get list of stops associated with trips and split into directions -----
        try:
            # If a stop is used for trips going in both directions, count them separately.

            # Select unique set of stops used by trips in each direction
            stoplist = {}  # {Direction: [stop_id, stop_id, ...]}
            for direction in trip_dir_dict:
                stops = []
                for trip in trip_dir_dict[direction]:
                    stopsfetch = '''SELECT stop_id FROM stop_times
                                WHERE trip_id == ?'''
                    c.execute(stopsfetch, (trip, ))
                    for stop in c:
                        stops.append(stop[0])
                stoplist[direction] = list(set(stops))

            # If there is more than one direction, we will append the direction number
            # to the output fc names, so add an _ here for prettiness.
            if len(stoplist) > 1:
                arcpy.AddMessage(
                    "Route %s contains trips going in more than one \
direction. A separate feature class will be created for each direction, and the \
GTFS direction_id will be appended to the feature class name." %
                    route_short_name)
                outStopsname += "_"
                outPolysname += "_"

        except:
            arcpy.AddError("Error getting stops associated with route.")
            raise

        # ===== Create output =====

        # ----- Create a feature class of stops ------
        try:

            arcpy.AddMessage("Creating feature class of GTFS stops...")

            for direction in stoplist:
                stops = stoplist[direction]
                outputname = outStopsname
                if direction != None:
                    outputname += str(direction)
                outStops = os.path.join(outGDB, outputname)

                outStops, outStopList = BBB_SharedFunctions.MakeStopsFeatureClass(
                    outStops, stops)

                # Add a route_id and direction_id field and populate it
                arcpy.management.AddField(outStops, "route_id", "TEXT")
                arcpy.management.AddField(outStops, "direction_id", "TEXT")
                fields = ["route_id", "direction_id"]
                with arcpy.da.UpdateCursor(outStops, fields) as cursor:
                    for row in cursor:
                        row[0] = route_id
                        row[1] = direction
                        cursor.updateRow(row)

        except:
            arcpy.AddError("Error creating feature class of GTFS stops.")
            raise

        #----- Create Service Areas around stops -----
        try:

            arcpy.AddMessage("Creating buffers around stops...")

            for direction in stoplist:
                outputname = outStopsname
                if direction != None:
                    outputname += str(direction)
                outStops = os.path.join(outGDB, outputname)

                TrimPolys, TrimPolysValue = BBB_SharedFunctions.CleanUpTrimSettings(
                    TrimSettings)
                polygons = BBB_SharedFunctions.MakeServiceAreasAroundStops(
                    outStops, inNetworkDataset,
                    BBB_SharedFunctions.CleanUpImpedance(imp), BufferSize,
                    restrictions, TrimPolys, TrimPolysValue)

                # Join stop information to polygons and save as feature class
                arcpy.management.AddJoin(polygons, "stop_id", outStops,
                                         "stop_id")
                outPolys = outPolysname
                if direction != None:
                    outPolys += str(direction)
                outPolysFC = os.path.join(outGDB, outPolys)
                arcpy.management.CopyFeatures(polygons, outPolysFC)

                # Add a route_id and direction_id field and populate it
                arcpy.management.AddField(outPolysFC, "route_id", "TEXT")
                arcpy.management.AddField(outPolysFC, "direction_id", "TEXT")
                fields = ["route_id", "direction_id"]
                with arcpy.da.UpdateCursor(outPolysFC, fields) as cursor:
                    for row in cursor:
                        row[0] = route_id
                        row[1] = direction
                        cursor.updateRow(row)

        except:
            arcpy.AddError("Error creating buffers around stops.")
            raise

        arcpy.AddMessage("Done!")
        arcpy.AddMessage("Output written to %s is:" % outGDB)
        outFClist = []
        for direction in stoplist:
            outPolysFC = outPolysname
            outStopsFC = outStopsname
            if direction != None:
                outStopsFC += str(direction)
                outPolysFC += str(direction)
            outFClist.append(outStopsFC)
            outFClist.append(outPolysFC)
            arcpy.AddMessage("- " + outStopsFC)
            arcpy.AddMessage("- " + outPolysFC)

        # Tell the tool that this is output. This will add the output to the map.
        outFClistwpaths = [os.path.join(outGDB, fc) for fc in outFClist]
        arcpy.SetParameterAsText(8, ';'.join(outFClistwpaths))

    except BBB_SharedFunctions.CustomError:
        arcpy.AddError("Failed to create buffers around stops for this route.")
        pass

    except:
        arcpy.AddError("Failed to create buffers around stops for this route.")
        raise

    finally:
        if OverwriteOutput:
            arcpy.env.overwriteOutput = OverwriteOutput
def runTool(outFile, SQLDbase, inPointsLayer, inLocUniqueID, day, start_time,
            end_time, inNetworkDataset, imp, BufferSize, restrictions,
            DepOrArrChoice):
    try:
        # Source FC names are not prepended to field names.
        arcpy.env.qualifiedFieldNames = False
        # It's okay to overwrite in-memory stuff.
        OverwriteOutput = arcpy.env.overwriteOutput  # Get the orignal value so we can reset it.
        arcpy.env.overwriteOutput = True

        BBB_SharedFunctions.CheckArcVersion(min_version_pro="1.2")
        ProductName = BBB_SharedFunctions.ProductName
        BBB_SharedFunctions.CheckWorkspace()
        BBB_SharedFunctions.CheckOutNALicense()

        BBB_SharedFunctions.ConnectToSQLDatabase(SQLDbase)

        Specific, day = BBB_SharedFunctions.CheckSpecificDate(day)
        start_sec, end_sec = BBB_SharedFunctions.ConvertTimeWindowToSeconds(
            start_time, end_time)

        # Will we calculate the max wait time?
        CalcWaitTime = True

        impedanceAttribute = BBB_SharedFunctions.CleanUpImpedance(imp)

        # Hard-wired OD variables
        ExcludeRestricted = "EXCLUDE"
        PathShape = "NO_LINES"
        accumulate = ""
        uturns = "ALLOW_UTURNS"
        hierarchy = "NO_HIERARCHY"

        # Output file designated by user
        outDir = os.path.dirname(outFile)
        outFilename = os.path.basename(outFile)

        inLocUniqueID = BBB_SharedFunctions.HandleOIDUniqueID(
            inPointsLayer, inLocUniqueID)
        inLocUniqueID_qualified = inLocUniqueID + "_Input"

        arcpy.AddMessage("Run set up successfully.")

        # ----- Create a feature class of stops ------
        try:
            arcpy.AddMessage("Getting GTFS stops...")
            tempstopsname = "Temp_Stops"
            if ".shp" in outFilename:
                tempstopsname += ".shp"
            StopsLayer, StopList = BBB_SharedFunctions.MakeStopsFeatureClass(
                os.path.join(outDir, tempstopsname))
        except:
            arcpy.AddError("Error creating feature class of GTFS stops.")
            raise

        #----- Create OD Matrix between stops and user's points -----
        try:
            arcpy.AddMessage("Creating OD matrix between points and stops...")
            arcpy.AddMessage(
                "(This step could take a while for large datasets or buffer sizes.)"
            )

            # Name to refer to OD matrix layer
            outNALayer_OD = "ODMatrix"

            # ODLayer is the NA Layer object returned by getOutput(0)
            ODLayer = arcpy.na.MakeODCostMatrixLayer(
                inNetworkDataset, outNALayer_OD, impedanceAttribute,
                BufferSize, "", accumulate, uturns, restrictions, hierarchy,
                "", PathShape).getOutput(0)

            # To refer to the OD sublayers, get the sublayer names.  This is essential for localization.
            naSubLayerNames = arcpy.na.GetNAClassNames(ODLayer)
            points = naSubLayerNames["Origins"]
            stops = naSubLayerNames["Destinations"]

            # Add a field for stop_id as a unique identifier for stops.
            arcpy.na.AddFieldToAnalysisLayer(outNALayer_OD, stops, "stop_id",
                                             "TEXT")
            # Specify the field mappings for the stop_id field.
            fieldMappingStops = arcpy.na.NAClassFieldMappings(ODLayer, stops)
            fieldMappingStops["Name"].mappedFieldName = "stop_id"
            fieldMappingStops["stop_id"].mappedFieldName = "stop_id"
            # Add the GTFS stops as locations for the analysis.
            arcpy.na.AddLocations(outNALayer_OD, stops, StopsLayer,
                                  fieldMappingStops, "500 meters", "", "", "",
                                  "", "", "", ExcludeRestricted)
            # Clear out the memory because we don't need this anymore.
            arcpy.management.Delete(StopsLayer)

            # Add a field for unique identifier for points.
            arcpy.na.AddFieldToAnalysisLayer(outNALayer_OD, points,
                                             inLocUniqueID_qualified, "TEXT")
            # Specify the field mappings for the unique id field.
            fieldMappingPoints = arcpy.na.NAClassFieldMappings(ODLayer, points)
            fieldMappingPoints["Name"].mappedFieldName = inLocUniqueID
            fieldMappingPoints[
                inLocUniqueID_qualified].mappedFieldName = inLocUniqueID
            # Add the input points as locations for the analysis.
            arcpy.na.AddLocations(outNALayer_OD, points, inPointsLayer,
                                  fieldMappingPoints, "500 meters", "", "", "",
                                  "", "", "", ExcludeRestricted)

            # Solve the OD matrix.
            try:
                arcpy.na.Solve(outNALayer_OD)
            except:
                errs = arcpy.GetMessages(2)
                if "No solution found" in errs:
                    impunits = imp.split(" (Units: ")[1].split(")")[0]
                    arcpy.AddError(
                        "No transit stops were found within a %s %s walk of any of your input points.  \
Consequently, there is no transit service available to your input points, so no output will be generated."
                        % (str(BufferSize), impunits))
                else:
                    arcpy.AddError(
                        "Failed to calculate travel time or distance between transit stops and input points.  OD Cost Matrix error messages:"
                    )
                    arcpy.AddError(errs)
                raise BBB_SharedFunctions.CustomError

            # Make layer objects for each sublayer we care about.
            if ProductName == 'ArcGISPro':
                naSubLayerNames = arcpy.na.GetNAClassNames(ODLayer)
                subLayerDict = dict(
                    (lyr.name, lyr) for lyr in ODLayer.listLayers())
                subLayers = {}
                for subL in naSubLayerNames:
                    subLayers[subL] = subLayerDict[naSubLayerNames[subL]]
            else:
                subLayers = dict(
                    (lyr.datasetName, lyr)
                    for lyr in arcpy.mapping.ListLayers(ODLayer)[1:])
            linesSubLayer = subLayers["ODLines"]
            pointsSubLayer = subLayers["Origins"]
            stopsSubLayer = subLayers["Destinations"]

            # Get the OID fields, just to be thorough
            desc1 = arcpy.Describe(pointsSubLayer)
            points_OID = desc1.OIDFieldName
            desc2 = arcpy.Describe(stopsSubLayer)
            stops_OID = desc2.OIDFieldName

            # Join polygons layer with input facilities to port over the stop_id
            arcpy.management.JoinField(linesSubLayer, "OriginID",
                                       pointsSubLayer, points_OID,
                                       [inLocUniqueID_qualified])
            arcpy.management.JoinField(linesSubLayer, "DestinationID",
                                       stopsSubLayer, stops_OID, ["stop_id"])

            # Use searchcursor on lines to find the stops that are reachable from points.
            global PointsAndStops
            # PointsAndStops = {LocID: [stop_1, stop_2, ...]}
            PointsAndStops = {}
            ODCursor = arcpy.da.SearchCursor(
                linesSubLayer, [inLocUniqueID_qualified, "stop_id"])
            for row in ODCursor:
                PointsAndStops.setdefault(str(row[0]), []).append(str(row[1]))
            del ODCursor

        except:
            arcpy.AddError(
                "Error creating OD matrix between stops and input points.")
            raise

        #----- Query the GTFS data to count the trips at each stop -----
        try:
            arcpy.AddMessage(
                "Calculating the number of transit trips available during the time window..."
            )

            # Get a dictionary of stop times in our time window {stop_id: [[trip_id, stop_time]]}
            stoptimedict = BBB_SharedFunctions.CountTripsAtStops(
                day, start_sec, end_sec,
                BBB_SharedFunctions.CleanUpDepOrArr(DepOrArrChoice), Specific)

        except:
            arcpy.AddError(
                "Error calculating the number of transit trips available during the time window."
            )
            raise

        # ----- Generate output data -----
        try:
            arcpy.AddMessage("Writing output data...")

            arcpy.management.CopyFeatures(inPointsLayer, outFile)
            # Add a field to the output file for number of trips and num trips / hour.
            if ".shp" in outFilename:
                arcpy.management.AddField(outFile, "NumTrips", "SHORT")
                arcpy.management.AddField(outFile, "TripsPerHr", "DOUBLE")
                arcpy.management.AddField(outFile, "NumStops", "SHORT")
                arcpy.management.AddField(outFile, "MaxWaitTm", "SHORT")
            else:
                arcpy.management.AddField(outFile, "NumTrips", "SHORT")
                arcpy.management.AddField(outFile, "NumTripsPerHr", "DOUBLE")
                arcpy.management.AddField(outFile, "NumStopsInRange", "SHORT")
                arcpy.management.AddField(outFile, "MaxWaitTime", "SHORT")

            if ".shp" in outFilename:
                ucursor = arcpy.da.UpdateCursor(outFile, [
                    inLocUniqueID[0:10], "NumTrips", "TripsPerHr", "NumStops",
                    "MaxWaitTm"
                ])
            else:
                ucursor = arcpy.da.UpdateCursor(outFile, [
                    inLocUniqueID, "NumTrips", "NumTripsPerHr",
                    "NumStopsInRange", "MaxWaitTime"
                ])
            for row in ucursor:
                try:
                    ImportantStops = PointsAndStops[str(row[0])]
                except KeyError:
                    # This point had no stops in range
                    ImportantStops = []
                NumTrips, NumTripsPerHr, NumStopsInRange, MaxWaitTime =\
                                BBB_SharedFunctions.RetrieveStatsForSetOfStops(
                                    ImportantStops, stoptimedict, CalcWaitTime,
                                    start_sec, end_sec)
                row[1] = NumTrips
                row[2] = NumTripsPerHr
                row[3] = NumStopsInRange
                if ".shp" in outFilename and MaxWaitTime == None:
                    row[4] = -1
                else:
                    row[4] = MaxWaitTime
                ucursor.updateRow(row)

        except:
            arcpy.AddError("Error writing output.")
            raise

        arcpy.AddMessage("Done!")
        arcpy.AddMessage("Output files written:")
        arcpy.AddMessage("- " + outFile)

    except BBB_SharedFunctions.CustomError:
        arcpy.AddError("Error counting transit trips at input locations.")
        pass

    except:
        arcpy.AddError("Error counting transit trips at input locations.")
        raise

    finally:
        # Reset overwriteOutput to what it was originally.
        arcpy.env.overwriteOutput = OverwriteOutput