Доступ к Python CIM

Модуль arcpy.mp является крупномодульным интерфейсом Python API, который разработан для предоставления доступа ко множеству часто используемых задач автоматизации карты. Он включает набор разнообразных отображаемых классов, свойств классов, методов и вспомогательных функций, но не предоставляет доступа ко всем свойствам, настройкам и возможностям, доступным в ArcGIS Pro. Одной из причин является сохранение интерфейса API продуманным, простым и управляемым. Другой причиной является то, что ArcGIS Pro разрабатывается с такой скоростью, что интерфейсы arcpy.mp API не успевают за ним. Прежде чем у вас появится доступ через общедоступный API, если он вообще появится, может потребоваться выпуск одной или нескольких версий. Начиная с ArcGIS Pro 2.4, разработчики Python имеют детальный доступ к Картографической информационной модели (CIM) и могут получить доступ к множеству других параметров, свойств и возможностей, которые сохраняются в проекте или внешнем документе, например файле слоя (.lyrx). Сообщество разработчиков .NET SDK имело доступ к CIM, начиная с версии ArcGIS Pro 1.1, а теперь это доступно и для сообщества разработчиков Python.

Внимание:

В следующих разделах описано, что можно сделать с помощью CIM. Прежде чем изменять CIM, важно осознавать, чего не стоит делать. Дополнительную информацию см. в разделе ниже, озаглавленному Будьте аккуратны при изменении CIM.

Что такое CIM и как его посмотреть

CIM ­- это Картографическая информационная модель от Esri (Cartographic Information Model). Это спецификация содержания карты, используемая для документирования того, как информация, описывающая различные компоненты проекта, сохраняется при чтении или записи проекта. Независимо от аббревиатуры, не воспринимайте CIM как что-то, ограниченное только картографическими настройками. Возможности, предоставляемые этим классам через CIM, выходят далеко за рамки, и приведенные ниже примеры демонстрируют лишь небольшую часть этих расширенных возможностей.

Спецификация представлена в формате JSON, и ее можно просмотреть при экспорте карт, компоновок, слоев, стилей и других элементов в такие файлы *.*x, как *.mapx, *.pagx, *.lyrx, *.stylx и т.д. Самый простой способ просмотреть и узнать, как свойства класса сохраняются в спецификации JSON, - открыть один из этих файлов во вьюере JSON, например в Notepad++. Структура файла JSON параллельна объектной модели CIM, представленной модулю arcpy.mp, поэтому необходимо просмотреть эти файлы, чтобы понять, где в структуре находится свойство и как оно называется в CIM, поскольку оно может не совпадать с тем, как оно называется в приложении.

Подсказка:

Переименование одного из этих файлов JSON, например, из *.pagx в *.json, может показаться более удобным для чтения в вашем редакторе, поскольку его можно настроить на раскрашивание различных частей кода в зависимости от того, что он представляет; в противном случае весь текст может выглядеть одинаковым.

Далее показано представление JSON для модифицированного списка с аббревиатурами для свойств корневого уровня, доступных для CIMLayerDocument (*.lyrx). Некоторые из элементов ниже, например, featureTable, можно развернуть, чтобы открыть больше настроек с отступом.

{
  "type" : "CIMLayerDocument",
  "version" : "3.2.0",
  "layers" : [
    "CIMPATH=map/greatlakes.xml"
  ],
  "layerDefinitions" : [
    {
      "type" : "CIMFeatureLayer",
      "name" : "GreatLakes",
      "uRI" : "CIMPATH=map/greatlakes.xml",
      "useSourceMetadata" : true,
      "description" : "GreatLakes",
      "layerType" : "Operational",
      "showLegends" : true,
      "visibility" : true,
      "displayCacheType" : "Permanent",
      "maxDisplayCacheAge" : 5,
      "showPopups" : true,
      "serviceLayerID" : -1,
      "autoGenerateFeatureTemplates" : true,
      "featureElevationExpression" : "0",
      "featureTable" : {
      "htmlPopupEnabled" : true,
      "selectable" : true,
      "featureCacheType" : "Session",
      "scaleSymbols" : true,
      "snappable" : true

На снимке экрана выше основные метаданные отображаются в верхней части файла, описывающего документ и слой. Также имеется множество свойств корневого уровня для layerDefinitions. Список зависит от настроек, сохраняемых с объектом. Важно понимать, что свойства, предоставляемые при дополнении кода IDE, будут отображать гораздо больше свойств, чем вы обычно видите в файле JSON. Это связано с тем, что в файле JSON хранятся только текущие настройки проекта, а не все возможные настройки по умолчанию. Это сделано для повышения производительности и уменьшения излишнего увеличения объема. Одним из примеров, иллюстрирующих, что файл JSON не отображает все возможные свойства, является использование Булевых значений. Только свойства, которые заданы в текущий момент равными true, отображаются в файле JSON. Например, при завершении кода в IDE для объекта CIM слоя должны отображаться дополнительные атрибуты, такие как expanded или showMapTips, но, поскольку они в настоящее время установлены на false на снимке экрана выше, они не отображаются в приведенном выше файле JSON.

Изменение определения CIM

Основные точки доступа относятся к следующим классам arcpy.mp: Layer, Layout, Map, Report и Таблица. Каждый из этих классов может быть сохранен в формате файла JSON: .lyrx, .mapx, .pagx и .rptx. Основной рабочий процесс заключается в возврате определения CIM одного из этих объектов с помощью метода getDefinition(), внесении соответствующих изменений API CIM и передаче изменений обратно в тот же объект с помощью метода setDefinition().

Если вы хотите получить определение CIM объекта, вы должны указать cim_version. Esri использует спецификацию семантического версионирования. Это означает, что в основных версиях, таких как 2.0 или 3.0, разрешены критические изменения API. Это дает авторам скриптов Python контроль над тем, какую версию CIM использовать при запуске скрипта, чтобы избежать возможных критических изменений. Если вы создаете скрипты для ArcGIS Pro 2.x, укажите значение cim_version как 'V2'. Если вы создаете скрипты для ArcGIS Pro 3.x, укажите значение cim_version как 'V3'. Скрипты, созданные с использованием cim_version 'V2', продолжат работать в ArcGIS Pro 3.x, но будут использовать версию CIM 2x.

После возврата определения CIM вы можете попытаться перемещаться по его структуре, просматривая завершение его кода в IDE, или использовать упомянутый выше метод, просматривая файл JSON напрямую. Полезнее всего читать структуру JSON в отдельном вьюере при навигации по завершению кода в IDE.

Внимание:
Не редактируйте эти файлы напрямую. Всегда используйте API для внесения изменений.

Пример 1: Основные булевы свойства на корневом уровне

Следующий скрипт arcpy.mp изменит несколько атрибутов корневого уровня для объекта слоя CIM с именем l_cim. Атрибут selectable появляется в файле JSON выше, поскольку его текущим значением является true, но оно будет задано как False в скрипте Python. Атрибуты showMapTips и expanded не появляются в файле JSON, поскольку их текущими значениями является false, но они будут заданы как True. Если вы сохраняете слой в другой файл слоя (*.lyrx), соответствующие изменения будут отображены в файле JSON.

# Reference a project, map, and layer using arcpy.mp
p = arcpy.mp.ArcGISProject('current')
m = p.listMaps('Map')[0]
l = m.listLayers('GreatLakes')[0]

# Return the layer's CIM definition
l_cim = l.getDefinition('V3')

# Modify a few boolean properties
l_cim.showMapTips = True  #Turn on map tips for bubble tips to appear
l_cim.selectable = False  #Set the layer to not be selectable
l_cim.expanded = True     #Expand the Layer in the Contents pane

# Push the changes back to the layer object
l.setDefinition(l_cim)

# Save changes
p.save()

Примечание:

В ArcGIS Pro 3.2 количество классов, поддерживающих методы getDefinition и setDefinition, было расширено за счет предоставления ряда функций конструктора. Это повышает производительность и упрощает изменение одного элемента компоновки, например, вместо того, чтобы получать полное определение компоновки, можно находить элемент, вносить изменения и возвращать изменения в компоновку. Новые классы, поддерживающие методы getDefinition и setDefinition: BookmarkMapSeries, GraphicElement, GroupElement, LegendElement, MapFrame, MapSeries, MapSurroundElement, PictureElement и TextElement.

Пример 2: Изменение свойств поля

Примеры JSON и Python выше относительно просты, потому что были изменены только атрибуты на корневом уровне. Модель объекта CIM может быть вложенной в зависимости от сложности определения каждого класса CIM и его вложенного набора возможностей. Один класс CIM может иметь от нуля до многих участников, которые установлены для другого класса CIM, и эти классы также могут иметь зависимости от других классов. Каждый зависимый класс CIM имеет отступ внутри структуры JSON. Все, что имеет отступ одного уровня, доступно как свойство этого класса CIM. При просмотре структуры JSON в редакторе JSON вы можете разворачивать и сворачивать иерархию классов. Еще одним преимуществом работы с файлом JSON является то, что вы можете использовать поиск свойств и определить, как получить доступ к вложенной структуре с помощью CIM API.

Подсказка:

При навигации по структуре CIM в IDE завершение кода занимает только четыре уровня, поэтому, если вам нужно увидеть завершение кода за пределами четвертого уровня, создайте промежуточную переменную.

В этом примере модель объекта рассматривается глубже, чем корневой уровень. Если участник featureTable в приведенном выше примере снимка экрана JSON развернут, вы увидите, что у него есть дополнительные участники: type (которые представляют имя CIMClass), displayField, editable и fieldDescriptions. Свойство fieldDescriptions представляет собой список классов CIMFieldDescription, и в зависимости от типа поля этот класс может иметь свойство numberFormat, которое берет класс CIMNumericFormat в качестве значения. Обратите внимание, как замечательно каждый уровень зависимых классов организован с помощью системы отступов.

     "featureTable" : {
        "type" : "CIMFeatureTable",
        "displayField" : "NAME",
        "editable" : true,
        "fieldDescriptions" : [
          {
            "type" : "CIMFieldDescription",
            "alias" : "OBJECTID",
            "fieldName" : "OBJECTID",
            "numberFormat" : {
              "type" : "CIMNumericFormat",
              "alignmentOption" : "esriAlignRight",
              "alignmentWidth" : 0,
              "roundingOption" : "esriRoundNumberOfDecimals",
              "roundingValue" : 0
            },
            "readOnly" : true,
            "visible" : true,
            "searchMode" : "Exact"
          },

Общим запросом является возможность изменения псевдонима поля таблицы или видимости, особенно при создании таблицы на лету, используя, например, MakeFeatureLayer. Это нельзя выполнить с использованием arcpy.mpуправляемых API. Следующий скрипт использует CIM для доступа к объекту featureTable векторного слоя и его объекту fieldDescriptions, в котором можно задать атрибуты alias и visible. Синтаксис Python следует объектной модели CIM, где каждая точка открывает набор свойств следующего объекта на основе отступа в структуре JSON.

# Reference a project, map, and layer using arcpy.mp
p = arcpy.mp.ArcGISProject('current') 
m = p.listMaps('Map')[0]
lyr = m.listLayers('GreatLakes')[0]

# Get the layer's CIM definition
cim_lyr = lyr.getDefinition('V3')

# Make changes to field properties
for fd in cim_lyr.featureTable.fieldDescriptions:
    if fd.fieldName == "OBJECTID":
        fd.visible = False            #Do not display this field
    if fd.fieldName == "Shape_Area":
        fd.alias = "Area (hectares)"  #Change field alias

# Push the changes back to the layer object
lyr.setDefinition(cim_lyr)

Примечание:

Предыдущий пример кода идеально подходит для демонстрации навигации по модели объекта CIM, но он также демонстрирует высказанное ранее замечание о том, что не все свойства и классы сохраняются в CIM. Скорее всего, если вы добавите на карту новый класс объектов или таблицу, в CIM fieldDescriptions не будет, и, следовательно, вы не сможете изменить свойства поля. Опять же, CIM сохраняет только свойства, отличные от свойств по умолчанию, поэтому, если в описания полей ранее не вносились изменения, свойство fieldDescriptions вернет пустой список. В этом сценарии есть два подхода. Первый подход заключается в создании необходимых классов CIM, и он будет обсуждаться в разделе Создание классов CIM ниже. Второй подход для этого конкретного примера - заставить CIM обновиться описаниями полей с помощью функции arcpy.AlterField. В большинстве случаев обходных путей нет, поэтому вам потребуется создать классы CIM.

Следующий скрипт демонстрирует, как принудительно включить описания полей в определение CIM. Если в слое нет описаний полей, он вызывает функцию AlterField для первого поля, которая передает информацию в CIM без внесения изменений в поле. Скрипт должен показывать другое количество полей после работы с классом объектов.

p = arcpy.mp.ArcGISProject('current')
m = p.listMaps()[0]
lyr = m.listLayers('NE_State_Boundaries')[0]

lyr_cim = lyr.getDefinition('V3')
featTab = lyr_cim.featureTable
if len(featTab.fieldDescriptions) == 0: #No CIM field descriptions
  print(f'LYR desc count PRE mod: {len(featTab.fieldDescriptions)}')
  lyrFld = arcpy.ListFields(lyr)[0]             #First field
  arcpy.management.AlterField(lyr, lyrFld.name) #Force CIM update  

lyr_cim = lyr.getDefinition('V3')
featTab = lyr_cim.featureTable
print(f'LYR desc count POST mod: {len(featTab.fieldDescriptions)}')

Пример 3: Изменение символов слоя

Этот пример демонстрирует, насколько вложенной может быть модель объекта CIM, а также демонстрирует преимущества доступа на уровне CIM. У управляемого arcpy.mp API ограничен доступ к отрисовке и сложным свойствам. Он может изменять только простые свойства и только для слоя по умолчанию для символа. Однако CIM может получать доступ к символам со множеством слоев. Следующий скриншот показывает настройки в приложении, которые недоступны для arcpy.mp API:

Панель Символов для векторного полигонального слоя

Ниже приведен отредактированный и упрощенный файл JSON, в котором отображается только информация метода отображения для символов слоя. renderer, который представляет собой CIMSimpleRenderer, имеет свойство symbol, представляющее CIMSymbolReference, и имеет свойство symbol, которое является CIMPolygonSymbol, и оно имеет два symbolLayers: CIMSolidStroke и CIMSolidFill, каждый из которых отображает свойства, доступные на панели Символы.

  "layerDefinitions" : [
    {
      "renderer" : {
        "type" : "CIMSimpleRenderer",
        "patch" : "Default",
        "symbol" : {
          "type" : "CIMSymbolReference",
          "symbol" : {
            "type" : "CIMPolygonSymbol",
            "symbolLayers" : [
              {
                "type" : "CIMSolidStroke",
                "effects" : [
                  {
                    "type" : "CIMGeometricEffectDashes",
                    "dashTemplate" : [ 5, 5],
                    "lineDashEnding" : "NoConstraint",
                    "controlPointEnding" : "NoConstraint"
                  }
                ],
                "enable" : true,
                "capStyle" : "Round",
                "joinStyle" : "Round",
                "lineStyle3D" : "Strip",
                "miterLimit" : 10,
                "width" : 3,
                "color" : {
                  "type" : "CIMRGBColor",
                  "values" : [0, 0, 0, 100]
                }
              },
              {
                "type" : "CIMSolidFill",
                "enable" : true,
                "color" : {
                  "type" : "CIMRGBColor",
                  "values" : [ 255, 127, 0, 100 ]
                }
              }

Следующий код Python использует доступ CIM для изменения символов слоя. Оба символьных слоя изменены.

# Reference a project, map, and layer using arcpy.mp
p = arcpy.mp.ArcGISProject('current')
m = p.listMaps('Trail Routes')[0]
lyr = m.listLayers('Loops')[0]

# Return the layer's CIM definition
cim_lyr = lyr.getDefinition('V3')

# Modify the color, width and dash template for the SolidStroke layer
symLvl1 = cim_lyr.renderer.symbol.symbol.symbolLayers[0]
symLvl1.color.values = [250, 250, 40, 50]
symLvl1.width = 8
ef1 = symLvl1.effects[0]    #Note, deeper indentation 
ef1.dashTemplate = [20, 30] #Only works if there is an existing dash template

# Modify the color/transparency for the SolidFill layer
symLvl2 = cim_lyr.renderer.symbol.symbol.symbolLayers[1]
symLvl2.color.values = [140, 70, 20, 20]

# Push the changes back to the layer object
lyr.setDefinition(cim_lyr)

Будьте аккуратны при изменении CIM

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

Пример 1: Вы не можете изменить пространственную привязку

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

  "mapDefinition" : {
    "type" : "CIMMap",
    "name" : "Map",
    "uRI" : "CIMPATH=map/map.xml",
    "metadataURI" : "CIMPATH=Metadata/a7afc904584d1037910b2cfe65fe94f8.xml",
    "useSourceMetadata" : true,
    "illumination" : {
    "layers" : [
    "standaloneTables" : [
    "defaultViewingMode" : "Map",
    "mapType" : "Map",
    "datumTransforms" : [
    "defaultExtent" : {
    "elevationSurfaces" : [
    "spatialReference" : {
      "wkid" : 4326,
      "latestWkid" : 4326
    },

Если вы попытаетесь изменить только свойства wkid и latestWkid для карты, вы не получите ожидаемого результата. Это происходит потому, что многие другие части приложения связаны с пространственной привязкой, и необходимо внести множество изменений в CIM карты, чтобы итоговое изменение работало корректно. Например, изменение пространственной привязки также влияет на преобразования датума, число экстентов, вырезание геометрии и т.д. Этот тип операции необходимо выполнять в приложении или с помощью управляемого API, где будут произведены все нужные изменения. Вот почему мы продолжим расширять API arcpy.mp. В этом сценарии вместо использования CIM следует использовать свойство spatialReference класса Карта.

Пример 2: Не доводите до сбоя в работе приложения

В данном примере, механизм построения выражения, связанный со свойствами надписей слоя, изменяется в CIM. На снимке экрана ниже, механизм построения выражения изменяется с Arcade (заданного по умолчанию) на Python. Ниже представлены графики, как это выглядит в UI и в разделе JSON.

Панель выражения Класса надписей для векторного слоя

      "labelClasses" : [
        {
          "type" : "CIMLabelClass",
          "expression" : "$feature.NAME",
          "expressionEngine" : "Arcade",
          "featuresToLabel" : "AllVisibleFeatures",
          "maplexLabelPlacementProperties" : {
          "name" : "Class 1",
          "priority" : -1,
          "standardLabelPlacementProperties" : {
          "textSymbol" : {
          "useCodedValue" : true,
          "visibility" : true,
          "iD" : -1
        }

Далее представлен скрипт Python, который изменяет только expressionEngine, но не expression. Это приведет к хаотичным сбоям в работе приложения. Например, после запуска кода, приведенного ниже, когда вы просмотрите свойства надписей для слоя, expressionEngine корректно установлен на Python, но expression все еще использует формат Arcade. В пользовательском интерфейсе, когда вы изменяете механизм выражения обратно на Arcade, выражение отображается в формате Python, в противоположность тому, что должно происходить. Чтобы избежать этого, важно обновить оба свойства expressionEngine и expression.

# Update the label expression engine from Arcade, the default, to Python.
# You must also update the expression otherwise the UI won't behave correctly after.

p = arcpy.mp.ArcGISProject('current')
m = p.listMaps('Map')[0]
l = m.listLayers()[0]

l_cim = l.getDefinition('V3')

lc = l_cim.labelClasses[0]
lc.expressionEngine = 'Python'    #From 'Arcade'
lc.expression = '[STATE_NAME]'    #From '$feature.STATE_NAME'

l.setDefinition(l_cim)

Советы по изменению свойств CIM

Иногда надпись свойства в файле JSON найти непросто, поскольку она не интуитивно понятна, не соответствует приложению или может быть вложена в модель объекта. Один из способов решения этой проблемы - установить действительно уникальное значение и выполнить поиск по этому значению в файле JSON. Например, вы задаете ширину элемента компоновки равным 0.7777 или цвет RGB равным 111.

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

Еще один совет - создать файл JSON до и после изменения и сравнить изменения с помощью такого приложения, как WinMerge, DeltaJSON, JSON Diff или других. Это важно при создании классов CIM, которые обсуждаются далее в следующем разделе.

Раздел справки ArcGIS.Core.CIM Namespace .NET SDK API предоставляет список объектов CIM и документацию для каждого участника класса.

Создание классов CIM

В версиях ниже 2.5 API Python CIM позволял изменять только существующие свойства. Начиная с версии 2.5 появляется новая техника для создания классов CIM, которую можно использовать для разворачивания возможностей, представленных в определении объекта CIM. Имя этой функции звучит как CreateCIMObjectFromClassName и находится в модуле arcpy.cim. Параметр cim_class_name - это имя класса CIM, как оно указано в свойстве type, и параметр cim_version используется так же, как и функция getDefiniton, определенная в разделах выше. CreateCIMObjectFromClassName будет возвращать объект, который содержит соответствующих участников, но не будет автоматически создавать дополнительные зависимости для объекта. Например, если у созданного класса есть участник, для которого нужен другой класс и его значение, нужно выполнить функцию снова, чтобы также создать тот класс. Этот процесс должен выполняться для всех зависимых классов CIM и подклассов и может потребовать значительных усилий. Это будет рассмотрено в последующих примерах, но вы можете начать с простого сценария, в котором для выполнения решения необходимо создать только один объект.

Пример 1: Один класс - цвет RGB

Если вы вставляете новую карту по умолчанию и возвращаете CIM карты backgroundColor, то значение будет NoneType, поскольку это значение не определено в CIM. По умолчанию новый фоновый цвет задается как Нет цвета. Вы даже не увидите, что этот участник определен в файле JSON. Простой способ определить, что именно должно быть выполнено, это создать карту с фоновым цветом и сохранить изменения до и после в виде файлов карт (.mapx), а затем сравнить различия. На рисунке ниже слева показан снимок экрана JSON до внесения изменений, а справа - после изменений. Их просматривает приложение для сравнения текста WinMerge. Обратите внимание, что справа участник backgroundColor был вставлен между свойствами mapType и bookmarks. Чтобы увидеть backgroundColor для карты, нужно создать объект цвета; в данном примере это объект CIMRGBColor. Цвет мог бы быть определен с использованием других цветовых моделей, и объект CIM type, представляющий тип класса, был бы другим.

Скриншот результатов до и после вставки фонового цвета в файл JSON.

Следующий код создаст класс CIMRGBColor, который используется для настройки фонового цвета карты. Обратите внимание, что вам может потребоваться отключить базовую карту или другие слои, чтобы увидеть изменения.

p = arcpy.mp.ArcGISProject('current')
m = p.listMaps()[0]
m_cim = m.getDefinition('V3')        #Get the map's CIM definition

#Check to see if a background color exists, if not create a color
if m_cim.backgroundColor is None:
  RGBColor = arcpy.cim.CreateCIMObjectFromClassName('CIMRGBColor', 'V3')
  RGBColor.values = [115, 178, 255, 100]
  m_cim.backgroundColor = RGBColor   #Set the property to the new object
m.setDefinition(m_cim)               #Set the map's CIM definition

Пример 2: Один класс - Пространственные серии карт

Создание пространственных серий карт с помощью CIM такая же простая задача, что и создание цвета RGB. Пространственная серия карт - это также один объект, но свойств у него больше. На рисунке ниже показан скриншот того, как выглядит пространственная серия карт в файле JSON. indexLayerURI - это концепт, который является уникальным в CIM и который не выделяется как настройка в пользовательском интерфейсе. URI - это уникальный идентификатор для объекта, представленного в CIM для гаранта уникальной ссылки. В случае с данным слоем URI не поменяется, даже если вы измените имя слоя. Единственный способ получения значения indexLayerURI - через определение CIM слоя.

Скриншот результатов пространственных серий карт вставляется в файл JSON.

Следующий код создаст класс CIMSpatialMapSeries, который используется для создания новых пространственных серий карт в компоновке. Этот пример включает дополнительные строки кода для получения значения URI. Обратите внимание, что функции get/setDefinition нужно будет вызвать повторно, чтобы убедиться, что вкладка Страницы серии карт обновилась на панели Содержание.

p = arcpy.mp.ArcGISProject('current')
m = p.listMaps('GreatLakes')[0]
l = m.listLayers('GreatLakes')[0]

l_cim = l.getDefinition('V3')         #Get layer's CIM / Layer URI
lURI = l_cim.uRI                      #Needed to specific the index layer 

lyt = p.listLayouts('GreatLakes')[0]
lyt_cim = lyt.getDefinition('V3')     #Get Layout's CIM definition

#Create CIM Spatial Map Series Object and populate its properties
ms = arcpy.cim.CreateCIMObjectFromClassName('CIMSpatialMapSeries', 'V3')
ms.enabled = True
ms.mapFrameName = "Great Lakes MF"
ms.startingPageNumber = 1
ms.currentPageID = 2
ms.indexLayerURI = lURI               #Index layer URI from Layer's CIM 
ms.nameField = "NAME"
ms.sortField = "NAME"
ms.sortAscending = True
ms.scaleRounding = 1000
ms.extentOptions = "BestFit"
ms.marginType = "Percent"
ms.margin = 10

lyt_cim.mapSeries = ms                #Set new map series to layout
lyt.setDefinition(lyt_cim)            #Set the Layout's CIM definition

#Force a refresh of the layout and its associated panes
lyt_cim = lyt.getDefinition('V3')
lyt.setDefinition(lyt_cim)

Пример 3: Один зависимый класс - Серии карт с закладкой

В двух предыдущих примерах были показаны классы CIM с простыми свойствами. Как было упомянуто выше, бывают случаи, когда у нового созданного объекта CIM есть значения свойств, которые зависят от других объектов CIM. Функция CreateCIMObjectFromClassName автоматически не сможет создать все зависимые подклассы, так что от вас будет зависеть, следует ли использовать тот же метод для создания зависимых классов объектов. Класс CIMBookmarkMapSeries является примером. У него есть свойство с именем pages, которое представляет собой набор отдельных объектов CIMBookmarkMapSeriesPage. Ниже приведен скриншот того, как Серия карт с закладками представлена в файле JSON. Вы должны обратить внимание, что класс CIMBookmarkMapSeriesPage вложен внутрь класса CIMBookmarkMapSeries. Каждая страница определяется своим bookmarkName, а у каждой закладки есть свой mapURI, который является уникальным идентификатором для карты, с которой связана закладка.

Скриншот результатов серий карт закладок, вставленных в файл JSON.

Код ниже создаст класс CIMBookmarkMapSeries и несколько классов CIMBookmarkMapSeriesPage, которые используются для создания новой Серии карт с закладками в компоновке. Для серий карт с закладками необходима ссылка на URI карт и аналогичные URI для слоев, поэтому это значение вы сможете получить только из определения карты CIM.

p = arcpy.mp.ArcGISProject('current')
m = p.listMaps('Map')[0]

#Get map URI from map CIM
m_cim = m.getDefinition('V3')           
mURI = m_cim.uRI

#Get Layout CIM
lyt = p.listLayouts('No Map Series')[0] 
lyt_cim = lyt.getDefinition('V3')

#Create the CIMBookmarkMapSeriesPage(s) necessary for creating the CIMBookmarkMapSeries
#Iterate through each bookmark in the order you want them added to the map series
bkmkNames = ["Northeast", "Southeast", "Northwest",]  
pageList = []
for bkmk in bkmkNames:
  bkmkPage = arcpy.cim.CreateCIMObjectFromClassName('CIMBookmarkMapSeriesPage', 'V3')
  bkmkPage.bookmarkName = bkmk
  bkmkPage.mapURI = mURI                
  pageList.append(bkmkPage)             

#Create a Bookmark Object and populate
bmMS = arcpy.cim.CreateCIMObjectFromClassName('CIMBookmarkMapSeries', 'V3')
bmMS.enabled = True
bmMS.mapFrameName = "Map Frame"
bmMS.startingPageNumber = 1
bmMS.currentPageID = 0
bmMS.extentOptions = "BestFit"
bmMS.marginType = "Percent"
bmMS.pages = pageList                   

#Set map series to layout and set the layout CIM definition
lyt_cim.mapSeries = bmMS                
lyt.setDefinition(lyt_cim)              

#Force a refresh of the layout and its associated panes
lyt_cim = lyt.getDefinition('V3')
lyt.setDefinition(lyt_cim)

Пример 4: Несколько зависимых классов - Создание полигонального элемента в компоновке

В зависимости от объекта, который вы пытаетесь создать, количество вложенных зависимых объектов может стать большим. Если это возможно, рекомендуется использовать управляемый API, прежде чем пытаться создавать объекты с помощью CIM. Следующий пример кода представляет собой типичный сценарий. Вы увидите, что создание чего-то простого вроде полигонального элемента в компоновке, оказывается имеет множество зависимых объектов. И снова, простой подход - это создать простую компоновку с одним полигональным элементом и экспортировать компоновку в файл .pagx, а затем посмотреть структуру JSON. Другой подход к решению этих более сложных сценариев - построение объектов в обратном порядке. Начните с наиболее вложенных зависимых подклассов и работайте этим способом, чтобы завершить итоговый объект. Ниже приведен пример кода.

Следующий код создает полигон CIMGraphicElement в компоновке. Он использует CreateCIMObjectFromClassName несколько раз для всех зависимых классов CIM, необходимых для построения полигональных графических элементов.

# A simplified JSON structure outlining the objects needed to be generated for
# creating a polygon graphic element on a layout.
#
# CIMGraphicElement
#   CIMPolygonGraphic
#     CIMSymbolReference
#       CIMPolygonSymbol
#         CIMSolidStroke
#           CIMRGBColor
#         CIMSolidFill
#           CIMRGBColor

#Reference layout and its CIM definition
p = arcpy.mp.ArcGISProject('current')
lyt = p.listLayouts()[0]
lyt_cim = lyt.getDefinition('V3')       

#CIMSolidStoke/CIMRGBColor 
strokeRGBColor = arcpy.cim.CreateCIMObjectFromClassName('CIMRGBColor', 'V3')
strokeRGBColor.values = [0, 0, 0, 100]

symLyr1 = arcpy.cim.CreateCIMObjectFromClassName('CIMSolidStroke', 'V3')
symLyr1.capStyle = "Round"
symLyr1.joinStyle = "Round"
symLyr1.width = 1
symLyr1.color = strokeRGBColor

#CIMSolidFill/CIMRGBColor
fillRGBColor = arcpy.cim.CreateCIMObjectFromClassName('CIMRGBColor', 'V3')
fillRGBColor.values = [130, 130, 130, 100] 

symLyr2 = arcpy.cim.CreateCIMObjectFromClassName('CIMSolidFill', 'V3')
symLyr2.color = fillRGBColor

#CIMPolygonSymbol
polySym = arcpy.cim.CreateCIMObjectFromClassName('CIMPolygonSymbol', 'V3')
polySym.symbolLayers = [symLyr1, symLyr2]

#CIMSymbolReference
symRef = arcpy.cim.CreateCIMObjectFromClassName('CIMSymbolReference', 'V3')
symRef.symbol = polySym

#CIMPolygonGraphic
polyGraphic = arcpy.cim.CreateCIMObjectFromClassName('CIMPolygonGraphic', 'V3')
polyGraphic.symbol = symRef
polyGraphic.blendingMode = "Alpha"
polyGraphic.placement = "Unspecified"
polyGraphic.polygon =  {"hasZ": True, "rings": [[[5, 6, None], [1, 6, None], [1, 9, None], [5, 9, None], [5, 6, None]]]}

#CIMGraphicElement
graphicElm = arcpy.cim.CreateCIMObjectFromClassName('CIMGraphicElement', 'V3')
graphicElm.anchor = "BottomLeftCorner"
graphicElm.name = "New Rectangle Graphic"
graphicElm.visible = True
graphicElm.rotationCenter = {"x" : 1, "y": 6}
graphicElm.graphic = polyGraphic

#Add element to EMPTY layout element list and set CIM definition
lyt_cim.elements = [graphicElm]
lyt.setDefinition(lyt_cim)

Дополнительные материалы и примеры скриптов

Подробную спецификацию CIM и репозитория GIT см. в статье Картографическое информационное моделирование (cim-spec).

Подборку из 30 архивов с примерами скриптов, которые используют Python CIM в различных вариациях, см. по ссылке Примеры ArcGIS Pro Python CIM.

Подход Python CIM также может быть полезен в процессах обновления источников данных. Больше примеров по CIM см. в статье справки Обновление и исправление источников данных.