def __init__(self): self.label = "Create Random Points on a Network Dataset" self.description = "Create a series of random points on a network dataset." self.canRunInBackground = False arcpy.env.overwriteOutput = True self.kfHelper = KFunctionHelper()
def __init__(self): self.kfHelper = KFunctionHelper()
def __init__(self): self.label = "Global K Function" self.description = "Uses a Global Network K Function to analyze clustering and dispersion trends in a set of crash points." self.canRunInBackground = False env.overwriteOutput = True self.kfHelper = KFunctionHelper()
class GlobalKFunction(object): ### # Initialize the tool. ### def __init__(self): self.label = "Global K Function" self.description = "Uses a Global Network K Function to analyze clustering and dispersion trends in a set of crash points." self.canRunInBackground = False env.overwriteOutput = True self.kfHelper = KFunctionHelper() ### # Get input from the users. ### def getParameterInfo(self): # Input origin features. points = arcpy.Parameter( displayName="Input Points Feature Dataset", name="points", datatype="Feature Class", parameterType="Required", direction="Input") points.filter.list = ["Point"] # Network dataset. networkDataset = arcpy.Parameter( displayName="Input Network Dataset", name = "network_dataset", datatype="Network Dataset Layer", parameterType="Required", direction="Input") # Number of distance increments. numBands = arcpy.Parameter( displayName="Input Number of Distance Bands", name="num_dist_bands", datatype="Long", parameterType="Optional", direction="Input") # Beginning distance. begDist = arcpy.Parameter( displayName="Input Beginning Distance", name="beginning_distance", datatype="Double", parameterType="Required", direction="Input") begDist.value = 0 # Distance increment. distInc = arcpy.Parameter( displayName="Input Distance Increment", name="distance_increment", datatype="Double", parameterType="Required", direction="Input") distInc.value = 1000 # Snap distance. snapDist = arcpy.Parameter( displayName="Input Snap Distance", name="snap_distance", datatype="Double", parameterType="Required", direction="Input") snapDist.value = 25 # Output location. outNetKLoc = arcpy.Parameter( displayName="Output Location (Database Path)", name="out_location", datatype="DEWorkspace", parameterType="Required", direction="Input") outNetKLoc.value = arcpy.env.workspace # The raw ODCM data. outRawODCMFCName = arcpy.Parameter( displayName="Raw ODCM Data Table", name = "output_raw_odcm_feature_class", datatype="GPString", parameterType="Required", direction="Output") outRawODCMFCName.value = "Global_K_Raw_ODCM_Data" # The raw data feature class (e.g. observed and random point computations). outRawFCName = arcpy.Parameter( displayName="Raw Global-K Data Table (Raw Analysis Data)", name = "output_raw_analysis_feature_class", datatype="GPString", parameterType="Required", direction="Output") outRawFCName.value = "Global_K_Raw_Analysis_Data" # The analysis feature class. outAnlFCName = arcpy.Parameter( displayName="Global-K Summary Data (Plottable Data)", name = "output_analysis_feature_class", datatype="GPString", parameterType="Required", direction="Output") outAnlFCName.value = "Global_K_Summary_Data" # Confidence envelope (number of permutations). numPerms = arcpy.Parameter( displayName="Number of Random Point Permutations", name = "num_permutations", datatype="GPString", parameterType="Required", direction="Input") permKeys = self.kfHelper.getPermutationSelection().keys() numPerms.filter.list = permKeys numPerms.value = permKeys[0] # Projected coordinate system. outCoordSys = arcpy.Parameter( displayName="Output Network Dataset Length Projected Coordinate System", name="coordinate_system", datatype="GPSpatialReference", parameterType="Optional", direction="Input") # Number of points field. numPointsFieldName = arcpy.Parameter( displayName="Number of Points Field", name = "num_points_field", datatype="GPString", parameterType="Optional", direction="Input") return [points, networkDataset, numBands, begDist, distInc, snapDist, outNetKLoc, outRawODCMFCName, outRawFCName, outAnlFCName, numPerms, outCoordSys, numPointsFieldName] ### # Check if the tool is available for use. ### def isLicensed(self): # Network Analyst tools must be available. return arcpy.CheckExtension("Network") == "Available" ### # Set parameter defaults. ### def updateParameters(self, parameters): networkDataset = parameters[1].value outCoordSys = parameters[11].value # Default the coordinate system. if networkDataset is not None and outCoordSys is None: ndDesc = arcpy.Describe(networkDataset) # If the network dataset's coordinate system is a projected one, # use its coordinate system as the defualt. if (ndDesc.spatialReference.projectionName != "" and ndDesc.spatialReference.linearUnitName == "Meter" and ndDesc.spatialReference.factoryCode != 0): parameters[11].value = ndDesc.spatialReference.factoryCode # Set the source of the fields (the network dataset). if networkDataset is not None: parameters[12].filter.list = self.kfHelper.getEdgeSourceFieldNames(networkDataset) ### # If any fields are invalid, show an appropriate error message. ### def updateMessages(self, parameters): outCoordSys = parameters[11].value if outCoordSys is not None: if outCoordSys.projectionName == "": parameters[11].setErrorMessage("Output coordinate system must be a projected coordinate system.") elif outCoordSys.linearUnitName != "Meter": parameters[11].setErrorMessage("Output coordinate system must have a linear unit code of 'Meter.'") else: parameters[11].clearMessage() ### # Execute the tool. ### def execute(self, parameters, messages): points = parameters[0].valueAsText networkDataset = parameters[1].valueAsText numBands = parameters[2].value begDist = parameters[3].value distInc = parameters[4].value snapDist = parameters[5].value outNetKLoc = parameters[6].valueAsText outRawODCMFCName = parameters[7].valueAsText outRawFCName = parameters[8].valueAsText outAnlFCName = parameters[9].valueAsText numPermsDesc = parameters[10].valueAsText numPerms = self.kfHelper.getPermutationSelection()[numPermsDesc] outCoordSys = parameters[11].value numPointsFieldName = parameters[12].value ndDesc = arcpy.Describe(networkDataset) gkfSvc = GlobalKFunctionSvc() # Refer to the note in the NetworkDatasetLength tool. if outCoordSys is None: outCoordSys = ndDesc.spatialReference messages.addMessage("\nOrigin points: {0}".format(points)) messages.addMessage("Network dataset: {0}".format(networkDataset)) messages.addMessage("Number of distance bands: {0}".format(numBands)) messages.addMessage("Beginning distance: {0}".format(begDist)) messages.addMessage("Distance increment: {0}".format(distInc)) messages.addMessage("Snap distance: {0}".format(snapDist)) messages.addMessage("Output location (database path): {0}".format(outNetKLoc)) messages.addMessage("Raw ODCM data table: {0}".format(outRawODCMFCName)) messages.addMessage("Raw global-K data table (raw analysis data): {0}".format(outRawFCName)) messages.addMessage("Global-K summary data (plottable data): {0}".format(outAnlFCName)) messages.addMessage("Number of random permutations: {0}".format(numPerms)) messages.addMessage("Network dataset length projected coordinate system: {0}".format(outCoordSys.name)) messages.addMessage("Number of Points Field Name: {0}\n".format(numPointsFieldName)) # Calculate the length of the network. networkLength = self.kfHelper.calculateLength(networkDataset, outCoordSys) messages.addMessage("Total network length: {0}".format(networkLength)) # Count the number of crashes. numPoints = self.kfHelper.countNumberOfFeatures(os.path.join(outNetKLoc, points)) # Set up a cutoff lenght for the ODCM data if possible. (Optimization.) cutoff = gkfSvc.getCutoff(numBands, distInc, begDist) # The results of all the calculations end up here. netKCalculations = [] # Use a mutable container for the number of bands so that the below callback # can write to it. The "nonlocal" keyword not available in Python 2.x. numBandsCont = [numBands] # Callback function that does the Network K calculation on an OD cost matrix. def doNetKCalc(odDists, iteration): # Do the actual network k-function calculation. netKCalc = NetworkKCalculation(networkLength, numPoints, odDists, begDist, distInc, numBandsCont[0]) netKCalculations.append(netKCalc.getDistanceBands()) # If the user did not specifiy a number of distance bands explicitly, # store the number of bands. It's computed from the observed data. if numBandsCont[0] is None: numBandsCont[0] = netKCalc.getNumberOfDistanceBands() # Generate the ODCM permutations, including the ODCM for the observed data. # doNetKCalc is called on each iteration. randODCMPermSvc = RandomODCMPermutationsSvc() randODCMPermSvc.generateODCMPermutations("Global Analysis", points, points, networkDataset, snapDist, cutoff, outNetKLoc, outRawODCMFCName, numPerms, outCoordSys, numPointsFieldName, messages, doNetKCalc) # Store the raw analysis data. messages.addMessage("Writing raw analysis data.") gkfSvc.writeRawAnalysisData(outNetKLoc, outRawFCName, netKCalculations) # Analyze the data and store the results. messages.addMessage("Analyzing data.") gkfSvc.writeAnalysisSummaryData(numPerms, netKCalculations, outNetKLoc, outAnlFCName)
def __init__(self): self.label = "Cross K Function" self.description = "Uses a Cross K Function to analyze clustering and dispersion trends in a set of origin and destination points (for example, bridges and crashes)." self.canRunInBackground = False env.overwriteOutput = True self.kfHelper = KFunctionHelper()
def __init__(self): self.label = "Random ODCM Permutations" self.description = "Generates OD Cost Matrices with oberved data and a complementrary set of random point permutations." self.canRunInBackground = False env.overwriteOutput = True self.kfHelper = KFunctionHelper()
class RandomODCMPermutations(object): ### # Initialize the tool. ### def __init__(self): self.label = "Random ODCM Permutations" self.description = "Generates OD Cost Matrices with oberved data and a complementrary set of random point permutations." self.canRunInBackground = False env.overwriteOutput = True self.kfHelper = KFunctionHelper() ### # Get input from the users. ### def getParameterInfo(self): # Analysis type. analysisType = arcpy.Parameter( displayName="Analysis Type", name = "analysis_type", datatype="GPString", parameterType="Required", direction="Input") atKeys = self.kfHelper.getAnalysisTypeSelection().keys() analysisType.filter.list = atKeys analysisType.value = atKeys[0] # Input origin points features. srcPoints = arcpy.Parameter( displayName="Input Origin Points Feature Dataset (e.g. bridges)", name="srcPoints", datatype="Feature Class", parameterType="Required", direction="Input") srcPoints.filter.list = ["Point"] # Input destination origin features. destPoints = arcpy.Parameter( displayName="Input Destination Points Feature Dataset (e.g. crashes)", name="destPoints", datatype="Feature Class", parameterType="Required", direction="Input") destPoints.filter.list = ["Point"] # Network dataset. networkDataset = arcpy.Parameter( displayName="Input Network Dataset", name = "network_dataset", datatype="Network Dataset Layer", parameterType="Required", direction="Input") # Snap distance. snapDist = arcpy.Parameter( displayName="Input Snap Distance", name="snap_distance", datatype="Double", parameterType="Required", direction="Input") snapDist.value = 25 # Cutoff distance. cutoff = arcpy.Parameter( displayName="Input Cutoff Distance", name="cutoff_distance", datatype="Double", parameterType="Optional", direction="Input") # Output location. outLoc = arcpy.Parameter( displayName="Output Location (Database Path)", name="out_location", datatype="DEWorkspace", parameterType="Required", direction="Input") outLoc.value = arcpy.env.workspace # Raw data feature class. outFC = arcpy.Parameter( displayName="Output Feature Class Name (Raw ODCM Data)", name = "output_raw_feature_class", datatype="GPString", parameterType="Required", direction="Output") outFC.value = "ODCM_Raw_Data" # Confidence envelope (number of permutations). numPerms = arcpy.Parameter( displayName="Number of Random Point Permutations", name = "num_permutations", datatype="GPString", parameterType="Required", direction="Input") permKeys = self.kfHelper.getPermutationSelection().keys() numPerms.filter.list = permKeys numPerms.value = permKeys[0] # Projected coordinate system. outCoordSys = arcpy.Parameter( displayName="Output Network Dataset Length Projected Coordinate System", name="coordinate_system", datatype="GPSpatialReference", parameterType="Optional", direction="Input") # Number of points field. numPointsFieldName = arcpy.Parameter( displayName="Number of Points Field", name = "num_points_field", datatype="GPString", parameterType="Optional", direction="Input") return [analysisType, srcPoints, destPoints, networkDataset, snapDist, cutoff, outLoc, outFC, numPerms, outCoordSys, numPointsFieldName] ### # Check if the tool is available for use. ### def isLicensed(self): # Network Analyst tools must be available. return arcpy.CheckExtension("Network") == "Available" ### # Set parameter defaults. ### def updateParameters(self, parameters): networkDataset = parameters[3].value outCoordSys = parameters[9].value # Default the coordinate system. if networkDataset is not None and outCoordSys is None: ndDesc = arcpy.Describe(networkDataset) # If the network dataset's coordinate system is a projected one, # use its coordinate system as the defualt. if (ndDesc.spatialReference.projectionName != "" and ndDesc.spatialReference.linearUnitName == "Meter" and ndDesc.spatialReference.factoryCode != 0): parameters[9].value = ndDesc.spatialReference.factoryCode # Enable/disable the destination points based on the analysis type. CROSS has # sources and destinations; GLOBAL only has one set of points. analysisTypes = self.kfHelper.getAnalysisTypeSelection() if parameters[0].valueAsText in analysisTypes: if analysisTypes[parameters[0].valueAsText] == "CROSS": parameters[2].enabled = True else: parameters[2].enabled = False parameters[2].value = parameters[1].valueAsText # Set the source of the fields (the network dataset). if networkDataset is not None: parameters[10].filter.list = self.kfHelper.getEdgeSourceFieldNames(networkDataset) ### # If any fields are invalid, show an appropriate error message. ### def updateMessages(self, parameters): outCoordSys = parameters[9].value if outCoordSys is not None: if outCoordSys.projectionName == "": parameters[9].setErrorMessage("Output coordinate system must be a projected coordinate system.") elif outCoordSys.linearUnitName != "Meter": parameters[9].setErrorMessage("Output coordinate system must have a linear unit code of 'Meter.'") else: parameters[9].clearMessage() ### # Execute the tool. ### def execute(self, parameters, messages): analysisType = self.kfHelper.getAnalysisTypeSelection()[parameters[0].valueAsText] srcPoints = parameters[1].valueAsText destPoints = parameters[2].valueAsText networkDataset = parameters[3].valueAsText snapDist = parameters[4].value cutoff = parameters[5].value outLoc = parameters[6].valueAsText outFC = parameters[7].valueAsText numPerms = self.kfHelper.getPermutationSelection()[parameters[8].valueAsText] outCoordSys = parameters[9].value numPointsFieldName = parameters[10].value ndDesc = arcpy.Describe(networkDataset) # Refer to the note in the NetworkDatasetLength tool. if outCoordSys is None: outCoordSys = ndDesc.spatialReference messages.addMessage("\nAnalysis type: {0}".format(analysisType)) messages.addMessage("Origin points: {0}".format(srcPoints)) messages.addMessage("Destination points: {0}".format(destPoints)) messages.addMessage("Network dataset: {0}".format(networkDataset)) messages.addMessage("Snap distance: {0}".format(snapDist)) messages.addMessage("Cutoff distance: {0}".format(cutoff)) messages.addMessage("Path to output feature class: {0}".format(outLoc)) messages.addMessage("Output feature class name: {0}".format(outFC)) messages.addMessage("Number of random permutations: {0}".format(numPerms)) messages.addMessage("Network dataset length projected coordinate system: {0}".format(outCoordSys.name)) messages.addMessage("Number of Points Field Name: {0}\n".format(numPointsFieldName)) # The actual work is done in a reusable service. randODCMPermSvc = RandomODCMPermutationsSvc() randODCMPermSvc.generateODCMPermutations(analysisType, srcPoints, destPoints, networkDataset, snapDist, cutoff, outLoc, outFC, numPerms, outCoordSys, numPointsFieldName, messages)
class CrossKFunction(object): ### # Initialize the tool. ### def __init__(self): self.label = "Cross K Function" self.description = "Uses a Cross K Function to analyze clustering and dispersion trends in a set of origin and destination points (for example, bridges and crashes)." self.canRunInBackground = False env.overwriteOutput = True self.kfHelper = KFunctionHelper() ### # Get input from the users. ### def getParameterInfo(self): # Input origin points features. srcPoints = arcpy.Parameter( displayName="Input Origin Points Feature Dataset (e.g. bridges)", name="srcPoints", datatype="Feature Class", parameterType="Required", direction="Input") srcPoints.filter.list = ["Point"] # Input destination origin features. destPoints = arcpy.Parameter( displayName= "Input Destination Points Feature Dataset (e.g. crashes)", name="destPoints", datatype="Feature Class", parameterType="Required", direction="Input") destPoints.filter.list = ["Point"] # Network dataset. networkDataset = arcpy.Parameter(displayName="Input Network Dataset", name="network_dataset", datatype="Network Dataset Layer", parameterType="Required", direction="Input") # Number of distance increments. numBands = arcpy.Parameter( displayName="Input Number of Distance Bands", name="num_dist_bands", datatype="Long", parameterType="Optional", direction="Input") # Beginning distance. begDist = arcpy.Parameter(displayName="Input Beginning Distance", name="beginning_distance", datatype="Double", parameterType="Required", direction="Input") begDist.value = 0 # Distance increment. distInc = arcpy.Parameter(displayName="Input Distance Increment", name="distance_increment", datatype="Double", parameterType="Required", direction="Input") distInc.value = 1000 # Snap distance. snapDist = arcpy.Parameter(displayName="Input Snap Distance", name="snap_distance", datatype="Double", parameterType="Required", direction="Input") snapDist.value = 25 # Output location. outNetKLoc = arcpy.Parameter( displayName="Output Location (Database Path)", name="out_location", datatype="DEWorkspace", parameterType="Required", direction="Input") outNetKLoc.value = arcpy.env.workspace # The raw ODCM data. outRawODCMFCName = arcpy.Parameter( displayName="Raw ODCM Data Table", name="output_raw_odcm_feature_class", datatype="GPString", parameterType="Required", direction="Output") outRawODCMFCName.value = "Cross_K_Raw_ODCM_Data" # The raw data feature class (e.g. observed and random point computations). outRawFCName = arcpy.Parameter( displayName="Raw Network-K Data Table (Raw Analysis Data)", name="output_raw_analysis_feature_class", datatype="GPString", parameterType="Required", direction="Output") outRawFCName.value = "Cross_K_Raw_Analysis_Data" # The analysis feature class. outAnlFCName = arcpy.Parameter( displayName="Network-K Summary Data (Plottable Data)", name="output_analysis_feature_class", datatype="GPString", parameterType="Required", direction="Output") outAnlFCName.value = "Cross_K_Summary_Data" # Confidence envelope (number of permutations). numPerms = arcpy.Parameter( displayName="Number of Random Point Permutations", name="num_permutations", datatype="GPString", parameterType="Required", direction="Input") permKeys = self.kfHelper.getPermutationSelection().keys() numPerms.filter.list = permKeys numPerms.value = permKeys[0] # Projected coordinate system. outCoordSys = arcpy.Parameter( displayName= "Output Network Dataset Length Projected Coordinate System", name="coordinate_system", datatype="GPSpatialReference", parameterType="Optional", direction="Input") # Number of points field. numPointsFieldName = arcpy.Parameter( displayName="Number of Points Field", name="num_points_field", datatype="GPString", parameterType="Optional", direction="Input") return [ srcPoints, destPoints, networkDataset, numBands, begDist, distInc, snapDist, outNetKLoc, outRawODCMFCName, outRawFCName, outAnlFCName, numPerms, outCoordSys, numPointsFieldName ] ### # Check if the tool is available for use. ### def isLicensed(self): # Network Analyst tools must be available. return arcpy.CheckExtension("Network") == "Available" ### # Set parameter defaults. ### def updateParameters(self, parameters): networkDataset = parameters[2].value outCoordSys = parameters[12].value # Default the coordinate system. if networkDataset is not None and outCoordSys is None: ndDesc = arcpy.Describe(networkDataset) # If the network dataset's coordinate system is a projected one, # use its coordinate system as the defualt. if (ndDesc.spatialReference.projectionName != "" and ndDesc.spatialReference.linearUnitName == "Meter" and ndDesc.spatialReference.factoryCode != 0): parameters[12].value = ndDesc.spatialReference.factoryCode # Set the source of the fields (the network dataset). if networkDataset is not None: parameters[13].filter.list = self.kfHelper.getEdgeSourceFieldNames( networkDataset) ### # If any fields are invalid, show an appropriate error message. ### def updateMessages(self, parameters): outCoordSys = parameters[12].value if outCoordSys is not None: if outCoordSys.projectionName == "": parameters[12].setErrorMessage( "Output coordinate system must be a projected coordinate system." ) elif outCoordSys.linearUnitName != "Meter": parameters[12].setErrorMessage( "Output coordinate system must have a linear unit code of 'Meter.'" ) else: parameters[12].clearMessage() ### # Execute the tool. ### def execute(self, parameters, messages): srcPoints = parameters[0].valueAsText destPoints = parameters[1].valueAsText networkDataset = parameters[2].valueAsText numBands = parameters[3].value begDist = parameters[4].value distInc = parameters[5].value snapDist = parameters[6].value outNetKLoc = parameters[7].valueAsText outRawODCMFCName = parameters[8].valueAsText outRawFCName = parameters[9].valueAsText outAnlFCName = parameters[10].valueAsText numPerms = self.kfHelper.getPermutationSelection()[ parameters[11].valueAsText] outCoordSys = parameters[12].value numPointsFieldName = parameters[13].value ndDesc = arcpy.Describe(networkDataset) gkfSvc = GlobalKFunctionSvc() # Refer to the note in the NetworkDatasetLength tool. if outCoordSys is None: outCoordSys = ndDesc.spatialReference messages.addMessage("\nOrigin points: {0}".format(srcPoints)) messages.addMessage("Destination points: {0}".format(destPoints)) messages.addMessage("Network dataset: {0}".format(networkDataset)) messages.addMessage("Number of distance bands: {0}".format(numBands)) messages.addMessage("Beginning distance: {0}".format(begDist)) messages.addMessage("Distance increment: {0}".format(distInc)) messages.addMessage("Snap distance: {0}".format(snapDist)) messages.addMessage( "Path to output cross-K feature class: {0}".format(outNetKLoc)) messages.addMessage( "Raw ODCM data table: {0}".format(outRawODCMFCName)) messages.addMessage( "Raw cross-K data table (raw analysis data): {0}".format( outRawFCName)) messages.addMessage( "Cross-K summary data (plottable data): {0}".format(outAnlFCName)) messages.addMessage( "Number of random permutations: {0}".format(numPerms)) messages.addMessage( "Network dataset length projected coordinate system: {0}".format( outCoordSys.name)) messages.addMessage( "Number of Points Field Name: {0}\n".format(numPointsFieldName)) # Calculate the length of the network. networkLength = self.kfHelper.calculateLength(networkDataset, outCoordSys) messages.addMessage("Total network length: {0}".format(networkLength)) # Count the number of crashes. numDests = self.kfHelper.countNumberOfFeatures( os.path.join(outNetKLoc, destPoints)) # Set up a cutoff lenght for the ODCM data if possible. (Optimization.) cutoff = gkfSvc.getCutoff(numBands, distInc, begDist) # The results of all the calculations end up here. netKCalculations = [] # Use a mutable container for the number of bands so that the below callback # can write to it. The "nonlocal" keyword not available in Python 2.x. numBandsCont = [numBands] # Callback function that does the Network K calculation on an OD cost matrix. def doNetKCalc(odDists, iteration): # Do the actual network k-function calculation. netKCalc = CrossKCalculation(networkLength, numDests, odDists, begDist, distInc, numBandsCont[0]) netKCalculations.append(netKCalc.getDistanceBands()) # If the user did not specifiy a number of distance bands explicitly, # store the number of bands. It's computed from the observed data. if numBandsCont[0] is None: numBandsCont[0] = netKCalc.getNumberOfDistanceBands() # Generate the ODCM permutations, including the ODCM for the observed data. # doNetKCalc is called on each iteration. randODCMPermSvc = RandomODCMPermutationsSvc() randODCMPermSvc.generateODCMPermutations( "Cross Analysis", srcPoints, destPoints, networkDataset, snapDist, cutoff, outNetKLoc, outRawODCMFCName, numPerms, outCoordSys, numPointsFieldName, messages, doNetKCalc) # Store the raw analysis data. messages.addMessage("Writing raw analysis data.") gkfSvc.writeRawAnalysisData(outNetKLoc, outRawFCName, netKCalculations) # Analyze the data and store the results. messages.addMessage("Analyzing data.") gkfSvc.writeAnalysisSummaryData(numPerms, netKCalculations, outNetKLoc, outAnlFCName)
class RandomODCMPermutationsSvc: ### # Initialize the service (stateless). ### def __init__(self): self.kfHelper = KFunctionHelper() ### # Generate the ODCM permutations. # @param analysisType Either Global Analysis or Cross Analysis. # @param srcPoints The source points. # @param destPoints The destination points. Ignored if analysisType is GLOBAL. # @param networkDataset The network dataset to use for the ODCMs. # @param snapDist The snap distance for points that are not directly on the network. # @param cutoff The cutoff distance. Ignored if None. # @param outLoc The full path to a database. The ODCM data will be written here. # @param outFC The name of the feature class in outLoc where the ODCM data will be written. # @param numPerms The number of permutations (string representation). # @param outCoordSys The coordinate system to project the points into (optional). # @param numPointsFieldName The optional name of a field in the network dataset's edge source # from which the number of random points should be derived. # @param messages A messages instances with addMessage() implemented (for debug output). # @param callback A callback function(odDists, iteration) called on each iteration # with the current OD cost matrix. ### def generateODCMPermutations(self, analysisType, srcPoints, destPoints, networkDataset, snapDist, cutoff, outLoc, outFC, numPerms, outCoordSys, numPointsFieldName, messages, callback = None): # Default no-op for the callback. if callback is None: callback = lambda odDists, iteration: None # For global analysis the destination points are the same as the source points # (e.g. destPoints is ignored). if analysisType == "GLOBAL" or destPoints is None: destPoints = srcPoints # Count the number of crashes (there may be fewer points in the ODCM, but it's the total # number of crashes that is needed). numDests = self.kfHelper.countNumberOfFeatures(os.path.join(outLoc, destPoints)) messages.addMessage("Number of crashes: {0}".format(numDests)) # Make the observed ODCM and calculate the distance between each set of # points. If a cross analysis is selected, find the distance between the # source and destination points. Otherwise there is only one set of points odDists = self._calculateDistances(networkDataset, srcPoints, destPoints, snapDist, cutoff) self._writeODCMData(odDists, outLoc, outFC, 0) callback(odDists, 0) messages.addMessage("Iteration 0 (observed) complete.") # Generate the OD Cost matrix permutations. kfTimer = KFunctionTimer(numPerms) for i in range(1, numPerms + 1): if numPointsFieldName: randPoints = self.kfHelper.generateRandomPoints(networkDataset, outCoordSys, None, numPointsFieldName) else: randPoints = self.kfHelper.generateRandomPoints(networkDataset, outCoordSys, numDests, None) # See the note above: Either find the distance from the source points to the random points, # or the distance between the random points. if analysisType == "CROSS": odDists = self._calculateDistances(networkDataset, srcPoints, randPoints, snapDist, cutoff) else: odDists = self._calculateDistances(networkDataset, randPoints, randPoints, snapDist, cutoff) self._writeODCMData(odDists, outLoc, outFC, i) callback(odDists, i) # Clean up the random points table. arcpy.Delete_management(randPoints) # Show the progress. kfTimer.increment() messages.addMessage("Iteration {0} complete. Elapsed time: {1}s. ETA: {2}s.".format( i, kfTimer.getElapsedTime(), kfTimer.getETA())) ### # Calculate the distances between each set of points using an OD Cost Matrix. # @param networkDataset A network dataset which the points are on. # @param srcPoints The source points to calculate distances from. # @param destPoints The destination points to calculate distances to. # @param snapDist If a point is not directly on the network, it will be # snapped to the nearset line if it is within this threshold. # @param cutoff The cutoff distance for the ODCM (optional). ### def _calculateDistances(self, networkDataset, srcPoints, destPoints, snapDist, cutoff): # This is the current map, which should be an OSM base map. curMapDoc = arcpy.mapping.MapDocument("CURRENT") # Get the data from from the map (see the DataFrame object of arcpy). dataFrame = arcpy.mapping.ListDataFrames(curMapDoc, "Layers")[0] # Create the cost matrix. costMatResult = arcpy.na.MakeODCostMatrixLayer(networkDataset, "TEMP_ODCM_NETWORK_K", "Length", cutoff) odcmLayer = costMatResult.getOutput(0) # The OD Cost Matrix layer will have Origins and Destinations layers. Get # a reference to each of these. odcmSublayers = arcpy.na.GetNAClassNames(odcmLayer) odcmOriginLayer = odcmSublayers["Origins"] odcmDestLayer = odcmSublayers["Destinations"] # Add the origins and destinations to the ODCM. arcpy.na.AddLocations(odcmLayer, odcmOriginLayer, srcPoints, "", snapDist) arcpy.na.AddLocations(odcmLayer, odcmDestLayer, destPoints, "", snapDist) # Solve the matrix. arcpy.na.Solve(odcmLayer) # Show the ODCM layer (it must be showing to open th ODLines sub layer below). #arcpy.mapping.AddLayer(dataFrame, odcmLayer, "TOP") #arcpy.RefreshTOC() # Get the "Lines" layer, which has the distance between each point. odcmLines = arcpy.mapping.ListLayers(odcmLayer, odcmSublayers["ODLines"])[0] # This array will hold all the OD distances. odDists = [] if srcPoints == destPoints: # If the source points and destination points are the same, exclude the # distance from the point to itself. where = """{0} <> {1}""".format( arcpy.AddFieldDelimiters(odcmLines, "originID"), arcpy.AddFieldDelimiters(odcmLines, "destinationID")) else: where = "" with arcpy.da.SearchCursor( in_table=odcmLines, field_names=["Total_Length", "originID", "destinationID"], where_clause=where) as cursor: for row in cursor: odDists.append({"Total_Length": row[0], "OriginID": row[1], "DestinationID": row[2]}) return odDists ### # Write the ODCM data to a table. # @param odDists An array of raw ODCM data. # @param outLoc The location of a database. # @param outFC The feature class name, in outLoc, to write the data to. # @param iteration The iteration number (0 is observed). ### def _writeODCMData(self, odDists, outLoc, outFC, iteration): # This is the full path to the output feature class. outFCFullPath = os.path.join(outLoc, outFC) if iteration == 0: # Create the output table. arcpy.CreateTable_management(outLoc, outFC) arcpy.AddField_management(outFCFullPath, "Iteration_Number", "LONG") arcpy.AddField_management(outFCFullPath, "OriginID", "LONG") arcpy.AddField_management(outFCFullPath, "DestinationID", "LONG") arcpy.AddField_management(outFCFullPath, "Total_Length", "DOUBLE") with arcpy.da.InsertCursor(outFCFullPath, ["Iteration_Number", "OriginID", "DestinationID", "Total_Length"]) as cursor: for odDist in odDists: cursor.insertRow([iteration, odDist["OriginID"], odDist["DestinationID"], odDist["Total_Length"]])
class NetworkDatasetRandomPoints(object): ### # Initialize the tool. ### def __init__(self): self.label = "Create Random Points on a Network Dataset" self.description = "Create a series of random points on a network dataset." self.canRunInBackground = False arcpy.env.overwriteOutput = True self.kfHelper = KFunctionHelper() ### # Get input from the users. ### def getParameterInfo(self): # Network dataset. networkDataset = arcpy.Parameter( displayName="Existing Network Dataset", name = "network_dataset", datatype="Network Dataset Layer", parameterType="Required", direction="Input") # Output location. outLocation = arcpy.Parameter( displayName="Path to Output Random Point Feature Class", name="out_location", datatype="DEWorkspace", parameterType="Required", direction="Input") outLocation.value = arcpy.env.workspace # The random point feature class to create. outPointClass = arcpy.Parameter( displayName="Output Random Point Feature Class Name", name = "output_point_feature_class", datatype="GPString", parameterType="Required", direction="Output") # Num points or long. useField = arcpy.Parameter( displayName="Use a Data Field for Point Generation", name = "use_field", datatype="GPBoolean", parameterType="Optional", direction="Input") # Number of random points to generate. numPoints = arcpy.Parameter( displayName="Number of Points", name = "num_points", datatype="GPLong", parameterType="Optional", direction="Input") # Number of points field. numPointsFieldName = arcpy.Parameter( displayName="Number of Points Field", name = "num_points_field", datatype="GPString", parameterType="Optional", direction="Input") return [networkDataset, outLocation, outPointClass, useField, numPoints, numPointsFieldName] ### # Check if the tool is available for use. ### def isLicensed(self): return True ### # Set the parameter defaults. ### def updateParameters(self, parameters): networkDataset = parameters[0].value outPointClass = parameters[2].value useField = parameters[3].value if networkDataset is not None and outPointClass is None: # Default name for the output table. ndDesc = arcpy.Describe(networkDataset) parameters[2].value = ndDesc.name + "_Random_Points" # Num points/num points field is based on the useField checkbox. parameters[4].enabled = not useField parameters[5].enabled = useField # Set the source of the fields (the network dataset). if useField == True and networkDataset is not None: parameters[5].filter.list = self.kfHelper.getEdgeSourceFieldNames(networkDataset) ### # If any fields are invalid, show an appropriate error message. ### def updateMessages(self, parameters): networkDataset = parameters[0].value useField = parameters[3].value numPoints = parameters[4].value numPointsFieldName = parameters[5].value if numPoints is not None: if numPoints <= 0: parameters[2].setErrorMessage("The number of points must be greater than 0.") else: parameters[2].clearMessage() if useField: if numPointsFieldName is None: parameters[5].setErrorMessage("Number of points field is required.") else: parameters[5].clearMessage() # Check that there is a single edge source if a field is used. if networkDataset is not None: if self.kfHelper.getNumEdgeSources(networkDataset) != 1: parameters[5].setErrorMessage("If using a field, only a single edge source is supported.") else: parameters[5].clearMessage() else: if numPoints is None: parameters[4].setErrorMessage("Number of points is required.") else: parameters[4].clearMessage() ### # Execute the tool. ### def execute(self, parameters, messages): networkDataset = parameters[0].value outPath = parameters[1].value outPointClass = parameters[2].value useField = parameters[3].value numPoints = parameters[4].value numPointsFieldName = parameters[5].value wsPath = arcpy.env.workspace ndDesc = arcpy.Describe(networkDataset) messages.addMessage("Network Dataset: {0}".format(ndDesc.catalogPath)) messages.addMessage("Location to Output Random Point Feature Class: {0}".format(outPath)) messages.addMessage("Output Random Point Feature Class: {0}".format(outPointClass)) messages.addMessage("Use a Data Field for Point Generation: {0}".format(useField)) messages.addMessage("Number of Points: {0}".format(numPoints)) messages.addMessage("Number of Points Field: {0}".format(numPointsFieldName)) if useField: # Use the single edge source for the constraining feature. esFullPath = self.kfHelper.getEdgeSourcePath(networkDataset) # Create a series of random points on the new line class. messages.addMessage("Creating point feature class. Name: {0} Path: {1}" .format(outPointClass, outPath)) # Use the field name for the number of points (e.g. AADT). arcpy.CreateRandomPoints_management(out_path=outPath, out_name=outPointClass, constraining_feature_class=esFullPath, number_of_points_or_field=numPointsFieldName) else: # All the edge sources that make up the network dataset are combined into # a single feature class. lineClassName = "TEMP_LINES_{0}".format(ndDesc.name) lineClassFullPath = os.path.join(wsPath, lineClassName) arcpy.CreateFeatureclass_management(out_path=wsPath, out_name=lineClassName, geometry_type="POLYLINE", spatial_reference=ndDesc.spatialReference) with arcpy.da.InsertCursor(lineClassName, ["SHAPE@"]) as insCursor: # Get the edge sources that make up the network. edgeSources = ndDesc.edgeSources for edgeSource in edgeSources: edgePath = os.path.join(ndDesc.path, edgeSource.name) with arcpy.da.SearchCursor(edgePath, ["SHAPE@"]) as cursor: for row in cursor: insCursor.insertRow([row[0]]) # Combine all the line segments into a single line. singleLineName = "TEMP_SINGLE_LINE_{0}".format(ndDesc.name) singleLineFullPath = os.path.join(wsPath, singleLineName) arcpy.Dissolve_management(lineClassFullPath, singleLineFullPath) # Create a series of random points on the new line class. messages.addMessage("Creating point feature class. Name: {0} Path: {1}" .format(outPointClass, outPath)) arcpy.CreateRandomPoints_management(out_path=outPath, out_name=outPointClass, constraining_feature_class=singleLineFullPath, number_of_points_or_field=numPoints) # Clean up the temporary feature class. arcpy.Delete_management(lineClassFullPath) arcpy.Delete_management(singleLineFullPath)
class NetworkDatasetRandomPoints(object): ### # Initialize the tool. ### def __init__(self): self.label = "Create Random Points on a Network Dataset" self.description = "Create a series of random points on a network dataset." self.canRunInBackground = False arcpy.env.overwriteOutput = True self.kfHelper = KFunctionHelper() ### # Get input from the users. ### def getParameterInfo(self): # Network dataset. networkDataset = arcpy.Parameter( displayName="Existing Network Dataset", name="network_dataset", datatype="Network Dataset Layer", parameterType="Required", direction="Input") # Output location. outLocation = arcpy.Parameter( displayName="Path to Output Random Point Feature Class", name="out_location", datatype="DEWorkspace", parameterType="Required", direction="Input") outLocation.value = arcpy.env.workspace # The random point feature class to create. outPointClass = arcpy.Parameter( displayName="Output Random Point Feature Class Name", name="output_point_feature_class", datatype="GPString", parameterType="Required", direction="Output") # Num points or long. useField = arcpy.Parameter( displayName="Use a Data Field for Point Generation", name="use_field", datatype="GPBoolean", parameterType="Optional", direction="Input") # Number of random points to generate. numPoints = arcpy.Parameter(displayName="Number of Points", name="num_points", datatype="GPLong", parameterType="Optional", direction="Input") # Number of points field. numPointsFieldName = arcpy.Parameter( displayName="Number of Points Field", name="num_points_field", datatype="GPString", parameterType="Optional", direction="Input") return [ networkDataset, outLocation, outPointClass, useField, numPoints, numPointsFieldName ] ### # Check if the tool is available for use. ### def isLicensed(self): return True ### # Set the parameter defaults. ### def updateParameters(self, parameters): networkDataset = parameters[0].value outPointClass = parameters[2].value useField = parameters[3].value if networkDataset is not None and outPointClass is None: # Default name for the output table. ndDesc = arcpy.Describe(networkDataset) parameters[2].value = ndDesc.name + "_Random_Points" # Num points/num points field is based on the useField checkbox. parameters[4].enabled = not useField parameters[5].enabled = useField # Set the source of the fields (the network dataset). if useField == True and networkDataset is not None: parameters[5].filter.list = self.kfHelper.getEdgeSourceFieldNames( networkDataset) ### # If any fields are invalid, show an appropriate error message. ### def updateMessages(self, parameters): networkDataset = parameters[0].value useField = parameters[3].value numPoints = parameters[4].value numPointsFieldName = parameters[5].value if numPoints is not None: if numPoints <= 0: parameters[2].setErrorMessage( "The number of points must be greater than 0.") else: parameters[2].clearMessage() if useField: if numPointsFieldName is None: parameters[5].setErrorMessage( "Number of points field is required.") else: parameters[5].clearMessage() # Check that there is a single edge source if a field is used. if networkDataset is not None: if self.kfHelper.getNumEdgeSources(networkDataset) != 1: parameters[5].setErrorMessage( "If using a field, only a single edge source is supported." ) else: parameters[5].clearMessage() else: if numPoints is None: parameters[4].setErrorMessage("Number of points is required.") else: parameters[4].clearMessage() ### # Execute the tool. ### def execute(self, parameters, messages): networkDataset = parameters[0].value outPath = parameters[1].value outPointClass = parameters[2].value useField = parameters[3].value numPoints = parameters[4].value numPointsFieldName = parameters[5].value wsPath = arcpy.env.workspace ndDesc = arcpy.Describe(networkDataset) messages.addMessage("Network Dataset: {0}".format(ndDesc.catalogPath)) messages.addMessage( "Location to Output Random Point Feature Class: {0}".format( outPath)) messages.addMessage( "Output Random Point Feature Class: {0}".format(outPointClass)) messages.addMessage( "Use a Data Field for Point Generation: {0}".format(useField)) messages.addMessage("Number of Points: {0}".format(numPoints)) messages.addMessage( "Number of Points Field: {0}".format(numPointsFieldName)) if useField: # Use the single edge source for the constraining feature. esFullPath = self.kfHelper.getEdgeSourcePath(networkDataset) # Create a series of random points on the new line class. messages.addMessage( "Creating point feature class. Name: {0} Path: {1}".format( outPointClass, outPath)) # Use the field name for the number of points (e.g. AADT). arcpy.CreateRandomPoints_management( out_path=outPath, out_name=outPointClass, constraining_feature_class=esFullPath, number_of_points_or_field=numPointsFieldName) else: # All the edge sources that make up the network dataset are combined into # a single feature class. lineClassName = "TEMP_LINES_{0}".format(ndDesc.name) lineClassFullPath = os.path.join(wsPath, lineClassName) arcpy.CreateFeatureclass_management( out_path=wsPath, out_name=lineClassName, geometry_type="POLYLINE", spatial_reference=ndDesc.spatialReference) with arcpy.da.InsertCursor(lineClassName, ["SHAPE@"]) as insCursor: # Get the edge sources that make up the network. edgeSources = ndDesc.edgeSources for edgeSource in edgeSources: edgePath = os.path.join(ndDesc.path, edgeSource.name) with arcpy.da.SearchCursor(edgePath, ["SHAPE@"]) as cursor: for row in cursor: insCursor.insertRow([row[0]]) # Combine all the line segments into a single line. singleLineName = "TEMP_SINGLE_LINE_{0}".format(ndDesc.name) singleLineFullPath = os.path.join(wsPath, singleLineName) arcpy.Dissolve_management(lineClassFullPath, singleLineFullPath) # Create a series of random points on the new line class. messages.addMessage( "Creating point feature class. Name: {0} Path: {1}".format( outPointClass, outPath)) arcpy.CreateRandomPoints_management( out_path=outPath, out_name=outPointClass, constraining_feature_class=singleLineFullPath, number_of_points_or_field=numPoints) # Clean up the temporary feature class. arcpy.Delete_management(lineClassFullPath) arcpy.Delete_management(singleLineFullPath)