Пользовательские анализаторы

Пользовательские анализаторы позволяют изменять значения стоимости и ограничений для сетевых элементов во время решения с помощью скрипта Python. Они могут запрашивать базовое значение элемента и обновлять его по мере необходимости или задавать другое значение на основе других входных данных. Это позволяет вам задавать значения атрибутов во время решения без обновления базового набора сетевых данных или исходных объектов.

Более подробно о сетевых атрибутах и анализаторах

Пользовательский анализатор можно использовать в следующих случаях:

  • Масштабирование стоимости улиц на основе запроса таблицы в другой базе данных.
  • Ограничение объектов улиц на основе чтения OID из внешнего файла.
  • Динамическое масштабирование стоимости улиц в зависимости от времени суток.

Пользовательские анализаторы реализуются путем создания класса Python, который наследуется от класса arcpy.nax.AttributeEvaluator, и связывания класса Python с определенным набором сетевых данных. Они могут быть связаны с атрибутами стоимости, ограничения или дескриптора. Они могут обновлять ребра, соединения или повороты как для решений с поддержкой времени, так и для решений, для которых время не включено. В зависимости от внесенных изменений обновления могут изменить затраты и пути, обнаруженные в ходе анализа.

С отдельным сетевым атрибутом должен быть связан только один пользовательский анализатор, но любое количество сетевых атрибутов может быть связано с анализатором. При реализации пользовательского анализатора существует несколько объектов, которые можно использовать для сбора информации о сети и элементах, таких как Attribute, Edge, Junction и др.

Класс пользовательского анализатора

Пользовательский анализатор создается путем определения класса, который наследуется от arcpy.nax.AttributeEvaluator и реализует по крайней мере один из методов значения элемента (например, edgeValue или edgeValueAtTime). Пользовательский анализатор также может реализовать методы __init__, attach и refresh.

Методы пользовательского анализатора

В подразделах ниже описываются методы, которые можно использовать для задания значений атрибутов во время решения.

Initializer

Вы можете реализовать метод initializer __init__. Если он реализован, метод initializer базового класса должен быть вызван явным способом перед добавлением логики инициализации. Иначе метод initializer базового класса вызывается автоматически.

Метод initializer базового класса будет использовать переданное имя атрибута и имена источников для задания свойств self.attributeName и self.sourceNames для объекта.

class CustomEvaluatorExample(arcpy.nax.AttributeEvaluator):
    """Example custom evaluator."""

    def __init__(self, attributeName, sourceNames=None):
        """Example initializer."""
        super().__init__(attributeName, sourceNames)
        # Do additional custom initialization

Attach

Когда пользовательский анализатор связан с сетевым набором данных, вызывается внутренний метод attach. Внутренний код запрашивает атрибуты сетевого набора данных для получения указанного имени атрибута. Если имя атрибута найдено, свойству self.attribute присваивается значение индекса атрибута; в противном случае пользовательский анализатор не связывается с сетевым набором данных и никакие другие методы класса не вызываются (включая любой метод attach).

Метод attach, реализованный в пользовательском анализаторе, может использоваться для изучения и проверки набора сетевых данных, чтобы убедиться, что он соответствует требованиям кода пользовательского анализатора, например, для проверки наличия других атрибутов. Если набор сетевых данных корректен, метод attach возвращает True; в противном случае он возвращает False. Когда возвращается False, пользовательский анализатор не будет связан с сетевым набором данных, и никакие другие методы вызываться не будут.

Реализация метода attach в пользовательском анализаторе необязательна.

Примечание:

Набор сетевых данных может быть открыт несколько раз в зависимости от количества потоков, используемых приложением для доступа к нему.

Обновить

Метод refresh пользовательского анализатора вызывается в начале каждого решения, перед вычислением каких-либо элементов. В этом методе может быть задано любое внутреннее состояние (например, кэшированные значения или проверка несетевого набора данных), которое меняется в зависимости от решения. Свойство self.networkQuery доступно в этом методе. Это свойство задается внутренне, после успешного завершения метода attach.

Реализация метода refresh в пользовательском анализаторе необязательна.

Методы значений

Могут быть реализованы следующие методы значений: edgeValue, edgeValueAtTime, junctionValue, junctionValueAtTime, turnValue и turnValueAtTime. Методы с префиксом edge влияют на значения атрибутов для источников ребер, методы с префиксом junction влияют на значения атрибутов для источников соединений и методы с префиксом turn влияют на значения атрибутов для источников поворотов. Методы с суффиксом AtTime вызываются во время решения с включенным временем, а методы без этого суффикса вызываются, когда время не используется.

В общем, во время решения, когда вычисляется атрибут и имеется подключенный пользовательский анализатор, он переопределяет основной. Значение, возвращаемое методом связанного элемента (например, edgeValue), будет использоваться механизмом решения в оставшейся части анализа. Используя эти методы значений, вы можете реализовать пользовательскую логику для задания конечного значения атрибута элемента.

Методы ValueAtTime предоставляют параметр datetime, который является датой и временем появления элемента на потенциальном маршруте. Эти методы вызываются для каждого элемента, потенциально несколько раз, во время решения. Любой код в этих методах должен быть исполняемым.

Вы должны реализовать хотя бы один из этих методов значений.

Примеры

В приведенных ниже примерах показана базовая реализация класса пользовательского анализатора.

Пример 1: Следующий код представляет собой класс пользовательского анализатора, который удваивает стоимость указанного атрибута для всех вычисляемых ребер для решения, не зависящего от времени, без реализации каких-либо необязательных методов:

import arcpy

class EdgeCustomizer(arcpy.nax.AttributeEvaluator):
    """Defines a custom evaluator that multiplies the edge cost by 2."""

    def edgeValue(self, edge: arcpy.nax.Edge):
        """Multiplies the edge cost by 2."""
        base_value = self.networkQuery.attributeValue(edge, self.attribute)
        return base_value * 2

Пример 2: Следующий код представляет собой класс пользовательского анализатора, который удваивает стоимость указанного атрибута для всех вычисляемых ребер как для решения, не зависящего от времени, так и для решения с поддержкой времени, с минимальной реализацией необязательных методов:

import datetime
from typing import Union, Optional, List
import arcpy

class EdgeCustomizer(arcpy.nax.AttributeEvaluator):
    """Defines a custom evaluator that multiplies the edge cost by 2."""

    def __init__(self, attributeName: str, sourceNames: Optional[List] = None):
        """Example initializer."""
        super().__init__(attributeName, sourceNames)
        # Do additional custom initialization

    def attach(self, network_query: arcpy.nax.NetworkQuery) -> bool:
        """Connect to and validate the network dataset."""
        # Do additional validation checks before returning Boolean
        return True

    def refresh(self) -> None:
        """Reset internal state before solve."""
        # Reset internal state in this method as needed
        pass

    def edgeValue(self, edge: arcpy.nax.Edge):
        """Multiplies the edge cost by 2."""
        base_value = self.networkQuery.attributeValue(edge, self.attribute)
        return base_value * 2

    def edgeValueAtTime(
            self, edge: arcpy.nax.Edge,
            time: datetime.datetime, time_usage: arcpy.nax.NetworkTimeUsage
    ) -> Union[int, float, bool]:
        """Multiplies the edge cost by 2 when the solve uses a time of day."""
        base_value_at_time = self.networkQuery.attributeValue(
            edge, self.attribute, time_usage, time)
        return base_value_at_time * 2

Связывание пользовательского анализатора с набором сетевых данных

Существует два способа развернуть пользовательский анализатор, чтобы он был связан с набором сетевых данных и чтобы логика настройки вызывалась во время решения. Эти два метода называются временными и постоянными.

Подсказка:

Создание класса пользовательского анализатора и его тестирование как временного пользовательского анализатора. Затем запустите скрипт в режиме отладки в таком редакторе, как Visual Studio Code. Как только он заработает должным образом, и если необходимо, вы можете сделать его постоянным пользовательским анализатором. Затем проверьте постоянный анализатор, чтобы убедиться, что все работает правильно.

Временный пользовательский анализатор

Временные пользовательские анализаторы связаны только с объектом набора сетевых данных, созданным в скрипте; они не сохраняются постоянно в наборе сетевых данных. Временные пользовательские анализаторы настраиваются с использованием свойства customEvaluators объекта набора сетевых данных в сценарии, выполняющем решение.

Временный пользовательский анализатор можно использовать для приложений, в которых пользовательский анализатор должен вызываться из скрипта Python. Они также могут быть полезны для разработки и отладки постоянных пользовательских анализаторов.

Настройка временного пользовательского анализатора

Чтобы настроить временный пользовательский анализатор, создайте объект пользовательского анализатора, а затем используйте свойство customEvaluators объекта набора сетевых данных, чтобы связать с ним объект пользовательского анализатора.

В приведенном ниже примере показано, как создать экземпляр пользовательского объекта анализатора, который настраивает сетевой атрибут TravelTime, и связать его с объектом набора сетевых данных. Чтобы вызвать пользовательские анализаторы во время решения, создайте экземпляр объекта решения маршрута, используя объект набора сетевых данных.

# Instantiate a custom evaluator object that will customize the
# TravelTime cost attribute
travel_time_customizer = EdgeCustomizer("TravelTime")

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

# Attach the custom evaluator object to the network dataset
network_dataset.customEvaluators = [travel_time_customizer]

# Instantiate a route analysis
route = arcpy.nax.Route(network_dataset)
Примечание:

При запуске пользовательского анализатора при необходимости может быть предоставлен список имен определенных сетевых источников, где указываются источники, к которым будет применяться пользовательский анализатор. Если список не задан, пользовательский анализатор будет применен ко всем источникам. Подробнее см. документацию к AttributeEvaluator.

Постоянные пользовательские анализаторы

Постоянные пользовательские анализаторы хранят ссылку на класс пользовательских анализаторов как часть схемы набора сетевых данных, которая хранится в базе геоданных. Эти пользовательские анализаторы будут вызываться всякий раз, когда выполняется решение с использованием этого набора сетевых данных. Такой вариант называется постоянным, поскольку ссылка является частью самого набора сетевых данных. Они настраиваются с использованием updateNetworkDatasetSchema объекта набора сетевых данных.

При открытии сети с сохраненным пользовательским анализатором, вызывается метод attach, а пользовательский анализатор загружается и кэшируется. Этот кэш сохраняется в течение всего срока работы приложения. Это означает, что любые изменения, внесенные в класс постоянного пользовательского анализатора, не будут прочитаны до тех пор, пока приложение, в котором открыт набор сетевых данных, не будет закрыто и перезапущено. Это применимо и к ArcGIS Pro и к ArcGIS Server.

Важно отметить, что для постоянных пользовательских анализаторов схема набора сетевых данных содержит только ссылку на пользовательский анализатор, а не код класса. Эта ссылка позволяет набору сетевых данных при доступе к нему неявно найти и загрузить соответствующий пользовательский анализатор. Файл, содержащий код класса, должен находиться в папке site-packages активной среды ArcGIS Pro Python, чтобы набор сетевых данных мог его найти.

Более подробно о средах Python

Примечание:
Рекомендуется клонировать среду по умолчанию ArcGIS Pro Python, прежде чем вносить какие-либо изменения. При добавлении файла пользовательских анализаторов Python в каталог пакетов сайта рекомендуется сначала клонировать среду и активировать клонированную среду.

Сохраняемый пользовательский анализатор будет использоваться, когда вам нужно вызвать пользовательский оценщик при выполнении решения вне скрипта Python, например, в ArcGIS Pro или ArcGIS Server.

Если слой сетевого анализа, использующий набор сетевых данных с постоянным пользовательским анализатором, публикуется как сервис, пакет пользовательского анализатора должен быть вручную скопирован в каталог пакетов сайта ArcGIS Server Python. Кроме того, когда пользовательский анализатор используется в сервисе, любой внешний ресурс, который он использует (например, файлы), должен быть доступен пользователю ArcGIS Server, поскольку это зависит от настройки сервера.

Настройка постоянного пользовательского анализатора

Используйте метод updateNetworkDatasetSchema для объекта набора сетевых данных, чтобы постоянно обновлять схему набора сетевых данных, передавая словарь, определяющий сетевой атрибут, для которого следует вызвать пользовательский анализатор, и путь к классу пользовательского анализатора. Этот путь использует точечное обозначение для определения имени папки (в каталоге site-packages), имени файла, в котором находится класс, и самого имени класса.

В приведенном ниже примере показано, как обновить набор сетевых данных с помощью сохраненного класса пользовательских анализаторов. Класс в этом примере называется EdgeCustomizer, а его код находится в модуле Python с именем customization.py в папке с именем na_customizers, которая находится в папке site-packages для 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 evaluators to apply to the
# TravelTime attribute
my_custom_evaluators = {
    "TravelTime": {"class": "na_customizers.customization.EdgeCustomizer"}
}

# Update the network dataset to use the custom evaluator
network_dataset.updateNetworkDatasetSchema(custom_evaluators=my_custom_evaluators)
Примечание:

При обновлении схемы со ссылкой на пользовательский анализатор при необходимости может быть предоставлен список имен определенных сетевых источников, где указываются источники, к которым будет применяться пользовательский анализатор. Если список не задан, пользовательский анализатор будет применен ко всем источникам. Чтобы установить это, включите ключ sourceNames в список имен источников. Например:

{
    "TravelTime": {
        "class": "na_customizers.customization.EdgeCustomizer",
        "sourceNames": ["Streets"]
    }
}

Когда этот набор сетевых данных и назначенный атрибут используются в сетевом анализе, набор сетевых данных будет вызывать пользовательский анализатор во время решения. Сеть проверит существование указанного атрибута и источников, а также найдет и загрузит указанный пакет и класс из папки пакетов сайта активной среды ArcGIS Pro Python. Если атрибут, имена источников, пакет или класс не найдены, пользовательский анализатор использоваться не будет. Решение завершится появлением предупреждающего сообщения о том, что при использовании пользовательского анализатора возникла проблема.

Если в наборе сетевых данных сохранены пользовательские анализаторы, они будут перечислены в разделе Общее > Итоговое описание диалогового окна свойств набора сетевых данных. Они также будут отображаться вместе с указанным сетевым атрибутом при просмотре соответствующей вкладки, например, Атрибуты передвижения > Стоимость. Если при загрузке класса возникла проблема, появятся предупреждающие сообщения.

Жизненный цикл объекта

Когда набор сетевых данных изначально создается и имеет постоянный пользовательский анализатор, он создает экземпляр объекта пользовательского анализатора, на который ссылаются на протяжении всего его существования, причем время жизни может варьироваться в зависимости от используемой платформы (например, ArcGIS Pro, ArcGIS Server или Python).

Поскольку конкретный экземпляр объекта пользовательского анализатора может использоваться для нескольких решений, важно управлять состоянием этого объекта, в частности, сбрасывать переменные в методе refresh по мере необходимости. Например, если пользовательскому анализатору необходимо записать количество ребер для каждого решения, переменная, используемая для отслеживания этого значения, должна быть сброшена в методе refresh.

В контексте ArcGIS Server каждый процесс SOC (во время запуска и во время перезапуска) создаст новый объект набора сетевых данных, а также создаст новый экземпляр объекта пользовательского анализатора. Этот экземпляр объекта пользовательского анализатора будет использоваться на протяжении всего жизненного цикла процесса SOC; только метод refresh и методы Value будут запускаться для каждого запроса.

Ограничения

К пользовательским анализаторам применяются следующие ограничения:

  • Пользовательские анализаторы доступны только в ArcGIS Pro и ArcGIS Server.
  • Пользовательские анализаторы могут быть вызваны только для файловой или корпоративной базы геоданных.
  • Атрибуты дескриптора вызываются только тогда, когда на них ссылается атрибут стоимости или ограничения.
  • Из соображений производительности пользовательские анализаторы не поддерживают использование ключевых слов в качестве аргументов.

Руководство по быстрому началу работы по созданию и использованию временного пользовательского анализатора

Ниже приведены разделы из руководства по быстрому началу работы по созданию и использованию временного пользовательского анализатора Каждый пример кода иллюстрирует определенный компонент полного рабочего процесса. Используются следующие компоненты:

  1. Расчет анализа маршрутов
  2. Настройка класса пользовательского анализатора
  3. Создание экземпляра класса и связывание его с набором сетевых данных, который используется для расчета маршрута

Последний пример кода показывает, как соединить все компоненты вместе.

Примеры кода созданы на основе руководства по сетевому анализу, которое можно загрузить со страницы загрузки данных.

Расчет анализа маршрутов

В приведенном ниже примере кода показан рабочий процесс для анализа маршрута с использованием объекта механизма расчета arcpy.nax и получения времени передвижения по маршруту.

Примечание:

Путь к базе геоданных в приведенном ниже коде необходимо обновить, чтобы отразить расположение данных в вашей системе.

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)

# 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 total travel time for the route
for row in result.searchCursor(
    arcpy.nax.RouteOutputDataType.Routes,
    ["Total_Minutes"]
):
    print(f"Solved Total_Minutes: {row[0]}")

Настройка класса пользовательского анализатора

В приведенном ниже примере кода показано определение класса пользовательских анализаторов. Пользовательский анализатор в этом примере умножает исходную стоимость времени на 2. Время в пути по маршруту, рассчитанному с помощью этого пользовательского анализатора, должно быть в два раза больше времени в пути того же маршрута, решенного без анализатора.

class EdgeCustomizer(arcpy.nax.AttributeEvaluator):
    """Defines a custom evaluator that multiplies the edge cost by 2."""

    def edgeValue(self, edge: arcpy.nax.Edge):
        """Multiplies the edge cost by 2."""
        base_value = self.networkQuery.attributeValue(edge, self.attribute)
        return base_value * 2

Связывание пользовательского анализатора с набором сетевых данных

В приведенном ниже примере кода показано создание экземпляра класса пользовательского анализатора и его связывание с объектом набора сетевых данных для использования с атрибутом стоимости TravelTime. Это следует сделать до вызова метода расчета маршрута (route.solve()).

# Create a custom evaluator object that will customize the
# TravelTime cost attribute
travel_time_customizer = EdgeCustomizer("TravelTime")

# Attach the custom evaluator object to the network dataset
network_dataset.customEvaluators = [travel_time_customizer]

Сборка всех компонентов

В приведенном ниже примере кода показано, как объединить все компоненты в полный рабочий процесс, который определяет и использует временный пользовательский анализатор для рабочего процесса анализа маршрута.

import arcpy

class EdgeCustomizer(arcpy.nax.AttributeEvaluator):
    """Defines a custom evaluator that multiplies the edge cost by 2."""

    def edgeValue(self, edge: arcpy.nax.Edge):
        """Multiplies the edge cost by 2."""
        base_value = self.networkQuery.attributeValue(edge, self.attribute)
        return base_value * 2

# Create a custom evaluator object that will customize the
# TravelTime cost attribute
travel_time_customizer = EdgeCustomizer("TravelTime")

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

# Attach the custom evaluator object to the network dataset
network_dataset.customEvaluators = [travel_time_customizer]

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

# 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 total travel time for the route
for row in result.searchCursor(
    arcpy.nax.RouteOutputDataType.Routes,
    ["Total_Minutes"]
):
    print(f"Solved Total_Minutes: {row[0]}")

Руководство по быстрому началу работы по созданию и использованию постоянного пользовательского анализатора

Ниже приведены разделы из руководства по быстрому началу работы по созданию и использованию постоянного пользовательского анализатора Ниже вы создадите собственный класс пользовательского анализатора в активной среде Python, обновите набор сетевых данных для использования пользовательского анализатора и протестируете его, выполнив анализ маршрута.

  1. Клонируйте среду Python по умолчанию
  2. Настройка и сохранение класса пользовательского анализатора
  3. Обновление схемы набора сетевых данных
  4. Решение маршрута

Примеры кода созданы на основе руководства по сетевому анализу, которое можно загрузить со страницы загрузки данных.

Клонируйте среду Python по умолчанию

Код пользовательского анализатора должен быть сохранен в среде ArcGIS Pro Python. Если вы используете среду ArcGIS Python по умолчанию, рекомендуется сначала клонировать и активировать новую среду.

Более подробно о клонировании среды

Настройка и сохранение класса пользовательского анализатора

В приведенном ниже примере кода показано определение класса пользовательских анализаторов. Пользовательский анализатор в этом примере умножает исходную стоимость времени на 2. Время в пути по маршруту, рассчитанному с помощью этого пользовательского анализатора, должно быть в два раза больше времени в пути того же маршрута, решенного без анализатора.

import arcpy

class EdgeCustomizer(arcpy.nax.AttributeEvaluator):
    """Defines a custom evaluator that multiplies the edge cost by 2."""

    def edgeValue(self, edge: arcpy.nax.Edge):
        """Multiplies the edge cost by 2."""
        base_value = self.networkQuery.attributeValue(edge, self.attribute)
        return base_value * 2

В активной среде Python найдите папку site-packages. Внутри нее создайте папку с именем na_customizers. Сохраните приведенный выше код, определяющий класс пользовательского анализатора, в папку na_customizers как cost_customization.py.

Примечание:

Следующие имена примеров, использованные в примере кода важны, так как в дальнейшем вы обновите схему набора сетевых данных этими значениями.

Обновление схемы набора сетевых данных

Копируйте Network Analyst\Tutorial\SanFrancisco.gdb из данных руководства в SanFrancisco_Persisted.gdb.

Используйте приведенный ниже код в автономном скрипте, чтобы постоянно обновлять набор сетевых данных SanFrancisco_Persisted.gdb, чтобы он ссылался на пользовательский анализатор атрибута стоимости TravelTime. Словарь my_custom_evaluators ссылается на имя папки, имя файла и имя класса пользовательского анализатора, определенного в приведенном выше примере кода.

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 evaluators to apply to the
# TravelTime attribute
my_custom_evaluators = {
    "TravelTime": {"class": "na_customizers.customization.EdgeCustomizer"}
}

# Update the network dataset to use the custom evaluator
network_dataset.updateNetworkDatasetSchema(custom_evaluators=my_custom_evaluators)

Решение маршрута

В ArcGIS Pro воспользуйтесь набором сетевых данных SanFrancisco_Persisted.gdb для построения маршрута с использованием режима передвижения Время в пути. Решите второй маршрут, используя SanFrancisco.gdb и те же остановки, и сравните время передвижения для выходного маршрута. Время передвижения по маршруту по SanFrancisco_Persisted.gdb должно быть в два раза больше времени поездки по SanFrancisco.gdb по тому же маршруту из-за пользовательского анализатора.

Подробнее о расчетах в анализе маршрутов в ArcGIS Pro