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