自定义方向

在求解过程生成最初的方向后,可以通过自定义方向对逐步方向文本进行更改。 可以更新或移除路径的现有方向,也可以添加新的方向点。 此功能允许在不更新基础网络数据集的情况下更改输出方向。

以下是自定义方向的一些应用示例:

  • 在进入或离开收费公路或从地面街道转到地铁线路时添加方向事件
  • 更改方向行进策略文本以强调急转角度
  • 每行驶 120 分钟后提醒驾驶员
  • 针对特定应用简化或详述方向指示

自定义方向的实现方式是创建一个从 arcpy.nax.DirectionsCustomizer 继承而来的 Python 类,并将 Python 类与特定的网络数据集关联起来。

当实现方向定制器时,可以使用多个对象来收集有关网络和方向的信息,例如 DirectionPointDirectionsNameReferenceLandmark 等。

自定义方向类

方向定制器的创建方式是定义一个从 arcpy.nax.DirectionsCustomizer 类继承的类,然后实现 customize 方法。 该类可以选择实现 __init__attach 方法。

自定义方向方法

以下小节描述了可用于在求解时自定义方向的方法。

初始化程序

可以选择实现初始化程序 (__init__) 方法。 这是 Python 对象的标准初始化程序,可以根据需要进行使用。

附加

可以选择实现 attach 方法。 该方法可用于检查和验证网络数据集,以确保其符合使用传入此方法的网络查询对象自定义方向代码的要求。 如果网络数据集有效,则 attach 方法应返回 True;否则,应返回 False,在这种情况下,方向定制器不会与网络数据集相关联,并且不会调用其他类方法。

对于临时方向定制器,当分配的脚本中的网络数据集对象具有 directionsCustomizer 属性时,将调用 attach 方法。

对于持久化自定义方向,当打开网络数据集时,核心网络数据集代码将在内部调用 attach 方法。

注:

可以多次打开网络数据集,具体取决于应用程序用于访问网络数据集的线程数量。

自定义

必须选择实现 customize 方法。 在初始方向已经创建完成并且可以更改的情况下,将调用此方法。

传入此方法的 arcpy.nax.DirectionsQuery 对象可用于遍历交汇点和边,以获取其关联的方向点。

可以对现有方向点进行更新,例如更改显示文本。 如有需要,可以创建新的方向点并将其添加到某个元素的方向点列表。 或者,可以将现有方向点移除。

注:

  • 创建方向点时,至少应设置 displayTextdirectionPointType 属性。
  • azimutharrivalTimeexitNumber 属性在内部设置,无法在方向定制器中设置。

元素具有各种属性,这些属性在自定义路径方向时可能很有帮助。 self.networkQuery 对象可用于从网络数据集检索信息。

路径由停靠点、边和交汇点组成。 这些要素类用于创建路径的方向。 方向本身由方向点和方向线要素类组成。 方向的创建方式是通过分析停靠点、边和交汇点,找到需要告知用户的某些事件的发生位置。 方向点即会在这些位置创建,并且这些点会与适当的元素关联。 通常,方向点与交汇点相关联。 目前,仅当边上发现地标时,方向点才与边关联。 然后,方向点之间的边会追加到一起以创建方向线。

注:

可以有多个方向点与同一个要素相关联。

示例

示例 1:下方代码是一个自定义方向类的简单示例,该方向类在每个方向行进策略的开头添加一个数字。

import arcpy

class DirectionsIndexer(arcpy.nax.DirectionsCustomizer):
    """Defines a directions customizer that adds and index number to directions."""

    def customize(self, directions_query: arcpy.nax.DirectionsQuery):
        """Add an index number to each directions maneuver."""
        index = 1
        for junction in directions_query.junctions:
            for point in junction.directionPoints:
                point.displayText = f"{index} -> {point.displayText}"
                index += 1

示例 2:下方代码显示了一个实现了可选方法的自定义方向类。

import arcpy

class DirectionsIndexer(arcpy.nax.DirectionsCustomizer):
    """Defines a directions customizer that adds and index number to directions."""

    def __init__(self):
        """Example initializer."""
        super().__init__()
        # Do additional custom initialization

    def attach(self, network_query: arcpy.nax.NetworkQuery) -> bool:
        # Do additional validation checks before returning Boolean
        return True

    def customize(self, directions_query: arcpy.nax.DirectionsQuery):
        """Add an index number to each directions maneuver."""
        index = 1
        for junction in directions_query.junctions:
            for point in junction.directionPoints:
                point.displayText = f"{index} -> {point.displayText}"
                index += 1

示例 3:下方显示了一个自定义方向类,它添加了包含自定义消息的新方向点,该消息可以在驾驶员驾驶超过两小时的情况下予以提醒。

import arcpy

class LongDriveAlert(arcpy.nax.DirectionsCustomizer):
    """Defines a directions customizer that reports an alert after a long drive."""

    def customize(self, directions_query: arcpy.nax.DirectionsQuery) -> None:
        """Customize directions.

        If traversed junction's accumulated time is over alert interval, add an alert as a directions point.
        Enhance existing directions text to show time elapsed since beginning of the trip.
        """
        alert_interval = 120  # This assumes the travel mode's time attribute unit is minutes.
        # Loop through traversed junctions and get accumulated time at each junction.
        # If the accumulated time is over the alert interval, add a directions point with an alert message.
        i = 1
        for junction in directions_query.junctions:
            elapsed_time = junction.accumulatedTime
            if elapsed_time > alert_interval * i:
                # Create a new directions point at junction.
                point = arcpy.nax.DirectionPoint(junction)
                point.displayText = f"You have driven for over 2 hours. Consider taking a break."
                point.directionPointType = arcpy.nax.DirectionPointType.Event
                # Update junction direction points to add the new break reminder
                junction.directionPoints.append(point)
                i += 1
            else:
                for point in junction.directionPoints:
                    # For existing directions, report the elapsed time at each direction point.
                    point.displayText = f"{point.displayText} (Time elapsed: {elapsed_time:.2f} minutes)"

示例 4:下方代码显示了一个自定义方向类,该方向类可检查方向的点类型以识别左转弯,并向方向指示添加附加的警告文本。

import arcpy

class LeftTurnsHighlighter(arcpy.nax.DirectionsCustomizer):
    """Add warning text for left turn maneuvers."""

    def __init__(self) -> None:
        """Initialize customizer.

        Set left_turn_maneuver_types in initialization so it is set only once
        when the network dataset object is constructed and when the
        customizer instance is initialized.
        """
        super().__init__()
        self.left_turn_manuever_types = [
            arcpy.nax.DirectionPointType.ManeuverForkLeft,
            arcpy.nax.DirectionPointType.ManeuverRampLeft,
            arcpy.nax.DirectionPointType.ManeuverUTurnLeft,
            arcpy.nax.DirectionPointType.ManeuverBearLeft,
            arcpy.nax.DirectionPointType.ManeuverTurnLeft,
            arcpy.nax.DirectionPointType.ManeuverSharpLeft,
            arcpy.nax.DirectionPointType.ManeuverTurnLeftLeft,
            arcpy.nax.DirectionPointType.ManeuverTurnLeftRight,
            arcpy.nax.DirectionPointType.ManeuverTurnRightLeft
        ]

    def customize(self, directions_query: arcpy.nax.DirectionsQuery) -> None:
        """Alter directions text to highlight left turns with warning text."""
        for junction in directions_query.junctions:
            for point in junction.directionPoints:
                if point.directionPointType in self.left_turn_manuever_types:
                    point.displayText = f"{point.displayText} (LEFT TURN WARNING!)"

将方向定制器与网络数据集相关联

有两种方法可以部署方向定制器,使其与网络数据集相关联,并在求解期间调用自定义逻辑。 这两种方法称为临时方法和持久方法。

提示:

创建您的自定义方向类,并将其作为临时方向定制器进行测试。 然后在诸如 Visual Studio Code 等编辑器中以调试模式运行该脚本。 脚本按预期运行后,可以根据需要将转为持久化方向定制器。 验证持久化方向定制器以确保一切正常运行。

临时方向定制器

临时方向定制器仅与在脚本内创建的网络数据集对象相关联;它们不会永久保存到网络数据集。 在执行求解的脚本中,会使用网络数据集对象的 directionsCustomizer 属性配置临时方向定制器。

在临时方向定制器适用的应用中,必须通过 Python 脚本(例如独立 Python 脚本、Python 脚本工具、自定义地理处理服务或 Web 工具)调用方向定制器。 临时方向定制器也可用于开发和调试持久化方向定制器。

配置临时方向定制器

要设置一个临时方向定制器,请创建一个方向定制器对象,然后使用网络数据集对象的 directionsCustomizer 属性以将其与方向定制器对象关联。

下图显示了如何实例化一个自定义方向对象并将其关联至网络数据集对象。 要在求解时调用自定义方向,需使用网络数据集对象实例化路径求解程序对象。

# Instantiate a directions customizer object that adds an index number
# to each directions maneuver's display text
add_numbers_customizer = DirectionsIndexer()

# Create a network dataset object
network_dataset = arcpy.nax.NetworkDataset(
    r"C:\Data\Tutorial\SanFrancisco.gdb\Transportation\Streets_ND")

# Attach the custom directions object to the network dataset
network_dataset.directionsCustomizer = add_numbers_customizer

# Instantiate a route analysis
route = arcpy.nax.Route(network_dataset)

持久化方向定制器

持久化方向定制器将对方向定制器类的引用存储为网络数据集方案的一部分,而该方案存储在地理数据库中。 当使用该网络数据集完成求解时,即会调用这些方向定制器。 这些定制器称为持久化定制器是因为引用是网络数据集本身的一部分。 它们通过对网络数据集对象使用 updateNetworkDatasetSchema 方法来配置。

当包含持久化方向定制器的网络处于打开状态时,会加载并缓存方向定制器类。 将在应用程序的生命周期内保留此缓存。 这意味着,在关闭并重新启动已打开网络数据集的应用程序之前,将不会读取对持久化类所做的任何更改。 这适用于 ArcGIS ProArcGIS Server

值得注意的是,对于持久化方向定制器,网络数据集方案仅包含对方向定制器的引用,不包含类代码。 此引用使得网络数据集在被访问时隐式查找并加载其引用的方向定制器。 包含类代码的 Python 模块必须位于活动 ArcGIS Pro Python 环境的站点包文件夹中,以便网络数据集可以找到它。

了解有关 Python 环境的详细信息

注:

在进行任何修改之前,建议您先克隆默认 ArcGIS Pro Python 环境。 将自定义方向 Python 文件添加到站点包目录时,建议您先克隆环境并激活克隆的环境。

Python 脚本外部求解(例如在 ArcGIS ProArcGIS Server 中)时,如果需要调用方向定制器,则会使用持久化方向定制器。 例如,您要在 ArcGIS Pro 中创建一个网络分析图层,对图层求解并使用您的自定义设置生成方向,或者您想使用基于网络数据集发布的标准路径服务,但希望自定义方向输出。

如果 Network Analyst 图层使用的网络数据集包含已发布为服务的持久化方向定制器,则自定义方向包必须被手动复制到 ArcGIS Server Python 环境的站点包目录。 此外,当在服务上使用方向定制器时,ArcGIS Server 用户应该可以访问它所使用的任何外部资源(如文件),因为这取决于服务器的配置方式。

配置持久化方向定制器

对网络数据集对象使用 updateNetworkDatasetSchema 方法传入定义方向定制器类路径的字典,以便永久更新网络数据集方案。 该路径使用点标记来定义文件夹名称(在站点包目录中)、类所在文件的文件名以及类名本身。

下例显示了如何更新包含持久化自定义方向类的网络数据集。 此示例中的类名为 DirectionsIndexer,其代码位于名为 customization.pyPython 模块中,模块所在的文件夹名为 na_customizers,上述内容位于 ArcGIS Pro 活动 Python 环境的站点包文件夹中。

import arcpy

# Create a network dataset object
network_dataset = arcpy.nax.NetworkDataset(
    r"C:\Data\Tutorial\SanFrancisco_Persisted.gdb\Transportation\Streets_ND")

# Create a dictionary referencing the custom directions class to use
my_custom_directions = {"class": "na_customizers.customization.DirectionsIndexer"}

# Update the network dataset to use the custom directions class
network_dataset.updateNetworkDatasetSchema(
    custom_directions=my_custom_directions
)

当在网络分析中使用该网络数据集时,将创建方向定制器类对象,并且网络数据集将在生成初始方向后调用方向定制器对象以自定义方向。 在创建方向定制器的对象时,网络将从活动 ArcGIS Pro 环境的站点包文件夹中找到并加载包和模块中的指定类。 如果未找到包、模块或类,则不会使用方向定制器。 求解和方向生成在完成时将提示警告消息,指出使用方向定制器时出现问题。

当网络数据集包含持久化方向定制器时,该定制器将在常规 > 汇总部分和网络数据集属性对话框的方向页面上列出。 如果加载引用的类时出现问题,则会显示错误或警告消息。

对象生命周期

在最初构建网络数据集时,如果其具有持久化方向定制器,则其会实例化在其整个生命周期中引用的方向定制器对象,其中生命周期可能因所使用的框架(例如 ArcGIS ProArcGIS ServerPython)而有所不同。

由于方向定制器对象的特定实例可以在多次求解中使用,因此管理该对象的状态非常重要,特别是在根据需要遍历当前方向之前,在 Customize 方法开始时重置变量。 例如,如果方向定制器有一个在迭代方向时更新的实例变量,则应在 Customize 方法开始时将其重置,否则,它可能从上次求解过程中的某个值开始。

ArcGIS Server 环境中,每个 SOC 进程(在启动时和循环时)将构造新的网络数据集对象,并且还将创建方向定制器对象的新实例。 方向定制器对象的这个实例将在 SOC 过程的整个生命周期中使用;只有 Customize 方法会针对每个请求运行。

局限性

以下限制仅适用于自定义方向:

  • 自定义方向仅可用于 ArcGIS ProArcGIS Server
  • 只能在文件或企业级地理数据库上调用自定义方向。
  • 出于性能考虑,方向定制器不支持使用关键字作为参数。

创建和使用临时方向定制器的快速入门指南

以下部分作为快速指南,将介绍如何创建和使用临时方向定制器。 每个代码示例均表示完整工作流中的一个特定组件。 各个组件如下所示:

  1. 求解路径分析
  2. 定义方向定制器类
  3. 创建类的实例,并将其与用于路径求解的网络数据集相关联

最后的代码示例显示了如何将所有组件结合到一起。

代码示例是使用 Network Analyst 教程创建的,该教程可从数据下载页面下载。

求解路径分析

下方代码示例演示了使用 arcpy.nax 求解程序对象求解路径分析并打印逐步方向的工作流。

注:

下方代码中 gdb 的路径必须更新以反映数据在您的系统中的位置。

import arcpy

# Create a network dataset object
network_dataset = arcpy.nax.NetworkDataset(
    r"C:\Data\Tutorial\SanFrancisco.gdb\Transportation\Streets_ND")

# Instantiate a route analysis
route = arcpy.nax.Route(network_dataset)
route.returnDirections = True

# Insert stops for the route
with route.insertCursor(
    arcpy.nax.RouteInputDataType.Stops,
    ["NAME", "SHAPE@XY"]
) as cursor:
    cursor.insertRow(["Stop1", (-122.501, 37.757)])
    cursor.insertRow(["Stop2", (-122.445, 37.767)])

# Solve the route
result = route.solve()

# Print the directions
if result.solveSucceeded:
    for row in result.searchCursor(
        arcpy.nax.RouteOutputDataType.DirectionPoints, ["DisplayText"]
    ):
        print(row[0])

定义方向定制器类

下面的代码示例演示了方向定制器类的定义。 此示例会在每个方向行进策略的开头添加一个数字。 例如,在未进行自定义的情况下,第一个方向会是 Start at Stop1,在进行自定义的情况下,第一个方向会是 1 -> Start at Stop1

import arcpy

class DirectionsIndexer(arcpy.nax.DirectionsCustomizer):
    """Defines a directions customizer that adds and index number to directions."""

    def customize(self, directions_query: arcpy.nax.DirectionsQuery):
        """Add an index number to each directions maneuver."""
        index = 1
        for junction in directions_query.junctions:
            for point in junction.directionPoints:
                point.displayText = f"{index} -> {point.displayText}"
                index += 1

将方向定制器与网络数据集相关联

下面的代码示例演示了如何创建方向定制器类实例并将其与网络数据集对象关联。 此操作应该在调用路径求解之前完成 (route.solve())。

# Instantiate a directions customizer object that adds an index number
# to each directions maneuver's display text
add_numbers_customizer = DirectionsIndexer()

# Attach the custom directions object to the network dataset
network_dataset.directionsCustomizer = add_numbers_customizer

合并所有组件

下面的代码示例显示了如何将所有组件一起放入一个完整的工作流中,该工作流用于为路径分析工作流定义并使用临时方向定制器。

import arcpy

class DirectionsIndexer(arcpy.nax.DirectionsCustomizer):
    """Defines a directions customizer that adds and index number to directions."""

    def customize(self, directions_query: arcpy.nax.DirectionsQuery):
        """Add an index number to each directions maneuver."""
        index = 1
        for junction in directions_query.junctions:
            for point in junction.directionPoints:
                point.displayText = f"{index} -> {point.displayText}"
                index += 1

# Create a network dataset object
network_dataset = arcpy.nax.NetworkDataset(
    r"C:\Data\Tutorial\SanFrancisco.gdb\Transportation\Streets_ND")

# Instantiate a directions customizer object that adds an index number
# to each directions maneuver's display text
add_numbers_customizer = DirectionsIndexer()

# Attach the custom directions object to the network dataset
network_dataset.directionsCustomizer = add_numbers_customizer

# Instantiate a route analysis
route = arcpy.nax.Route(network_dataset)
route.returnDirections = True

# Insert stops for the route
with route.insertCursor(
    arcpy.nax.RouteInputDataType.Stops,
    ["NAME", "SHAPE@XY"]
) as cursor:
    cursor.insertRow(["Stop1", (-122.501, 37.757)])
    cursor.insertRow(["Stop2", (-122.445, 37.767)])

# Solve the route
result = route.solve()

# Print the directions
if result.solveSucceeded:
    for row in result.searchCursor(
        arcpy.nax.RouteOutputDataType.DirectionPoints, ["DisplayText"]
    ):
        print(row[0])

创建和使用持久化方向定制器的快速入门指南

以下部分作为快速指南,将介绍如何创建和使用持久化方向定制器。 在下方内容中,您将创建一个包含方向定制器类的 Python 模块,将 Python 模块存储在活动 Python 环境的站点包目录中,更新网络数据集以使用方向定制器,然后通过求解路径分析来测试方向定制器。

  1. 克隆默认 Python 环境
  2. 定义方向定制器类
  3. 更新网络数据集方案
  4. 求解路径

代码示例是使用 Network Analyst 教程创建的,该教程可从数据下载页面下载。

克隆默认 Python 环境

方向定制器代码必须保存到 ArcGIS Pro Python 环境。 如果使用默认 ArcGIS Pro Python 环境,建议您先克隆并激活新环境。

了解有关克隆环境的详细信息

定义方向定制器类

下面的代码示例演示了方向定制器类的定义。 此示例会在每个方向行进策略的开头添加一个数字。

import arcpy

class DirectionsIndexer(arcpy.nax.DirectionsCustomizer):
    """Defines a directions customizer that adds and index number to directions."""

    def customize(self, directions_query: arcpy.nax.DirectionsQuery):
        """Add an index number to each directions maneuver."""
        index = 1
        for junction in directions_query.junctions:
            for point in junction.directionPoints:
                point.displayText = f"{index} -> {point.displayText}"
                index += 1

在活动的 ArcGIS Pro Python 环境中,找到站点包文件夹。 在该目录内创建一个名为 na_customizers 的文件夹。 将上方用于定义方向定制器类的代码保存至 na_customizers 文件夹,命名为 direction_customization.py

更新网络数据集方案

Network Analyst\Tutorial\SanFrancisco.gdb 从教程数据复制SanFrancisco_Persisted.gdb

在独立脚本中使用下方代码永久更新 SanFrancisco_Persisted.gdb 中包含持久化自定义方向类的网络数据集。

import arcpy

# Create a network dataset object
network_dataset = arcpy.nax.NetworkDataset(
    r"C:\Data\Tutorial\SanFrancisco_Persisted.gdb\Transportation\Streets_ND")

# Create a dictionary referencing the custom directions class to use
my_custom_directions = {
    {"class": "na_customizers.customization.DirectionsIndexer"}
}

# Update the network dataset to use the custom directions class
network_dataset.updateNetworkDatasetSchema(
    custom_directions=my_custom_directions
)

求解路径

ArcGIS Pro 中,使用 SanFrancisco_Persisted.gdb 中的网络数据集求解路径。 使用 SanFrancisco.gdb 和相同的停靠点求解第二条路径,然后比较输出的路径方向。 在引用 SanFrancisco_Persisted.gdb 的路径中,因为方向定制器已生效,所以每个方向之前应该有一个数字前缀。 例如,使用 SanFrancisco.gdb 时,第一个方向会是 Start at Stop1,而使用 SanFrancisco_Persisted.gdb 时,第一个方向会是 1 -> Start at Stop1

了解如何在 ArcGIS Pro 中求解路径分析问题