执行网络分析

可以使用 Network Analyst 模块 arcpy.nax 在 Python 中执行网络分析。此模块提供对六种分析类型的访问权限。对于每种分析(也称为求解器),可使用特定于该分析的两个对象:一个对象用于初始化分析、设置分析设置、加载输入和执行分析,另一个对象用于在执行分析后处理分析结果。下图显示了用于执行服务区分析的 ServiceAreaServiceAreaResult 对象。该图显示了每个对象的可用属性和方法。属性用于修改分析设置,方法用于执行操作,例如加载输入或执行分析。

ServiceArea 和 ServiceAreaResult 对象的可用属性和方法

注:

本主题以服务区分析为例;但是,此处提供的信息可以应用于任何类型的网络分析。

要执行网络分析,请遵循以下五步工作流:

  1. 初始化分析
  2. 设置分析的属性
  3. 加载输入
  4. 求解分析
  5. 处理结果

初始化分析

要初始化分析,需要提供网络数据集以对将要执行分析的运输网络进行建模。可以使用网络数据集的完整目录路径或使用 MakeNetworkDatasetLayer 函数基于网络数据集创建的网络数据集图层名称来指定网络数据集。

提示:

要获得最佳性能,请在初始化分析时使用网络数据集图层的名称。如果使用目录路径,则每次初始化分析时都会打开网络数据集。打开网络数据集非常耗时,因为数据集包含读取和缓存的高级数据结构和表。如果使用同一图层,则网络数据集图层将仅打开数据集一次,从而提高性能。

以下代码片显示了如何初始化服务区分析:

import arcpy
nd_path = "C:/data/NorthAmerica.gdb/Routing/Routing_ND"
nd_layer_name = "NorthAmerica"
# Create a network dataset layer. The layer will be referenced using its name.
arcpy.nax.MakeNetworkDatasetLayer(nd_path, nd_layer_name)
# Instantiate a ServiceArea analysis object.
service_area = arcpy.nax.ServiceArea(nd_layer_name)

设置分析的属性

初始化分析后,需要设置分析的属性。要确定分析对象支持的属性并了解给定属性如何影响分析,请参阅有关特定对象的帮助主题(可在帮助中的“类”类别下找到)。许多属性都是使用 Python 枚举对象(一组表示某些常量值的符号名称)设置的。例如,可以使用 arcpy.nax.TimeUnits 枚举中的 Minutes 成员将分析的时间单位设置为分钟。对象的帮助主题介绍了是否需要将属性设置为枚举。还指定了应使用的枚举名称。

应该为任何分析设置的最重要的属性是用于分析的出行模式。要设置出行模式,必须先获取网络数据集支持的出行模式列表。要实现此目的,请使用 GetTravelModes 函数。从支持的列表中,根据名称选择分析的出行模式。GetTravelModes 函数将返回一个字典,其中键是出行模式名称,值是出行模式对象

以下代码片段显示了如何设置服务区分析的属性。该片段假设已初始化服务区分析对象。

# Get the desired travel mode for the analysis. nd_travel_modes = arcpy.nax.GetTravelModes(nd_layer_name) travel_mode = nd_travel_modes["Driving Time"]
# Set properties. service_area.timeUnits = arcpy.nax.TimeUnits.Minutes service_area.defaultImpedanceCutoffs = [5, 10, 15] service_area.travelMode = travel_mode service_area.outputType = arcpy.nax.ServiceAreaOutputType.Polygons service_area.geometryAtOverlap = arcpy.nax.ServiceAreaOverlapGeometry.Split

加载输入

在执行分析之前,必须加载要用于分析的输入位置。根据分析类型,需要将一个或多个位置加载到该分析支持的一个或多个输入数据类型中。例如,要执行服务区分析,必须将一个或多个设施点(在其周围生成服务区域面和服务区域线的位置)加载到 ServiceAreaInputDataType.Facilities 数据类型中。

可通过两种方式加载输入: 如果输入位置已作为要素类或表提供,则可以使用分析对象上的 load 方法加载输入。如果要从其他数据源读取输入位置(例如,包含纬度和经度值的 CSV 文件),或者如果将输入位置设置为 ArcPy 点对象,则可以使用分析对象上的 insertCursor 方法加载输入。

以下代码片段显示了执行服务区分析时如何使用 load 方法从要素类加载设施点:

input_facilities = "C:/data/io.gdb/Facilities"
service_area.load(arcpy.nax.ServiceAreaInputDataType.Facilities, input_facilities)

以下代码片段显示了执行服务区分析时如何使用 insertCursor 方法从 CSV 文件加载设施点: 该片段假设已初始化 service_area

# Read the CSV file with the following content. csv_file = "facilities.csv"
"""
ID,FacilityName,X,Y 1,Store 3,-117.101911,32.634351 2,Store 5,-116.979706,32.562102 3,Store 6,-116.971414,32.654230
"""
cursor_fields = ["Name", "SHAPE@XY"] with service_area.insertCursor(arcpy.nax.ServiceAreaInputDataType.Facilities, cursor_fields) as cur:
    with open(csv_file, "r", encoding="utf-8", newline="") as facilities:
        csv_reader = csv.reader(facilities)        # Assume the first line is the header and skip it.        next(csv_reader)        for row in csv_reader:
            location = (float(row[2]), float(row[3]))            cur.insertRow([row[1], location])

执行分析时,首先需要找到运输网络上所有输入的位置。如果存在大量输入要素,则此过程可能需要花费很长的时间。如果分析需要为不同的分析重新加载相同的输入要素集(例如,要执行时间设置为上午 8 点和上午 10 点的两个服务区分析,但在这两个分析中使用相同的设施点集),则可以通过预先计算分析用于定位输入设施点的信息来优化总体性能。可使用 CalculateLocations 函数实现此目的。如果输入包含位置字段(来自 CalculateLocations 函数),请使用 fieldMappings 方法(use_location_fields 参数设置为 True),然后在加载用于分析的输入时使用这些字段映射。执行分析的速度将更快,因为分析无需再次执行此处理。

了解有关网络位置字段以及如何在网络中定位输入的详细信息

提示:

仅当为不同分析重新加载相同的输入时,才建议使用 CalculateLocations。如果仅加载一次输入,则运行 CalculateLocations 无法改进性能。

以下代码片段显示了在加载输入时如何使用网络位置字段:

# Run the Calculate Locations tool to calculate network location fields arcpy.nax.CalculateLocations(    input_facilities, nd_layer_name, "5000 Meters",    [["Streets", "SHAPE"], ["Streets_ND_Junctions", "NONE"]],     travel_mode=travel_mode
)
# Initialize a Service Area analysis service_area = arcpy.nax.ServiceArea(nd_layer_name)
# Create a fieldMappings object for my Service Area with the use_location_fields 
# parameter set to True field_mappings = service_area.fieldMappings(    arcpy.nax.ServiceAreaInputDataType.Facilities, True)
# Map the names of my facilities to the Service Area's Name field field_mappings["Name"].mappedFieldName = "FacilityName"
# Map the network location fields. These lines are not strictly necessary 
# because the location fields will be mapped from the input table by default if 
# the fields are present. field_mappings["SourceID"].mappedFieldName = "SourceID"
field_mappings["SourceOID"].mappedFieldName = "SourceOID"
field_mappings["PosAlong"].mappedFieldName = "PosAlong"
field_mappings["SideOfEdge"].mappedFieldName = "SideOfEdge"
# Load the facilities using the field mappings service_area.load(arcpy.nax.ServiceAreaInputDataType.Facilities,                   input_facilities, field_mappings)

求解分析

要执行分析,请调用 solve 方法。此方法将返回一个结果对象,以供处理分析结果,例如将结果导出到要素类。

如果分析没有找到任何结果(例如,如果在输入停靠点之间找不到路径),则 solve 方法不会引发 Python 异常。要确定分析是否生成有效结果,请使用结果对象的 solveSucceeded 属性。

许可:

需要 Network Analyst 扩展模块许可,solve 才能成功。在调用 solve 方法之前,请检出许可。

以下代码片段显示了如何执行分析:

# Check out the Network Analyst extension license. arcpy.CheckOutExtension("network")
# Solve the analysis. result = service_area.solve()

处理结果

执行分析后,可使用通过 solve 方法获得的结果对象来处理结果。结果对象可用于确定分析是成功还是失败(使用 solveSucceeded 属性)以及分析是否生成部分解决方案(使用 isPartialSolution 属性)。如果分析失败,则可以确定使用 solverMessages 方法的原因。要将结果导出到要素类,请使用 export 方法。

以下代码片段显示了如何执行分析并将结果导出到要素类:

# Solve the analysis. result = service_area.solve()
# Export the results to a feature class. If the analysis failed print all the 
# messages. if result.solveSucceeded:
    result.export(arcpy.nax.ServiceAreaOutputDataType.Polygons, output_polygons) else:
    arcpy.AddError("Analysis failed")    # Print all the warning messages.    for message in result.solverMessages(arcpy.nax.MessageSeverity.Warning):
        arcpy.AddWarning(message[-1])    # Print all the error messages.    for message in result.solverMessages(arcpy.nax.MessageSeverity.Error):
        arcpy.AddError(message[-1])

要仅显示结果中的某些信息(例如路线的总时间和距离),请使用 searchCursor 方法对结果要素进行迭代。与先将结果导出到要素类,然后从要素类获取此类信息,这样做可能速度更快。

以下代码片段显示了如何使用 searchCursor 方法打印路径分析的总时间和距离:

# Solve the analysis. result = route.solve()
with result.searchCursor(arcpy.nax.RouteOutputDataType.Routes, ["Name", "Total_Minutes", "Total_Miles"]) as cur:
    for row in cur:
        print(f"Summary for route: '{row[0]}'")        print(f"Total time: {row[1]} minutes.")        print(f"Total distance: {row[2]} miles.")