Доступ к Python CIM

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

Внимание:

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

Что такое CIM?

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

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

Важно понимать структуру JSON объекта, чтобы успешно изменять определение CIM для любого объекта. Структура JSON соответствует объектной модели CIM, раскрытой в модуле arcpy.mp. Точками доступа являются классы arcpy.mp Слой, Таблица, Карта, Компоновка и Отчет. Каждый из этих классов может быть сохранен в формате файла JSON: .lyrx, .mapx и .pagx. Вы можете открывать эти файлы в редакторе и просматривать метод организации и хранения информации.

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

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

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

После того, как описание CIM будет возвращено, вы можете попробовать перемещаться по её структуре, просматривая полный код и его интеллектуальный смысл, или вы можете использовать целый ряд выражений Python, чтобы узнать больше о доступных атрибутах. Полезной технологией изучения способов организации атрибутов и перемещения по объектной модели CIM для каждого класса является создание соответствующего файла экспорта. Например, карта может быть сохранена в файл .mapx, слой — в файл .lyrx, а компоновка — в файл .pagx. Все эти файлы экспорта используют формат JSON и могут быть просмотрены в редакторе. Иногда ваш редактор может отображать лучшее форматирование, если вы измените расширение файла *.***x на *.json.

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

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

{
  "type" : "CIMLayerDocument",
  "version" : "2.4.0",
  "layers" : [
    "CIMPATH=map/greatlakes.json"
  ],
  "layerDefinitions" : [
    {
      "type" : "CIMFeatureLayer",
      "name" : "GreatLakes",
      "uRI" : "CIMPATH=map/greatlakes.json",
      "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. Например, ваше завершение кода должно отображать дополнительные атрибуты, такие как expanded или showMapTips, но, поскольку их значение в текущий момент задано как false, они не появляются в файле JSON, приведенном выше.

Следующий скрипт 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('V2')

# 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()

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

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

Этот пример углубляется в объектную модель на один уровень ниже, чем корневой. Если участник featureTable в примере JSON выше будет развернут, то вы увидите дополнительных участников. У класса CIMFeatureTable есть участник с именем 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. Это нельзя выполнить с использованием управляемых 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('V2')

# 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)

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

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

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

Это отредактированный и упрощенный файл JSON, который отображает только информацию о методе отображения для символов слоя. Метод отображения renderer имеет symbol с типом CIMSimpleRenderer и у него есть 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('V2')

# 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.json",
    "metadataURI" : "CIMPATH=Metadata/a7afc904584d1037910b2cfe65fe94f8.json",
    "useSourceMetadata" : true,
    "illumination" : {
    "layers" : [
    "standaloneTables" : [
    "defaultViewingMode" : "Map",
    "mapType" : "Map",
    "datumTransforms" : [
    "defaultExtent" : {
    "elevationSurfaces" : [
    "spatialReference" : {
      "wkid" : 4326,
      "latestWkid" : 4326
    },

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

Пример 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()

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

l.setDefinition(l_cim)

Подсказки

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

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

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

Создание объектов CIM

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

Примечание:

Модуль arcpy.cim не импортируется автоматически. При необходимости методы getDefinition и setDefinition импортируют модуль CIM. Если вы вызывали CreateCIMObjectFromClassName раньше getDefinition или setDefinition, нужно точно импортировать модуль CIM.

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

Если вы вставляете новую карту по умолчанию и возвращаете Map's CIM backgroundColor, то значение будет NoneType, поскольку это значение не определено в CIM. По умолчанию новый фоновый цвет задается как "Нет цвета". Вы даже не увидите, что этот участник определен в файле JSON. Простой способ определить, что именно должно быть выполнено, это создать карту с фоновым цветом и сохранить изменения до и после в виде файлов карт (.mapx), а затем сравнить различия. На рисунке ниже представлен скриншот изменений до JSON слева и после — справа. Обратите внимание, что справа участник backgroundColor был вставлен между участниками mapType и bookmarks. Объект CIM представлен в JSON в двойных кавычках; он начинается с букв CIM и завершается словом "type". Чтобы увидеть 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 см. в статье справки Обновление и исправление источников данных.